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