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