]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/cups-lpd.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / cups-lpd.c
CommitLineData
ef416fc2 1/*
bd7854cb 2 * "$Id: cups-lpd.c 5137 2006-02-19 19:44:50Z mike $"
ef416fc2 3 *
4 * Line Printer Daemon interface for the Common UNIX Printing System (CUPS).
5 *
bd7854cb 6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
ef416fc2 7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
bd7854cb 26 * main() - Process an incoming LPD request...
27 * create_job() - Create a new print job.
28 * get_printer() - Get the named printer and its options.
29 * print_file() - Add a file to the current job.
30 * recv_print_job() - Receive a print job from the client.
31 * remove_jobs() - Cancel one or more jobs.
32 * send_state() - Send the queue state.
33 * smart_gets() - Get a line of text, removing the trailing CR and/or LF.
ef416fc2 34 */
35
36/*
37 * Include necessary headers...
38 */
39
4400e98d 40#include <cups/http-private.h>
ef416fc2 41#include <cups/cups.h>
42#include <cups/string.h>
43#include <cups/language.h>
44#include <stdlib.h>
45#include <errno.h>
46#include <syslog.h>
47#include <ctype.h>
48#include <unistd.h>
49#include <fcntl.h>
50
51#include <sys/types.h>
52#include <sys/socket.h>
53#include <netinet/in.h>
54#include <netdb.h>
55
bd7854cb 56#ifdef HAVE_INTTYPES_H
57# include <inttypes.h>
58#endif /* HAVE_INTTYPES_H */
59
60#ifdef HAVE_COREFOUNDATION_H
61# include <CoreFoundation/CoreFoundation.h>
62#endif /* HAVE_COREFOUNDATION_H */
63#ifdef HAVE_CFPRIV_H
64# include <CoreFoundation/CFPriv.h>
65#endif /* HAVE_CFPRIV_H */
66
ef416fc2 67
68/*
69 * LPD "mini-daemon" for CUPS. This program must be used in conjunction
70 * with inetd or another similar program that monitors ports and starts
71 * daemons for each client connection. A typical configuration is:
72 *
73 * printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
74 *
75 * This daemon implements most of RFC 1179 (the unofficial LPD specification)
76 * except for:
77 *
78 * - This daemon does not check to make sure that the source port is
79 * between 721 and 731, since it isn't necessary for proper
80 * functioning and port-based security is no security at all!
81 *
82 * - The "Print any waiting jobs" command is a no-op.
83 *
84 * The LPD-to-IPP mapping is as defined in RFC 2569. The report formats
85 * currently match the Solaris LPD mini-daemon.
86 */
87
88/*
89 * Prototypes...
90 */
91
bd7854cb 92static int create_job(http_t *http, const char *dest, const char *title,
93 const char *user, int num_options,
94 cups_option_t *options);
95static int get_printer(http_t *http, const char *name, char *dest,
96 int destsize, cups_option_t **options,
97 int *accepting, int *shared, ipp_pstate_t *state);
98static int print_file(http_t *http, int id, const char *filename,
99 const char *docname, const char *user, int last);
100static int recv_print_job(const char *name, int num_defaults,
101 cups_option_t *defaults);
102static int remove_jobs(const char *name, const char *agent,
103 const char *list);
104static int send_state(const char *name, const char *list,
105 int longstatus);
106static char *smart_gets(char *s, int len, FILE *fp);
ef416fc2 107
108
109/*
110 * 'main()' - Process an incoming LPD request...
111 */
112
113int /* O - Exit status */
114main(int argc, /* I - Number of command-line arguments */
115 char *argv[]) /* I - Command-line arguments */
116{
117 int i; /* Looping var */
118 int num_defaults; /* Number of default options */
119 cups_option_t *defaults; /* Default options */
120 char line[256], /* Command string */
121 command, /* Command code */
122 *dest, /* Pointer to destination */
123 *list, /* Pointer to list */
124 *agent, /* Pointer to user */
125 status; /* Status for client */
126 socklen_t hostlen; /* Size of client address */
127 http_addr_t hostaddr; /* Address of client */
128 char hostname[256], /* Name of client */
129 hostip[256], /* IP address */
130 *hostfamily; /* Address family */
131
132
133 /*
134 * Don't buffer the output...
135 */
136
137 setbuf(stdout, NULL);
138
139 /*
140 * Log things using the "cups-lpd" name...
141 */
142
143 openlog("cups-lpd", LOG_PID, LOG_LPR);
144
145 /*
146 * Get the address of the client...
147 */
148
149 hostlen = sizeof(hostaddr);
150
151 if (getpeername(0, (struct sockaddr *)&hostaddr, &hostlen))
152 {
153 syslog(LOG_WARNING, "Unable to get client address - %s", strerror(errno));
154 strcpy(hostname, "unknown");
155 }
156 else
157 {
158 httpAddrLookup(&hostaddr, hostname, sizeof(hostname));
159 httpAddrString(&hostaddr, hostip, sizeof(hostip));
160
161#ifdef AF_INET6
162 if (hostaddr.addr.sa_family == AF_INET6)
163 hostfamily = "IPv6";
164 else
165#endif /* AF_INET6 */
166 hostfamily = "IPv4";
167
bd7854cb 168 syslog(LOG_INFO, "Connection from %s (%s %s)", hostname, hostfamily,
169 hostip);
ef416fc2 170 }
171
172 /*
173 * Scan the command-line for options...
174 */
175
176 num_defaults = 0;
177 defaults = NULL;
178
179 num_defaults = cupsAddOption("job-originating-host-name", hostname,
180 num_defaults, &defaults);
181
182 for (i = 1; i < argc; i ++)
183 if (argv[i][0] == '-')
184 {
185 switch (argv[i][1])
186 {
187 case 'o' : /* Option */
188 if (argv[i][2])
189 num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
190 &defaults);
191 else
192 {
193 i ++;
194 if (i < argc)
bd7854cb 195 num_defaults = cupsParseOptions(argv[i], num_defaults,
196 &defaults);
ef416fc2 197 else
198 syslog(LOG_WARNING, "Expected option string after -o option!");
199 }
200 break;
201 default :
202 syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]);
203 break;
204 }
205 }
206 else
bd7854cb 207 syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!",
208 argv[i]);
ef416fc2 209
210 /*
211 * RFC1179 specifies that only 1 daemon command can be received for
212 * every connection.
213 */
214
215 if (smart_gets(line, sizeof(line), stdin) == NULL)
216 {
217 /*
218 * Unable to get command from client! Send an error status and return.
219 */
220
221 syslog(LOG_ERR, "Unable to get command line from client!");
222 putchar(1);
223 return (1);
224 }
225
226 /*
227 * The first byte is the command byte. After that will be the queue name,
228 * resource list, and/or user name.
229 */
230
231 command = line[0];
232 dest = line + 1;
233
bd7854cb 234 if (command == 0x02)
235 list = NULL;
236 else
237 {
238 for (list = dest + 1; *list && !isspace(*list & 255); list ++);
ef416fc2 239
bd7854cb 240 while (isspace(*list & 255))
241 *list++ = '\0';
242 }
ef416fc2 243
244 /*
245 * Do the command...
246 */
247
248 switch (command)
249 {
250 default : /* Unknown command */
251 syslog(LOG_ERR, "Unknown LPD command 0x%02X!", command);
252 syslog(LOG_ERR, "Command line = %s", line + 1);
253 putchar(1);
254
255 status = 1;
256 break;
257
258 case 0x01 : /* Print any waiting jobs */
259 syslog(LOG_INFO, "Print waiting jobs (no-op)");
260 putchar(0);
261
262 status = 0;
263 break;
264
265 case 0x02 : /* Receive a printer job */
266 syslog(LOG_INFO, "Receive print job for %s", dest);
267 /* recv_print_job() sends initial status byte */
268
269 status = recv_print_job(dest, num_defaults, defaults);
270 break;
271
272 case 0x03 : /* Send queue state (short) */
273 syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list);
274 /* no status byte for this command */
275
276 status = send_state(dest, list, 0);
277 break;
278
279 case 0x04 : /* Send queue state (long) */
280 syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list);
281 /* no status byte for this command */
282
283 status = send_state(dest, list, 1);
284 break;
285
286 case 0x05 : /* Remove jobs */
287 /*
288 * Grab the agent and skip to the list of users and/or jobs.
289 */
290
291 agent = list;
292
293 for (; *list && !isspace(*list & 255); list ++);
294 while (isspace(*list & 255))
295 *list++ = '\0';
296
297 syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);
298
299 status = remove_jobs(dest, agent, list);
300
301 putchar(status);
302 break;
303 }
304
305 syslog(LOG_INFO, "Closing connection");
306 closelog();
307
308 return (status);
309}
310
311
312/*
bd7854cb 313 * 'create_job()' - Create a new print job.
ef416fc2 314 */
315
bd7854cb 316static int /* O - Job ID or -1 on error */
317create_job(http_t *http, /* I - HTTP connection */
318 const char *dest, /* I - Destination name */
319 const char *title, /* I - job-name */
320 const char *user, /* I - requesting-user-name */
321 int num_options, /* I - Number of options for job */
322 cups_option_t *options) /* I - Options for job */
ef416fc2 323{
ef416fc2 324 ipp_t *request; /* IPP request */
325 ipp_t *response; /* IPP response */
bd7854cb 326 ipp_attribute_t *attr; /* IPP attribute */
ef416fc2 327 char uri[HTTP_MAX_URI]; /* Printer URI */
bd7854cb 328 int id; /* Job ID */
ef416fc2 329
330
331 /*
bd7854cb 332 * Setup the Create-Job request...
ef416fc2 333 */
334
bd7854cb 335 request = ippNewRequest(IPP_CREATE_JOB);
ef416fc2 336
a4d04587 337 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
bd7854cb 338 "localhost", 0, "/printers/%s", dest);
ef416fc2 339
340 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
341 NULL, uri);
342
bd7854cb 343 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
344 "requesting-user-name", NULL, user);
345
346 if (title)
347 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
348 NULL, title);
349
350 cupsEncodeOptions(request, num_options, options);
ef416fc2 351
352 /*
353 * Do the request...
354 */
355
bd7854cb 356 snprintf(uri, sizeof(uri), "/printers/%s", dest);
ef416fc2 357
bd7854cb 358 response = cupsDoRequest(http, request, uri);
359
360 if (!response || cupsLastError() > IPP_OK_CONFLICT)
ef416fc2 361 {
bd7854cb 362 syslog(LOG_ERR, "Unable to create job - %s", cupsLastErrorString());
363
364 ippDelete(response);
365
366 return (-1);
ef416fc2 367 }
bd7854cb 368
369 /*
370 * Get the job-id value from the response and return it...
371 */
372
373 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
ef416fc2 374 {
bd7854cb 375 id = -1;
376
377 syslog(LOG_ERR, "No job-id attribute found in response from server!");
ef416fc2 378 }
379 else
bd7854cb 380 {
381 id = attr->values[0].integer;
ef416fc2 382
bd7854cb 383 syslog(LOG_INFO, "Print file - job ID = %d", id);
384 }
ef416fc2 385
bd7854cb 386 ippDelete(response);
ef416fc2 387
bd7854cb 388 return (id);
ef416fc2 389}
390
391
392/*
bd7854cb 393 * 'get_printer()' - Get the named printer and its options.
ef416fc2 394 */
395
bd7854cb 396static int /* O - Number of options or -1 on error */
397get_printer(http_t *http, /* I - HTTP connection */
398 const char *name, /* I - Printer name from request */
399 char *dest, /* I - Destination buffer */
400 int destsize, /* I - Size of destination buffer */
401 cups_option_t **options, /* O - Printer options */
402 int *accepting, /* O - printer-is-accepting-jobs value */
403 int *shared, /* O - printer-is-shared value */
404 ipp_pstate_t *state) /* O - printer-state value */
ef416fc2 405{
bd7854cb 406 int num_options; /* Number of options */
407 cups_file_t *fp; /* lpoptions file */
408 char line[1024], /* Line from lpoptions file */
409 *value, /* Pointer to value on line */
410 *optptr; /* Pointer to options on line */
411 int linenum; /* Line number in file */
412 const char *cups_serverroot; /* CUPS_SERVERROOT env var */
ef416fc2 413 ipp_t *request; /* IPP request */
414 ipp_t *response; /* IPP response */
bd7854cb 415 ipp_attribute_t *attr; /* IPP attribute */
ef416fc2 416 char uri[HTTP_MAX_URI]; /* Printer URI */
bd7854cb 417 static const char * const requested[] =
418 { /* Requested attributes */
419 "printer-info",
420 "printer-is-accepting-jobs",
421 "printer-is-shared",
422 "printer-name",
423 "printer-state"
424 };
ef416fc2 425
426
427 /*
bd7854cb 428 * Initialize everything...
ef416fc2 429 */
430
bd7854cb 431 if (accepting)
432 *accepting = 0;
433 if (shared)
434 *shared = 0;
435 if (state)
436 *state = IPP_PRINTER_STOPPED;
437 if (options)
438 *options = NULL;
439
440 /*
441 * If the queue name contains a space, lookup the printer-name using
442 * the printer-info value...
443 */
444
445 if (strchr(name, ' '))
ef416fc2 446 {
bd7854cb 447 /*
448 * Lookup the printer-info...
449 */
450
451 ipp_attribute_t *accepting_attr,/* printer-is-accepting-jobs */
452 *info_attr, /* printer-info */
453 *name_attr, /* printer-name */
454 *shared_attr, /* printer-is-shared */
455 *state_attr; /* printer-state */
456
457
458 /*
459 * Setup the CUPS-Get-Printers request...
460 */
461
462 request = ippNewRequest(CUPS_GET_PRINTERS);
463
464 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
465 "requested-attributes",
466 (int)(sizeof(requested) / sizeof(requested[0])),
467 NULL, requested);
468
469 /*
470 * Do the request...
471 */
472
473 response = cupsDoRequest(http, request, "/");
474
475 if (!response || cupsLastError() > IPP_OK_CONFLICT)
476 {
477 syslog(LOG_ERR, "Unable to get list of printers - %s",
478 cupsLastErrorString());
479
480 ippDelete(response);
481
482 return (-1);
483 }
484
485 /*
486 * Scan the response for printers...
487 */
488
489 *dest = '\0';
490 attr = response->attrs;
491
492 while (attr)
493 {
494 /*
495 * Skip to the next printer...
496 */
497
498 while (attr && attr->group_tag != IPP_TAG_PRINTER)
499 attr = attr->next;
500
501 if (!attr)
502 break;
503
504 /*
505 * Get all of the attributes for the current printer...
506 */
507
508 accepting_attr = NULL;
509 info_attr = NULL;
510 name_attr = NULL;
511 shared_attr = NULL;
512 state_attr = NULL;
513
514 while (attr && attr->group_tag == IPP_TAG_PRINTER)
515 {
516 if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
517 attr->value_tag == IPP_TAG_BOOLEAN)
518 accepting_attr = attr;
519 else if (!strcmp(attr->name, "printer-info") &&
520 attr->value_tag == IPP_TAG_TEXT)
521 info_attr = attr;
522 else if (!strcmp(attr->name, "printer-name") &&
523 attr->value_tag == IPP_TAG_NAME)
524 name_attr = attr;
525 else if (!strcmp(attr->name, "printer-is-shared") &&
526 attr->value_tag == IPP_TAG_BOOLEAN)
527 shared_attr = attr;
528 else if (!strcmp(attr->name, "printer-state") &&
529 attr->value_tag == IPP_TAG_ENUM)
530 state_attr = attr;
531
532 attr = attr->next;
533 }
534
535 if (info_attr && name_attr &&
536 !strcasecmp(name, info_attr->values[0].string.text))
537 {
538 /*
539 * Found a match, use this one!
540 */
541
542 strlcpy(dest, name_attr->values[0].string.text, destsize);
543
544 if (accepting && accepting_attr)
545 *accepting = accepting_attr->values[0].boolean;
546
547 if (shared && shared_attr)
548 *shared = shared_attr->values[0].boolean;
549
550 if (state && state_attr)
551 *state = (ipp_pstate_t)state_attr->values[0].integer;
552
553 break;
554 }
555 }
556
557 ippDelete(response);
558
559 if (!*dest)
560 {
561 syslog(LOG_ERR, "Unable to find \"%s\" in list of printers!", name);
562
563 return (-1);
564 }
565
566 name = dest;
567 }
568 else
569 {
570 /*
571 * Otherwise treat it as a queue name optionally with an instance name.
572 */
573
574 strlcpy(dest, name, destsize);
575 if ((value = strchr(dest, '/')) != NULL)
576 *value = '\0';
577
578 /*
579 * Setup the Get-Printer-Attributes request...
580 */
581
582 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
583
584 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
585 "localhost", 0, "/printers/%s", dest);
586
587 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
588 NULL, uri);
589
590 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
591 "requested-attributes",
592 (int)(sizeof(requested) / sizeof(requested[0])),
593 NULL, requested);
594
595 /*
596 * Do the request...
597 */
598
599 response = cupsDoRequest(http, request, "/");
600
601 if (!response || cupsLastError() > IPP_OK_CONFLICT)
602 {
603 syslog(LOG_ERR, "Unable to check printer status - %s",
604 cupsLastErrorString());
605
606 ippDelete(response);
607
608 return (-1);
609 }
610
611 /*
612 * Get values from the response...
613 */
614
615 if (accepting)
616 {
617 if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
618 IPP_TAG_BOOLEAN)) == NULL)
619 syslog(LOG_ERR, "No printer-is-accepting-jobs attribute found in "
620 "response from server!");
621 else
622 *accepting = attr->values[0].boolean;
623 }
624
625 if (shared)
626 {
627 if ((attr = ippFindAttribute(response, "printer-is-shared",
628 IPP_TAG_BOOLEAN)) == NULL)
629 {
630 syslog(LOG_ERR, "No printer-is-shared attribute found in "
631 "response from server!");
632 *shared = 1;
633 }
634 else
635 *shared = attr->values[0].boolean;
636 }
637
638 if (state)
639 {
640 if ((attr = ippFindAttribute(response, "printer-state",
641 IPP_TAG_INTEGER)) == NULL)
642 syslog(LOG_ERR, "No printer-state attribute found in "
643 "response from server!");
644 else
645 *state = (ipp_pstate_t)attr->values[0].integer;
646 }
647
648 ippDelete(response);
ef416fc2 649 }
650
651 /*
bd7854cb 652 * Override shared value for LPD using system-specific APIs...
ef416fc2 653 */
654
bd7854cb 655#ifdef HAVE_CFPRIV_H /* MacOS X */
656 if (shared && *shared)
ef416fc2 657 {
bd7854cb 658 CFURLRef prefsurl; /* */
659 CFDataRef xmldata; /* */
660 CFPropertyListRef plist; /* */
661 CFStringRef queueid; /* */
662 CFArrayRef lprqarray; /* */
663 CFBooleanRef serverflag; /* */
664 Boolean prefsok; /* */
665 static const char printerprefsfile[] =
666 "/Library/Preferences/com.apple.printservice.plist";
667 /* Preferences file */
668
669
670 /*
671 * See if we are running on MacOS X Server...
672 */
673
674 CFDictionaryRef versdict = _CFCopyServerVersionDictionary();
675
676 if (versdict)
677 {
678 /*
679 * Yes, use the LPR sharing preference...
680 */
681
682 CFRelease(versdict);
683
684 *shared = 0;
685
686 prefsurl = CFURLCreateFromFileSystemRepresentation(
687 kCFAllocatorDefault,
688 (const UInt8 *)printerprefsfile,
689 (CFIndex)strlen(printerprefsfile),
690 false);
691 if (prefsurl)
692 {
693 prefsok = CFURLCreateDataAndPropertiesFromResource(
694 kCFAllocatorDefault, prefsurl, &xmldata,
695 NULL, NULL, NULL);
696 if (prefsok)
697 {
698 plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, xmldata,
699 kCFPropertyListImmutable, NULL);
700 if (plist)
701 {
702 serverflag = (CFBooleanRef)CFDictionaryGetValue(
703 (CFDictionaryRef)plist, CFSTR("serviceState"));
704
705 if (serverflag && CFBooleanGetValue(serverflag))
706 {
707 lprqarray = (CFArrayRef)CFDictionaryGetValue(
708 (CFDictionaryRef)plist, CFSTR("lprSharedQueues"));
709
710 if (lprqarray)
711 {
712 queueid = CFStringCreateWithCString(CFAllocatorGetDefault(),
713 dest,
714 kCFStringEncodingUTF8);
715
716 if (queueid)
717 {
718 *shared = CFArrayContainsValue(
719 lprqarray,
720 CFRangeMake(0, CFArrayGetCount(lprqarray)),
721 queueid);
722
723 CFRelease(queueid);
724 }
725
726 CFRelease(lprqarray);
727 }
728 }
729
730 if (serverflag)
731 CFRelease(serverflag);
732
733 CFRelease(plist);
734 }
735
736 CFRelease(prefsok);
737 }
738
739 CFRelease(prefsurl);
740 }
741
742 if (!shared)
743 syslog(LOG_ERR, "Warning - Print Service sharing disabled for LPD "
744 "on queue: %s", name);
745 }
ef416fc2 746 }
bd7854cb 747#endif /* HAVE_CFPRIV_H */
ef416fc2 748
bd7854cb 749 /*
750 * Next look for the printer in the lpoptions file...
751 */
ef416fc2 752
bd7854cb 753 num_options = 0;
ef416fc2 754
bd7854cb 755 if (options && shared && accepting)
756 {
757 if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
758 cups_serverroot = CUPS_SERVERROOT;
ef416fc2 759
bd7854cb 760 snprintf(line, sizeof(line), "%s/lpoptions", cups_serverroot);
761 if ((fp = cupsFileOpen(line, "r")) != NULL)
762 {
763 linenum = 0;
764 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
765 {
766 /*
767 * Make sure we have "Dest name options" or "Default name options"...
768 */
ef416fc2 769
bd7854cb 770 if ((strcasecmp(line, "Dest") && strcasecmp(line, "Default")) || !value)
771 continue;
ef416fc2 772
bd7854cb 773 /*
774 * Separate destination name from options...
775 */
ef416fc2 776
bd7854cb 777 for (optptr = value; *optptr && !isspace(*optptr & 255); optptr ++);
ef416fc2 778
bd7854cb 779 while (*optptr == ' ')
780 *optptr++ = '\0';
781
782 /*
783 * If this is our destination, parse the options and break out of
784 * the loop - we're done!
785 */
786
787 if (!strcasecmp(value, name))
788 {
789 num_options = cupsParseOptions(optptr, num_options, options);
790 break;
791 }
792 }
793
794 cupsFileClose(fp);
795 }
796 }
797 else if (options)
798 *options = NULL;
ef416fc2 799
800 /*
bd7854cb 801 * Return the number of options for this destination...
ef416fc2 802 */
803
bd7854cb 804 return (num_options);
805}
806
807
808/*
809 * 'print_file()' - Add a file to the current job.
810 */
811
812static int /* O - 0 on success, -1 on failure */
813print_file(http_t *http, /* I - HTTP connection */
814 int id, /* I - Job ID */
815 const char *filename, /* I - File to print */
816 const char *docname, /* I - document-name */
817 const char *user, /* I - requesting-user-name */
818 int last) /* I - 1 = last file in job */
819{
820 ipp_t *request; /* IPP request */
821 char uri[HTTP_MAX_URI]; /* Printer URI */
822
823
824 /*
825 * Setup the Send-Document request...
826 */
827
828 request = ippNewRequest(IPP_SEND_DOCUMENT);
829
830 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", id);
831 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
832
833 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
834 "requesting-user-name", NULL, user);
835
836 if (docname)
837 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
838 "document-name", NULL, docname);
839
840 if (last)
841 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
ef416fc2 842
843 /*
844 * Do the request...
845 */
846
bd7854cb 847 snprintf(uri, sizeof(uri), "/jobs/%d", id);
ef416fc2 848
bd7854cb 849 ippDelete(cupsDoFileRequest(http, request, uri, filename));
ef416fc2 850
bd7854cb 851 if (cupsLastError() > IPP_OK_CONFLICT)
ef416fc2 852 {
bd7854cb 853 syslog(LOG_ERR, "Unable to send document - %s", cupsLastErrorString());
ef416fc2 854
bd7854cb 855 return (-1);
ef416fc2 856 }
857
bd7854cb 858 return (0);
ef416fc2 859}
860
861
862/*
863 * 'recv_print_job()' - Receive a print job from the client.
864 */
865
bd7854cb 866static int /* O - Command status */
867recv_print_job(
868 const char *queue, /* I - Printer name */
869 int num_defaults, /* I - Number of default options */
870 cups_option_t *defaults) /* I - Default options */
ef416fc2 871{
bd7854cb 872 http_t *http; /* HTTP connection */
ef416fc2 873 int i; /* Looping var */
874 int status; /* Command status */
875 int fd; /* Temporary file */
876 FILE *fp; /* File pointer */
877 char filename[1024]; /* Temporary filename */
878 int bytes; /* Bytes received */
879 char line[256], /* Line from file/stdin */
880 command, /* Command from line */
881 *count, /* Number of bytes */
882 *name; /* Name of file */
bd7854cb 883 const char *job_sheets; /* Job sheets */
ef416fc2 884 int num_data; /* Number of data files */
885 char control[1024], /* Control filename */
bd7854cb 886 data[100][256], /* Data files */
887 temp[100][1024]; /* Temporary files */
ef416fc2 888 char user[1024], /* User name */
889 title[1024], /* Job title */
890 docname[1024], /* Document name */
bd7854cb 891 dest[256]; /* Printer/class queue */
892 int accepting, /* printer-is-accepting */
893 shared, /* printer-is-shared */
894 num_options; /* Number of options */
ef416fc2 895 cups_option_t *options; /* Options */
bd7854cb 896 int id; /* Job ID */
897 int docnumber, /* Current document number */
898 doccount; /* Count of documents */
ef416fc2 899
ef416fc2 900
bd7854cb 901 /*
902 * Connect to the server...
903 */
ef416fc2 904
bd7854cb 905 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
906 if (!http)
ef416fc2 907 {
bd7854cb 908 syslog(LOG_ERR, "Unable to connect to server: %s", strerror(errno));
ef416fc2 909
bd7854cb 910 putchar(1);
ef416fc2 911
bd7854cb 912 return (1);
913 }
ef416fc2 914
bd7854cb 915 /*
916 * See if the printer is available...
917 */
ef416fc2 918
bd7854cb 919 num_options = get_printer(http, queue, dest, sizeof(dest), &options,
920 &accepting, &shared, NULL);
ef416fc2 921
bd7854cb 922 if (num_options < 0 || !accepting || !shared)
ef416fc2 923 {
bd7854cb 924 if (dest[0])
925 syslog(LOG_INFO, "Rejecting job because \"%s\" is not %s", dest,
926 !accepting ? "accepting jobs" : "shared");
927 else
928 syslog(LOG_ERR, "Unable to get printer information for \"%s\"", queue);
929
930 httpClose(http);
ef416fc2 931
932 putchar(1);
933
934 return (1);
935 }
936
bd7854cb 937 putchar(0); /* OK so far... */
938
939 /*
940 * Read the request...
941 */
942
943 status = 0;
944 num_data = 0;
945 fd = -1;
946
947 control[0] = '\0';
ef416fc2 948
949 while (smart_gets(line, sizeof(line), stdin) != NULL)
950 {
951 if (strlen(line) < 2)
952 {
953 status = 1;
954 break;
955 }
956
957 command = line[0];
958 count = line + 1;
959
960 for (name = count + 1; *name && !isspace(*name & 255); name ++);
961 while (isspace(*name & 255))
962 *name++ = '\0';
963
964 switch (command)
965 {
966 default :
967 case 0x01 : /* Abort */
968 status = 1;
969 break;
970
971 case 0x02 : /* Receive control file */
972 if (strlen(name) < 2)
973 {
974 syslog(LOG_ERR, "Bad control file name \"%s\"", name);
975 putchar(1);
976 status = 1;
977 break;
978 }
979
980 if (control[0])
981 {
982 /*
983 * Append to the existing control file - the LPD spec is
984 * not entirely clear, but at least the OS/2 LPD code sends
985 * multiple control files per connection...
986 */
987
988 if ((fd = open(control, O_WRONLY)) < 0)
989 {
990 syslog(LOG_ERR,
991 "Unable to append to temporary control file \"%s\" - %s",
992 control, strerror(errno));
993 putchar(1);
994 status = 1;
995 break;
996 }
997
998 lseek(fd, 0, SEEK_END);
999 }
1000 else
1001 {
1002 if ((fd = cupsTempFd(control, sizeof(control))) < 0)
1003 {
1004 syslog(LOG_ERR, "Unable to open temporary control file \"%s\" - %s",
1005 control, strerror(errno));
1006 putchar(1);
1007 status = 1;
1008 break;
1009 }
1010
1011 strcpy(filename, control);
1012 }
1013 break;
1014
1015 case 0x03 : /* Receive data file */
1016 if (strlen(name) < 2)
1017 {
1018 syslog(LOG_ERR, "Bad data file name \"%s\"", name);
1019 putchar(1);
1020 status = 1;
1021 break;
1022 }
1023
bd7854cb 1024 if (num_data >= (int)(sizeof(data) / sizeof(data[0])))
ef416fc2 1025 {
1026 /*
1027 * Too many data files...
1028 */
1029
1030 syslog(LOG_ERR, "Too many data files (%d)", num_data);
1031 putchar(1);
1032 status = 1;
1033 break;
1034 }
1035
1036 strlcpy(data[num_data], name, sizeof(data[0]));
1037
1038 if ((fd = cupsTempFd(temp[num_data], sizeof(temp[0]))) < 0)
1039 {
1040 syslog(LOG_ERR, "Unable to open temporary data file \"%s\" - %s",
1041 temp[num_data], strerror(errno));
1042 putchar(1);
1043 status = 1;
1044 break;
1045 }
1046
1047 strcpy(filename, temp[num_data]);
1048
1049 num_data ++;
1050 break;
1051 }
1052
1053 putchar(status);
1054
1055 if (status)
1056 break;
1057
1058 /*
1059 * Copy the data or control file from the client...
1060 */
1061
1062 for (i = atoi(count); i > 0; i -= bytes)
1063 {
1064 if (i > sizeof(line))
1065 bytes = sizeof(line);
1066 else
1067 bytes = i;
1068
1069 if ((bytes = fread(line, 1, bytes, stdin)) > 0)
1070 bytes = write(fd, line, bytes);
1071
1072 if (bytes < 1)
1073 {
1074 syslog(LOG_ERR, "Error while reading file - %s",
1075 strerror(errno));
1076 status = 1;
1077 break;
1078 }
1079 }
1080
1081 /*
1082 * Read trailing nul...
1083 */
1084
1085 if (!status)
1086 {
1087 if (fread(line, 1, 1, stdin) < 1)
1088 {
1089 status = 1;
1090 syslog(LOG_ERR, "Error while reading trailing nul - %s",
1091 strerror(errno));
1092 }
1093 else if (line[0])
1094 {
1095 status = 1;
1096 syslog(LOG_ERR, "Trailing character after file is not nul (%02X)!",
1097 line[0]);
1098 }
1099 }
1100
1101 /*
1102 * Close the file and send an acknowledgement...
1103 */
1104
1105 close(fd);
1106
1107 putchar(status);
1108
1109 if (status)
1110 break;
1111 }
1112
1113 if (!status)
1114 {
1115 /*
1116 * Process the control file and print stuff...
1117 */
1118
1119 if ((fp = fopen(control, "rb")) == NULL)
1120 status = 1;
1121 else
1122 {
1123 /*
bd7854cb 1124 * Copy the default options...
ef416fc2 1125 */
1126
bd7854cb 1127 for (i = 0; i < num_defaults; i ++)
1128 num_options = cupsAddOption(defaults[i].name,
1129 defaults[i].value,
1130 num_options, &options);
1131
1132 /*
1133 * Grab the job information...
1134 */
1135
1136 title[0] = '\0';
1137 user[0] = '\0';
1138 doccount = 0;
ef416fc2 1139
1140 while (smart_gets(line, sizeof(line), fp) != NULL)
1141 {
1142 /*
1143 * Process control lines...
1144 */
1145
1146 switch (line[0])
1147 {
1148 case 'J' : /* Job name */
1149 strlcpy(title, line + 1, sizeof(title));
1150 break;
1151
ef416fc2 1152 case 'P' : /* User identification */
1153 strlcpy(user, line + 1, sizeof(user));
1154 break;
1155
1156 case 'L' : /* Print banner page */
bd7854cb 1157 /*
1158 * If a banner was requested and it's not overridden by a
1159 * command line option and the destination's default is none
1160 * then add the standard banner...
1161 */
ef416fc2 1162
bd7854cb 1163 if (cupsGetOption("job-sheets", num_defaults, defaults) == NULL &&
1164 ((job_sheets = cupsGetOption("job-sheets", num_options,
1165 options)) == NULL ||
1166 !strcmp(job_sheets, "none,none")))
1167 {
1168 num_options = cupsAddOption("job-sheets", "standard",
1169 num_options, &options);
1170 }
1171 break;
ef416fc2 1172
ef416fc2 1173 case 'c' : /* Plot CIF file */
1174 case 'd' : /* Print DVI file */
1175 case 'f' : /* Print formatted file */
1176 case 'g' : /* Plot file */
1177 case 'l' : /* Print file leaving control characters (raw) */
1178 case 'n' : /* Print ditroff output file */
1179 case 'o' : /* Print PostScript output file */
1180 case 'p' : /* Print file with 'pr' format (prettyprint) */
1181 case 'r' : /* File to print with FORTRAN carriage control */
1182 case 't' : /* Print troff output file */
1183 case 'v' : /* Print raster file */
bd7854cb 1184 doccount ++;
ef416fc2 1185
bd7854cb 1186 if (line[0] == 'l' &&
1187 !cupsGetOption("document-format", num_options, options))
1188 num_options = cupsAddOption("raw", "", num_options, &options);
ef416fc2 1189
bd7854cb 1190 if (line[0] == 'p')
1191 num_options = cupsAddOption("prettyprint", "", num_options,
1192 &options);
1193 break;
1194 }
ef416fc2 1195
bd7854cb 1196 if (status)
1197 break;
1198 }
ef416fc2 1199
bd7854cb 1200 /*
1201 * Check that we have a username...
1202 */
ef416fc2 1203
bd7854cb 1204 if (!user[0])
1205 {
1206 syslog(LOG_WARNING, "No username specified by client! "
1207 "Using \"anonymous\"...");
1208 strcpy(user, "anonymous");
1209 }
ef416fc2 1210
bd7854cb 1211 /*
1212 * Create the job...
1213 */
ef416fc2 1214
bd7854cb 1215 if ((id = create_job(http, dest, title, user, num_options, options)) < 0)
1216 status = 1;
1217 else
1218 {
1219 /*
1220 * Then print the job files...
1221 */
ef416fc2 1222
bd7854cb 1223 rewind(fp);
ef416fc2 1224
bd7854cb 1225 docname[0] = '\0';
1226 docnumber = 0;
ef416fc2 1227
bd7854cb 1228 while (smart_gets(line, sizeof(line), fp) != NULL)
1229 {
1230 /*
1231 * Process control lines...
1232 */
1233
1234 switch (line[0])
1235 {
1236 case 'N' : /* Document name */
1237 strlcpy(docname, line + 1, sizeof(docname));
1238 break;
ef416fc2 1239
bd7854cb 1240 case 'c' : /* Plot CIF file */
1241 case 'd' : /* Print DVI file */
1242 case 'f' : /* Print formatted file */
1243 case 'g' : /* Plot file */
1244 case 'l' : /* Print file leaving control characters (raw) */
1245 case 'n' : /* Print ditroff output file */
1246 case 'o' : /* Print PostScript output file */
1247 case 'p' : /* Print file with 'pr' format (prettyprint) */
1248 case 'r' : /* File to print with FORTRAN carriage control */
1249 case 't' : /* Print troff output file */
1250 case 'v' : /* Print raster file */
1251 /*
1252 * Figure out which file we are printing...
1253 */
1254
1255 for (i = 0; i < num_data; i ++)
1256 if (!strcmp(data[i], line + 1))
1257 break;
1258
1259 if (i >= num_data)
1260 {
1261 status = 1;
ef416fc2 1262 break;
bd7854cb 1263 }
ef416fc2 1264
bd7854cb 1265 /*
1266 * Send the print file...
1267 */
ef416fc2 1268
bd7854cb 1269 docnumber ++;
ef416fc2 1270
bd7854cb 1271 if (print_file(http, id, temp[i], docname, user,
1272 docnumber == doccount))
1273 status = 1;
1274 else
1275 status = 0;
ef416fc2 1276
bd7854cb 1277 break;
1278 }
1279
1280 if (status)
1281 break;
ef416fc2 1282 }
1283
bd7854cb 1284 fclose(fp);
ef416fc2 1285 }
ef416fc2 1286 }
1287 }
1288
bd7854cb 1289 cupsFreeOptions(num_options, options);
1290
1291 httpClose(http);
1292
ef416fc2 1293 /*
1294 * Clean up all temporary files and return...
1295 */
1296
1297 unlink(control);
1298
1299 for (i = 0; i < num_data; i ++)
1300 unlink(temp[i]);
1301
ef416fc2 1302 return (status);
1303}
1304
1305
1306/*
1307 * 'remove_jobs()' - Cancel one or more jobs.
1308 */
1309
1310int /* O - Command status */
1311remove_jobs(const char *dest, /* I - Destination */
1312 const char *agent, /* I - User agent */
1313 const char *list) /* I - List of jobs or users */
1314{
1315 int id; /* Job ID */
1316 http_t *http; /* HTTP server connection */
bd7854cb 1317 ipp_t *request; /* IPP Request */
ef416fc2 1318 char uri[HTTP_MAX_URI]; /* Job URI */
1319
1320
1321 (void)dest; /* Suppress compiler warnings... */
1322
1323 /*
1324 * Try connecting to the local server...
1325 */
1326
1327 if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
1328 cupsEncryption())) == NULL)
1329 {
1330 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1331 strerror(errno));
1332 return (1);
1333 }
1334
ef416fc2 1335 /*
1336 * Loop for each job...
1337 */
1338
1339 while ((id = atoi(list)) > 0)
1340 {
1341 /*
1342 * Skip job ID in list...
1343 */
1344
1345 while (isdigit(*list & 255))
1346 list ++;
1347 while (isspace(*list & 255))
1348 list ++;
1349
1350 /*
1351 * Build an IPP_CANCEL_JOB request, which requires the following
1352 * attributes:
1353 *
1354 * attributes-charset
1355 * attributes-natural-language
1356 * job-uri
1357 * requesting-user-name
1358 */
1359
bd7854cb 1360 request = ippNewRequest(IPP_CANCEL_JOB);
ef416fc2 1361
1362 sprintf(uri, "ipp://localhost/jobs/%d", id);
1363 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
1364
1365 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1366 "requesting-user-name", NULL, agent);
1367
1368 /*
1369 * Do the request and get back a response...
1370 */
1371
bd7854cb 1372 ippDelete(cupsDoRequest(http, request, "/jobs"));
ef416fc2 1373
bd7854cb 1374 if (cupsLastError() > IPP_OK_CONFLICT)
ef416fc2 1375 {
1376 syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
bd7854cb 1377 cupsLastErrorString());
ef416fc2 1378 httpClose(http);
1379 return (1);
1380 }
bd7854cb 1381 else
1382 syslog(LOG_INFO, "Job ID %d cancelled", id);
ef416fc2 1383 }
1384
ef416fc2 1385 httpClose(http);
1386
1387 return (0);
1388}
1389
1390
1391/*
1392 * 'send_state()' - Send the queue state.
1393 */
1394
1395int /* O - Command status */
bd7854cb 1396send_state(const char *queue, /* I - Destination */
ef416fc2 1397 const char *list, /* I - Job or user */
1398 int longstatus) /* I - List of jobs or users */
1399{
1400 int id; /* Job ID from list */
1401 http_t *http; /* HTTP server connection */
1402 ipp_t *request, /* IPP Request */
1403 *response; /* IPP Response */
1404 ipp_attribute_t *attr; /* Current attribute */
ef416fc2 1405 ipp_pstate_t state; /* Printer state */
1406 const char *jobdest, /* Pointer into job-printer-uri */
1407 *jobuser, /* Pointer to job-originating-user-name */
1408 *jobname; /* Pointer to job-name */
1409 ipp_jstate_t jobstate; /* job-state */
1410 int jobid, /* job-id */
1411 jobsize, /* job-k-octets */
1412 jobcount, /* Number of jobs */
1413 jobcopies, /* Number of copies */
1414 rank; /* Rank of job */
1415 char rankstr[255]; /* Rank string */
1416 char namestr[1024]; /* Job name string */
1417 char uri[HTTP_MAX_URI]; /* Printer URI */
bd7854cb 1418 char dest[256]; /* Printer/class queue */
ef416fc2 1419 static const char * const ranks[10] = /* Ranking strings */
1420 {
1421 "th",
1422 "st",
1423 "nd",
1424 "rd",
1425 "th",
1426 "th",
1427 "th",
1428 "th",
1429 "th",
1430 "th"
1431 };
1432 static const char * const requested[] =
1433 { /* Requested attributes */
1434 "job-id",
1435 "job-k-octets",
1436 "job-state",
1437 "job-printer-uri",
1438 "job-originating-user-name",
1439 "job-name",
1440 "copies"
1441 };
1442
1443
ef416fc2 1444 /*
1445 * Try connecting to the local server...
1446 */
1447
1448 if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
1449 cupsEncryption())) == NULL)
1450 {
1451 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1452 strerror(errno));
1453 printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno));
1454 return (1);
1455 }
1456
1457 /*
bd7854cb 1458 * Get the actual destination name and printer state...
ef416fc2 1459 */
1460
bd7854cb 1461 if (get_printer(http, queue, dest, sizeof(dest), NULL, NULL, NULL, &state))
1462 {
1463 syslog(LOG_ERR, "Unable to get printer %s: %s", queue,
1464 cupsLastErrorString());
1465 printf("Unable to get printer %s: %s", queue, cupsLastErrorString());
1466 return (1);
1467 }
ef416fc2 1468
1469 /*
bd7854cb 1470 * Show the queue state...
ef416fc2 1471 */
1472
bd7854cb 1473 switch (state)
ef416fc2 1474 {
bd7854cb 1475 case IPP_PRINTER_IDLE :
1476 printf("%s is ready\n", dest);
1477 break;
1478 case IPP_PRINTER_PROCESSING :
1479 printf("%s is ready and printing\n", dest);
1480 break;
1481 case IPP_PRINTER_STOPPED :
1482 printf("%s is not ready\n", dest);
1483 break;
ef416fc2 1484 }
1485
1486 /*
1487 * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
1488 * the following attributes:
1489 *
1490 * attributes-charset
1491 * attributes-natural-language
1492 * job-uri or printer-uri
1493 */
1494
1495 id = atoi(list);
1496
bd7854cb 1497 request = ippNewRequest(id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS);
ef416fc2 1498
a4d04587 1499 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
bd7854cb 1500 "localhost", 0, "/printers/%s", dest);
ef416fc2 1501
1502 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1503 NULL, uri);
1504
1505 if (id)
1506 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1507 else
1508 {
1509 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1510 "requesting-user-name", NULL, list);
1511 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
1512 }
1513
1514 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
bd7854cb 1515 "requested-attributes",
1516 sizeof(requested) / sizeof(requested[0]),
ef416fc2 1517 NULL, requested);
1518
1519 /*
1520 * Do the request and get back a response...
1521 */
1522
1523 jobcount = 0;
bd7854cb 1524 response = cupsDoRequest(http, request, "/");
ef416fc2 1525
bd7854cb 1526 if (cupsLastError() > IPP_OK_CONFLICT)
ef416fc2 1527 {
bd7854cb 1528 printf("get-jobs failed: %s\n", cupsLastErrorString());
1529 ippDelete(response);
1530 return (1);
1531 }
ef416fc2 1532
bd7854cb 1533 /*
1534 * Loop through the job list and display them...
1535 */
ef416fc2 1536
bd7854cb 1537 for (attr = response->attrs, rank = 1; attr; attr = attr->next)
1538 {
ef416fc2 1539 /*
bd7854cb 1540 * Skip leading attributes until we hit a job...
ef416fc2 1541 */
1542
bd7854cb 1543 while (attr && (attr->group_tag != IPP_TAG_JOB || !attr->name))
1544 attr = attr->next;
ef416fc2 1545
bd7854cb 1546 if (!attr)
1547 break;
ef416fc2 1548
bd7854cb 1549 /*
1550 * Pull the needed attributes from this job...
1551 */
ef416fc2 1552
bd7854cb 1553 jobid = 0;
1554 jobsize = 0;
1555 jobstate = IPP_JOB_PENDING;
1556 jobname = "untitled";
1557 jobuser = NULL;
1558 jobdest = NULL;
1559 jobcopies = 1;
ef416fc2 1560
bd7854cb 1561 while (attr && attr->group_tag == IPP_TAG_JOB)
1562 {
1563 if (!strcmp(attr->name, "job-id") &&
1564 attr->value_tag == IPP_TAG_INTEGER)
1565 jobid = attr->values[0].integer;
ef416fc2 1566
bd7854cb 1567 if (!strcmp(attr->name, "job-k-octets") &&
1568 attr->value_tag == IPP_TAG_INTEGER)
1569 jobsize = attr->values[0].integer;
ef416fc2 1570
bd7854cb 1571 if (!strcmp(attr->name, "job-state") &&
1572 attr->value_tag == IPP_TAG_ENUM)
1573 jobstate = (ipp_jstate_t)attr->values[0].integer;
ef416fc2 1574
bd7854cb 1575 if (!strcmp(attr->name, "job-printer-uri") &&
1576 attr->value_tag == IPP_TAG_URI)
1577 if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
1578 jobdest ++;
ef416fc2 1579
bd7854cb 1580 if (!strcmp(attr->name, "job-originating-user-name") &&
1581 attr->value_tag == IPP_TAG_NAME)
1582 jobuser = attr->values[0].string.text;
ef416fc2 1583
bd7854cb 1584 if (!strcmp(attr->name, "job-name") &&
1585 attr->value_tag == IPP_TAG_NAME)
1586 jobname = attr->values[0].string.text;
ef416fc2 1587
bd7854cb 1588 if (!strcmp(attr->name, "copies") &&
1589 attr->value_tag == IPP_TAG_INTEGER)
1590 jobcopies = attr->values[0].integer;
ef416fc2 1591
bd7854cb 1592 attr = attr->next;
1593 }
ef416fc2 1594
bd7854cb 1595 /*
1596 * See if we have everything needed...
1597 */
ef416fc2 1598
bd7854cb 1599 if (!jobdest || !jobid)
1600 {
1601 if (!attr)
1602 break;
1603 else
1604 continue;
1605 }
ef416fc2 1606
bd7854cb 1607 if (!longstatus && jobcount == 0)
1608 puts("Rank Owner Job File(s) Total Size");
ef416fc2 1609
bd7854cb 1610 jobcount ++;
ef416fc2 1611
bd7854cb 1612 /*
1613 * Display the job...
1614 */
ef416fc2 1615
bd7854cb 1616 if (jobstate == IPP_JOB_PROCESSING)
1617 strcpy(rankstr, "active");
1618 else
1619 {
1620 snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
1621 rank ++;
1622 }
ef416fc2 1623
bd7854cb 1624 if (longstatus)
1625 {
1626 puts("");
ef416fc2 1627
bd7854cb 1628 if (jobcopies > 1)
1629 snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
1630 jobname);
ef416fc2 1631 else
bd7854cb 1632 strlcpy(namestr, jobname, sizeof(namestr));
ef416fc2 1633
bd7854cb 1634 printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid);
1635 printf(" %-39.39s %.0f bytes\n", namestr, 1024.0 * jobsize);
ef416fc2 1636 }
bd7854cb 1637 else
1638 printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr, jobuser,
1639 jobid, jobname, 1024.0 * jobsize);
ef416fc2 1640
bd7854cb 1641 if (!attr)
1642 break;
ef416fc2 1643 }
1644
bd7854cb 1645 ippDelete(response);
1646
ef416fc2 1647 if (jobcount == 0)
1648 puts("no entries");
1649
ef416fc2 1650 httpClose(http);
1651
1652 return (0);
1653}
1654
1655
1656/*
1657 * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1658 */
1659
1660char * /* O - Line read or NULL */
1661smart_gets(char *s, /* I - Pointer to line buffer */
1662 int len, /* I - Size of line buffer */
1663 FILE *fp) /* I - File to read from */
1664{
1665 char *ptr, /* Pointer into line */
1666 *end; /* End of line */
1667 int ch; /* Character from file */
1668
1669
1670 /*
1671 * Read the line; unlike fgets(), we read the entire line but dump
1672 * characters that go past the end of the buffer. Also, we accept
1673 * CR, LF, or CR LF for the line endings to be "safe", although
1674 * RFC 1179 specifically says "just use LF".
1675 */
1676
1677 ptr = s;
1678 end = s + len - 1;
1679
1680 while ((ch = getc(fp)) != EOF)
1681 {
1682 if (ch == '\n')
1683 break;
1684 else if (ch == '\r')
1685 {
1686 /*
1687 * See if a LF follows...
1688 */
1689
1690 ch = getc(fp);
1691
1692 if (ch != '\n')
1693 ungetc(ch, fp);
1694
1695 break;
1696 }
1697 else if (ptr < end)
1698 *ptr++ = ch;
1699 }
1700
1701 *ptr = '\0';
1702
1703 if (ch == EOF && ptr == s)
1704 return (NULL);
1705 else
1706 return (s);
1707}
1708
1709
1710/*
bd7854cb 1711 * End of "$Id: cups-lpd.c 5137 2006-02-19 19:44:50Z mike $".
ef416fc2 1712 */