1 /*
2 * Line Printer Daemon interface for CUPS.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright © 2007-2016 by Apple Inc.
6 * Copyright © 1997-2006 by Easy Software Products, all rights reserved.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 /*
13 * Include necessary headers...
14 */
15
16 #define _CUPS_NO_DEPRECATED
17 #include <cups/cups-private.h>
18 #include <syslog.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <netdb.h>
25
26 #ifdef HAVE_INTTYPES_H
27 # include <inttypes.h>
28 #endif /* HAVE_INTTYPES_H */
29 #ifdef __APPLE__
30 # include <xpc/xpc.h>
31 #endif /* __APPLE__ */
32
33
34 /*
35 * LPD "mini-daemon" for CUPS. This program must be used in conjunction
36 * with inetd or another similar program that monitors ports and starts
37 * daemons for each client connection. A typical configuration is:
38 *
39 * printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
40 *
41 * This daemon implements most of RFC 1179 (the unofficial LPD specification)
42 * except for:
43 *
44 * - This daemon does not check to make sure that the source port is
45 * between 721 and 731, since it isn't necessary for proper
46 * functioning and port-based security is no security at all!
47 *
48 * - The "Print any waiting jobs" command is a no-op.
49 *
50 * The LPD-to-IPP mapping is as defined in RFC 2569. The report formats
51 * currently match the Solaris LPD mini-daemon.
52 */
53
54 /*
55 * Prototypes...
56 */
57
58 static int create_job(http_t *http, const char *dest, const char *title, const char *user, int num_options, cups_option_t *options);
59 static int get_printer(http_t *http, const char *name, char *dest,
60 size_t destsize, cups_option_t **options,
61 int *accepting, int *shared, ipp_pstate_t *state);
62 static int print_file(http_t *http, int id, const char *filename,
63 const char *docname, const char *user,
64 const char *format, int last);
65 static int recv_print_job(const char *name, int num_defaults,
66 cups_option_t *defaults);
67 static int remove_jobs(const char *name, const char *agent,
68 const char *list);
69 static int send_state(const char *name, const char *list,
70 int longstatus);
71 static char *smart_gets(char *s, int len, FILE *fp);
72 static void smart_strlcpy(char *dst, const char *src, size_t dstsize);
73
74
75 /*
76 * 'main()' - Process an incoming LPD request...
77 */
78
79 int /* O - Exit status */
main(int argc,char * argv[])80 main(int argc, /* I - Number of command-line arguments */
81 char *argv[]) /* I - Command-line arguments */
82 {
83 int i; /* Looping var */
84 int num_defaults; /* Number of default options */
85 cups_option_t *defaults; /* Default options */
86 char line[256], /* Command string */
87 command, /* Command code */
88 *dest, /* Pointer to destination */
89 *list, /* Pointer to list */
90 *agent, /* Pointer to user */
91 status; /* Status for client */
92 socklen_t hostlen; /* Size of client address */
93 http_addr_t hostaddr; /* Address of client */
94 char hostname[256], /* Name of client */
95 hostip[256], /* IP address */
96 *hostfamily; /* Address family */
97 int hostlookups; /* Do hostname lookups? */
98
99
100 #ifdef __APPLE__
101 xpc_transaction_begin();
102 #endif /* __APPLE__ */
103
104 /*
105 * Don't buffer the output...
106 */
107
108 setbuf(stdout, NULL);
109
110 /*
111 * Log things using the "cups-lpd" name...
112 */
113
114 openlog("cups-lpd", LOG_PID, LOG_LPR);
115
116 /*
117 * Scan the command-line for options...
118 */
119
120 num_defaults = 0;
121 defaults = NULL;
122 hostlookups = 1;
123
124 for (i = 1; i < argc; i ++)
125 if (argv[i][0] == '-')
126 {
127 switch (argv[i][1])
128 {
129 case 'h' : /* -h hostname[:port] */
130 if (argv[i][2])
131 cupsSetServer(argv[i] + 2);
132 else
133 {
134 i ++;
135 if (i < argc)
136 cupsSetServer(argv[i]);
137 else
138 syslog(LOG_WARNING, "Expected hostname string after -h option!");
139 }
140 break;
141
142 case 'o' : /* Option */
143 if (argv[i][2])
144 num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
145 &defaults);
146 else
147 {
148 i ++;
149 if (i < argc)
150 num_defaults = cupsParseOptions(argv[i], num_defaults,
151 &defaults);
152 else
153 syslog(LOG_WARNING, "Expected option string after -o option!");
154 }
155 break;
156
157 case 'n' : /* Don't do hostname lookups */
158 hostlookups = 0;
159 break;
160
161 default :
162 syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]);
163 break;
164 }
165 }
166 else
167 syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!",
168 argv[i]);
169
170 /*
171 * Get the address of the client...
172 */
173
174 hostlen = sizeof(hostaddr);
175
176 if (getpeername(0, (struct sockaddr *)&hostaddr, &hostlen))
177 {
178 syslog(LOG_WARNING, "Unable to get client address - %s", strerror(errno));
179 strlcpy(hostname, "unknown", sizeof(hostname));
180 }
181 else
182 {
183 httpAddrString(&hostaddr, hostip, sizeof(hostip));
184
185 if (hostlookups)
186 httpAddrLookup(&hostaddr, hostname, sizeof(hostname));
187 else
188 strlcpy(hostname, hostip, sizeof(hostname));
189
190 #ifdef AF_INET6
191 if (hostaddr.addr.sa_family == AF_INET6)
192 hostfamily = "IPv6";
193 else
194 #endif /* AF_INET6 */
195 hostfamily = "IPv4";
196
197 syslog(LOG_INFO, "Connection from %s (%s %s)", hostname, hostfamily,
198 hostip);
199 }
200
201 num_defaults = cupsAddOption("job-originating-host-name", hostname,
202 num_defaults, &defaults);
203
204 /*
205 * RFC1179 specifies that only 1 daemon command can be received for
206 * every connection.
207 */
208
209 if (smart_gets(line, sizeof(line), stdin) == NULL)
210 {
211 /*
212 * Unable to get command from client! Send an error status and return.
213 */
214
215 syslog(LOG_ERR, "Unable to get command line from client!");
216 putchar(1);
217
218 #ifdef __APPLE__
219 xpc_transaction_end();
220 #endif /* __APPLE__ */
221
222 return (1);
223 }
224
225 /*
226 * The first byte is the command byte. After that will be the queue name,
227 * resource list, and/or user name.
228 */
229
230 if ((command = line[0]) == '\0')
231 dest = line;
232 else
233 dest = line + 1;
234
235 if (command == 0x02)
236 list = NULL;
237 else
238 {
239 for (list = dest; *list && !isspace(*list & 255); list ++);
240
241 while (isspace(*list & 255))
242 *list++ = '\0';
243 }
244
245 /*
246 * Do the command...
247 */
248
249 switch (command)
250 {
251 default : /* Unknown command */
252 syslog(LOG_ERR, "Unknown LPD command 0x%02X!", command);
253 syslog(LOG_ERR, "Command line = %s", line + 1);
254 putchar(1);
255
256 status = 1;
257 break;
258
259 case 0x01 : /* Print any waiting jobs */
260 syslog(LOG_INFO, "Print waiting jobs (no-op)");
261 putchar(0);
262
263 status = 0;
264 break;
265
266 case 0x02 : /* Receive a printer job */
267 syslog(LOG_INFO, "Receive print job for %s", dest);
268 /* recv_print_job() sends initial status byte */
269
270 status = (char)recv_print_job(dest, num_defaults, defaults);
271 break;
272
273 case 0x03 : /* Send queue state (short) */
274 syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list);
275 /* no status byte for this command */
276
277 status = (char)send_state(dest, list, 0);
278 break;
279
280 case 0x04 : /* Send queue state (long) */
281 syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list);
282 /* no status byte for this command */
283
284 status = (char)send_state(dest, list, 1);
285 break;
286
287 case 0x05 : /* Remove jobs */
288 /*
289 * Grab the agent and skip to the list of users and/or jobs.
290 */
291
292 agent = list;
293
294 for (; *list && !isspace(*list & 255); list ++);
295 while (isspace(*list & 255))
296 *list++ = '\0';
297
298 syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);
299
300 status = (char)remove_jobs(dest, agent, list);
301
302 putchar(status);
303 break;
304 }
305
306 syslog(LOG_INFO, "Closing connection");
307 closelog();
308
309 #ifdef __APPLE__
310 xpc_transaction_end();
311 #endif /* __APPLE__ */
312
313 return (status);
314 }
315
316
317 /*
318 * 'create_job()' - Create a new print job.
319 */
320
321 static int /* O - Job ID or -1 on error */
create_job(http_t * http,const char * dest,const char * title,const char * user,int num_options,cups_option_t * options)322 create_job(http_t *http, /* I - HTTP connection */
323 const char *dest, /* I - Destination name */
324 const char *title, /* I - job-name */
325 const char *user, /* I - requesting-user-name */
326 int num_options, /* I - Number of options for job */
327 cups_option_t *options) /* I - Options for job */
328 {
329 ipp_t *request; /* IPP request */
330 ipp_t *response; /* IPP response */
331 ipp_attribute_t *attr; /* IPP attribute */
332 char uri[HTTP_MAX_URI]; /* Printer URI */
333 int id; /* Job ID */
334
335
336 /*
337 * Setup the Create-Job request...
338 */
339
340 request = ippNewRequest(IPP_OP_CREATE_JOB);
341
342 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
343 "localhost", 0, "/printers/%s", dest);
344
345 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
346 NULL, uri);
347
348 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
349 "requesting-user-name", NULL, user);
350
351 if (title[0])
352 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
353 NULL, title);
354
355 cupsEncodeOptions(request, num_options, options);
356
357 /*
358 * Do the request...
359 */
360
361 snprintf(uri, sizeof(uri), "/printers/%s", dest);
362
363 response = cupsDoRequest(http, request, uri);
364
365 if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING)
366 {
367 syslog(LOG_ERR, "Unable to create job - %s", cupsLastErrorString());
368
369 ippDelete(response);
370
371 return (-1);
372 }
373
374 /*
375 * Get the job-id value from the response and return it...
376 */
377
378 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
379 {
380 id = -1;
381
382 syslog(LOG_ERR, "No job-id attribute found in response from server!");
383 }
384 else
385 {
386 id = attr->values[0].integer;
387
388 syslog(LOG_INFO, "Print file - job ID = %d", id);
389 }
390
391 ippDelete(response);
392
393 return (id);
394 }
395
396
397 /*
398 * 'get_printer()' - Get the named printer and its options.
399 */
400
401 static int /* O - Number of options or -1 on error */
get_printer(http_t * http,const char * name,char * dest,size_t destsize,cups_option_t ** options,int * accepting,int * shared,ipp_pstate_t * state)402 get_printer(http_t *http, /* I - HTTP connection */
403 const char *name, /* I - Printer name from request */
404 char *dest, /* I - Destination buffer */
405 size_t destsize, /* I - Size of destination buffer */
406 cups_option_t **options, /* O - Printer options */
407 int *accepting, /* O - printer-is-accepting-jobs value */
408 int *shared, /* O - printer-is-shared value */
409 ipp_pstate_t *state) /* O - printer-state value */
410 {
411 int num_options; /* Number of options */
412 cups_file_t *fp; /* lpoptions file */
413 char line[1024], /* Line from lpoptions file */
414 *value, /* Pointer to value on line */
415 *optptr; /* Pointer to options on line */
416 int linenum; /* Line number in file */
417 const char *cups_serverroot; /* CUPS_SERVERROOT env var */
418 ipp_t *request; /* IPP request */
419 ipp_t *response; /* IPP response */
420 ipp_attribute_t *attr; /* IPP attribute */
421 char uri[HTTP_MAX_URI]; /* Printer URI */
422 static const char * const requested[] =
423 { /* Requested attributes */
424 "printer-info",
425 "printer-is-accepting-jobs",
426 "printer-is-shared",
427 "printer-name",
428 "printer-state"
429 };
430
431
432 /*
433 * Initialize everything...
434 */
435
436 if (accepting)
437 *accepting = 0;
438 if (shared)
439 *shared = 0;
440 if (state)
441 *state = IPP_PSTATE_STOPPED;
442 if (options)
443 *options = NULL;
444
445 /*
446 * See if the name is a queue name optionally with an instance name.
447 */
448
449 strlcpy(dest, name, destsize);
450 if ((value = strchr(dest, '/')) != NULL)
451 *value = '\0';
452
453 /*
454 * Setup the Get-Printer-Attributes request...
455 */
456
457 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
458
459 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
460 "localhost", 0, "/printers/%s", dest);
461
462 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
463 NULL, uri);
464
465 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
466 "requested-attributes",
467 (int)(sizeof(requested) / sizeof(requested[0])),
468 NULL, requested);
469
470 /*
471 * Do the request...
472 */
473
474 response = cupsDoRequest(http, request, "/");
475
476 if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING)
477 {
478 /*
479 * If we can't find the printer by name, look up the printer-name
480 * using the printer-info values...
481 */
482
483 ipp_attribute_t *accepting_attr,/* printer-is-accepting-jobs */
484 *info_attr, /* printer-info */
485 *name_attr, /* printer-name */
486 *shared_attr, /* printer-is-shared */
487 *state_attr; /* printer-state */
488
489
490 ippDelete(response);
491
492 /*
493 * Setup the CUPS-Get-Printers request...
494 */
495
496 request = ippNewRequest(IPP_OP_CUPS_GET_PRINTERS);
497
498 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
499 "requested-attributes",
500 (int)(sizeof(requested) / sizeof(requested[0])),
501 NULL, requested);
502
503 /*
504 * Do the request...
505 */
506
507 response = cupsDoRequest(http, request, "/");
508
509 if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING)
510 {
511 syslog(LOG_ERR, "Unable to get list of printers - %s",
512 cupsLastErrorString());
513
514 ippDelete(response);
515
516 return (-1);
517 }
518
519 /*
520 * Scan the response for printers...
521 */
522
523 *dest = '\0';
524 attr = response->attrs;
525
526 while (attr)
527 {
528 /*
529 * Skip to the next printer...
530 */
531
532 while (attr && attr->group_tag != IPP_TAG_PRINTER)
533 attr = attr->next;
534
535 if (!attr)
536 break;
537
538 /*
539 * Get all of the attributes for the current printer...
540 */
541
542 accepting_attr = NULL;
543 info_attr = NULL;
544 name_attr = NULL;
545 shared_attr = NULL;
546 state_attr = NULL;
547
548 while (attr && attr->group_tag == IPP_TAG_PRINTER)
549 {
550 if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
551 attr->value_tag == IPP_TAG_BOOLEAN)
552 accepting_attr = attr;
553 else if (!strcmp(attr->name, "printer-info") &&
554 attr->value_tag == IPP_TAG_TEXT)
555 info_attr = attr;
556 else if (!strcmp(attr->name, "printer-name") &&
557 attr->value_tag == IPP_TAG_NAME)
558 name_attr = attr;
559 else if (!strcmp(attr->name, "printer-is-shared") &&
560 attr->value_tag == IPP_TAG_BOOLEAN)
561 shared_attr = attr;
562 else if (!strcmp(attr->name, "printer-state") &&
563 attr->value_tag == IPP_TAG_ENUM)
564 state_attr = attr;
565
566 attr = attr->next;
567 }
568
569 if (info_attr && name_attr &&
570 !_cups_strcasecmp(name, info_attr->values[0].string.text))
571 {
572 /*
573 * Found a match, use this one!
574 */
575
576 strlcpy(dest, name_attr->values[0].string.text, destsize);
577
578 if (accepting && accepting_attr)
579 *accepting = accepting_attr->values[0].boolean;
580
581 if (shared && shared_attr)
582 *shared = shared_attr->values[0].boolean;
583
584 if (state && state_attr)
585 *state = (ipp_pstate_t)state_attr->values[0].integer;
586
587 break;
588 }
589 }
590
591 ippDelete(response);
592
593 if (!*dest)
594 {
595 syslog(LOG_ERR, "Unable to find \"%s\" in list of printers!", name);
596
597 return (-1);
598 }
599
600 name = dest;
601 }
602 else
603 {
604 /*
605 * Get values from the response...
606 */
607
608 if (accepting)
609 {
610 if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
611 IPP_TAG_BOOLEAN)) == NULL)
612 syslog(LOG_ERR, "No printer-is-accepting-jobs attribute found in "
613 "response from server!");
614 else
615 *accepting = attr->values[0].boolean;
616 }
617
618 if (shared)
619 {
620 if ((attr = ippFindAttribute(response, "printer-is-shared",
621 IPP_TAG_BOOLEAN)) == NULL)
622 {
623 syslog(LOG_ERR, "No printer-is-shared attribute found in "
624 "response from server!");
625 *shared = 1;
626 }
627 else
628 *shared = attr->values[0].boolean;
629 }
630
631 if (state)
632 {
633 if ((attr = ippFindAttribute(response, "printer-state",
634 IPP_TAG_ENUM)) == NULL)
635 syslog(LOG_ERR, "No printer-state attribute found in "
636 "response from server!");
637 else
638 *state = (ipp_pstate_t)attr->values[0].integer;
639 }
640
641 ippDelete(response);
642 }
643
644 /*
645 * Next look for the printer in the lpoptions file...
646 */
647
648 num_options = 0;
649
650 if (options && shared && accepting)
651 {
652 if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
653 cups_serverroot = CUPS_SERVERROOT;
654
655 snprintf(line, sizeof(line), "%s/lpoptions", cups_serverroot);
656 if ((fp = cupsFileOpen(line, "r")) != NULL)
657 {
658 linenum = 0;
659 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
660 {
661 /*
662 * Make sure we have "Dest name options" or "Default name options"...
663 */
664
665 if ((_cups_strcasecmp(line, "Dest") && _cups_strcasecmp(line, "Default")) || !value)
666 continue;
667
668 /*
669 * Separate destination name from options...
670 */
671
672 for (optptr = value; *optptr && !isspace(*optptr & 255); optptr ++);
673
674 while (*optptr == ' ')
675 *optptr++ = '\0';
676
677 /*
678 * If this is our destination, parse the options and break out of
679 * the loop - we're done!
680 */
681
682 if (!_cups_strcasecmp(value, name))
683 {
684 num_options = cupsParseOptions(optptr, num_options, options);
685 break;
686 }
687 }
688
689 cupsFileClose(fp);
690 }
691 }
692 else if (options)
693 *options = NULL;
694
695 /*
696 * Return the number of options for this destination...
697 */
698
699 return (num_options);
700 }
701
702
703 /*
704 * 'print_file()' - Add a file to the current job.
705 */
706
707 static int /* O - 0 on success, -1 on failure */
print_file(http_t * http,int id,const char * filename,const char * docname,const char * user,const char * format,int last)708 print_file(http_t *http, /* I - HTTP connection */
709 int id, /* I - Job ID */
710 const char *filename, /* I - File to print */
711 const char *docname, /* I - document-name */
712 const char *user, /* I - requesting-user-name */
713 const char *format, /* I - document-format */
714 int last) /* I - 1 = last file in job */
715 {
716 ipp_t *request; /* IPP request */
717 char uri[HTTP_MAX_URI]; /* Printer URI */
718
719
720 /*
721 * Setup the Send-Document request...
722 */
723
724 request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
725
726 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", id);
727 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
728
729 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
730 "requesting-user-name", NULL, user);
731
732 if (docname)
733 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
734 "document-name", NULL, docname);
735
736 if (format)
737 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
738 "document-format", NULL, format);
739
740 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last);
741
742 /*
743 * Do the request...
744 */
745
746 snprintf(uri, sizeof(uri), "/jobs/%d", id);
747
748 ippDelete(cupsDoFileRequest(http, request, uri, filename));
749
750 if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
751 {
752 syslog(LOG_ERR, "Unable to send document - %s", cupsLastErrorString());
753
754 return (-1);
755 }
756
757 return (0);
758 }
759
760
761 /*
762 * 'recv_print_job()' - Receive a print job from the client.
763 */
764
765 static int /* O - Command status */
recv_print_job(const char * queue,int num_defaults,cups_option_t * defaults)766 recv_print_job(
767 const char *queue, /* I - Printer name */
768 int num_defaults, /* I - Number of default options */
769 cups_option_t *defaults) /* I - Default options */
770 {
771 http_t *http; /* HTTP connection */
772 int i; /* Looping var */
773 int status; /* Command status */
774 int fd; /* Temporary file */
775 FILE *fp; /* File pointer */
776 char filename[1024]; /* Temporary filename */
777 ssize_t bytes; /* Bytes received */
778 size_t total; /* Total bytes */
779 char line[256], /* Line from file/stdin */
780 command, /* Command from line */
781 *count, /* Number of bytes */
782 *name; /* Name of file */
783 const char *job_sheets; /* Job sheets */
784 int num_data; /* Number of data files */
785 char control[1024], /* Control filename */
786 data[100][256], /* Data files */
787 temp[100][1024]; /* Temporary files */
788 char user[1024], /* User name */
789 title[1024], /* Job title */
790 docname[1024], /* Document name */
791 dest[256]; /* Printer/class queue */
792 int accepting, /* printer-is-accepting */
793 shared, /* printer-is-shared */
794 num_options; /* Number of options */
795 cups_option_t *options; /* Options */
796 int id; /* Job ID */
797 int docnumber, /* Current document number */
798 doccount; /* Count of documents */
799
800
801 /*
802 * Connect to the server...
803 */
804
805 http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL);
806 if (!http)
807 {
808 syslog(LOG_ERR, "Unable to connect to server: %s", strerror(errno));
809
810 putchar(1);
811
812 return (1);
813 }
814
815 /*
816 * See if the printer is available...
817 */
818
819 num_options = get_printer(http, queue, dest, sizeof(dest), &options,
820 &accepting, &shared, NULL);
821
822 if (num_options < 0 || !accepting || !shared)
823 {
824 if (dest[0])
825 syslog(LOG_INFO, "Rejecting job because \"%s\" is not %s", dest,
826 !accepting ? "accepting jobs" : "shared");
827 else
828 syslog(LOG_ERR, "Unable to get printer information for \"%s\"", queue);
829
830 httpClose(http);
831
832 putchar(1);
833
834 return (1);
835 }
836
837 putchar(0); /* OK so far... */
838
839 /*
840 * Read the request...
841 */
842
843 status = 0;
844 num_data = 0;
845 fd = -1;
846
847 control[0] = '\0';
848
849 while (smart_gets(line, sizeof(line), stdin) != NULL)
850 {
851 if (strlen(line) < 2)
852 {
853 status = 1;
854 break;
855 }
856
857 command = line[0];
858 count = line + 1;
859
860 for (name = count + 1; *name && !isspace(*name & 255); name ++);
861 while (isspace(*name & 255))
862 *name++ = '\0';
863
864 switch (command)
865 {
866 default :
867 case 0x01 : /* Abort */
868 status = 1;
869 break;
870
871 case 0x02 : /* Receive control file */
872 if (strlen(name) < 2)
873 {
874 syslog(LOG_ERR, "Bad control file name \"%s\"", name);
875 putchar(1);
876 status = 1;
877 break;
878 }
879
880 if (control[0])
881 {
882 /*
883 * Append to the existing control file - the LPD spec is
884 * not entirely clear, but at least the OS/2 LPD code sends
885 * multiple control files per connection...
886 */
887
888 if ((fd = open(control, O_WRONLY)) < 0)
889 {
890 syslog(LOG_ERR,
891 "Unable to append to temporary control file \"%s\" - %s",
892 control, strerror(errno));
893 putchar(1);
894 status = 1;
895 break;
896 }
897
898 lseek(fd, 0, SEEK_END);
899 }
900 else
901 {
902 if ((fd = cupsTempFd(control, sizeof(control))) < 0)
903 {
904 syslog(LOG_ERR, "Unable to open temporary control file \"%s\" - %s",
905 control, strerror(errno));
906 putchar(1);
907 status = 1;
908 break;
909 }
910
911 strlcpy(filename, control, sizeof(filename));
912 }
913 break;
914
915 case 0x03 : /* Receive data file */
916 if (strlen(name) < 2)
917 {
918 syslog(LOG_ERR, "Bad data file name \"%s\"", name);
919 putchar(1);
920 status = 1;
921 break;
922 }
923
924 if (num_data >= (int)(sizeof(data) / sizeof(data[0])))
925 {
926 /*
927 * Too many data files...
928 */
929
930 syslog(LOG_ERR, "Too many data files (%d)", num_data);
931 putchar(1);
932 status = 1;
933 break;
934 }
935
936 strlcpy(data[num_data], name, sizeof(data[0]));
937
938 if ((fd = cupsTempFd(temp[num_data], sizeof(temp[0]))) < 0)
939 {
940 syslog(LOG_ERR, "Unable to open temporary data file \"%s\" - %s",
941 temp[num_data], strerror(errno));
942 putchar(1);
943 status = 1;
944 break;
945 }
946
947 strlcpy(filename, temp[num_data], sizeof(filename));
948
949 num_data ++;
950 break;
951 }
952
953 putchar(status);
954
955 if (status)
956 break;
957
958 /*
959 * Copy the data or control file from the client...
960 */
961
962 for (total = (size_t)strtoll(count, NULL, 10); total > 0; total -= (size_t)bytes)
963 {
964 if (total > sizeof(line))
965 bytes = (ssize_t)sizeof(line);
966 else
967 bytes = (ssize_t)total;
968
969 if ((bytes = (ssize_t)fread(line, 1, (size_t)bytes, stdin)) > 0)
970 bytes = write(fd, line, (size_t)bytes);
971
972 if (bytes < 1)
973 {
974 syslog(LOG_ERR, "Error while reading file - %s",
975 strerror(errno));
976 status = 1;
977 break;
978 }
979 }
980
981 /*
982 * Read trailing nul...
983 */
984
985 if (!status)
986 {
987 if (fread(line, 1, 1, stdin) < 1)
988 {
989 status = 1;
990 syslog(LOG_ERR, "Error while reading trailing nul - %s",
991 strerror(errno));
992 }
993 else if (line[0])
994 {
995 status = 1;
996 syslog(LOG_ERR, "Trailing character after file is not nul (%02X)!",
997 line[0]);
998 }
999 }
1000
1001 /*
1002 * Close the file and send an acknowledgement...
1003 */
1004
1005 close(fd);
1006
1007 putchar(status);
1008
1009 if (status)
1010 break;
1011 }
1012
1013 if (!status)
1014 {
1015 /*
1016 * Process the control file and print stuff...
1017 */
1018
1019 if ((fp = fopen(control, "rb")) == NULL)
1020 status = 1;
1021 else
1022 {
1023 /*
1024 * Copy the default options...
1025 */
1026
1027 for (i = 0; i < num_defaults; i ++)
1028 num_options = cupsAddOption(defaults[i].name,
1029 defaults[i].value,
1030 num_options, &options);
1031
1032 /*
1033 * Grab the job information...
1034 */
1035
1036 title[0] = '\0';
1037 user[0] = '\0';
1038 docname[0] = '\0';
1039 doccount = 0;
1040
1041 while (smart_gets(line, sizeof(line), fp) != NULL)
1042 {
1043 /*
1044 * Process control lines...
1045 */
1046
1047 switch (line[0])
1048 {
1049 case 'J' : /* Job name */
1050 smart_strlcpy(title, line + 1, sizeof(title));
1051 break;
1052
1053 case 'N' : /* Document name */
1054 smart_strlcpy(docname, line + 1, sizeof(docname));
1055 break;
1056
1057 case 'P' : /* User identification */
1058 smart_strlcpy(user, line + 1, sizeof(user));
1059 break;
1060
1061 case 'L' : /* Print banner page */
1062 /*
1063 * If a banner was requested and it's not overridden by a
1064 * command line option and the destination's default is none
1065 * then add the standard banner...
1066 */
1067
1068 if (cupsGetOption("job-sheets", num_defaults, defaults) == NULL &&
1069 ((job_sheets = cupsGetOption("job-sheets", num_options,
1070 options)) == NULL ||
1071 !strcmp(job_sheets, "none,none")))
1072 {
1073 num_options = cupsAddOption("job-sheets", "standard",
1074 num_options, &options);
1075 }
1076 break;
1077
1078 case 'c' : /* Plot CIF file */
1079 case 'd' : /* Print DVI file */
1080 case 'f' : /* Print formatted file */
1081 case 'g' : /* Plot file */
1082 case 'l' : /* Print file leaving control characters (raw) */
1083 case 'n' : /* Print ditroff output file */
1084 case 'o' : /* Print PostScript output file */
1085 case 'p' : /* Print file with 'pr' format (prettyprint) */
1086 case 'r' : /* File to print with FORTRAN carriage control */
1087 case 't' : /* Print troff output file */
1088 case 'v' : /* Print raster file */
1089 doccount ++;
1090
1091 if (line[0] == 'l' &&
1092 !cupsGetOption("document-format", num_options, options))
1093 num_options = cupsAddOption("raw", "", num_options, &options);
1094
1095 if (line[0] == 'p')
1096 num_options = cupsAddOption("prettyprint", "", num_options,
1097 &options);
1098 break;
1099 }
1100 }
1101
1102 /*
1103 * Check that we have a username...
1104 */
1105
1106 if (!user[0])
1107 {
1108 syslog(LOG_WARNING, "No username specified by client! "
1109 "Using \"anonymous\"...");
1110 strlcpy(user, "anonymous", sizeof(user));
1111 }
1112
1113 /*
1114 * Create the job...
1115 */
1116
1117 if ((id = create_job(http, dest, title, user, num_options, options)) < 0)
1118 status = 1;
1119 else
1120 {
1121 /*
1122 * Then print the job files...
1123 */
1124
1125 rewind(fp);
1126
1127 docname[0] = '\0';
1128 docnumber = 0;
1129
1130 while (smart_gets(line, sizeof(line), fp) != NULL)
1131 {
1132 /*
1133 * Process control lines...
1134 */
1135
1136 switch (line[0])
1137 {
1138 case 'N' : /* Document name */
1139 smart_strlcpy(docname, line + 1, sizeof(docname));
1140 break;
1141
1142 case 'c' : /* Plot CIF file */
1143 case 'd' : /* Print DVI file */
1144 case 'f' : /* Print formatted file */
1145 case 'g' : /* Plot file */
1146 case 'l' : /* Print file leaving control characters (raw) */
1147 case 'n' : /* Print ditroff output file */
1148 case 'o' : /* Print PostScript output file */
1149 case 'p' : /* Print file with 'pr' format (prettyprint) */
1150 case 'r' : /* File to print with FORTRAN carriage control */
1151 case 't' : /* Print troff output file */
1152 case 'v' : /* Print raster file */
1153 /*
1154 * Figure out which file we are printing...
1155 */
1156
1157 for (i = 0; i < num_data; i ++)
1158 if (!strcmp(data[i], line + 1))
1159 break;
1160
1161 if (i >= num_data)
1162 {
1163 status = 1;
1164 break;
1165 }
1166
1167 /*
1168 * Send the print file...
1169 */
1170
1171 docnumber ++;
1172
1173 if (print_file(http, id, temp[i], docname, user,
1174 cupsGetOption("document-format", num_options,
1175 options),
1176 docnumber == doccount))
1177 status = 1;
1178 else
1179 status = 0;
1180
1181 break;
1182 }
1183
1184 if (status)
1185 break;
1186 }
1187 }
1188
1189 fclose(fp);
1190 }
1191 }
1192
1193 cupsFreeOptions(num_options, options);
1194
1195 httpClose(http);
1196
1197 /*
1198 * Clean up all temporary files and return...
1199 */
1200
1201 unlink(control);
1202
1203 for (i = 0; i < num_data; i ++)
1204 unlink(temp[i]);
1205
1206 return (status);
1207 }
1208
1209
1210 /*
1211 * 'remove_jobs()' - Cancel one or more jobs.
1212 */
1213
1214 static int /* O - Command status */
remove_jobs(const char * dest,const char * agent,const char * list)1215 remove_jobs(const char *dest, /* I - Destination */
1216 const char *agent, /* I - User agent */
1217 const char *list) /* I - List of jobs or users */
1218 {
1219 int id; /* Job ID */
1220 http_t *http; /* HTTP server connection */
1221 ipp_t *request; /* IPP Request */
1222 char uri[HTTP_MAX_URI]; /* Job URI */
1223
1224
1225 (void)dest; /* Suppress compiler warnings... */
1226
1227 /*
1228 * Try connecting to the local server...
1229 */
1230
1231 if ((http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL)) == NULL)
1232 {
1233 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1234 strerror(errno));
1235 return (1);
1236 }
1237
1238 /*
1239 * Loop for each job...
1240 */
1241
1242 while ((id = atoi(list)) > 0)
1243 {
1244 /*
1245 * Skip job ID in list...
1246 */
1247
1248 while (isdigit(*list & 255))
1249 list ++;
1250 while (isspace(*list & 255))
1251 list ++;
1252
1253 /*
1254 * Build an IPP_OP_CANCEL_JOB request, which requires the following
1255 * attributes:
1256 *
1257 * attributes-charset
1258 * attributes-natural-language
1259 * job-uri
1260 * requesting-user-name
1261 */
1262
1263 request = ippNewRequest(IPP_OP_CANCEL_JOB);
1264
1265 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", id);
1266 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
1267
1268 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1269 "requesting-user-name", NULL, agent);
1270
1271 /*
1272 * Do the request and get back a response...
1273 */
1274
1275 ippDelete(cupsDoRequest(http, request, "/jobs"));
1276
1277 if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1278 {
1279 syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
1280 cupsLastErrorString());
1281 httpClose(http);
1282 return (1);
1283 }
1284 else
1285 syslog(LOG_INFO, "Job ID %d canceled", id);
1286 }
1287
1288 httpClose(http);
1289
1290 return (0);
1291 }
1292
1293
1294 /*
1295 * 'send_state()' - Send the queue state.
1296 */
1297
1298 static int /* O - Command status */
send_state(const char * queue,const char * list,int longstatus)1299 send_state(const char *queue, /* I - Destination */
1300 const char *list, /* I - Job or user */
1301 int longstatus) /* I - List of jobs or users */
1302 {
1303 int id; /* Job ID from list */
1304 http_t *http; /* HTTP server connection */
1305 ipp_t *request, /* IPP Request */
1306 *response; /* IPP Response */
1307 ipp_attribute_t *attr; /* Current attribute */
1308 ipp_pstate_t state; /* Printer state */
1309 const char *jobdest, /* Pointer into job-printer-uri */
1310 *jobuser, /* Pointer to job-originating-user-name */
1311 *jobname; /* Pointer to job-name */
1312 ipp_jstate_t jobstate; /* job-state */
1313 int jobid, /* job-id */
1314 jobsize, /* job-k-octets */
1315 jobcount, /* Number of jobs */
1316 jobcopies, /* Number of copies */
1317 rank; /* Rank of job */
1318 char rankstr[255]; /* Rank string */
1319 char namestr[1024]; /* Job name string */
1320 char uri[HTTP_MAX_URI]; /* Printer URI */
1321 char dest[256]; /* Printer/class queue */
1322 static const char * const ranks[10] = /* Ranking strings */
1323 {
1324 "th",
1325 "st",
1326 "nd",
1327 "rd",
1328 "th",
1329 "th",
1330 "th",
1331 "th",
1332 "th",
1333 "th"
1334 };
1335 static const char * const requested[] =
1336 { /* Requested attributes */
1337 "job-id",
1338 "job-k-octets",
1339 "job-state",
1340 "job-printer-uri",
1341 "job-originating-user-name",
1342 "job-name",
1343 "copies"
1344 };
1345
1346
1347 /*
1348 * Try connecting to the local server...
1349 */
1350
1351 if ((http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL)) == NULL)
1352 {
1353 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1354 strerror(errno));
1355 printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno));
1356 return (1);
1357 }
1358
1359 /*
1360 * Get the actual destination name and printer state...
1361 */
1362
1363 if (get_printer(http, queue, dest, sizeof(dest), NULL, NULL, NULL, &state))
1364 {
1365 syslog(LOG_ERR, "Unable to get printer %s: %s", queue,
1366 cupsLastErrorString());
1367 printf("Unable to get printer %s: %s", queue, cupsLastErrorString());
1368 return (1);
1369 }
1370
1371 /*
1372 * Show the queue state...
1373 */
1374
1375 switch (state)
1376 {
1377 case IPP_PSTATE_IDLE :
1378 printf("%s is ready\n", dest);
1379 break;
1380 case IPP_PSTATE_PROCESSING :
1381 printf("%s is ready and printing\n", dest);
1382 break;
1383 case IPP_PSTATE_STOPPED :
1384 printf("%s is not ready\n", dest);
1385 break;
1386 }
1387
1388 /*
1389 * Build an IPP_OP_GET_JOBS or IPP_OP_GET_JOB_ATTRIBUTES request, which requires
1390 * the following attributes:
1391 *
1392 * attributes-charset
1393 * attributes-natural-language
1394 * job-uri or printer-uri
1395 */
1396
1397 id = atoi(list);
1398
1399 request = ippNewRequest(id ? IPP_OP_GET_JOB_ATTRIBUTES : IPP_OP_GET_JOBS);
1400
1401 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1402 "localhost", 0, "/printers/%s", dest);
1403
1404 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1405 NULL, uri);
1406
1407 if (id)
1408 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1409 else
1410 {
1411 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1412 "requesting-user-name", NULL, list);
1413 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
1414 }
1415
1416 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1417 "requested-attributes",
1418 sizeof(requested) / sizeof(requested[0]),
1419 NULL, requested);
1420
1421 /*
1422 * Do the request and get back a response...
1423 */
1424
1425 jobcount = 0;
1426 response = cupsDoRequest(http, request, "/");
1427
1428 if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1429 {
1430 printf("get-jobs failed: %s\n", cupsLastErrorString());
1431 ippDelete(response);
1432 return (1);
1433 }
1434
1435 /*
1436 * Loop through the job list and display them...
1437 */
1438
1439 for (attr = response->attrs, rank = 1; attr; attr = attr->next)
1440 {
1441 /*
1442 * Skip leading attributes until we hit a job...
1443 */
1444
1445 while (attr && (attr->group_tag != IPP_TAG_JOB || !attr->name))
1446 attr = attr->next;
1447
1448 if (!attr)
1449 break;
1450
1451 /*
1452 * Pull the needed attributes from this job...
1453 */
1454
1455 jobid = 0;
1456 jobsize = 0;
1457 jobstate = IPP_JSTATE_PENDING;
1458 jobname = "untitled";
1459 jobuser = NULL;
1460 jobdest = NULL;
1461 jobcopies = 1;
1462
1463 while (attr && attr->group_tag == IPP_TAG_JOB)
1464 {
1465 if (!strcmp(attr->name, "job-id") &&
1466 attr->value_tag == IPP_TAG_INTEGER)
1467 jobid = attr->values[0].integer;
1468
1469 if (!strcmp(attr->name, "job-k-octets") &&
1470 attr->value_tag == IPP_TAG_INTEGER)
1471 jobsize = attr->values[0].integer;
1472
1473 if (!strcmp(attr->name, "job-state") &&
1474 attr->value_tag == IPP_TAG_ENUM)
1475 jobstate = (ipp_jstate_t)attr->values[0].integer;
1476
1477 if (!strcmp(attr->name, "job-printer-uri") &&
1478 attr->value_tag == IPP_TAG_URI)
1479 if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
1480 jobdest ++;
1481
1482 if (!strcmp(attr->name, "job-originating-user-name") &&
1483 attr->value_tag == IPP_TAG_NAME)
1484 jobuser = attr->values[0].string.text;
1485
1486 if (!strcmp(attr->name, "job-name") &&
1487 attr->value_tag == IPP_TAG_NAME)
1488 jobname = attr->values[0].string.text;
1489
1490 if (!strcmp(attr->name, "copies") &&
1491 attr->value_tag == IPP_TAG_INTEGER)
1492 jobcopies = attr->values[0].integer;
1493
1494 attr = attr->next;
1495 }
1496
1497 /*
1498 * See if we have everything needed...
1499 */
1500
1501 if (!jobdest || !jobid)
1502 {
1503 if (!attr)
1504 break;
1505 else
1506 continue;
1507 }
1508
1509 if (!longstatus && jobcount == 0)
1510 puts("Rank Owner Job File(s) Total Size");
1511
1512 jobcount ++;
1513
1514 /*
1515 * Display the job...
1516 */
1517
1518 if (jobstate == IPP_JSTATE_PROCESSING)
1519 strlcpy(rankstr, "active", sizeof(rankstr));
1520 else
1521 {
1522 snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
1523 rank ++;
1524 }
1525
1526 if (longstatus)
1527 {
1528 puts("");
1529
1530 if (jobcopies > 1)
1531 snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
1532 jobname);
1533 else
1534 strlcpy(namestr, jobname, sizeof(namestr));
1535
1536 printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid);
1537 printf(" %-39.39s %.0f bytes\n", namestr, 1024.0 * jobsize);
1538 }
1539 else
1540 printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr, jobuser,
1541 jobid, jobname, 1024.0 * jobsize);
1542
1543 if (!attr)
1544 break;
1545 }
1546
1547 ippDelete(response);
1548
1549 if (jobcount == 0)
1550 puts("no entries");
1551
1552 httpClose(http);
1553
1554 return (0);
1555 }
1556
1557
1558 /*
1559 * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1560 */
1561
1562 static char * /* O - Line read or NULL */
smart_gets(char * s,int len,FILE * fp)1563 smart_gets(char *s, /* I - Pointer to line buffer */
1564 int len, /* I - Size of line buffer */
1565 FILE *fp) /* I - File to read from */
1566 {
1567 char *ptr, /* Pointer into line */
1568 *end; /* End of line */
1569 int ch; /* Character from file */
1570
1571
1572 /*
1573 * Read the line; unlike fgets(), we read the entire line but dump
1574 * characters that go past the end of the buffer. Also, we accept
1575 * CR, LF, or CR LF for the line endings to be "safe", although
1576 * RFC 1179 specifically says "just use LF".
1577 */
1578
1579 ptr = s;
1580 end = s + len - 1;
1581
1582 while ((ch = getc(fp)) != EOF)
1583 {
1584 if (ch == '\n')
1585 break;
1586 else if (ch == '\r')
1587 {
1588 /*
1589 * See if a LF follows...
1590 */
1591
1592 ch = getc(fp);
1593
1594 if (ch != '\n')
1595 ungetc(ch, fp);
1596
1597 break;
1598 }
1599 else if (ptr < end)
1600 *ptr++ = (char)ch;
1601 }
1602
1603 *ptr = '\0';
1604
1605 if (ch == EOF && ptr == s)
1606 return (NULL);
1607 else
1608 return (s);
1609 }
1610
1611
1612 /*
1613 * 'smart_strlcpy()' - Copy a string and convert from ISO-8859-1 to UTF-8 as needed.
1614 */
1615
1616 static void
smart_strlcpy(char * dst,const char * src,size_t dstsize)1617 smart_strlcpy(char *dst, /* I - Output buffer */
1618 const char *src, /* I - Input string */
1619 size_t dstsize) /* I - Size of output buffer */
1620 {
1621 const unsigned char *srcptr; /* Pointer into input string */
1622 unsigned char *dstptr, /* Pointer into output buffer */
1623 *dstend; /* End of output buffer */
1624 int saw_8859 = 0; /* Saw an extended character that was not UTF-8? */
1625
1626
1627 for (srcptr = (unsigned char *)src, dstptr = (unsigned char *)dst, dstend = dstptr + dstsize - 1; *srcptr;)
1628 {
1629 if (*srcptr < 0x80)
1630 *dstptr++ = *srcptr++; /* ASCII */
1631 else if (saw_8859)
1632 {
1633 /*
1634 * Map ISO-8859-1 (most likely character set for legacy LPD clients) to
1635 * UTF-8...
1636 */
1637
1638 if (dstptr > (dstend - 2))
1639 break;
1640
1641 *dstptr++ = 0xc0 | (*srcptr >> 6);
1642 *dstptr++ = 0x80 | (*srcptr++ & 0x3f);
1643 }
1644 else if ((*srcptr & 0xe0) == 0xc0 && (srcptr[1] & 0xc0) == 0x80)
1645 {
1646 /*
1647 * 2-byte UTF-8 sequence...
1648 */
1649
1650 if (dstptr > (dstend - 2))
1651 break;
1652
1653 *dstptr++ = *srcptr++;
1654 *dstptr++ = *srcptr++;
1655 }
1656 else if ((*srcptr & 0xf0) == 0xe0 && (srcptr[1] & 0xc0) == 0x80 && (srcptr[2] & 0xc0) == 0x80)
1657 {
1658 /*
1659 * 3-byte UTF-8 sequence...
1660 */
1661
1662 if (dstptr > (dstend - 3))
1663 break;
1664
1665 *dstptr++ = *srcptr++;
1666 *dstptr++ = *srcptr++;
1667 *dstptr++ = *srcptr++;
1668 }
1669 else if ((*srcptr & 0xf8) == 0xf0 && (srcptr[1] & 0xc0) == 0x80 && (srcptr[2] & 0xc0) == 0x80 && (srcptr[3] & 0xc0) == 0x80)
1670 {
1671 /*
1672 * 4-byte UTF-8 sequence...
1673 */
1674
1675 if (dstptr > (dstend - 4))
1676 break;
1677
1678 *dstptr++ = *srcptr++;
1679 *dstptr++ = *srcptr++;
1680 *dstptr++ = *srcptr++;
1681 *dstptr++ = *srcptr++;
1682 }
1683 else
1684 {
1685 /*
1686 * Bad UTF-8 sequence, this must be an ISO-8859-1 string...
1687 */
1688
1689 saw_8859 = 1;
1690
1691 if (dstptr > (dstend - 2))
1692 break;
1693
1694 *dstptr++ = 0xc0 | (*srcptr >> 6);
1695 *dstptr++ = 0x80 | (*srcptr++ & 0x3f);
1696 }
1697 }
1698
1699 *dstptr = '\0';
1700 }
1701