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