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