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