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