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