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