1  /***
2   This file is part of cups-filters.
3 
4   This file is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8 
9   This file is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13 
14   You should have received a copy of the GNU Lesser General Public
15   License along with avahi; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
17   USA.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <cups/cups.h>
27 #include <cups/backend.h>
28 #include <cupsfilters/ipp.h>
29 #include <cupsfilters/ppdgenerator.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <signal.h>
33 #include <sys/wait.h>
34 #include <sys/types.h>
35 
36 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
37 #define HAVE_CUPS_1_6 1
38 #endif
39 
40 enum resolve_uri_converter_type	/**** Resolving DNS-SD based URI ****/
41 {
42   CUPS_BACKEND_URI_CONVERTER = -1,
43   IPPFIND_BASED_CONVERTER_FOR_PRINT_URI = 0,
44   IPPFIND_BASED_CONVERTER_FOR_FAX_URI = 1
45 };
46 
47 char get_printer_attributes_log[LOGSIZE];
48 
49 static int
convert_to_port(char * a)50 convert_to_port(char *a)
51 {
52   int port = 0;
53   for (int i = 0; i<strlen(a); i++)
54     port = port*10 + (a[i] - '0');
55 
56   return (port);
57 }
58 
59 void
log_printf(char * log,const char * format,...)60 log_printf(char *log,
61 	   const char *format, ...)
62 {
63   va_list arglist;
64   va_start(arglist, format);
65   vsnprintf(log + strlen(log),
66 	    LOGSIZE - strlen(log) - 1,
67 	    format, arglist);
68   log[LOGSIZE - 1] = '\0';
69   va_end(arglist);
70 }
71 
72 char *
resolve_uri(const char * raw_uri)73 resolve_uri(const char *raw_uri)
74 {
75   char *pseudo_argv[2];
76   const char *uri;
77   int fd1, fd2;
78 
79   /* Eliminate any output to stderr, to get rid of the CUPS-backend-specific
80      output of the cupsBackendDeviceURI() function */
81   fd1 = dup(2);
82   fd2 = open("/dev/null", O_WRONLY);
83   dup2(fd2, 2);
84   close(fd2);
85 
86   /* Use the URI resolver of libcups to support DNS-SD-service-name-based
87      URIs. The function returns the corresponding host-name-based URI */
88   pseudo_argv[0] = (char *)raw_uri;
89   pseudo_argv[1] = NULL;
90   uri = cupsBackendDeviceURI(pseudo_argv);
91 
92   /* Re-activate stderr output */
93   dup2(fd1, 2);
94   close(fd1);
95 
96   return (uri ? strdup(uri) : NULL);
97 }
98 
99 #ifdef HAVE_CUPS_1_6
100 /* Check how the driverless support is provided */
101 int
check_driverless_support(const char * uri)102 check_driverless_support(const char* uri)
103 {
104   int support_status = DRVLESS_CHECKERR;
105   ipp_t *response = NULL;
106 
107   response = get_printer_attributes3(NULL, uri, NULL, 0, NULL, 0, 1,
108 				     &support_status);
109   if (response != NULL)
110     ippDelete(response);
111 
112   return support_status;
113 }
114 
115 /* Get attributes of a printer specified only by URI */
116 ipp_t *
get_printer_attributes(const char * raw_uri,const char * const pattrs[],int pattrs_size,const char * const req_attrs[],int req_attrs_size,int debug)117 get_printer_attributes(const char* raw_uri,
118 		       const char* const pattrs[],
119 		       int pattrs_size,
120 		       const char* const req_attrs[],
121 		       int req_attrs_size,
122 		       int debug)
123 {
124   return get_printer_attributes2(NULL, raw_uri, pattrs, pattrs_size,
125 				 req_attrs, req_attrs_size, debug);
126 }
127 
128 /* Get attributes of a printer specified by URI and under a given HTTP
129    connection, for example via a domain socket */
130 ipp_t *
get_printer_attributes2(http_t * http_printer,const char * raw_uri,const char * const pattrs[],int pattrs_size,const char * const req_attrs[],int req_attrs_size,int debug)131 get_printer_attributes2(http_t *http_printer,
132 			const char* raw_uri,
133 			const char* const pattrs[],
134 			int pattrs_size,
135 			const char* const req_attrs[],
136 			int req_attrs_size,
137 			int debug)
138 {
139   return get_printer_attributes3(http_printer, raw_uri, pattrs, pattrs_size,
140 				 req_attrs, req_attrs_size, debug, NULL);
141 }
142 
143 /* Get attributes of a printer specified by URI and under a given HTTP
144    connection, for example via a domain socket, and give info about used
145    fallbacks */
146 ipp_t *
get_printer_attributes3(http_t * http_printer,const char * raw_uri,const char * const pattrs[],int pattrs_size,const char * const req_attrs[],int req_attrs_size,int debug,int * driverless_info)147 get_printer_attributes3(http_t *http_printer,
148 			const char* raw_uri,
149 			const char* const pattrs[],
150 			int pattrs_size,
151 			const char* const req_attrs[],
152 			int req_attrs_size,
153 			int debug,
154                         int* driverless_info)
155 {
156   return get_printer_attributes5(http_printer, raw_uri, pattrs, pattrs_size,
157 				 req_attrs, req_attrs_size, debug,
158 				 driverless_info, CUPS_BACKEND_URI_CONVERTER);
159 }
160 
161 /* Get attributes of a printer specified only by URI and given info about fax-support*/
get_printer_attributes4(const char * raw_uri,const char * const pattrs[],int pattrs_size,const char * const req_attrs[],int req_attrs_size,int debug,int is_fax)162 ipp_t   *get_printer_attributes4(const char* raw_uri,
163 				 const char* const pattrs[],
164 				 int pattrs_size,
165 				 const char* const req_attrs[],
166 				 int req_attrs_size,
167 				 int debug,
168 				 int is_fax)
169 {
170   if(is_fax)
171     return get_printer_attributes5(NULL, raw_uri, pattrs, pattrs_size,
172 				   req_attrs, req_attrs_size, debug, NULL,
173 				   IPPFIND_BASED_CONVERTER_FOR_FAX_URI);
174   else
175     return get_printer_attributes5(NULL, raw_uri, pattrs, pattrs_size,
176 				   req_attrs, req_attrs_size, debug, NULL,
177 				   IPPFIND_BASED_CONVERTER_FOR_PRINT_URI);
178 }
179 
180 /* Get attributes of a printer specified by URI and under a given HTTP
181    connection, for example via a domain socket, and give info about used
182    fallbacks */
183 ipp_t *
get_printer_attributes5(http_t * http_printer,const char * raw_uri,const char * const pattrs[],int pattrs_size,const char * const req_attrs[],int req_attrs_size,int debug,int * driverless_info,int resolve_uri_type)184 get_printer_attributes5(http_t *http_printer,
185 			const char* raw_uri,
186 			const char* const pattrs[],
187 			int pattrs_size,
188 			const char* const req_attrs[],
189 			int req_attrs_size,
190 			int debug,
191 			int* driverless_info,
192 			int resolve_uri_type )
193 {
194   char *uri;
195   int have_http, uri_status, host_port, i = 0, total_attrs = 0, fallback,
196     cap = 0;
197   char scheme[10], userpass[1024], host_name[1024], resource[1024];
198   ipp_t *request, *response = NULL;
199   ipp_attribute_t *attr;
200   char valuebuffer[65536];
201   const char *kw;
202   ipp_status_t ipp_status;
203   /* Default attributes for get-printer-attributes requests to
204      obtain complete capability lists of a printer */
205   const char * const pattrs_cap_standard[] = {
206     "all",
207     "media-col-database",
208   };
209   const char * const pattrs_cap_fallback[] = {
210     "all",
211   };
212   /* Attributes required in the IPP response of a complete printer
213      capability list */
214   const char * const req_attrs_cap[] = {
215     "attributes-charset",
216     "attributes-natural-language",
217     "charset-configured",
218     "charset-supported",
219     "compression-supported",
220     "document-format-default",
221     "document-format-supported",
222     "generated-natural-language-supported",
223     "ipp-versions-supported",
224     "natural-language-configured",
225     "operations-supported",
226     "printer-is-accepting-jobs",
227     "printer-name",
228     "printer-state",
229     "printer-state-reasons",
230     "printer-up-time",
231     "printer-uri-supported",
232     "uri-authentication-supported",
233     "uri-security-supported"
234   };
235 
236   /* Expect a device capable of standard IPP Everywhere */
237   if (driverless_info != NULL)
238     *driverless_info = FULL_DRVLESS;
239 
240   /* Request printer properties via IPP, for example to
241       - generate a PPD file for the printer
242         (mainly driverless-capable printers)
243       - generally find capabilities, options, and default settinngs,
244       - printers status: Accepting jobs? Busy? With how many jobs? */
245 
246   get_printer_attributes_log[0] = '\0';
247 
248   /* Convert DNS-SD-service-name-based URIs to host-name-based URIs */
249   if(resolve_uri_type == CUPS_BACKEND_URI_CONVERTER)
250     uri = resolve_uri(raw_uri);
251   else
252     uri = ippfind_based_uri_converter(raw_uri, resolve_uri_type);
253 
254   if (uri == NULL)
255   {
256     log_printf(get_printer_attributes_log,
257         "get-printer-attibutes: Cannot resolve URI: %s\n", raw_uri);
258     return NULL;
259   }
260 
261   /* Extract URI componants needed for the IPP request */
262   uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri,
263 			       scheme, sizeof(scheme),
264 			       userpass, sizeof(userpass),
265 			       host_name, sizeof(host_name),
266 			       &(host_port),
267 			       resource, sizeof(resource));
268   if (uri_status != HTTP_URI_OK) {
269     /* Invalid URI */
270     log_printf(get_printer_attributes_log,
271 	       "get-printer-attributes: Cannot parse the printer URI: %s\n",
272 	       uri);
273     if (uri) free(uri);
274     return NULL;
275   }
276 
277   /* Connect to the server if not already done */
278   if (http_printer == NULL) {
279     have_http = 0;
280     if ((http_printer =
281 	 httpConnect2 (host_name, host_port, NULL, AF_UNSPEC,
282 		       HTTP_ENCRYPT_IF_REQUESTED, 1, 3000, NULL)) == NULL) {
283       log_printf(get_printer_attributes_log,
284 		 "get-printer-attributes: Cannot connect to printer with URI %s.\n",
285 		 uri);
286       if (uri) free(uri);
287       return NULL;
288     }
289   } else
290     have_http = 1;
291 
292   /* If we got called without attribute list, use the attributes for polling
293      a complete list of capabilities of the printer.
294      If also no list of required attributes in the response is supplied, use
295      the default list */
296   if (pattrs == NULL || pattrs_size == 0) {
297     cap = 1;
298     pattrs = pattrs_cap_standard;
299     pattrs_size = sizeof(pattrs_cap_standard) / sizeof(pattrs_cap_standard[0]);
300     if (req_attrs == NULL || req_attrs_size == 0) {
301       req_attrs = req_attrs_cap;
302       req_attrs_size = sizeof(req_attrs_cap) / sizeof(req_attrs_cap[0]);
303     }
304   }
305 
306   /* Loop through all fallbacks until getting a successful result */
307   for (fallback = 0; fallback < 2 + cap; fallback ++) {
308     request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
309     if (fallback == 1)
310       /* Fallback 1: Try IPP 1.1 instead of 2.0 */
311       ippSetVersion(request, 1, 1);
312     if (fallback == 2 && cap) {
313       /* Fallback 2: (Only for full capability list) Try only "all",
314 	 without "media-col-database */
315       pattrs = pattrs_cap_fallback;
316       pattrs_size = sizeof(pattrs_cap_fallback) /
317 	sizeof(pattrs_cap_fallback[0]);
318     }
319     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
320 		 uri);
321     ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
322 		  "requested-attributes", pattrs_size,
323 		  NULL, pattrs);
324 
325     response = cupsDoRequest(http_printer, request, resource);
326     ipp_status = cupsLastError();
327 
328     if (response) {
329       log_printf(get_printer_attributes_log,
330 		 "Requested IPP attributes (get-printer-attributes) for printer with URI %s\n",
331 		 uri);
332       /* Log all printer attributes for debugging and count them */
333       if (debug)
334 	log_printf(get_printer_attributes_log,
335 		   "Full list of all IPP attributes:\n");
336       attr = ippFirstAttribute(response);
337       while (attr) {
338 	total_attrs ++;
339 	if (debug) {
340 	  ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
341 	  log_printf(get_printer_attributes_log,
342 		     "  Attr: %s\n",ippGetName(attr));
343 	  log_printf(get_printer_attributes_log,
344 		     "  Value: %s\n", valuebuffer);
345 	  for (i = 0; i < ippGetCount(attr); i ++) {
346 	    if ((kw = ippGetString(attr, i, NULL)) != NULL) {
347 	      log_printf(get_printer_attributes_log, "  Keyword: %s\n", kw);
348 	    }
349 	  }
350 	}
351 	attr = ippNextAttribute(response);
352       }
353 
354       /* Check whether the IPP response contains the required attributes
355 	 and is not incomplete */
356       if (req_attrs)
357 	for (i = req_attrs_size; i > 0; i --)
358 	  if (ippFindAttribute(response, req_attrs[i - 1], IPP_TAG_ZERO) ==
359 	      NULL)
360 	    break;
361       if (ipp_status == IPP_STATUS_ERROR_BAD_REQUEST ||
362 	  ipp_status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED ||
363 	  (req_attrs && i > 0) || (cap && total_attrs < 20)) {
364 	log_printf(get_printer_attributes_log,
365 		   "get-printer-attributes IPP request failed:\n");
366 	if (ipp_status == IPP_STATUS_ERROR_BAD_REQUEST)
367 	  log_printf(get_printer_attributes_log,
368 		     "  - ipp_status == IPP_STATUS_ERROR_BAD_REQUEST\n");
369 	else if (ipp_status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
370 	  log_printf(get_printer_attributes_log,
371 		     "  - ipp_status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED\n");
372 	if (req_attrs && i > 0)
373 	  log_printf(get_printer_attributes_log,
374 		     "  - Required IPP attribute %s not found\n",
375 		     req_attrs[i - 1]);
376 	if (cap && total_attrs < 20)
377 	  log_printf(get_printer_attributes_log,
378 		     "  - Too few IPP attributes: %d (30 or more expected)\n",
379 		     total_attrs);
380 	ippDelete(response);
381       } else {
382 	/* Suitable response, we are done */
383 	if (have_http == 0) httpClose(http_printer);
384 	if (uri) free(uri);
385 	return response;
386       }
387     } else {
388       log_printf(get_printer_attributes_log,
389 		 "Request for IPP attributes (get-printer-attributes) for printer with URI %s failed: %s\n",
390 		 uri, cupsLastErrorString());
391       log_printf(get_printer_attributes_log, "get-printer-attributes IPP request failed:\n");
392       log_printf(get_printer_attributes_log, "  - No response\n");
393     }
394     if (fallback == 1 + cap) {
395       log_printf(get_printer_attributes_log,
396 		 "No further fallback available, giving up\n");
397       if (driverless_info != NULL)
398         *driverless_info = DRVLESS_CHECKERR;
399     } else if (cap && fallback == 1) {
400       log_printf(get_printer_attributes_log,
401 		 "The server doesn't support the standard IPP request, trying request without media-col\n");
402       if (driverless_info != NULL)
403         *driverless_info = DRVLESS_INCOMPLETEIPP;
404     } else if (fallback == 0) {
405       log_printf(get_printer_attributes_log,
406 		 "The server doesn't support IPP2.0 request, trying IPP1.1 request\n");
407       if (driverless_info != NULL)
408         *driverless_info = DRVLESS_IPP11;
409     }
410   }
411 
412   if (have_http == 0) httpClose(http_printer);
413   if (uri) free(uri);
414   return NULL;
415 }
416 
417 char*
ippfind_based_uri_converter(const char * uri,int is_fax)418 ippfind_based_uri_converter (const char *uri, int is_fax)
419 {
420   int  ippfind_pid = 0,	        /* Process ID of ippfind for IPP */
421        post_proc_pipe[2],	/* Pipe to post-processing for IPP */
422        wait_children,		/* Number of child processes left */
423        wait_pid,		/* Process ID from wait() */
424        wait_status,		/* Status from child */
425        exit_status = 0,		/* Exit status */
426        bytes,
427        port,
428        i,
429        output_of_fax_uri = 0,
430        is_local;
431   char *ippfind_argv[100],	/* Arguments for ippfind */
432        *ptr_to_port = NULL,
433        *reg_type,
434        *resolved_uri = NULL,		/*  Buffer for resolved URI */
435        *resource_field = NULL,
436        *service_hostname = NULL,
437        /* URI components... */
438        scheme[32],
439        userpass[256],
440        hostname[1024],
441        resource[1024],
442        *buffer = NULL,		/* Copy buffer */
443        *ptr;			/* Pointer into string */;
444   cups_file_t *fp;		/* Post-processing input file */
445   int  status;			/* Status of GET request */
446 
447   status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
448 			   userpass, sizeof(userpass),
449 			   hostname, sizeof(hostname), &port, resource,
450 			   sizeof(resource));
451   if (status < HTTP_URI_OK) {
452     /* Invalid URI */
453     fprintf(stderr, "ERROR: Could not parse URI: %s\n", uri);
454     goto error;
455   }
456 
457   /* URI is not DNS-SD-based, so do not resolve */
458   if ((reg_type = strstr(hostname, "._tcp")) == NULL) {
459     return strdup(uri);
460   }
461 
462   resolved_uri = (char *)malloc(MAX_URI_LEN * (sizeof(char)));
463   if (resolved_uri == NULL) {
464     fprintf(stderr, "resolved_uri malloc: Out of memory\n");
465     goto error;
466   }
467   memset(resolved_uri, 0, MAX_URI_LEN);
468 
469   reg_type --;
470   while (reg_type >= hostname && *reg_type != '.')
471     reg_type --;
472   if (reg_type < hostname) {
473     fprintf(stderr, "ERROR: Invalid DNS-SD service name: %s\n", hostname);
474     goto error;
475   }
476   *reg_type++ = '\0';
477 
478   i = 0;
479   ippfind_argv[i++] = "ippfind";
480   ippfind_argv[i++] = reg_type;           /* list IPP(S) entries */
481   ippfind_argv[i++] = "-T";               /* DNS-SD poll timeout */
482   ippfind_argv[i++] = "0";                /* Minimum time required */
483   if (is_fax) {
484     ippfind_argv[i++] = "--txt";
485     ippfind_argv[i++] = "rfo";
486   }
487   ippfind_argv[i++] = "-N";
488   ippfind_argv[i++] = hostname;
489   ippfind_argv[i++] = "-x";
490   ippfind_argv[i++] = "echo";             /* Output the needed data fields */
491   ippfind_argv[i++] = "-en";              /* separated by tab characters */
492   if(is_fax)
493     ippfind_argv[i++] = "\n{service_hostname}\t{txt_rfo}\t{service_port}\t";
494   else
495     ippfind_argv[i++] = "\n{service_hostname}\t{txt_rp}\t{service_port}\t";
496   ippfind_argv[i++] = ";";
497   ippfind_argv[i++] = "--local";          /* Rest only if local service */
498   ippfind_argv[i++] = "-x";
499   ippfind_argv[i++] = "echo";             /* Output an 'L' at the end of the */
500   ippfind_argv[i++] = "-en";              /* line */
501   ippfind_argv[i++] = "L";
502   ippfind_argv[i++] = ";";
503   ippfind_argv[i++] = NULL;
504 
505  /*
506   * Create a pipe for passing the ippfind output to post-processing
507   */
508 
509   if (pipe(post_proc_pipe)) {
510     perror("ERROR: Unable to create pipe to post-processing");
511     goto error;
512   }
513 
514   if ((ippfind_pid = fork()) == 0) {
515    /*
516     * Child comes here...
517     */
518 
519     dup2(post_proc_pipe[1], 1);
520     close(post_proc_pipe[0]);
521     close(post_proc_pipe[1]);
522 
523     execvp(CUPS_IPPFIND, ippfind_argv);
524     perror("ERROR: Unable to execute ippfind utility");
525 
526     exit(1);
527   }
528   else if (ippfind_pid < 0) {
529    /*
530     * Unable to fork!
531     */
532 
533     perror("ERROR: Unable to execute ippfind utility");
534     goto error;
535   }
536 
537   dup2(post_proc_pipe[0], 0);
538   close(post_proc_pipe[0]);
539   close(post_proc_pipe[1]);
540 
541   fp = cupsFileStdin();
542 
543   buffer = (char*)malloc(MAX_OUTPUT_LEN * sizeof(char));
544   if (buffer == NULL) {
545     fprintf(stderr, "buffer malloc: Out of memory.\n");
546     goto error;
547   }
548   memset(buffer, 0, MAX_OUTPUT_LEN);
549 
550   while ((bytes = cupsFileGetLine(fp, buffer, MAX_OUTPUT_LEN)) > 0) {
551     /* Mark all the fields of the output of ippfind */
552     ptr = buffer;
553 
554     /* ignore new lines */
555     if (bytes < 3)
556       goto read_error;
557 
558     /* First, build the DNS-SD-service-name-based URI ... */
559     while (ptr && !isalnum(*ptr & 255)) ptr ++;
560 
561     service_hostname = ptr;
562     ptr = memchr(ptr, '\t', MAX_OUTPUT_LEN - (ptr - buffer));
563     if (!ptr) goto read_error;
564     *ptr = '\0';
565     ptr ++;
566 
567     resource_field = ptr;
568     ptr = memchr(ptr, '\t', MAX_OUTPUT_LEN - (ptr - buffer));
569     if (!ptr) goto read_error;
570     *ptr = '\0';
571     ptr ++;
572 
573     ptr_to_port = ptr;
574     ptr = memchr(ptr, '\t', MAX_OUTPUT_LEN - (ptr - buffer));
575     if (!ptr) goto read_error;
576     *ptr = '\0';
577     ptr ++;
578 
579     /* Do we have a local service so that we have to set the host name to
580        "localhost"? */
581     is_local = (*ptr == 'L');
582 
583     ptr = strchr(reg_type, '.');
584     if (!ptr) goto read_error;
585     *ptr = '\0';
586 
587     port = convert_to_port(ptr_to_port);
588 
589     httpAssembleURIf(HTTP_URI_CODING_ALL, resolved_uri,
590 		     2047, reg_type + 1, NULL,
591 		     (is_local ? "localhost" : service_hostname), port, "/%s",
592 		     resource_field);
593 
594     if (is_fax)
595       output_of_fax_uri = 1; /* fax-uri requested from fax-capable device */
596 
597   read_error:
598     memset(buffer, 0, MAX_OUTPUT_LEN);
599   }
600 
601   if (buffer != NULL)
602     free(buffer);
603 
604  /*
605   * Wait for the child processes to exit...
606   */
607 
608   wait_children = 1;
609 
610   while (wait_children > 0) {
611    /*
612     * Wait until we get a valid process ID or the job is canceled...
613     */
614 
615     while ((wait_pid = wait(&wait_status)) < 0 && errno == EINTR) {
616     }
617 
618     if (wait_pid < 0)
619       break;
620 
621     wait_children --;
622 
623    /*
624     * Report child status...
625     */
626 
627     if (wait_status) {
628       if (WIFEXITED(wait_status)) {
629 	exit_status = WEXITSTATUS(wait_status);
630         if (wait_pid == ippfind_pid && exit_status <= 2)
631           exit_status = 0;
632       } else if (WTERMSIG(wait_status) == SIGTERM) {
633       } else {
634 	exit_status = WTERMSIG(wait_status);
635       }
636     }
637   }
638   if (is_fax && !output_of_fax_uri) {
639     fprintf(stderr, "fax URI requested from not fax-capable device\n");
640     goto error;
641   }
642 
643   return (resolved_uri);
644 
645  /*
646   * Exit...
647   */
648 
649  error:
650   if (resolved_uri != NULL)
651     free(resolved_uri);
652   return (NULL);
653 }
654 
655 
656 #endif /* HAVE_CUPS_1_6 */
657