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