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