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