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