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