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