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 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  */
27 
28 /* $Id: nss.c 166 2006-05-20 05:48:55Z njacobs $ */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <sys/types.h>
38 #include <syslog.h>
39 #include <papi.h>
40 #include <uri.h>
41 #include <papi_impl.h>
42 #ifdef NSS_EMULATION
43 #include <nss-emulation.h>
44 #elif NSS_SOLARIS
45 #include <nss_dbdefs.h>
46 #endif
47 #include <config-site.h>
48 #if defined(__sun) && defined(__SVR4)
49 #include <sys/systeminfo.h>
50 #endif
51 
52 
53 static char *
54 bsdaddr_to_uri(char *bsdaddr)
55 {
56 	char *result = NULL;
57 
58 	if (bsdaddr != NULL) {
59 		char *bsd[3], *tmp, *iter = NULL;
60 		char buf[512];
61 
62 		tmp = strdup(bsdaddr);
63 
64 		bsd[0] = strtok_r(tmp, ":,", &iter);
65 		bsd[1] = strtok_r(NULL, ":,", &iter);
66 		bsd[2] = strtok_r(NULL, ":,", &iter);
67 
68 		snprintf(buf, sizeof (buf), "lpd://%s/%s%s%s", bsd[0], bsd[1],
69 			(bsd[2] != NULL) ? "#" : "",
70 			(bsd[2] != NULL) ? bsd[2] : "");
71 
72 		free(tmp);
73 
74 		result = strdup(buf);
75 	}
76 
77 	return (result);
78 }
79 
80 #if defined(__sun) && defined(__SVR4)
81 /*
82  * This is an awful HACK to force the dynamic PAPI library to use the
83  * lpsched support when the destination apears to be a local lpsched
84  * queue on Solaris.
85  */
86 static void
87 solaris_lpsched_shortcircuit_hack(papi_attribute_t ***list)
88 {
89 	papi_attribute_t *attribute;
90 	uri_t *uri = NULL;
91 	char *printer = NULL;
92 	char hostname[BUFSIZ];
93 	char buf[128], buf2[128];
94 
95 	/* setting this in the calling env can be useful for debugging */
96 	if (getenv("DISABLE_LPSCHED_SHORTCIRCUIT") != NULL)
97 		return;
98 
99 	papiAttributeListGetString(*list, NULL,
100 				"printer-uri-supported", &printer);
101 	if (uri_from_string(printer, &uri) < 0)
102 		return;
103 
104 	/* already an lpsched URI ? */
105 	if (strcasecmp(uri->scheme, "lpsched") == 0)
106 		return;
107 
108 	sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
109 	if ((uri->host != NULL) &&
110 	    (strncasecmp(uri->host, hostname, strlen(hostname)) != 0) &&
111 	    (strncasecmp(uri->host, "localhost", 10) != 0))
112 		return;
113 
114 	if ((printer = strrchr(uri->path, '/')) == NULL)
115 		printer = uri->path;
116 	else
117 		printer++;
118 
119 	/* is there an lpsched queue (printer/class) */
120 	snprintf(buf, sizeof (buf), "/etc/lp/interfaces/%s", printer);
121 	snprintf(buf2, sizeof (buf2), "/etc/lp/classes/%s", printer);
122 	if ((access(buf, F_OK) < 0) && (access(buf2, F_OK) < 0))
123 		return;
124 
125 	snprintf(buf, sizeof (buf), "lpsched://localhost/printers/%s", printer);
126 	papiAttributeListAddString(list, PAPI_ATTR_REPLACE,
127 			"printer-uri-supported", buf);
128 }
129 #endif
130 
131 static void
132 fill_printer_uri_supported(papi_attribute_t ***list)
133 {
134 	papi_attribute_t *attribute;
135 	char *string = NULL;
136 
137 	/* do we have a printer-uri-supported */
138 	attribute = papiAttributeListFind(*list, "printer-uri-supported");
139 	if (attribute != NULL) /* we have what we need, return */
140 		return;
141 
142 	/* do we have a printer-uri to rename */
143 	attribute = papiAttributeListFind(*list, "printer-uri");
144 	if (attribute != NULL) { /* rename it in place and return */
145 		free(attribute->name);
146 		attribute->name = strdup("printer-uri-supported");
147 		return;
148 	}
149 
150 	/* do we have a printers.conf(4) "bsdaddr" to convert */
151 	papiAttributeListGetString(*list, NULL, "bsdaddr", &string);
152 	if (string != NULL) { /* parse it, convert it, add it */
153 		char *uri = bsdaddr_to_uri(string);
154 
155 		if (uri != NULL) {
156 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
157 					"printer-uri-supported", uri);
158 			papiAttributeListDelete(list, "bsdaddr");
159 			free(uri);
160 			return;
161 		}
162 	}
163 
164 	/* do we have a printers.conf(4) "rm" (and "rp") to convert */
165 	papiAttributeListGetString(*list, NULL, "rm", &string);
166 	if (string != NULL) {
167 		char *rp = NULL;
168 
169 		/* default to "printer-name", but use "rp" if we have it */
170 		papiAttributeListGetString(*list, NULL, "printer-name", &rp);
171 		papiAttributeListGetString(*list, NULL, "rp", &rp);
172 
173 		if (rp != NULL) { /* fill in the uri if we have the data */
174 			char buf[BUFSIZ];
175 
176 			snprintf(buf, sizeof (buf), "lpd://%s/printers/%s",
177 				string, rp);
178 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
179 					"printer-uri-supported", strdup(buf));
180 			return;
181 		}
182 	}
183 
184 	/* if were are here, we don't have a printer-uri-supported */
185 }
186 
187 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC
188 static void
189 fill_printer_uri(papi_attribute_t ***list)
190 {
191 	papi_attribute_t *attribute;
192 	char *uri = NULL;
193 
194 	if ((list == NULL) || (*list == NULL))
195 		return;
196 
197 	/* do we have a printer-uri-supported */
198 	attribute = papiAttributeListFind(*list, "printer-uri");
199 	if (attribute != NULL) /* we have what we need, return */
200 		return;
201 
202 	/*
203 	 * this is sufficient to fool libgnomeprintpapi, but not promote it's
204 	 * use in the future.
205 	 */
206 	papiAttributeListAddString(list, PAPI_ATTR_EXCL, "printer-uri",
207 			"broken printer-uri semantic");
208 }
209 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */
210 
211 static void
212 cvt_all_to_member_names(papi_attribute_t ***list)
213 {
214 	papi_status_t status;
215 	void *iter = NULL;
216 	char *string = NULL;
217 
218 	papiAttributeListGetString(*list, NULL, "member-names", &string);
219 	if (string != NULL) /* already have a member-names */
220 		return;
221 
222 	for (status = papiAttributeListGetString(*list, &iter, "all", &string);
223 	     status == PAPI_OK;
224 	     status = papiAttributeListGetString(*list, &iter, NULL, &string)) {
225 		char *s_iter = NULL, *value, *tmp = strdup(string);
226 
227 		for (value = strtok_r(tmp, ", \t", &s_iter);
228 		     value != NULL;
229 		     value = strtok_r(NULL, ", \t", &s_iter))
230 			papiAttributeListAddString(list, PAPI_ATTR_APPEND,
231 					"member-names", value);
232 		free(tmp);
233 	}
234 }
235 
236 static papi_attribute_t **
237 _cvt_nss_entry_to_printer(char *entry)
238 {
239 	char    *key = NULL,
240 		*cp,
241 		buf[BUFSIZ];
242 	int in_namelist = 1, buf_pos = 0;
243 	papi_attribute_t **list = NULL;
244 
245 	if (entry == NULL)
246 		return (NULL);
247 
248 	memset(buf, 0, sizeof (buf));
249 	for (cp = entry; *cp != '\0'; cp++) {
250 		switch (*cp) {
251 		case ':':	/* end of kvp */
252 			if (in_namelist != 0) {
253 				papiAttributeListAddString(&list,
254 					PAPI_ATTR_APPEND, "printer-name", buf);
255 				in_namelist = 0;
256 			} else if (key != NULL)
257 				papiAttributeListAddString(&list,
258 					PAPI_ATTR_APPEND, key, buf);
259 			memset(buf, 0, sizeof (buf));
260 			buf_pos = 0;
261 			key = NULL;
262 			break;
263 		case '=':	/* kvp seperator */
264 			if (key == NULL) {
265 				key = strdup(buf);
266 				memset(buf, 0, sizeof (buf));
267 				buf_pos = 0;
268 			} else
269 				buf[buf_pos++] = *cp;
270 			break;
271 		case '|':	/* namelist seperator */
272 			if (in_namelist != 0) {
273 				papiAttributeListAddString(&list,
274 					PAPI_ATTR_APPEND, "printer-name", buf);
275 				memset(buf, 0, sizeof (buf));
276 				buf_pos = 0;
277 			} else	/* add it to the buffer */
278 				buf[buf_pos++] = *cp;
279 			break;
280 		case '\\':	/* escape char */
281 			buf[buf_pos++] = *(++cp);
282 			break;
283 		default:
284 			buf[buf_pos++] = *cp;
285 		}
286 
287 	}
288 
289 	if (key != NULL)
290 		papiAttributeListAddString(&list, PAPI_ATTR_APPEND, key, buf);
291 
292 	/* resolve any "use" references in the configuration DB */
293 	key = NULL;
294 	papiAttributeListGetString(list, NULL, "use", &key);
295 	if (key != NULL) {
296 		papi_attribute_t **use_attrs = getprinterbyname(key, NULL);
297 
298 		list_concatenate(&list, use_attrs);
299 	}
300 
301 	fill_printer_uri_supported(&list);
302 #if defined(__sun) && defined(__SVR4)
303 	solaris_lpsched_shortcircuit_hack(&list);
304 #endif
305 	cvt_all_to_member_names(&list); /* convert "all" to "member-names" */
306 
307 	return (list);
308 }
309 
310 #if defined(NSS_SOLARIS) && !defined(NSS_EMULATION)
311 
312 #ifndef	NSS_DBNAM__PRINTERS	/* not in nss_dbdefs.h because it's private */
313 #define	NSS_DBNAM__PRINTERS	"_printers"
314 #endif
315 
316 static DEFINE_NSS_DB_ROOT(db_root);
317 static DEFINE_NSS_GETENT(context);
318 
319 static char *private_ns = NULL;
320 static char initialized = 0;
321 
322 static void
323 _nss_initf_printers(p)
324     nss_db_params_t *p;
325 {
326 	if (private_ns != NULL) {
327 		/*
328 		 * because we need to support a legacy interface that allows
329 		 * us to select a specific name service, we need to dummy up
330 		 * the parameters to use a private nsswitch database and set
331 		 * the * default_config entry to the name service we are
332 		 * looking into.
333 		 */
334 		p->name = NSS_DBNAM__PRINTERS;		/* "_printers" */
335 		p->default_config = private_ns;
336 		private_ns = NULL;
337 	} else if (initialized == 0) {
338 		/* regular behaviour */
339 		p->name = NSS_DBNAM_PRINTERS;	 /* "printers" */
340 		p->default_config = NSS_DEFCONF_PRINTERS;
341 		initialized = 1;
342 	}
343 	syslog(LOG_DEBUG, "database: %s, services: %s",
344 		(p->name ? p->name : "NULL"),
345 		(p->default_config ? p->default_config : "NULL"));
346 }
347 
348 /*
349  * Return values: 0 = success, 1 = parse error, 2 = erange ...
350  * The structure pointer passed in is a structure in the caller's space
351  * wherein the field pointers would be set to areas in the buffer if
352  * need be. instring and buffer should be separate areas.
353  */
354 /* ARGSUSED */
355 static int
356 str2printer(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
357 {
358 	if (lenstr + 1 > buflen)
359 		return (NSS_STR_PARSE_ERANGE);
360 	/*
361 	 * We copy the input string into the output buffer
362 	 */
363 	(void) memcpy(buffer, instr, lenstr);
364 	buffer[lenstr] = '\0';
365 
366 	return (NSS_STR_PARSE_SUCCESS);
367 }
368 #endif /* NSS_SOLARIS */
369 
370 int
371 setprinterentry(int stayopen, char *ns)
372 {
373 #ifdef NSS_EMULATION
374 	emul_setprinterentry(stayopen);
375 #elif NSS_SOLARIS
376 	initialized = 0;
377 	private_ns = ns;
378 	nss_setent(&db_root, _nss_initf_printers, &context);
379 #endif
380 	return (0);
381 }
382 
383 
384 int
385 endprinterentry(int i)
386 {
387 #ifdef NSS_EMULATION
388 	emul_endprinterentry();
389 #elif NSS_SOLARIS
390 	initialized = 0;
391 	nss_endent(&db_root, _nss_initf_printers, &context);
392 	nss_delete(&db_root);
393 #endif
394 	return (0);
395 }
396 
397 /* ARGSUSED2 */
398 papi_attribute_t **
399 getprinterentry(char *ns)
400 {
401 	papi_attribute_t **result = NULL;
402 
403 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS)
404 	char buf[10240];
405 	nss_status_t	res = NSS_NOTFOUND;
406 
407 #ifdef NSS_EMULATION
408 	res = emul_getprinterentry_r(buf, sizeof (buf));
409 #elif NSS_SOLARIS
410 	nss_XbyY_args_t arg;
411 
412 	NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer);
413 	res = nss_getent(&db_root, _nss_initf_printers, &context, &arg);
414 	(void) NSS_XbyY_FINI(&arg);
415 #endif
416 
417 	if (res != NSS_SUCCESS)
418 		buf[0] = '\0';
419 
420 	result = _cvt_nss_entry_to_printer(buf);
421 #ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC
422 	fill_printer_uri(&result);
423 #endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */
424 #endif
425 
426 #ifdef DEBUG
427 	printf("getprinterentry(%s): 0x%8.8x\n", (ns ? ns : "NULL"), result);
428 	if (result != NULL) {
429 		char buf[4096];
430 
431 		papiAttributeListToString(result, "\n\t", buf, sizeof (buf));
432 		printf("\t%s\n", buf);
433 	}
434 #endif /* DEBUG */
435 
436 	return (result);
437 }
438 
439 
440 papi_attribute_t **
441 getprinterbyname(char *name, char *ns)
442 {
443 	papi_attribute_t **result = NULL;
444 
445 	if (strstr(name, "://") != NULL) {	/* shortcut for URI form */
446 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
447 				"printer-name", name);
448 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
449 				"printer-uri-supported", name);
450 	} else if (strchr(name, ':') != NULL) {	/* shortcut for POSIX form */
451 		char *uri = bsdaddr_to_uri(name);
452 
453 		papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
454 				"printer-name", name);
455 		if (uri != NULL) {
456 			papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
457 					"printer-uri-supported", uri);
458 			free(uri);
459 		}
460 	} else {				/* anything else */
461 #if defined(NSS_EMULATION) || defined(NSS_SOLARIS)
462 		char buf[10240];
463 		nss_status_t	res = NSS_NOTFOUND;
464 
465 #ifdef NSS_EMULATION
466 		res = emul_getprinterbyname_r(name, buf, sizeof (buf));
467 #elif NSS_SOLARIS
468 		nss_XbyY_args_t arg;
469 
470 		private_ns = ns;
471 		NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer);
472 		arg.key.name = name;
473 		res = nss_search(&db_root, _nss_initf_printers,
474 				NSS_DBOP_PRINTERS_BYNAME, &arg);
475 		(void) NSS_XbyY_FINI(&arg);
476 
477 		if (res != NSS_SUCCESS)
478 			buf[0] = '\0';
479 #endif
480 
481 		result = _cvt_nss_entry_to_printer(buf);
482 #endif
483 	}
484 
485 #ifdef DEBUG
486 	printf("getprinterbyname(%s): %s = 0x%8.8x\n", (ns ? ns : "NULL"),
487 		name, result);
488 	if (result != NULL) {
489 		char buf[4096];
490 
491 		papiAttributeListToString(result, "\n\t", buf, sizeof (buf));
492 		printf("\t%s\n", buf);
493 	}
494 #endif /* DEBUG */
495 
496 	return (result);
497 }
498