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