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