• 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 cups-filters; 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 #include <ctype.h>
24 #include <errno.h>
25 #if defined(__OpenBSD__)
26 #include <sys/socket.h>
27 #endif /* __OpenBSD__ */
28 #include <sys/types.h>
29 #include <stdio.h>
30 #include <sys/stat.h>
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <sys/wait.h>
34 #include <cups/cups.h>
35 #include <cups/ppd.h>
36 #include <cups/raster.h>
37 #include <cupsfilters/ipp.h>
38 #include <cupsfilters/ppdgenerator.h>
39 
40 #define MAX_OUTPUT_LEN 8192
41 
42 static int              debug = 0;
43 static int		job_canceled = 0;
44 static void		cancel_job(int sig);
45 static cups_array_t     *uuids = NULL;
46 
47 static int
compare_service_uri(char * a,char * b)48 compare_service_uri(char *a,	char *b)
49 {
50   return (strcmp(a,b));
51 }
52 
53 static int
convert_to_port(char * a)54 convert_to_port(char *a)
55 {
56   int port = 0;
57   for( int i = 0; i<strlen(a); i++)
58     port = port*10 + (a[i] - '0');
59 
60   return (port);
61 }
62 
63 void
listPrintersInArray(int reg_type_no,int mode,int isFax,char * ippfind_output)64 listPrintersInArray(int reg_type_no, int mode, int isFax,
65 		    char* ippfind_output) {
66   int	port,
67         is_local;
68   char	buffer[8192],		/* Copy buffer */
69         *ptr,		        /* Pointer into string */
70         *scheme = NULL,
71         *service_name = NULL,
72         *resource = NULL,
73         *domain = NULL,
74         *ptr_to_port = NULL,    /* pointer to port */
75         *reg_type = NULL,
76         *service_hostname = NULL,
77         *txt_usb_mfg = NULL,
78         *txt_usb_mdl = NULL,
79         *txt_product = NULL,
80         *txt_ty = NULL,
81         *txt_pdl = NULL,
82         *txt_uuid = NULL,
83         value[256],             /* Value string */
84         *service_uri,           /* URI to list for this service */
85         service_host_name[1024],/* "Host name" for assembling URI */
86         make_and_model[1024],	/* Manufacturer and model */
87         make[512],              /* Manufacturer */
88       	model[256],		/* Model */
89         pdl[256],		/* PDL */
90         device_id[2048];	/* 1284 device ID */
91 
92   service_uri = (char *)malloc(2048*(sizeof(char)));
93   /* Mark all the fields of the output of ippfind */
94   ptr = ippfind_output;
95   if (reg_type_no < 1) {
96     scheme = "ipp";
97     reg_type = "_ipp._tcp";
98   } else if (reg_type_no > 1) {
99     scheme = "ipps";
100     reg_type = "_ipps._tcp";
101   }
102 
103   /* ... second, complete the output line, either URI-only or with
104      extra info for CUPS */
105 
106   if (mode == -1) {
107     /* Standard IPP URI (only manual call) */
108     service_hostname = ptr;
109     ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
110     if (!ptr)
111       goto read_error;
112     *ptr = '\0';
113     ptr ++;
114 
115     resource = ptr;
116     ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
117     if (!ptr) goto read_error;
118     *ptr = '\0';
119     ptr ++;
120 
121     ptr_to_port = ptr;
122     ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
123     if (!ptr) goto read_error;
124     *ptr = '\0';
125     ptr ++;
126     port = convert_to_port(ptr_to_port);
127 
128     /* Do we have a local service so that we have to set the host name to
129        "localhost"? */
130     is_local = (*ptr == 'L');
131 
132     httpAssembleURIf(HTTP_URI_CODING_ALL, service_uri,
133 		     2047,
134 		     scheme, NULL,
135 		     (is_local ? "localhost" : service_hostname),
136 		     port, "/%s", resource);
137     printf("%s\n", service_uri);
138   } else {
139     /* DNS-SD-service-name-based URI */
140     service_name = ptr;
141     ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
142     if (!ptr)
143       goto read_error;
144     *ptr = '\0';
145     ptr ++;
146 
147     domain = ptr;
148     ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
149     if (!ptr)
150       goto read_error;
151     *ptr = '\0';
152     ptr ++;
153 
154     snprintf(service_host_name, sizeof(service_host_name) - 1, "%s.%s.%s",
155 	     service_name, reg_type, domain);
156     httpAssembleURIf(HTTP_URI_CODING_ALL, service_uri,
157 		     2047,
158 		     scheme, NULL,
159 		     service_host_name, 0, "/");
160 
161     if (mode == 0)
162       /* Manual call, only show URI, nothing more */
163       printf("%s\n", service_uri);
164     else {
165       /* Call by CUPS, either as PPD generator
166 	 (/usr/lib/cups/driver/, with "list" command line argument)
167 	 or as backend in discovery mode (/usr/lib/cups/backend/,
168 	 env variable "SOFTWARE" starts with "CUPS") */
169       txt_usb_mfg = ptr;
170       ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
171       if (!ptr)
172 	goto read_error;
173       *ptr = '\0';
174       ptr ++;
175       txt_usb_mdl = ptr;
176       ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
177       if (!ptr)
178 	goto read_error;
179       *ptr = '\0';
180       ptr ++;
181       txt_product = ptr;
182       ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
183       if (!ptr)
184 	goto read_error;
185       *ptr = '\0';
186       ptr ++;
187       txt_ty = ptr;
188       ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
189       if (!ptr)
190 	goto read_error;
191       *ptr = '\0';
192       ptr ++;
193       txt_pdl = ptr;
194       ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
195       if (!ptr)
196 	goto read_error;
197       *ptr = '\0';
198       ptr ++;
199       txt_uuid = ptr;
200       ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
201       if (!ptr)
202 	goto read_error;
203       *ptr = '\0';
204       /* We check only for a fax resource (rfo) here, if there is none,
205 	 resource will stay blank meaning device does not support fax */
206       ptr ++;
207       resource = ptr;
208       ptr = memchr(ptr, '\t', sizeof(buffer) - (ptr - buffer));
209       if (!ptr)
210 	goto read_error;
211       *ptr = '\0';
212 
213       make_and_model[0] = '\0';
214       make[0] = '\0';
215       pdl[0] = '\0';
216       device_id[0] = '\0';
217       strncpy(model, "Unknown", sizeof(model) - 1);
218 
219       if (txt_usb_mfg[0] != '\0') {
220 	strncpy(make, txt_usb_mfg, sizeof(make) - 1);
221 	if (strlen(txt_usb_mfg) > 511)
222 	  make[511] = '\0';
223 	ptr = device_id + strlen(device_id);
224 	snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id),
225 		 "MFG:%s;", txt_usb_mfg);
226       }
227       if (txt_usb_mdl[0] != '\0') {
228 	strncpy(model, txt_usb_mdl, sizeof(model) - 1);
229 	if (strlen(txt_usb_mdl) > 255)
230 	  model[255] = '\0';
231 	ptr = device_id + strlen(device_id);
232 	snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id),
233 		 "MDL:%s;", txt_usb_mdl);
234       } else if (txt_product[0] != '\0') {
235 	if (txt_product[0] == '(') {
236 	  /* Strip parenthesis... */
237 	  if ((ptr = txt_product + strlen(txt_product) - 1) > txt_product &&
238 	      *ptr == ')')
239 	    *ptr = '\0';
240 	  strncpy(model, txt_product + 1, sizeof(model) - 1);
241 	  if ((strlen(txt_product) + 1) > 255)
242 	    model[255] = '\0';
243 	} else
244 	  strncpy(model, txt_product, sizeof(model) - 1);
245       } else if (txt_ty[0] != '\0') {
246 	strncpy(model, txt_ty, sizeof(model) - 1);
247 	if (strlen(txt_ty) > 255)
248 	  model[255] = '\0';
249 	if ((ptr = strchr(model, ',')) != NULL)
250 	  *ptr = '\0';
251       }
252       if (txt_pdl[0] != '\0') {
253 	strncpy(pdl, txt_pdl, sizeof(pdl) - 1);
254 	if (strlen(txt_pdl) > 255)
255 	  pdl[255] = '\0';
256       }
257 
258       if (!device_id[0] && strcasecmp(model, "Unknown")) {
259 	if (make[0])
260 	  snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
261 		   make, model);
262 	else if (!strncasecmp(model, "designjet ", 10))
263 	  snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s;",
264 		   model + 10);
265 	else if (!strncasecmp(model, "stylus ", 7))
266 	  snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s;",
267 		   model + 7);
268 	else if ((ptr = strchr(model, ' ')) != NULL) {
269 	  /* Assume the first word is the make...*/
270 	  memcpy(make, model, (size_t)(ptr - model));
271 	  make[ptr - model] = '\0';
272 	  snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
273 		   make, ptr + 1);
274 	}
275       }
276 
277       if (device_id[0] &&
278 	  !strcasestr(device_id, "CMD:") &&
279 	  !strcasestr(device_id, "COMMAND SET:") &&
280 	  (strcasestr(pdl, "application/pdf") ||
281 	   strcasestr(pdl, "application/postscript") ||
282 	   strcasestr(pdl, "application/vnd.hp-PCL") ||
283 	   strcasestr(pdl, "application/PCLm") ||
284 	   strcasestr(pdl, "image/"))) {
285 	value[0] = '\0';
286 	if (strcasestr(pdl, "application/pdf"))
287 	  strncat(value, ",PDF", sizeof(value));
288 	if (strcasestr(pdl, "application/PCLm"))
289 	  strncat(value, ",PCLM", sizeof(value));
290 	if (strcasestr(pdl, "application/postscript"))
291 	  strncat(value, ",PS", sizeof(value));
292 	if (strcasestr(pdl, "application/vnd.hp-PCL"))
293 	  strncat(value, ",PCL", sizeof(value));
294 	if (strcasestr(pdl, "image/pwg-raster"))
295 	  strncat(value, ",PWGRaster", sizeof(value));
296 	if (strcasestr(pdl, "image/urf"))
297 	  strncat(value, ",AppleRaster", sizeof(value));
298 	for (ptr = strcasestr(pdl, "image/"); ptr;
299 	     ptr = strcasestr(ptr, "image/")) {
300 	  char *valptr = value + strlen(value);
301 	  if (valptr < (value + sizeof(value) - 1))
302 	    *valptr++ = ',';
303 	  ptr += 6;
304 	  while (isalnum(*ptr & 255) || *ptr == '-' || *ptr == '.') {
305 	    if (isalnum(*ptr & 255) && valptr < (value + sizeof(value) - 1))
306 	      *valptr++ = (char)toupper(*ptr++ & 255);
307 	    else
308 	      break;
309 	  }
310 	  *valptr = '\0';
311 	}
312 	ptr = device_id + strlen(device_id);
313 	snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id),
314 		 "CMD:%s;", value + 1);
315       }
316 
317       if (make[0] &&
318 	  (strncasecmp(model, make, strlen(make)) ||
319 	   !isspace(model[strlen(make)])))
320 	snprintf(make_and_model, sizeof(make_and_model), "%s %s",
321 		 make, model);
322       else
323 	strncpy(make_and_model, model, sizeof(make_and_model) - 1);
324 
325       if (mode == 1) {
326 	/* Only output the entry if we had this UUID not already */
327 	if (!txt_uuid[0] || !cupsArrayFind(uuids, txt_uuid)) {
328 	  /* Save UUID as if an entry with the same UUID appears again, it
329 	     is the the same pair of print and fax PPDs */
330 	  if (txt_uuid[0]) cupsArrayAdd(uuids, strdup(txt_uuid));
331 	  /* Call with "list" argument  (PPD generator in list mode)   */
332 	  printf("\"%s%s\" en \"%s\" \"%s, %sdriverless, cups-filters "
333 		 VERSION "\" \"%s\"\n",
334 		 ((isFax) ? "driverless-fax:" : "driverless:"),
335 		 service_uri, make, make_and_model,
336 		 ((isFax) ? "Fax, " : ""),
337 		 device_id);
338 	  if (resource[0]) /* We have also fax on this device */
339 	    printf("\"%s%s\" en \"%s\" \"%s, Fax, driverless, cups-filters "
340 		   VERSION "\" \"%s\"\n",
341 		   "driverless-fax:",
342 		   service_uri, make, make_and_model,
343 		   device_id);
344 	}
345       } else {
346 	/* Call without arguments and env variable "SOFTWARE" starting
347 	   with "CUPS" (Backend in discovery mode) */
348 	printf("network %s \"%s\" \"%s (driverless)\" \"%s\" \"\"\n",
349 	       service_uri, make_and_model, make_and_model,
350 	       device_id);
351       }
352     }
353 
354   read_error:
355     free(service_uri);
356     return;
357   }
358 
359   free(service_uri);
360   return;
361 }
362 
363 int
list_printers(int mode,int reg_type_no,int isFax)364 list_printers (int mode, int reg_type_no, int isFax)
365 {
366   int		ippfind_pid = 0,	/* Process ID of ippfind for IPP */
367                 post_proc_pipe[2],	/* Pipe to post-processing for IPP */
368 		wait_res,		/* Process ID from wait() */
369 		wait_status,		/* Status from child */
370   	        exit_status = 0,	/* Exit status */
371                 i;
372   char		*ippfind_argv[100];	/* Arguments for ippfind */
373   cups_array_t  *service_uri_list_ipps, /* Array to store ippfind output for
374 					   IPPS */
375                 *service_uri_list_ipp;  /* Array to store ippfind output for
376 					   IPP */
377   cups_file_t	*fp;
378   int		bytes;
379   char		*ptr,
380 		buffer[MAX_OUTPUT_LEN],	/* Copy buffer */
381 		*ippfind_output;
382 
383   service_uri_list_ipps =
384     cupsArrayNew3((cups_array_func_t)compare_service_uri, NULL, NULL, 0, NULL,
385 		  (cups_afree_func_t)free);
386   service_uri_list_ipp =
387     cupsArrayNew3((cups_array_func_t)compare_service_uri, NULL, NULL, 0, NULL,
388 		  (cups_afree_func_t)free);
389 
390  /*
391   * Use CUPS' ippfind utility to discover all printers designed for
392   * driverless use (IPP Everywhere or Apple Raster), and only IPP
393   * network printers, not CUPS queues, output all data elements needed
394   * for our desired output.
395   */
396 
397   /* ippfind -T 0 _ipps._tcp _ipp._tcp ! --txt printer-type --and \( --txt-pdl image/pwg-raster --or --txt-pdl application/PCLm --or --txt-pdl image/urf --or --txt-pdl application/pdf \) -x echo -en '\n{service_scheme}\t{service_name}\t{service_domain}\t{txt_usb_MFG}\t{txt_usb_MDL}\t{txt_product}\t{txt_ty}\t{service_name}\t{txt_pdl}\t{txt_UUID}\t{txt_rfo}\t' \; --local -x echo -en L \;*/
398 
399   i = 0;
400   ippfind_argv[i++] = "ippfind";
401   ippfind_argv[i++] = "-T";               /* DNS-SD poll timeout */
402   ippfind_argv[i++] = "0";                /* Minimum time required */
403   if (reg_type_no >= 1)
404     ippfind_argv[i++] = "_ipps._tcp";     /* list IPPS entries */
405   if (reg_type_no <= 1)
406     ippfind_argv[i++] = "_ipp._tcp";     /* list IPPS entries */
407   ippfind_argv[i++] = "!";                /* ! --txt printer-type */
408   ippfind_argv[i++] = "--txt";            /* No remote CUPS queues */
409   ippfind_argv[i++] = "printer-type";     /* (no "printer-type" in TXT
410 					      record) */
411   if (isFax) {
412     ippfind_argv[i++] = "--and";
413     ippfind_argv[i++] = "--txt";
414     ippfind_argv[i++] = "rfo";
415   }
416   ippfind_argv[i++] = "--and";            /* and */
417   ippfind_argv[i++] = "(";
418   ippfind_argv[i++] = "--txt-pdl";        /* PDL list in TXT record contains */
419   ippfind_argv[i++] = "image/pwg-raster"; /* PWG Raster (IPP Everywhere) */
420 #ifdef QPDF_HAVE_PCLM
421   ippfind_argv[i++] = "--or";             /* or */
422   ippfind_argv[i++] = "--txt-pdl";
423   ippfind_argv[i++] = "application/PCLm"; /* PCLm */
424 #endif
425 #ifdef CUPS_RASTER_HAVE_APPLERASTER
426   ippfind_argv[i++] = "--or";             /* or */
427   ippfind_argv[i++] = "--txt-pdl";
428   ippfind_argv[i++] = "image/urf";        /* Apple Raster */
429 #endif
430   ippfind_argv[i++] = "--or";             /* or */
431   ippfind_argv[i++] = "--txt-pdl";
432   ippfind_argv[i++] = "application/pdf";  /* PDF */
433   ippfind_argv[i++] = ")";
434   ippfind_argv[i++] = "-x";
435   ippfind_argv[i++] = "echo";             /* Output the needed data fields */
436   ippfind_argv[i++] = "-en";              /* separated by tab characters */
437   if (mode < 0) {
438     if (isFax)
439       ippfind_argv[i++] =
440 	"\n{service_scheme}\t{service_hostname}\t{txt_rfo}\t{service_port}\t";
441     else
442       ippfind_argv[i++] =
443 	"\n{service_scheme}\t{service_hostname}\t{txt_rp}\t{service_port}\t";
444   } else if (mode > 0)
445     ippfind_argv[i++] =
446       "{service_scheme}\t{service_name}\t{service_domain}\t{txt_usb_MFG}\t"
447       "{txt_usb_MDL}\t{txt_product}\t{txt_ty}\t{txt_pdl}\t{txt_UUID}\t"
448       "{txt_rfo}\t\n";
449   else
450     ippfind_argv[i++] =
451       "{service_scheme}\t{service_name}\t{service_domain}\t\n";
452   ippfind_argv[i++] = ";";
453   if (mode < 0) {
454     ippfind_argv[i++] = "--local";        /* Rest only if local service */
455     ippfind_argv[i++] = "-x";
456     ippfind_argv[i++] = "echo";           /* Output an 'L' at the end of the */
457     ippfind_argv[i++] = "-en";            /* line */
458     ippfind_argv[i++] = "L";
459     ippfind_argv[i++] = ";";
460   }
461   ippfind_argv[i++] = NULL;
462 
463  /*
464   * Create a pipe for passing the ippfind output to post-processing
465   */
466 
467   if (pipe(post_proc_pipe)) {
468     perror("ERROR: Unable to create pipe to post-processing");
469 
470     exit_status = 1;
471     goto error;
472   }
473 
474   if ((ippfind_pid = fork()) == 0) {
475    /*
476     * Child comes here...
477     */
478 
479     dup2(post_proc_pipe[1], 1);
480 
481     close(post_proc_pipe[0]);
482     close(post_proc_pipe[1]);
483 
484     execvp(CUPS_IPPFIND, ippfind_argv);
485     perror("ERROR: Unable to execute ippfind utility");
486 
487     exit(1);
488   } else if (ippfind_pid < 0) {
489    /*
490     * Unable to fork!
491     */
492 
493     perror("ERROR: Unable to execute ippfind utility");
494 
495     exit_status = 1;
496     goto error;
497   }
498   if (debug)
499     fprintf(stderr, "DEBUG: Started %s (PID %d)\n", ippfind_argv[0],
500 	    ippfind_pid);
501 
502   close(post_proc_pipe[1]);
503 
504  /*
505   * Reading the ippfind output into CUPS Arrays
506   */
507   fp = cupsFileOpenFd(post_proc_pipe[0], "r");
508   if (fp) {
509     while ((bytes = cupsFileGetLine(fp, buffer, sizeof(buffer))) > 0 ||
510 	   (bytes < 0 && (errno == EAGAIN || errno == EINTR))) {
511       ippfind_output = (char *)malloc(MAX_OUTPUT_LEN*(sizeof(char)));
512       ptr = buffer;
513       while (ptr && !isalnum(*ptr & 255)) ptr ++;
514       if ((!strncasecmp(ptr, "ipps", 4) && ptr[4] == '\t')) {
515 	ptr += 4;
516 	*ptr = '\0';
517 	ptr ++;
518 	snprintf(ippfind_output, MAX_OUTPUT_LEN, "%s", ptr);
519 	cupsArrayAdd(service_uri_list_ipps, ippfind_output);
520       } else if ((!strncasecmp(ptr, "ipp", 3) && ptr[3] == '\t')) {
521 	ptr += 3;
522 	*ptr = '\0';
523 	ptr ++;
524 	snprintf(ippfind_output, MAX_OUTPUT_LEN, "%s", ptr);
525 	cupsArrayAdd(service_uri_list_ipp, ippfind_output);
526       } else
527 	continue;
528     }
529     if (bytes < 0) {
530       /* Read error - bail if we don't see EAGAIN or EINTR... */
531       if (errno != EAGAIN && errno != EINTR)
532       {
533 	perror("ERROR: Unable to read ippfind output");
534 	exit_status = 1;
535 	goto error;
536       }
537     }
538   } else {
539     perror("ERROR: Unable to open ippfind output data stream");
540     exit_status = 1;
541     goto error;
542   }
543 
544   for (int j = 0; j < cupsArrayCount(service_uri_list_ipp); j ++)
545   {
546     if (cupsArrayFind(service_uri_list_ipps,
547 		      (char*)cupsArrayIndex(service_uri_list_ipp, j))
548 	== NULL)
549       listPrintersInArray(0, mode, isFax,
550 			  (char *)cupsArrayIndex(service_uri_list_ipp, j));
551   }
552 
553   for (int j = 0; j < cupsArrayCount(service_uri_list_ipps); j++)
554   {
555      listPrintersInArray(2, mode, isFax,
556 			 (char *)cupsArrayIndex(service_uri_list_ipps, j));
557   }
558 
559  /*
560   * Wait for the child process to exit...
561   */
562 
563   while ((wait_res = waitpid(ippfind_pid, &wait_status, 0)) == -1 &&
564 	 errno == EINTR);
565   if (wait_res == -1) {
566     fprintf(stderr, "ERROR: ippfind (PID %d) stopped with an error: %s\n",
567 	    ippfind_pid, strerror(errno));
568     exit_status = errno;
569     goto error;
570   }
571   /* How did ippfind terminate */
572   if (WIFEXITED(wait_status)) {
573     /* Via exit() anywhere or return() in the main() function */
574     exit_status = WEXITSTATUS(wait_status);
575     /* if we get 1 from ippfind, it is actually a correct value, not an error,
576      * because CUPS backends return 0 if they don't find any queues */
577     if (exit_status == 1)
578       exit_status = 0;
579     if (exit_status)
580       fprintf(stderr, "ERROR: ippfind (PID %d) stopped with status %d!\n",
581 	      ippfind_pid, exit_status);
582   } else if (WIFSIGNALED(wait_status) && WTERMSIG(wait_status) != SIGTERM) {
583     /* Via signal */
584     exit_status = 256 * WTERMSIG(wait_status);
585     if (exit_status)
586       fprintf(stderr, "ERROR: ippfind (PID %d) stopped on signal %d!\n",
587 	      ippfind_pid, exit_status);
588   }
589   if (!exit_status && debug)
590     fprintf(stderr, "DEBUG: ippfind (PID %d) exited with no errors.\n",
591 	    ippfind_pid);
592 
593  /*
594   * Exit...
595   */
596 
597  error:
598   cupsArrayDelete(service_uri_list_ipps);
599   cupsArrayDelete(service_uri_list_ipp);
600   return (exit_status);
601 }
602 
603 int
generate_ppd(const char * uri,int isFax)604 generate_ppd (const char *uri, int isFax)
605 {
606   ipp_t *response = NULL;
607   char buffer[65536], ppdname[1024];
608   int  fd,
609        bytes;
610   char *ptr1,
611        *ptr2;
612 
613   /* Tread prefixes (CUPS PPD/driver URIs) */
614 
615   if (!strncasecmp(uri, "driverless:", 11)) {
616     uri += 11;
617     isFax = 0;
618   }
619   else if (!strncasecmp(uri, "driverless-fax:", 15)) {
620     uri += 15;
621     isFax = 1;
622   }
623 
624   /* Request printer properties via IPP to generate a PPD file for the
625      printer */
626 
627   response = get_printer_attributes4(uri, NULL, 0, NULL, 0, 1, isFax);
628 
629   if (debug) {
630     ptr1 = get_printer_attributes_log;
631     while(ptr1) {
632       ptr2 = strchr(ptr1, '\n');
633       if (ptr2) *ptr2 = '\0';
634       fprintf(stderr, "DEBUG2: %s\n", ptr1);
635       if (ptr2) *ptr2 = '\n';
636       ptr1 = ptr2 ? (ptr2 + 1) : NULL;
637     }
638   }
639   if (response == NULL) {
640     fprintf(stderr, "ERROR: Unable to create PPD file: Could not poll "
641 	    "sufficient capability info from the printer (%s, %s) via IPP!\n",
642 	    uri, resolve_uri(uri));
643     goto fail;
644   }
645 
646   /* Generate the PPD file */
647   if (!ppdCreateFromIPP(ppdname, sizeof(ppdname), response, NULL, NULL, 0,
648 			0)) {
649     if (strlen(ppdgenerator_msg) > 0)
650       fprintf(stderr, "ERROR: Unable to create PPD file: %s\n",
651 	      ppdgenerator_msg);
652     else if (errno != 0)
653       fprintf(stderr, "ERROR: Unable to create PPD file: %s\n",
654 	      strerror(errno));
655     else
656       fprintf(stderr, "ERROR: Unable to create PPD file: Unknown reason\n");
657     goto fail;
658   } else if (debug) {
659     fprintf(stderr, "DEBUG: PPD generation successful: %s\n", ppdgenerator_msg);
660     fprintf(stderr, "DEBUG: Created temporary PPD file: %s\n", ppdname);
661   }
662 
663   ippDelete(response);
664 
665   /* Output of PPD file to stdout */
666   fd = open(ppdname, O_RDONLY);
667   while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
668     bytes = fwrite(buffer, 1, bytes, stdout);
669   close(fd);
670   unlink(ppdname);
671 
672   return 0;
673 
674  fail:
675   if (response)
676     ippDelete(response);
677 
678   return 1;
679 }
680 
681 int
main(int argc,char * argv[])682 main(int argc, char*argv[]) {
683   int i,
684       reg_type_no = 1, /* reg_type 0 for only IPP
685                                    1 for both IPPS/IPP
686                                    2 for only IPPS        Default is 1*/
687       isFax = 0;       /* if driverless-fax is called  0 - not called
688 			                               1 - called */
689   char *val;
690 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
691   struct sigaction action;		/* Actions for POSIX signals */
692 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
693 
694  /*
695   * Make sure status messages are not buffered...
696   */
697 
698   setbuf(stderr, NULL);
699 
700  /*
701   * Ignore broken pipe signals...
702   */
703 
704   signal(SIGPIPE, SIG_IGN);
705 
706  /*
707   * Register a signal handler to cleanly cancel a job.
708   */
709 
710 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
711   sigset(SIGTERM, cancel_job);
712 #elif defined(HAVE_SIGACTION)
713   memset(&action, 0, sizeof(action));
714 
715   sigemptyset(&action.sa_mask);
716   action.sa_handler = cancel_job;
717   sigaction(SIGTERM, &action, NULL);
718 #else
719   signal(SIGTERM, cancel_job);
720 #endif /* HAVE_SIGSET */
721 
722   if ((val = getenv("DEVICE_TYPE")) != NULL &&
723       strncasecmp(val, "FAX", 3) == 0) {
724     isFax = 1;
725   }
726 
727   /* Read command line options */
728   if (argc >= 2) {
729     for (i = 1; i < argc; i++)
730       if (!strcasecmp(argv[i], "--debug") || !strcasecmp(argv[i], "-d") ||
731 	  !strncasecmp(argv[i], "-v", 2)) {
732 	/* Output debug messages on stderr also when not running under CUPS
733 	   ("list" and "cat" options) */
734 	debug = 1;
735       } else if (!strcasecmp(argv[i], "list")) {
736 	/* List a driver URI and metadata for each printer suitable for
737 	   driverless printing */
738 	/* Exit immediatly when called as "driverless-fax", as CUPS always
739 	   also calls "driverless" and we list the fax PPDs already there,
740 	   to reduce the number of "ippfind" calls. */
741 	if (isFax) exit(0);
742 	uuids = cupsArrayNew((cups_array_func_t)strcmp, NULL);
743 	debug = 1;
744 	exit(list_printers(1, reg_type_no, 0));
745       } else if (!strcasecmp(argv[i], "_ipps._tcp")) {
746 	/* reg_type_no = 2 for IPPS entries only*/
747 	reg_type_no = 2;
748       } else if (!strcasecmp(argv[i], "_ipp._tcp")) {
749 	/* reg_type_no = 0 for IPP entries only*/
750 	reg_type_no = 0;
751       } else if (!strcasecmp(argv[i], "--std-ipp-uris")) {
752 	/* Show URIS in standard form */
753 	exit(list_printers(-1, reg_type_no, isFax));
754       } else if (!strncasecmp(argv[i], "cat", 3)) {
755 	/* Generate the PPD file for the given driver URI */
756 	debug = 1;
757 	val = argv[i] + 3;
758 	if (strlen(val) == 0) {
759 	  i ++;
760 	  if (i < argc && *argv[i] != '-')
761 	    val = argv[i];
762 	  else
763 	    val = NULL;
764 	}
765 	if (val) {
766 	  /* Generate PPD file */
767 	  exit(generate_ppd(val, isFax));
768 	} else {
769 	  fprintf(stderr,
770 		  "Reading command line option \"cat\", no driver URI "
771 		  "supplied.\n\n");
772 	  goto help;
773 	}
774       } else if (!strcasecmp(argv[i], "--version") ||
775 		 !strcasecmp(argv[i], "--help") ||
776 		 !strcasecmp(argv[i], "-h")) {
777 	/* Help!! */
778 	goto help;
779       } else {
780 	/* Unknown option, consider as IPP printer URI */
781 	exit(generate_ppd(argv[i], isFax));
782       }
783   }
784 
785   /* Call without arguments, list printer URIs for all suitable printers
786      when started manually, list printer URIs and metadata like CUPS
787      backends do when started as CUPS backend (discovery mode only) */
788   if ((val = getenv("SOFTWARE")) != NULL &&
789       strncasecmp(val, "CUPS", 4) == 0) {
790     /* CUPS backend in discovery mode */
791     /* Exit immediatly when called as "driverless-fax", as CUPS always
792        also calls "driverless" and the DNS-SD-service-name-based URIs
793        are the same for both printer and fax. This way we reduce the
794        number of "ippfind" calls. */
795     if (isFax) exit(0);
796     debug = 1;
797     exit(list_printers(2, reg_type_no, 0));
798   } else {
799     /* Manual call */
800     exit(list_printers(0, reg_type_no, isFax));
801   }
802 
803  help:
804 
805   fprintf(stderr,
806 	  "\ndriverless of cups-filters version "VERSION"\n\n"
807 	  "Usage: driverless [options]\n"
808 	  "Options:\n"
809 	  "  -h\n"
810 	  "  --help\n"
811 	  "  --version               Show this usage message.\n"
812 	  "  -d\n"
813 	  "  -v\n"
814 	  "  --debug                 Debug/verbose mode.\n"
815 	  "  list                    List the driver URIs and metadata for "
816 	                            "all available\n"
817 	  "                          IPP/IPPS printers supporting driverless "
818 	                            "printing\n"
819 	  "                          (to be used by CUPS).\n"
820 	  "  _ipps._tcp              Check for only IPPS printers supporting "
821 	                            "driverless\n"
822 	  "                          printing\n"
823 	  "  _ipp._tcp               Check for only IPP printers supporting "
824 	                            "driverless\n"
825 	  "                          printing\n"
826 	  "  --std-ipp-uris          Show URIS in standard form\n"
827 	  "  cat <driver URI>        Generate the PPD file for the driver URI\n"
828 	  "                          <driver URI> (to be used by CUPS).\n"
829 	  "  <printer URI>           Generate the PPD file for the IPP/IPPS "
830 	                            "printer URI\n"
831 	  "                          <printer URI>.\n"
832 	  "\n"
833 	  "When called without options, the IPP/IPPS printer URIs of all "
834 	  "available\n"
835 	  "IPP/IPPS printers will be listed.\n\n"
836 	  );
837 
838   return 1;
839 }
840 
841 /*
842  * 'cancel_job()' - Flag the job as canceled.
843  */
844 
845 static void
cancel_job(int sig)846 cancel_job(int sig)			/* I - Signal number (unused) */
847 {
848   (void)sig;
849 
850   job_canceled = 1;
851 }
852