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:      %W%	%G%
31 //  ServiceStoreFactory.java: Factory for creating ServiceStore objects.
32 //  Author:           James Kempf
33 //  Created On:       Fri Apr 17 12:14:12 1998
34 //  Last Modified By: James Kempf
35 //  Last Modified On: Mon Jan  4 15:26:34 1999
36 //  Update Count:     34
37 //
38 
39 package com.sun.slp;
40 
41 import java.util.*;
42 import java.io.*;
43 
44 /**
45  * The ServiceStoreFactory provides a way to obtain a ServiceStore
46  * object. The exact implementation will depend on how the
47  * DA/slpd is configured. It could be an in-memory database,
48  * a connection to an LDAP server, or a persistent object
49  * database.
50  *
51  * @version %R%.%L% %D%
52  * @author James Kempf
53  */
54 
55 class ServiceStoreFactory extends Object {
56 
57     private static final String DEFAULT_SERVICE_STORE =
58 	"com.sun.slp.ServiceStoreInMemory";
59 
60     private static final String SERVICE_STORE_PROPERTY =
61 	"sun.net.slp.serviceStoreClass";
62 
63     // Comment characters for deserialization.
64 
65     final private static char COMMENT_CHAR1 = '#';
66     final private static char COMMENT_CHAR2 = ';';
67 
68     // Character for URL list separator.
69 
70     final private static String URL_LIST_SEP = ", ";
71 
72     // Identifies scopes pseudo-attribute.
73 
74     final private static String SCOPES_ATTR_ID = "scopes";
75 
76     /**
77      * Return the ServiceStore for the SLP agent.
78      *
79      * @return An object supporting the ServiceStore interface.
80      * @exception ServiceLocationException Thrown
81      *			if the ServiceStore object can't be
82      *			created or if the
83      *			class implementing the ServiceStore required
84      *			a network connnection (for example, an LDAP
85      *			server) and the connection couldn't be made.
86      */
87 
88     static ServiceStore createServiceStore()
89 	throws ServiceLocationException {
90 
91 	return createServiceStoreFromProperty(SERVICE_STORE_PROPERTY);
92 
93     }
94 
95     // Create the appropriate ServiceStore object from the property.
96 
97     private static ServiceStore
98 	createServiceStoreFromProperty(String property)
99 	throws ServiceLocationException {
100 
101 	Properties props = System.getProperties();
102 	String storeClassName =
103 	    props.getProperty(property,
104 			      DEFAULT_SERVICE_STORE);
105 	Class storeClass = null;
106 
107 	try {
108 
109 	    storeClass = Class.forName(storeClassName);
110 
111 	} catch (ClassNotFoundException ex) {
112 
113 	    throw
114 		new ServiceLocationException(
115 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
116 				"ssf_no_class",
117 				new Object[] {storeClassName});
118 	}
119 
120 	ServiceStore store = null;
121 
122 	try {
123 
124 	    store = (ServiceStore)storeClass.newInstance();
125 
126 	} catch (InstantiationException ex) {
127 
128 	    throw
129 		new ServiceLocationException(
130 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
131 				"ssf_inst_ex",
132 				new Object[] {
133 		    storeClassName,
134 			ex.getMessage()});
135 
136 	} catch (IllegalAccessException ex) {
137 
138 	    throw
139 		new ServiceLocationException(
140 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
141 				"ssf_ill_ex",
142 				new Object[] {
143 		    storeClassName,
144 			ex.getMessage()});
145 
146 	} catch (ClassCastException ex) {
147 
148 	    throw
149 		new ServiceLocationException(
150 				ServiceLocationException.INTERNAL_SYSTEM_ERROR,
151 				"ssf_class_cast",
152 				new Object[] {storeClassName});
153 	}
154 
155 	return store;
156     }
157 
158     /**
159      * Deserialize a service store from the open stream.
160      *
161      * @param is The object input stream for the service store.
162      * @return ServiceStore deserialized from the stream.
163      * @exception ServiceLocationException If anything goes
164      *				wrong with the deserialization.
165      */
166 
167     static ServiceStore deserializeServiceStore(BufferedReader is)
168 	throws ServiceLocationException {
169 
170 	ServiceStore ss = new ServiceStoreInMemory();
171 
172 	try {
173 
174 	    deserialize(is, ss);
175 
176 	} catch (IOException ex) {
177 	    throw
178 		new ServiceLocationException(
179 				ServiceLocationException.PARSE_ERROR,
180 				"ssf_io_deser",
181 				new Object[] {ex.getMessage()});
182 
183 	}
184 
185 	return ss;
186     }
187 
188     // Read the service store in the standard format from the input
189 
190     private static void deserialize(BufferedReader in, ServiceStore store)
191 	throws IOException, ServiceLocationException {
192 
193 	SLPConfig conf = SLPConfig.getSLPConfig();
194 	int linecount = 0;
195 	int scopeLinenum = 0;
196 
197 	// Parse input file until no bytes left.
198 
199 	while (in.ready()) {
200 	    linecount++;
201 	    String line = in.readLine().trim();
202 
203 	    // Skip any empty lines at this level.
204 
205 	    if (line.length() <= 0) {
206 		continue;
207 
208 	    }
209 
210 	    char cc = line.charAt(0);
211 
212 	    // If initial character is "#" or ";", ignore the line.
213 	    //  It's a comment. Also if the line is empty.
214 
215 	    if (cc == COMMENT_CHAR1 ||
216 		cc == COMMENT_CHAR2) {
217 		continue;
218 	    }
219 
220 	    // At this level, the line must be a URL registration,
221 	    //  with format:
222 	    //
223 	    // service-url ", " language ", " lifetime [ ", " type ]
224 	    //
225 	    //
226 	    //  We allow arbitrary whitespace around commas.
227 
228 	    StringTokenizer tk = new StringTokenizer(line, URL_LIST_SEP);
229 	    String surl = null;
230 	    String slang = null;
231 	    String slifetime = null;
232 	    String sType = null;
233 
234 	    if (tk.hasMoreTokens()) {
235 		surl = tk.nextToken().trim();
236 
237 		if (tk.hasMoreTokens()) {
238 		    slang = tk.nextToken().trim();
239 
240 		    if (tk.hasMoreTokens()) {
241 			slifetime = tk.nextToken().trim();
242 
243 			if (tk.hasMoreTokens()) {
244 			    sType = tk.nextToken().trim();
245 
246 			    if (tk.hasMoreTokens()) {
247 				slang = null;
248 					// should be nothing more on the line.
249 
250 			    }
251 			}
252 		    }
253 		}
254 	    }
255 
256 	    // Check for errors.
257 
258 	    if (surl == null || slifetime == null || slang == null) {
259 		throw
260 		    new ServiceLocationException(
261 				ServiceLocationException.PARSE_ERROR,
262 				"ssf_not_valid_url",
263 				new Object[] {line});
264 	    }
265 
266 	    // Create the service: URL.
267 
268 	    Locale locale = SLPConfig.langTagToLocale(slang);
269 	    ServiceURL url = null;
270 
271 	    try {
272 
273 		int lifetime = Integer.parseInt(slifetime);
274 
275 		// If lifetime is maximum, then set to LIFETIME_PERMANENT.
276 
277 		if (lifetime == ServiceURL.LIFETIME_MAXIMUM) {
278 		    lifetime = ServiceURL.LIFETIME_PERMANENT;
279 
280 		}
281 
282 		url = new ServiceURL(surl, lifetime);
283 
284 		if (sType != null) {
285 
286 		    // Check if it's OK for this service URL.
287 
288 		    ServiceType utype = url.getServiceType();
289 
290 		    if (utype.isServiceURL()) {
291 			conf.writeLog("ssf_set_servc_err",
292 				      new Object[] {
293 			    surl,
294 				utype});
295 
296 		    } else {
297 			ServiceType t = new ServiceType(sType);
298 
299 			if (!t.isServiceURL() &&
300 			    !t.equals(url.getServiceType())) {
301 			    url.setServiceType(t);
302 
303 			}
304 
305 		    }
306 		}
307 
308 	    } catch (NumberFormatException ex) {
309 		throw
310 		    new ServiceLocationException(
311 				ServiceLocationException.PARSE_ERROR,
312 				"ssf_not_valid_lifetime",
313 				new Object[] {
314 			slifetime, new Integer(linecount)});
315 
316 	    } catch (IllegalArgumentException ex) {
317 		throw
318 		    new ServiceLocationException(
319 				ServiceLocationException.PARSE_ERROR,
320 				"ssf_syntax_err",
321 				new Object[] {
322 			ex.getMessage(), new Integer(linecount)});
323 
324 	    }
325 
326 	    // Get attributes. Format should be:
327 	    //
328 	    //      attr-line    = attr-assign | keyword
329 	    //	attr-assign  = attr-id "=" attrval-list
330 	    //	keyword      = attr-id
331 	    //	attrval-list = attrval | attrval ", " attrval-list
332 
333 	    Vector attrs = new Vector();
334 	    Hashtable ht = new Hashtable();
335 	    ServiceLocationAttribute scopeAttr = null;
336 	    boolean firstLine = true;
337 
338 	    try {
339 		while (in.ready()) {
340 		    linecount++;
341 		    line = in.readLine();
342 
343 		    // Empty line indicates we're done with attributes.
344 
345 		    if (line.length() <= 0) {
346 			break;
347 		    }
348 
349 		    // Format the line for creating. Check whether it's a
350 		    // keyword or not.
351 
352 		    if (line.indexOf("=") != -1) {
353 			line = "(" + line + ")";
354 
355 		    }
356 
357 		    // Create the attribute from the string.
358 
359 		    ServiceLocationAttribute attr =
360 			new ServiceLocationAttribute(line, false);
361 
362 		    // If this is the scope attribute, save until later.
363 
364 		    if (firstLine) {
365 			firstLine = false;
366 
367 			if (attr.getId().equalsIgnoreCase(SCOPES_ATTR_ID)) {
368 			    scopeAttr = attr;
369 			    continue; // do NOT save as a regular attribute.
370 
371 			}
372 		    }
373 
374 		    ServiceLocationAttribute.mergeDuplicateAttributes(attr,
375 								      ht,
376 								      attrs,
377 								      false);
378 
379 		}
380 	    } catch (ServiceLocationException e) {
381 		// tack on the line count
382 		e.makeAddendum(" (line " + linecount + ")");
383 		throw e;
384 
385 	    }
386 
387 	    Vector scopes = null;
388 
389 	    // Use scopes we've been configured with if none.
390 
391 	    if (scopeAttr == null) {
392 		scopes = conf.getSAConfiguredScopes();
393 
394 	    } else {
395 
396 		scopes = (Vector)scopeAttr.getValues();
397 
398 		try {
399 		    // Unescape scope strings.
400 
401 		    SLPHeaderV2.unescapeScopeStrings(scopes);
402 
403 		    // Validate, lower case scope names.
404 
405 		    DATable.validateScopes(scopes, locale);
406 
407 		} catch (ServiceLocationException e) {
408 		    e.makeAddendum(" (line " + scopeLinenum + ")");
409 		    throw e;
410 		}
411 
412 	    }
413 
414 	    // We've got the attributes, the service URL, scope, and
415 	    //  locale, so add a record. Note that any crypto is
416 	    //  added when the registration is actually done.
417 
418 	    store.register(url, attrs, scopes, locale, null, null);
419 
420 	    // Create a CSrvReg for forwarding
421 	    CSrvReg creg = new CSrvReg(true, locale, url, scopes,
422 				       attrs, null, null);
423 
424 	    ServerDATable daTable = ServerDATable.getServerDATable();
425 	    daTable.forwardSAMessage(creg, conf.getLoopback());
426 
427 	}
428     }
429 
430     // Write the service store in the standard format to the output
431     // stream.
432 
433     static void serialize(BufferedWriter out, ServiceStore store)
434 	throws IOException, ServiceLocationException {
435 
436 	Enumeration recs = store.getServiceRecordsByScope(null);
437 
438 	while (recs.hasMoreElements()) {
439 	    ServiceStore.ServiceRecord rec =
440 		(ServiceStore.ServiceRecord)recs.nextElement();
441 	    ServiceURL url = rec.getServiceURL();
442 	    String surl = url.toString();
443 	    Vector attrs = (Vector)rec.getAttrList().clone();
444 	    Locale locale = rec.getLocale();
445 	    Vector scopes = rec.getScopes();
446 	    StringBuffer line = new StringBuffer();
447 
448 	    // Compose the registration line.
449 
450 	    line.append(surl);
451 	    line.append(", ");
452 	    line.append(SLPConfig.localeToLangTag(locale));
453 	    line.append(", ");
454 	    line.append(Integer.toString(url.getLifetime()));
455 
456 	    // Put out the service type and naming authority if the
457 	    //  URL is not a service URL.
458 
459 	    if (!surl.startsWith(Defaults.SERVICE_PREFIX)) {
460 		ServiceType type = url.getServiceType();
461 
462 		line.append(", ");
463 		line.append(type.toString());
464 
465 	    }
466 
467 	    // Write out line.
468 
469 	    out.write(line.toString(), 0, line.length());
470 	    out.newLine();
471 
472 	    // Zero line buffer.
473 
474 	    line.setLength(0);
475 
476 	    // Insert a scope attribute, if the scope isn't simply "DEFAULT".
477 
478 	    if (scopes.size() > 1 &&
479 		!Defaults.DEFAULT_SCOPE.equals((String)scopes.elementAt(0))) {
480 		attrs.insertElementAt(
481 				new ServiceLocationAttribute(SCOPES_ATTR_ID,
482 							     scopes),
483 				0);
484 	    }
485 
486 	    // Write out the attributes.
487 
488 	    int i, n = attrs.size();
489 
490 	    for (i = 0; i < n; i++) {
491 		ServiceLocationAttribute attr =
492 		    (ServiceLocationAttribute)attrs.elementAt(i);
493 		Vector vals = attr.getValues();
494 
495 		line.append(
496 		ServiceLocationAttribute.escapeAttributeString(attr.getId(),
497 							       false));
498 		// Add the escaped values.
499 
500 		if (vals != null) {
501 
502 		    line.append("=");
503 
504 		    int j, m = vals.size();
505 
506 		    for (j = 0; j < m; j++) {
507 			Object v = vals.elementAt(j);
508 
509 			if (j > 0) {
510 			    line.append(", ");
511 
512 			}
513 
514 			line.append(ServiceLocationAttribute.escapeValue(v));
515 
516 		    }
517 		}
518 
519 		out.write(line.toString(), 0, line.length());
520 		out.newLine();
521 
522 		// Zero out string buffer.
523 
524 		line.setLength(0);
525 
526 	    }
527 
528 	    // End of registration.
529 
530 	    out.newLine();
531 	}
532 
533 	out.flush();
534     }
535 
536 }
537