xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/ServiceURL.java (revision e7cbe64f7a72dae5cb44f100db60ca88f3313c65)
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