]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cups-lpd.c
74b1e5af7079c83e87ad96068795129f8340584b
[thirdparty/cups.git] / scheduler / cups-lpd.c
1 /*
2 * "$Id: cups-lpd.c,v 1.24.2.15 2004/02/17 21:32:58 mike Exp $"
3 *
4 * Line Printer Daemon interface for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2003 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-3111 USA
19 *
20 * Voice: (301) 373-9603
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 int 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); list ++);
212
213 while (isspace(*list))
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); list ++);
266 while (isspace(*list))
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 * 'print_file()' - Print a file to a printer or class.
286 */
287
288 int /* O - Job ID */
289 print_file(const char *name, /* I - Printer or class name */
290 const char *file, /* I - File to print */
291 const char *title, /* I - Title of job */
292 const char *docname, /* I - Name of job file */
293 const char *user, /* I - Owner of job */
294 int num_options, /* I - Number of options */
295 cups_option_t *options) /* I - Options */
296 {
297 http_t *http; /* Connection to server */
298 ipp_t *request; /* IPP request */
299 ipp_t *response; /* IPP response */
300 ipp_attribute_t *attr; /* IPP job-id attribute */
301 char uri[HTTP_MAX_URI]; /* Printer URI */
302 cups_lang_t *language; /* Language to use */
303 int jobid; /* New job ID */
304
305
306 /*
307 * Setup a connection and request data...
308 */
309
310 if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
311 cupsEncryption())) == NULL)
312 {
313 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
314 strerror(errno));
315 return (0);
316 }
317
318 language = cupsLangDefault();
319
320 /*
321 * Build a standard CUPS URI for the printer and fill the standard IPP
322 * attributes...
323 */
324
325 if ((request = ippNew()) == NULL)
326 {
327 syslog(LOG_ERR, "Unable to create request: %s", strerror(errno));
328 return (0);
329 }
330
331 request->request.op.operation_id = IPP_PRINT_JOB;
332 request->request.op.request_id = 1;
333
334 snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", name);
335
336 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
337 "attributes-charset", NULL, cupsLangEncoding(language));
338
339 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
340 "attributes-natural-language", NULL,
341 language != NULL ? language->language : "C");
342
343 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
344 NULL, uri);
345
346 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
347 NULL, user);
348
349 if (title)
350 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title);
351 if (docname)
352 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", NULL, docname);
353
354 /*
355 * Then add all options on the command-line...
356 */
357
358 cupsEncodeOptions(request, num_options, options);
359
360 /*
361 * Do the request...
362 */
363
364 snprintf(uri, sizeof(uri), "/printers/%s", name);
365
366 response = cupsDoFileRequest(http, request, uri, file);
367
368 if (response == NULL)
369 jobid = 0;
370 else if (response->request.status.status_code > IPP_OK_CONFLICT)
371 jobid = 0;
372 else if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
373 jobid = 0;
374 else
375 jobid = attr->values[0].integer;
376
377 if (jobid)
378 syslog(LOG_INFO, "Print file - job ID = %d", jobid);
379 else if (response)
380 syslog(LOG_ERR, "Unable to print file - %s",
381 ippErrorString(response->request.status.status_code));
382 else
383 syslog(LOG_ERR, "Unable to print file - %s",
384 ippErrorString(cupsLastError()));
385
386 if (response != NULL)
387 ippDelete(response);
388
389 httpClose(http);
390 cupsLangFree(language);
391
392 return (jobid);
393 }
394
395
396 /*
397 * 'recv_print_job()' - Receive a print job from the client.
398 */
399
400 int /* O - Command status */
401 recv_print_job(const char *dest, /* I - Destination */
402 int num_defaults,/* I - Number of default options */
403 cups_option_t *defaults) /* I - Default options */
404 {
405 int i; /* Looping var */
406 int status; /* Command status */
407 int fd; /* Temporary file */
408 FILE *fp; /* File pointer */
409 char filename[1024]; /* Temporary filename */
410 int bytes; /* Bytes received */
411 char line[256], /* Line from file/stdin */
412 command, /* Command from line */
413 *count, /* Number of bytes */
414 *name; /* Name of file */
415 int num_data; /* Number of data files */
416 char control[1024], /* Control filename */
417 data[32][256], /* Data files */
418 temp[32][1024]; /* Temporary files */
419 char user[1024], /* User name */
420 title[1024], /* Job title */
421 docname[1024], /* Document name */
422 queue[256], /* Printer/class queue */
423 *instance; /* Printer/class instance */
424 int num_dests; /* Number of destinations */
425 cups_dest_t *dests, /* Destinations */
426 *destptr; /* Current destination */
427 int num_options; /* Number of options */
428 cups_option_t *options; /* Options */
429 int banner; /* Print banner? */
430
431
432 status = 0;
433 num_data = 0;
434 fd = -1;
435
436 control[0] = '\0';
437
438 strlcpy(queue, dest, sizeof(queue));
439
440 if ((instance = strrchr(queue, '/')) != NULL)
441 *instance++ = '\0';
442
443 num_dests = cupsGetDests(&dests);
444 if ((destptr = cupsGetDest(queue, instance, num_dests, dests)) == NULL)
445 {
446 /*
447 * If the queue name is blank or "lp" then use the default queue.
448 */
449
450 if (!queue[0] || !strcmp(queue, "lp"))
451 if ((destptr = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
452 strlcpy(queue, destptr->name, sizeof(queue));
453
454 if (destptr == NULL)
455 {
456 if (instance)
457 syslog(LOG_ERR, "Unknown destination %s/%s!", queue, instance);
458 else
459 syslog(LOG_ERR, "Unknown destination %s!", queue);
460
461 cupsFreeDests(num_dests, dests);
462
463 putchar(1);
464
465 return (1);
466 }
467 }
468
469 putchar(0);
470
471 while (smart_gets(line, sizeof(line), stdin) != NULL)
472 {
473 if (strlen(line) < 2)
474 {
475 status = 1;
476 break;
477 }
478
479 command = line[0];
480 count = line + 1;
481
482 for (name = count + 1; *name && !isspace(*name); name ++);
483 while (isspace(*name))
484 *name++ = '\0';
485
486 switch (command)
487 {
488 default :
489 case 0x01 : /* Abort */
490 status = 1;
491 break;
492 case 0x02 : /* Receive control file */
493 if (strlen(name) < 2)
494 {
495 syslog(LOG_ERR, "Bad control file name \"%s\"", name);
496 putchar(1);
497 status = 1;
498 break;
499 }
500
501 if (control[0])
502 {
503 /*
504 * Append to the existing control file - the LPD spec is
505 * not entirely clear, but at least the OS/2 LPD code sends
506 * multiple control files per connection...
507 */
508
509 if ((fd = open(control, O_WRONLY)) < 0)
510 {
511 syslog(LOG_ERR,
512 "Unable to append to temporary control file \"%s\" - %s",
513 control, strerror(errno));
514 putchar(1);
515 status = 1;
516 break;
517 }
518
519 lseek(fd, 0, SEEK_END);
520 }
521 else
522 {
523 if ((fd = cupsTempFd(control, sizeof(control))) < 0)
524 {
525 syslog(LOG_ERR, "Unable to open temporary control file \"%s\" - %s",
526 control, strerror(errno));
527 putchar(1);
528 status = 1;
529 break;
530 }
531
532 strcpy(filename, control);
533 }
534 break;
535 case 0x03 : /* Receive data file */
536 if (strlen(name) < 2)
537 {
538 syslog(LOG_ERR, "Bad data file name \"%s\"", name);
539 putchar(1);
540 status = 1;
541 break;
542 }
543
544 if (num_data >= (sizeof(data) / sizeof(data[0])))
545 {
546 /*
547 * Too many data files...
548 */
549
550 syslog(LOG_ERR, "Too many data files (%d)", num_data);
551 putchar(1);
552 status = 1;
553 break;
554 }
555
556 strlcpy(data[num_data], name, sizeof(data[0]));
557
558 if ((fd = cupsTempFd(temp[num_data], sizeof(temp[0]))) < 0)
559 {
560 syslog(LOG_ERR, "Unable to open temporary data file \"%s\" - %s",
561 temp[num_data], strerror(errno));
562 putchar(1);
563 status = 1;
564 break;
565 }
566
567 strcpy(filename, temp[num_data]);
568
569 num_data ++;
570 break;
571 }
572
573 putchar(status);
574
575 if (status)
576 break;
577
578 /*
579 * Copy the data or control file from the client...
580 */
581
582 for (i = atoi(count); i > 0; i -= bytes)
583 {
584 if (i > sizeof(line))
585 bytes = sizeof(line);
586 else
587 bytes = i;
588
589 if ((bytes = fread(line, 1, bytes, stdin)) > 0)
590 bytes = write(fd, line, bytes);
591
592 if (bytes < 1)
593 {
594 syslog(LOG_ERR, "Error while reading file - %s",
595 strerror(errno));
596 status = 1;
597 break;
598 }
599 }
600
601 /*
602 * Read trailing nul...
603 */
604
605 if (!status)
606 {
607 if (fread(line, 1, 1, stdin) < 1)
608 {
609 status = 1;
610 syslog(LOG_ERR, "Error while reading trailing nul - %s",
611 strerror(errno));
612 }
613 else if (line[0])
614 {
615 status = 1;
616 syslog(LOG_ERR, "Trailing character after file is not nul (%02X)!",
617 line[0]);
618 }
619 }
620
621 /*
622 * Close the file and send an acknowledgement...
623 */
624
625 close(fd);
626
627 putchar(status);
628
629 if (status)
630 break;
631 }
632
633 if (!status)
634 {
635 /*
636 * Process the control file and print stuff...
637 */
638
639 if ((fp = fopen(control, "rb")) == NULL)
640 status = 1;
641 else
642 {
643 /*
644 * Grab the job information first...
645 */
646
647 title[0] = '\0';
648 user[0] = '\0';
649 docname[0] = '\0';
650 banner = 0;
651
652 while (smart_gets(line, sizeof(line), fp) != NULL)
653 {
654 /*
655 * Process control lines...
656 */
657
658 switch (line[0])
659 {
660 case 'J' : /* Job name */
661 strlcpy(title, line + 1, sizeof(title));
662 break;
663 case 'N' : /* Document name */
664 strlcpy(docname, line + 1, sizeof(docname));
665 break;
666 case 'P' : /* User identification */
667 strlcpy(user, line + 1, sizeof(user));
668 break;
669 case 'L' : /* Print banner page */
670 banner = 1;
671 break;
672 }
673
674 if (status)
675 break;
676 }
677
678 /*
679 * Then print the jobs...
680 */
681
682 rewind(fp);
683
684 while (smart_gets(line, sizeof(line), fp) != NULL)
685 {
686 /*
687 * Process control lines...
688 */
689
690 switch (line[0])
691 {
692 case 'c' : /* Plot CIF file */
693 case 'd' : /* Print DVI file */
694 case 'f' : /* Print formatted file */
695 case 'g' : /* Plot file */
696 case 'l' : /* Print file leaving control characters (raw) */
697 case 'n' : /* Print ditroff output file */
698 case 'o' : /* Print PostScript output file */
699 case 'p' : /* Print file with 'pr' format (prettyprint) */
700 case 'r' : /* File to print with FORTRAN carriage control */
701 case 't' : /* Print troff output file */
702 case 'v' : /* Print raster file */
703 /*
704 * Check that we have a username...
705 */
706
707 if (!user[0])
708 {
709 syslog(LOG_WARNING, "No username specified by client! "
710 "Using \"anonymous\"...");
711 strcpy(user, "anonymous");
712 }
713
714 /*
715 * Copy the default options...
716 */
717
718 num_options = 0;
719 options = NULL;
720
721 for (i = 0; i < num_defaults; i ++)
722 num_options = cupsAddOption(defaults[i].name,
723 defaults[i].value,
724 num_options, &options);
725 for (i = 0; i < destptr->num_options; i ++)
726 num_options = cupsAddOption(destptr->options[i].name,
727 destptr->options[i].value,
728 num_options, &options);
729
730 /*
731 * Add additional options as needed...
732 */
733
734 if (!banner)
735 num_options = cupsAddOption("job-sheets", "none",
736 num_options, &options);
737
738 if (line[0] == 'l')
739 num_options = cupsAddOption("raw", "", num_options, &options);
740
741 if (line[0] == 'p')
742 num_options = cupsAddOption("prettyprint", "", num_options,
743 &options);
744
745 /*
746 * Figure out which file we are printing...
747 */
748
749 for (i = 0; i < num_data; i ++)
750 if (strcmp(data[i], line + 1) == 0)
751 break;
752
753 if (i >= num_data)
754 {
755 status = 1;
756 break;
757 }
758
759 /*
760 * Send the print request...
761 */
762
763 if (print_file(queue, temp[i], title, docname, user, num_options,
764 options) == 0)
765 status = 1;
766 else
767 status = 0;
768
769 cupsFreeOptions(num_options, options);
770 break;
771 }
772
773 if (status)
774 break;
775 }
776
777 fclose(fp);
778 }
779 }
780
781 /*
782 * Clean up all temporary files and return...
783 */
784
785 unlink(control);
786
787 for (i = 0; i < num_data; i ++)
788 unlink(temp[i]);
789
790 cupsFreeDests(num_dests, dests);
791
792 return (status);
793 }
794
795
796 /*
797 * 'remove_jobs()' - Cancel one or more jobs.
798 */
799
800 int /* O - Command status */
801 remove_jobs(const char *dest, /* I - Destination */
802 const char *agent, /* I - User agent */
803 const char *list) /* I - List of jobs or users */
804 {
805 int id; /* Job ID */
806 http_t *http; /* HTTP server connection */
807 ipp_t *request, /* IPP Request */
808 *response; /* IPP Response */
809 cups_lang_t *language; /* Default language */
810 char uri[HTTP_MAX_URI]; /* Job URI */
811
812
813 (void)dest; /* Suppress compiler warnings... */
814
815 /*
816 * Try connecting to the local server...
817 */
818
819 if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
820 cupsEncryption())) == NULL)
821 {
822 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
823 strerror(errno));
824 return (1);
825 }
826
827 language = cupsLangDefault();
828
829 /*
830 * Loop for each job...
831 */
832
833 while ((id = atoi(list)) > 0)
834 {
835 /*
836 * Skip job ID in list...
837 */
838
839 while (isdigit(*list))
840 list ++;
841 while (isspace(*list))
842 list ++;
843
844 /*
845 * Build an IPP_CANCEL_JOB request, which requires the following
846 * attributes:
847 *
848 * attributes-charset
849 * attributes-natural-language
850 * job-uri
851 * requesting-user-name
852 */
853
854 request = ippNew();
855
856 request->request.op.operation_id = IPP_CANCEL_JOB;
857 request->request.op.request_id = 1;
858
859 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
860 "attributes-charset", NULL, cupsLangEncoding(language));
861
862 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
863 "attributes-natural-language", NULL, language->language);
864
865 sprintf(uri, "ipp://localhost/jobs/%d", id);
866 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
867
868 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
869 "requesting-user-name", NULL, agent);
870
871 /*
872 * Do the request and get back a response...
873 */
874
875 if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
876 {
877 if (response->request.status.status_code > IPP_OK_CONFLICT)
878 {
879 syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
880 ippErrorString(response->request.status.status_code));
881 ippDelete(response);
882 cupsLangFree(language);
883 httpClose(http);
884 return (1);
885 }
886 else
887 syslog(LOG_INFO, "Job ID %d cancelled", id);
888
889 ippDelete(response);
890 }
891 else
892 {
893 syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
894 ippErrorString(cupsLastError()));
895 cupsLangFree(language);
896 httpClose(http);
897 return (1);
898 }
899 }
900
901 cupsLangFree(language);
902 httpClose(http);
903
904 return (0);
905 }
906
907
908 /*
909 * 'send_state()' - Send the queue state.
910 */
911
912 int /* O - Command status */
913 send_state(const char *dest, /* I - Destination */
914 const char *list, /* I - Job or user */
915 int longstatus) /* I - List of jobs or users */
916 {
917 int id; /* Job ID from list */
918 http_t *http; /* HTTP server connection */
919 ipp_t *request, /* IPP Request */
920 *response; /* IPP Response */
921 ipp_attribute_t *attr; /* Current attribute */
922 cups_lang_t *language; /* Default language */
923 ipp_pstate_t state; /* Printer state */
924 const char *jobdest, /* Pointer into job-printer-uri */
925 *jobuser, /* Pointer to job-originating-user-name */
926 *jobname; /* Pointer to job-name */
927 ipp_jstate_t jobstate; /* job-state */
928 int jobid, /* job-id */
929 jobsize, /* job-k-octets */
930 jobcount, /* Number of jobs */
931 jobcopies, /* Number of copies */
932 rank; /* Rank of job */
933 char rankstr[255]; /* Rank string */
934 char namestr[1024]; /* Job name string */
935 char uri[HTTP_MAX_URI]; /* Printer URI */
936 char queue[256], /* Printer/class queue */
937 *instance; /* Printer/class instance */
938 static const char * const ranks[10] = /* Ranking strings */
939 {
940 "th",
941 "st",
942 "nd",
943 "rd",
944 "th",
945 "th",
946 "th",
947 "th",
948 "th",
949 "th"
950 };
951 static const char * const requested[] =
952 { /* Requested attributes */
953 "job-id",
954 "job-k-octets",
955 "job-state",
956 "job-printer-uri",
957 "job-originating-user-name",
958 "job-name",
959 "copies"
960 };
961
962
963 /*
964 * Remove instance from destination, if any...
965 */
966
967 strlcpy(queue, dest, sizeof(queue));
968
969 if ((instance = strrchr(queue, '/')) != NULL)
970 *instance++ = '\0';
971
972 /*
973 * Try connecting to the local server...
974 */
975
976 if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
977 cupsEncryption())) == NULL)
978 {
979 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
980 strerror(errno));
981 printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno));
982 return (1);
983 }
984
985 /*
986 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
987 * attributes:
988 *
989 * attributes-charset
990 * attributes-natural-language
991 * printer-uri
992 */
993
994 request = ippNew();
995
996 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
997 request->request.op.request_id = 1;
998
999 language = cupsLangDefault();
1000
1001 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1002 "attributes-charset", NULL, cupsLangEncoding(language));
1003
1004 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1005 "attributes-natural-language", NULL, language->language);
1006
1007 snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", queue);
1008 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1009 "printer-uri", NULL, uri);
1010
1011 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1012 "requested-attributes", NULL, "printer-state");
1013
1014 /*
1015 * Do the request and get back a response...
1016 */
1017
1018 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1019 {
1020 if (response->request.status.status_code > IPP_OK_CONFLICT)
1021 {
1022 syslog(LOG_WARNING, "Unable to get printer list: %s\n",
1023 ippErrorString(response->request.status.status_code));
1024 printf("Unable to get printer list: %s\n",
1025 ippErrorString(response->request.status.status_code));
1026 ippDelete(response);
1027 return (1);
1028 }
1029
1030 if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
1031 state = (ipp_pstate_t)attr->values[0].integer;
1032 else
1033 state = IPP_PRINTER_STOPPED;
1034
1035 switch (state)
1036 {
1037 case IPP_PRINTER_IDLE :
1038 printf("%s is ready\n", dest);
1039 break;
1040 case IPP_PRINTER_PROCESSING :
1041 printf("%s is ready and printing\n", dest);
1042 break;
1043 case IPP_PRINTER_STOPPED :
1044 printf("%s is not ready\n", dest);
1045 break;
1046 }
1047
1048 ippDelete(response);
1049 }
1050 else
1051 {
1052 syslog(LOG_WARNING, "Unable to get printer list: %s\n",
1053 ippErrorString(cupsLastError()));
1054 printf("Unable to get printer list: %s\n",
1055 ippErrorString(cupsLastError()));
1056 return (1);
1057 }
1058
1059 /*
1060 * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
1061 * the following attributes:
1062 *
1063 * attributes-charset
1064 * attributes-natural-language
1065 * job-uri or printer-uri
1066 */
1067
1068 id = atoi(list);
1069
1070 request = ippNew();
1071
1072 request->request.op.operation_id = id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
1073 request->request.op.request_id = 1;
1074
1075 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1076 "attributes-charset", NULL, cupsLangEncoding(language));
1077
1078 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1079 "attributes-natural-language", NULL, language->language);
1080
1081 snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", queue);
1082
1083 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1084 NULL, uri);
1085
1086 if (id)
1087 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1088 else
1089 {
1090 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1091 "requesting-user-name", NULL, list);
1092 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
1093 }
1094
1095 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1096 "requested-attributes", sizeof(requested) / sizeof(requested[0]),
1097 NULL, requested);
1098
1099 /*
1100 * Do the request and get back a response...
1101 */
1102
1103 jobcount = 0;
1104
1105 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1106 {
1107 if (response->request.status.status_code > IPP_OK_CONFLICT)
1108 {
1109 printf("get-jobs failed: %s\n",
1110 ippErrorString(response->request.status.status_code));
1111 ippDelete(response);
1112 return (1);
1113 }
1114
1115 rank = 1;
1116
1117 /*
1118 * Loop through the job list and display them...
1119 */
1120
1121 for (attr = response->attrs; attr != NULL; attr = attr->next)
1122 {
1123 /*
1124 * Skip leading attributes until we hit a job...
1125 */
1126
1127 while (attr != NULL &&
1128 (attr->group_tag != IPP_TAG_JOB || attr->name == NULL))
1129 attr = attr->next;
1130
1131 if (attr == NULL)
1132 break;
1133
1134 /*
1135 * Pull the needed attributes from this job...
1136 */
1137
1138 jobid = 0;
1139 jobsize = 0;
1140 jobstate = IPP_JOB_PENDING;
1141 jobname = "untitled";
1142 jobuser = NULL;
1143 jobdest = NULL;
1144 jobcopies = 1;
1145
1146 while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
1147 {
1148 if (strcmp(attr->name, "job-id") == 0 &&
1149 attr->value_tag == IPP_TAG_INTEGER)
1150 jobid = attr->values[0].integer;
1151
1152 if (strcmp(attr->name, "job-k-octets") == 0 &&
1153 attr->value_tag == IPP_TAG_INTEGER)
1154 jobsize = attr->values[0].integer * 1024;
1155
1156 if (strcmp(attr->name, "job-state") == 0 &&
1157 attr->value_tag == IPP_TAG_ENUM)
1158 jobstate = (ipp_jstate_t)attr->values[0].integer;
1159
1160 if (strcmp(attr->name, "job-printer-uri") == 0 &&
1161 attr->value_tag == IPP_TAG_URI)
1162 if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
1163 jobdest ++;
1164
1165 if (strcmp(attr->name, "job-originating-user-name") == 0 &&
1166 attr->value_tag == IPP_TAG_NAME)
1167 jobuser = attr->values[0].string.text;
1168
1169 if (strcmp(attr->name, "job-name") == 0 &&
1170 attr->value_tag == IPP_TAG_NAME)
1171 jobname = attr->values[0].string.text;
1172
1173 if (strcmp(attr->name, "copies") == 0 &&
1174 attr->value_tag == IPP_TAG_INTEGER)
1175 jobcopies = attr->values[0].integer;
1176
1177 attr = attr->next;
1178 }
1179
1180 /*
1181 * See if we have everything needed...
1182 */
1183
1184 if (jobdest == NULL || jobid == 0)
1185 {
1186 if (attr == NULL)
1187 break;
1188 else
1189 continue;
1190 }
1191
1192 if (!longstatus && jobcount == 0)
1193 puts("Rank Owner Job File(s) Total Size");
1194
1195 jobcount ++;
1196
1197 /*
1198 * Display the job...
1199 */
1200
1201 if (jobstate == IPP_JOB_PROCESSING)
1202 strcpy(rankstr, "active");
1203 else
1204 {
1205 snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
1206 rank ++;
1207 }
1208
1209 if (longstatus)
1210 {
1211 puts("");
1212
1213 if (jobcopies > 1)
1214 snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
1215 jobname);
1216 else
1217 strlcpy(namestr, jobname, sizeof(namestr));
1218
1219 printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid);
1220 printf(" %-39.39s %d bytes\n", namestr, jobsize);
1221 }
1222 else
1223 printf("%-7s %-7.7s %-7d %-31.31s %d bytes\n", rankstr, jobuser,
1224 jobid, jobname, jobsize);
1225
1226 if (attr == NULL)
1227 break;
1228 }
1229
1230 ippDelete(response);
1231 }
1232 else
1233 {
1234 printf("get-jobs failed: %s\n", ippErrorString(cupsLastError()));
1235 return (1);
1236 }
1237
1238 if (jobcount == 0)
1239 puts("no entries");
1240
1241 cupsLangFree(language);
1242 httpClose(http);
1243
1244 return (0);
1245 }
1246
1247
1248 /*
1249 * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1250 */
1251
1252 char * /* O - Line read or NULL */
1253 smart_gets(char *s, /* I - Pointer to line buffer */
1254 int len, /* I - Size of line buffer */
1255 FILE *fp) /* I - File to read from */
1256 {
1257 char *ptr, /* Pointer into line */
1258 *end; /* End of line */
1259 int ch; /* Character from file */
1260
1261
1262 /*
1263 * Read the line; unlike fgets(), we read the entire line but dump
1264 * characters that go past the end of the buffer. Also, we accept
1265 * CR, LF, or CR LF for the line endings to be "safe", although
1266 * RFC 1179 specifically says "just use LF".
1267 */
1268
1269 ptr = s;
1270 end = s + len - 1;
1271
1272 while ((ch = getc(fp)) != EOF)
1273 {
1274 if (ch == '\n')
1275 break;
1276 else if (ch == '\r')
1277 {
1278 /*
1279 * See if a LF follows...
1280 */
1281
1282 ch = getc(fp);
1283
1284 if (ch != '\n')
1285 ungetc(ch, fp);
1286
1287 break;
1288 }
1289 else if (ptr < end)
1290 *ptr++ = ch;
1291 }
1292
1293 *ptr = '\0';
1294
1295 if (ch == EOF && ptr == s)
1296 return (NULL);
1297 else
1298 return (s);
1299 }
1300
1301
1302 /*
1303 * End of "$Id: cups-lpd.c,v 1.24.2.15 2004/02/17 21:32:58 mike Exp $".
1304 */