1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 1999 by Sun Microsystems, Inc. 23 * All rights reserved. 24 * 25 */ 26 27 // ServiceURL.java : The service URL. 28 // Author: James Kempf, Erik Guttman 29 // 30 31 package com.sun.slp; 32 33 import java.util.*; 34 import java.io.*; 35 import java.net.*; 36 37 /** 38 * The ServiceURL object models the SLP service URL. Both service: URLs 39 * and regular URLs are handled by this class. 40 * 41 * @author James Kempf, Erik Guttman 42 */ 43 44 public class ServiceURL extends Object implements Serializable { 45 46 // Recognized transports. 47 48 private final static String IPX = "ipx"; 49 private final static String AT = "at"; 50 51 /** 52 * Indicates that no port information is required or was returned 53 * for this service URL. 54 */ 55 56 public static final int NO_PORT = 0; 57 58 /** 59 * No life time parameter is given. 60 */ 61 62 public static final int LIFETIME_NONE = 0; 63 64 /** 65 * Default lifetime, 3 hours. 66 */ 67 68 public static final int LIFETIME_DEFAULT = 10800; 69 70 /** 71 * Maximum lifetime, approximately 18 hours. 72 */ 73 74 public static final int LIFETIME_MAXIMUM = 0xFFFF; 75 76 /** 77 * Reregister periodically. 78 */ 79 80 public static final int LIFETIME_PERMANENT = -1; 81 82 // Maximum port size. 83 84 static final int PORT_MAXIMUM = 0xFFFF; 85 86 87 // 88 // data fields 89 // 90 91 private ServiceType serviceType = null; 92 private ServiceType originalServiceType = null; 93 private String transport = ""; 94 private String host = ""; 95 private int port = NO_PORT; 96 private String URLPath = ""; 97 private int lifetime = LIFETIME_DEFAULT; 98 private boolean isPermanent = false; 99 private boolean noDoubleSlash = false; 100 101 /** 102 * Construct a service URL object. 103 * 104 * @param URL The service URL as a string. 105 * @param iLifetime The service advertisement lifetime. 106 * @exception IllegalArgumentException Thrown if parse 107 * errors occur in the 108 * parameter. 109 */ 110 111 public ServiceURL(String URL, int iLifetime) 112 throws IllegalArgumentException { 113 114 Assert.nonNullParameter(URL, "URL"); 115 116 if ((iLifetime > LIFETIME_MAXIMUM) || 117 (iLifetime < LIFETIME_PERMANENT)) { 118 throw 119 new IllegalArgumentException( 120 SLPConfig.getSLPConfig().formatMessage("lifetime_error", 121 new Object[0])); 122 } 123 124 checkURLString(URL); 125 parseURL(URL); 126 127 if (iLifetime == LIFETIME_PERMANENT) { 128 isPermanent = true; 129 iLifetime = LIFETIME_MAXIMUM; 130 131 } 132 133 lifetime = iLifetime; 134 } 135 136 // 137 // ------------------------------------------------------------------ 138 // Accessors 139 // ------------------------------------------------------------------ 140 // 141 142 /** 143 * @return The service type name. 144 */ 145 146 public ServiceType getServiceType() { 147 return serviceType; 148 149 } 150 151 /** 152 * Set service type and naming authority if this is not a service: URL. 153 * 154 * @param type The new ServiceType object. 155 * @exception IllegalArgumentException If the service type name or 156 * naming authority name is invalid. 157 */ 158 159 public void setServiceType(ServiceType type) { 160 if (!serviceType.isServiceURL()) { 161 serviceType = type; 162 163 } 164 165 } 166 167 /** 168 * @return The machine name or IP address. 169 */ 170 171 public String getHost() { 172 return host; 173 174 } 175 176 /** 177 * @return The port number, if any. 178 */ 179 180 public int getPort() { 181 return port; 182 183 } 184 185 /** 186 * @return The URL path description, if any. 187 */ 188 189 public String getURLPath() { 190 return URLPath; 191 192 } 193 194 /** 195 * @return The service advertisement lifetime. 196 */ 197 198 public int getLifetime() { 199 return lifetime; 200 201 } 202 203 /** 204 * Formats the service URL into standard URL form. 205 * 206 * @return Formatted string with the service URL. 207 */ 208 209 public String toString() { // Overrides Object.toString(); 210 211 return 212 originalServiceType.toString() + 213 ":/" + transport + (noDoubleSlash == false ? "/":"") + 214 host + (port != NO_PORT ? (":" + port) : "") + 215 URLPath; 216 217 } 218 219 public int hashCode() { 220 return 221 serviceType.hashCode() + 222 transport.hashCode() + 223 host.hashCode() + 224 port + 225 URLPath.hashCode(); 226 } 227 228 public boolean equals(Object obj) { 229 230 if (obj == this) { 231 return true; 232 233 } 234 235 if (!(obj instanceof ServiceURL)) { 236 return false; 237 } 238 239 ServiceURL surl = (ServiceURL)obj; 240 241 return 242 serviceType.equals(surl.serviceType) && 243 transport.equals(surl.transport) && 244 host.equals(surl.host) && 245 (port == surl.port) && 246 (noDoubleSlash == surl.noDoubleSlash) && 247 URLPath.equals(surl.URLPath); 248 249 } 250 251 // Return permanent status. 252 253 boolean getIsPermanent() { 254 return isPermanent; 255 256 } 257 258 // Check URL characters for correctness. 259 260 private void checkURLString(String s) 261 throws IllegalArgumentException { 262 for (int i = 0; i < s.length(); i++) { 263 char c = s.charAt(i); 264 // allowed by RFC1738 265 if (c == '/' || c == ':' || c == '-' || c == ':' || 266 c == '.' || c == '%' || c == '_' || c == '\'' || 267 c == '*' || c == '(' || c == ')' || c == '$' || 268 c == '!' || c == ',' || c == '+' || c == '\\') { 269 // defer to Windows 270 continue; 271 272 } 273 274 // reserved by RFC1738, and thus allowed, pg. 20 275 if (c == ';' || c == '@' || c == '?' || c == '&' || c == '=') { 276 continue; 277 278 } 279 280 if (Character.isLetterOrDigit(c)) { 281 continue; 282 } 283 284 SLPConfig conf = SLPConfig.getSLPConfig(); 285 286 throw 287 new IllegalArgumentException( 288 conf.formatMessage("url_char_error", 289 new Object[] { 290 new Character(c)})); 291 } 292 } 293 294 // Parse the incoming service URL specification. 295 296 private void parseURL(String sURL) 297 throws IllegalArgumentException { 298 299 StringTokenizer st = new StringTokenizer(sURL, "/", true); 300 301 try { 302 303 // This loop is a kludgy way to break out of the parse so 304 // we only throw at one location in the code. 305 306 do { 307 String typeName = st.nextToken(); 308 309 // First token must be service type name. 310 311 if (typeName.equals("/")) { 312 break; // error! 313 314 } 315 316 // Check for colon terminator, not part of service 317 // type name. 318 319 if (!typeName.endsWith(":")) { 320 break; // error! 321 322 } 323 324 // Create service type, remove trailing colon. 325 326 serviceType = 327 new ServiceType(typeName.substring(0, 328 typeName.length() - 1)); 329 originalServiceType = serviceType; 330 331 // Separator between service type name and transport. 332 333 String slash1 = st.nextToken(); 334 335 if (!slash1.equals("/")) { 336 break; // error! 337 338 } 339 340 String slash2 = st.nextToken(); 341 342 String sAddr = ""; // address... 343 344 // Check for abstract type or alternate transport. 345 346 if (!slash2.equals("/")) { 347 348 // If this is an abstract type, then we could have 349 // something like: service:file-printer:file:/foo/bar. 350 // This is OK. Also, if this is a non-service: URL, 351 // something like file:/foo/bar is OK. 352 353 if (!serviceType.isServiceURL()) { 354 sAddr = slash2; 355 356 noDoubleSlash = true; 357 358 } else { 359 360 // We only recognize IPX and Appletalk at this point. 361 362 if (!slash2.equalsIgnoreCase(IPX) && 363 !slash2.equalsIgnoreCase(AT)) { 364 365 // Abstract type is OK. We must check here because 366 // something like 367 // service:printing:lpr:/ipx/foo/bar 368 // is allowed. 369 370 if (serviceType.isAbstractType()) { 371 sAddr = slash2; 372 373 noDoubleSlash = true; 374 375 } else { 376 377 break; // error! 378 379 } 380 } else { 381 382 transport = slash2.toLowerCase(); 383 384 // Check for separator between transport and host. 385 386 if (!st.nextToken().equals("/")) { 387 break; // error! 388 389 } 390 391 sAddr = st.nextToken(); 392 } 393 } 394 } else { 395 396 // Not abstract type, no alternate transport. Get host. 397 398 sAddr = st.nextToken(); 399 400 } 401 402 if (sAddr.equals("/")) {// no host part 403 URLPath = "/" + st.nextToken(""); 404 return; // we're done! 405 406 } 407 408 host = sAddr; 409 410 // Need to check for port number if this is an IP transport. 411 412 if (transport.equals("")) { 413 StringTokenizer tk = new StringTokenizer(host, ":"); 414 415 host = tk.nextToken(); 416 417 // Get port if any. 418 419 if (tk.hasMoreTokens()) { 420 String p = tk.nextToken(); 421 422 if (tk.hasMoreTokens()) { 423 break; // error! 424 425 } 426 427 try { 428 429 port = Integer.parseInt(p); 430 431 } catch (NumberFormatException ex) { 432 break; // error! 433 434 } 435 436 if (port <= 0 || port > PORT_MAXIMUM) { 437 break; // error! 438 439 } 440 } 441 } 442 443 // 444 // after this point we have to check if there is a token 445 // remaining before we read it: It is legal to stop at any 446 // point now. Before all the tokens were required, so 447 // missing any was an error. 448 // 449 if (st.hasMoreTokens() == false) { 450 // minimal url service:t:// a 451 return; // we're done! 452 453 } 454 455 String sSep = st.nextToken(); 456 457 if (!sSep.equals("/")) { 458 break; // error! 459 460 } 461 462 // there is a URL path 463 // URLPath is all remaining tokens 464 URLPath = sSep; 465 466 if (st.hasMoreTokens()) { 467 URLPath += st.nextToken(""); 468 469 } 470 471 URLPath = URLPath.trim(); 472 473 return; // done! 474 475 } while (false); // done with parse. 476 477 } catch (NoSuchElementException ex) { 478 throw 479 new IllegalArgumentException( 480 SLPConfig.getSLPConfig().formatMessage("url_syntax_error", 481 new Object[] {sURL})); 482 483 } 484 485 // The only way to get here is if there was an error in the 486 // parse. 487 488 throw 489 new IllegalArgumentException( 490 SLPConfig.getSLPConfig().formatMessage("url_syntax_error", 491 new Object[] {sURL})); 492 493 } 494 495 } 496