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