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