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