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