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