]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/cups-lpd.c
Fix source file header text duplication text duplication.
[thirdparty/cups.git] / scheduler / cups-lpd.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * Line Printer Daemon interface for CUPS.
ef416fc2 3 *
961a8a37 4 * Copyright 2007-2016 by Apple Inc.
7e86f2f6 5 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
ef416fc2 6 *
7e86f2f6
MS
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
57b7b66b 11 * missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 12 */
13
14/*
15 * Include necessary headers...
16 */
17
961a8a37 18#define _CUPS_NO_DEPRECATED
71e16022 19#include <cups/cups-private.h>
ef416fc2 20#include <syslog.h>
ef416fc2 21#include <unistd.h>
22#include <fcntl.h>
ef416fc2 23#include <sys/types.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include <netdb.h>
27
bd7854cb 28#ifdef HAVE_INTTYPES_H
29# include <inttypes.h>
30#endif /* HAVE_INTTYPES_H */
64dda396
MS
31#ifdef __APPLE__
32# include <vproc.h>
33#endif /* __APPLE__ */
bd7854cb 34
ef416fc2 35
36/*
37 * LPD "mini-daemon" for CUPS. This program must be used in conjunction
38 * with inetd or another similar program that monitors ports and starts
39 * daemons for each client connection. A typical configuration is:
40 *
41 * printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
42 *
43 * This daemon implements most of RFC 1179 (the unofficial LPD specification)
44 * except for:
45 *
46 * - This daemon does not check to make sure that the source port is
47 * between 721 and 731, since it isn't necessary for proper
48 * functioning and port-based security is no security at all!
49 *
50 * - The "Print any waiting jobs" command is a no-op.
51 *
52 * The LPD-to-IPP mapping is as defined in RFC 2569. The report formats
53 * currently match the Solaris LPD mini-daemon.
54 */
55
56/*
57 * Prototypes...
58 */
59
bb94e24c 60static int create_job(http_t *http, const char *dest, const char *title, const char *user, int num_options, cups_option_t *options);
bd7854cb 61static int get_printer(http_t *http, const char *name, char *dest,
07623986 62 size_t destsize, cups_option_t **options,
bd7854cb 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);
5eb74392 74static void smart_strlcpy(char *dst, const char *src, size_t dstsize);
ef416fc2 75
76
77/*
78 * 'main()' - Process an incoming LPD request...
79 */
80
81int /* O - Exit status */
82main(int argc, /* I - Number of command-line arguments */
83 char *argv[]) /* I - Command-line arguments */
84{
85 int i; /* Looping var */
86 int num_defaults; /* Number of default options */
87 cups_option_t *defaults; /* Default options */
88 char line[256], /* Command string */
89 command, /* Command code */
90 *dest, /* Pointer to destination */
91 *list, /* Pointer to list */
92 *agent, /* Pointer to user */
93 status; /* Status for client */
94 socklen_t hostlen; /* Size of client address */
95 http_addr_t hostaddr; /* Address of client */
96 char hostname[256], /* Name of client */
97 hostip[256], /* IP address */
98 *hostfamily; /* Address family */
89d46774 99 int hostlookups; /* Do hostname lookups? */
64dda396
MS
100#ifdef __APPLE__
101 vproc_transaction_t vtran = vproc_transaction_begin(NULL);
102#endif /* __APPLE__ */
ef416fc2 103
104
105 /*
106 * Don't buffer the output...
107 */
108
109 setbuf(stdout, NULL);
110
111 /*
112 * Log things using the "cups-lpd" name...
113 */
114
115 openlog("cups-lpd", LOG_PID, LOG_LPR);
116
ef416fc2 117 /*
118 * Scan the command-line for options...
119 */
120
121 num_defaults = 0;
122 defaults = NULL;
89d46774 123 hostlookups = 1;
ef416fc2 124
125 for (i = 1; i < argc; i ++)
126 if (argv[i][0] == '-')
127 {
128 switch (argv[i][1])
129 {
ee571f26
MS
130 case 'h' : /* -h hostname[:port] */
131 if (argv[i][2])
132 cupsSetServer(argv[i] + 2);
133 else
134 {
135 i ++;
136 if (i < argc)
137 cupsSetServer(argv[i]);
138 else
139 syslog(LOG_WARNING, "Expected hostname string after -h option!");
140 }
141 break;
142
ef416fc2 143 case 'o' : /* Option */
144 if (argv[i][2])
145 num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
146 &defaults);
147 else
148 {
149 i ++;
150 if (i < argc)
bd7854cb 151 num_defaults = cupsParseOptions(argv[i], num_defaults,
152 &defaults);
ef416fc2 153 else
154 syslog(LOG_WARNING, "Expected option string after -o option!");
155 }
156 break;
89d46774 157
158 case 'n' : /* Don't do hostname lookups */
159 hostlookups = 0;
160 break;
161
ef416fc2 162 default :
163 syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]);
164 break;
165 }
166 }
167 else
bd7854cb 168 syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!",
169 argv[i]);
ef416fc2 170
89d46774 171 /*
172 * Get the address of the client...
173 */
174
175 hostlen = sizeof(hostaddr);
176
177 if (getpeername(0, (struct sockaddr *)&hostaddr, &hostlen))
178 {
179 syslog(LOG_WARNING, "Unable to get client address - %s", strerror(errno));
5a9febac 180 strlcpy(hostname, "unknown", sizeof(hostname));
89d46774 181 }
182 else
183 {
184 httpAddrString(&hostaddr, hostip, sizeof(hostip));
185
186 if (hostlookups)
187 httpAddrLookup(&hostaddr, hostname, sizeof(hostname));
188 else
189 strlcpy(hostname, hostip, sizeof(hostname));
190
191#ifdef AF_INET6
192 if (hostaddr.addr.sa_family == AF_INET6)
193 hostfamily = "IPv6";
194 else
195#endif /* AF_INET6 */
196 hostfamily = "IPv4";
197
198 syslog(LOG_INFO, "Connection from %s (%s %s)", hostname, hostfamily,
199 hostip);
200 }
201
2abf387c 202 num_defaults = cupsAddOption("job-originating-host-name", hostname,
203 num_defaults, &defaults);
204
ef416fc2 205 /*
206 * RFC1179 specifies that only 1 daemon command can be received for
207 * every connection.
208 */
209
210 if (smart_gets(line, sizeof(line), stdin) == NULL)
211 {
212 /*
213 * Unable to get command from client! Send an error status and return.
214 */
215
216 syslog(LOG_ERR, "Unable to get command line from client!");
217 putchar(1);
64dda396
MS
218
219#ifdef __APPLE__
220 vproc_transaction_end(NULL, vtran);
221#endif /* __APPLE__ */
222
ef416fc2 223 return (1);
224 }
225
226 /*
227 * The first byte is the command byte. After that will be the queue name,
228 * resource list, and/or user name.
229 */
230
29067194
MS
231 if ((command = line[0]) == '\0')
232 dest = line;
233 else
234 dest = line + 1;
ef416fc2 235
bd7854cb 236 if (command == 0x02)
237 list = NULL;
238 else
239 {
82cc1f9a 240 for (list = dest; *list && !isspace(*list & 255); list ++);
ef416fc2 241
bd7854cb 242 while (isspace(*list & 255))
243 *list++ = '\0';
244 }
ef416fc2 245
246 /*
247 * Do the command...
248 */
249
250 switch (command)
251 {
252 default : /* Unknown command */
253 syslog(LOG_ERR, "Unknown LPD command 0x%02X!", command);
254 syslog(LOG_ERR, "Command line = %s", line + 1);
255 putchar(1);
256
257 status = 1;
258 break;
259
260 case 0x01 : /* Print any waiting jobs */
261 syslog(LOG_INFO, "Print waiting jobs (no-op)");
262 putchar(0);
263
264 status = 0;
265 break;
266
267 case 0x02 : /* Receive a printer job */
268 syslog(LOG_INFO, "Receive print job for %s", dest);
269 /* recv_print_job() sends initial status byte */
270
7e86f2f6 271 status = (char)recv_print_job(dest, num_defaults, defaults);
ef416fc2 272 break;
273
274 case 0x03 : /* Send queue state (short) */
275 syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list);
276 /* no status byte for this command */
277
7e86f2f6 278 status = (char)send_state(dest, list, 0);
ef416fc2 279 break;
280
281 case 0x04 : /* Send queue state (long) */
282 syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list);
283 /* no status byte for this command */
284
7e86f2f6 285 status = (char)send_state(dest, list, 1);
ef416fc2 286 break;
287
288 case 0x05 : /* Remove jobs */
1f0275e3
MS
289 if (list)
290 {
291 /*
292 * Grab the agent and skip to the list of users and/or jobs.
293 */
ef416fc2 294
1f0275e3 295 agent = list;
ef416fc2 296
1f0275e3
MS
297 for (; *list && !isspace(*list & 255); list ++);
298 while (isspace(*list & 255))
299 *list++ = '\0';
ef416fc2 300
1f0275e3 301 syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);
ef416fc2 302
7e86f2f6 303 status = (char)remove_jobs(dest, agent, list);
1f0275e3
MS
304 }
305 else
306 status = 1;
ef416fc2 307
308 putchar(status);
309 break;
310 }
311
312 syslog(LOG_INFO, "Closing connection");
313 closelog();
314
64dda396
MS
315#ifdef __APPLE__
316 vproc_transaction_end(NULL, vtran);
317#endif /* __APPLE__ */
318
ef416fc2 319 return (status);
320}
321
322
323/*
bd7854cb 324 * 'create_job()' - Create a new print job.
ef416fc2 325 */
326
bd7854cb 327static int /* O - Job ID or -1 on error */
328create_job(http_t *http, /* I - HTTP connection */
329 const char *dest, /* I - Destination name */
330 const char *title, /* I - job-name */
bb94e24c 331 const char *user, /* I - requesting-user-name */
bd7854cb 332 int num_options, /* I - Number of options for job */
333 cups_option_t *options) /* I - Options for job */
ef416fc2 334{
ef416fc2 335 ipp_t *request; /* IPP request */
336 ipp_t *response; /* IPP response */
bd7854cb 337 ipp_attribute_t *attr; /* IPP attribute */
ef416fc2 338 char uri[HTTP_MAX_URI]; /* Printer URI */
bd7854cb 339 int id; /* Job ID */
ef416fc2 340
341
342 /*
bd7854cb 343 * Setup the Create-Job request...
ef416fc2 344 */
345
961a8a37 346 request = ippNewRequest(IPP_OP_CREATE_JOB);
ef416fc2 347
a4d04587 348 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
bd7854cb 349 "localhost", 0, "/printers/%s", dest);
ef416fc2 350
351 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
352 NULL, uri);
353
bd7854cb 354 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
355 "requesting-user-name", NULL, user);
356
c5571a1d 357 if (title[0])
bd7854cb 358 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
359 NULL, title);
360
361 cupsEncodeOptions(request, num_options, options);
ef416fc2 362
363 /*
364 * Do the request...
365 */
366
bd7854cb 367 snprintf(uri, sizeof(uri), "/printers/%s", dest);
ef416fc2 368
bd7854cb 369 response = cupsDoRequest(http, request, uri);
370
961a8a37 371 if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING)
ef416fc2 372 {
bd7854cb 373 syslog(LOG_ERR, "Unable to create job - %s", cupsLastErrorString());
374
375 ippDelete(response);
376
377 return (-1);
ef416fc2 378 }
bd7854cb 379
380 /*
381 * Get the job-id value from the response and return it...
382 */
383
384 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
ef416fc2 385 {
bd7854cb 386 id = -1;
387
388 syslog(LOG_ERR, "No job-id attribute found in response from server!");
ef416fc2 389 }
390 else
bd7854cb 391 {
392 id = attr->values[0].integer;
ef416fc2 393
bd7854cb 394 syslog(LOG_INFO, "Print file - job ID = %d", id);
395 }
ef416fc2 396
bd7854cb 397 ippDelete(response);
ef416fc2 398
bd7854cb 399 return (id);
ef416fc2 400}
401
402
403/*
bd7854cb 404 * 'get_printer()' - Get the named printer and its options.
ef416fc2 405 */
406
bd7854cb 407static int /* O - Number of options or -1 on error */
408get_printer(http_t *http, /* I - HTTP connection */
409 const char *name, /* I - Printer name from request */
410 char *dest, /* I - Destination buffer */
07623986 411 size_t destsize, /* I - Size of destination buffer */
bd7854cb 412 cups_option_t **options, /* O - Printer options */
413 int *accepting, /* O - printer-is-accepting-jobs value */
414 int *shared, /* O - printer-is-shared value */
415 ipp_pstate_t *state) /* O - printer-state value */
ef416fc2 416{
bd7854cb 417 int num_options; /* Number of options */
418 cups_file_t *fp; /* lpoptions file */
419 char line[1024], /* Line from lpoptions file */
420 *value, /* Pointer to value on line */
421 *optptr; /* Pointer to options on line */
422 int linenum; /* Line number in file */
423 const char *cups_serverroot; /* CUPS_SERVERROOT env var */
ef416fc2 424 ipp_t *request; /* IPP request */
425 ipp_t *response; /* IPP response */
bd7854cb 426 ipp_attribute_t *attr; /* IPP attribute */
ef416fc2 427 char uri[HTTP_MAX_URI]; /* Printer URI */
bd7854cb 428 static const char * const requested[] =
429 { /* Requested attributes */
430 "printer-info",
431 "printer-is-accepting-jobs",
432 "printer-is-shared",
433 "printer-name",
434 "printer-state"
435 };
ef416fc2 436
437
438 /*
bd7854cb 439 * Initialize everything...
ef416fc2 440 */
441
bd7854cb 442 if (accepting)
443 *accepting = 0;
444 if (shared)
445 *shared = 0;
446 if (state)
961a8a37 447 *state = IPP_PSTATE_STOPPED;
bd7854cb 448 if (options)
449 *options = NULL;
450
451 /*
bc44d920 452 * See if the name is a queue name optionally with an instance name.
453 */
454
455 strlcpy(dest, name, destsize);
456 if ((value = strchr(dest, '/')) != NULL)
457 *value = '\0';
458
459 /*
460 * Setup the Get-Printer-Attributes request...
bd7854cb 461 */
462
961a8a37 463 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
bc44d920 464
465 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
466 "localhost", 0, "/printers/%s", dest);
467
468 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
469 NULL, uri);
470
471 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
472 "requested-attributes",
473 (int)(sizeof(requested) / sizeof(requested[0])),
474 NULL, requested);
475
476 /*
477 * Do the request...
478 */
479
480 response = cupsDoRequest(http, request, "/");
481
961a8a37 482 if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING)
ef416fc2 483 {
bd7854cb 484 /*
bc44d920 485 * If we can't find the printer by name, look up the printer-name
486 * using the printer-info values...
bd7854cb 487 */
488
489 ipp_attribute_t *accepting_attr,/* printer-is-accepting-jobs */
490 *info_attr, /* printer-info */
491 *name_attr, /* printer-name */
492 *shared_attr, /* printer-is-shared */
493 *state_attr; /* printer-state */
494
495
bc44d920 496 ippDelete(response);
497
bd7854cb 498 /*
499 * Setup the CUPS-Get-Printers request...
500 */
501
961a8a37 502 request = ippNewRequest(IPP_OP_CUPS_GET_PRINTERS);
bd7854cb 503
d09495fa 504 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
bd7854cb 505 "requested-attributes",
506 (int)(sizeof(requested) / sizeof(requested[0])),
507 NULL, requested);
508
509 /*
510 * Do the request...
511 */
512
513 response = cupsDoRequest(http, request, "/");
514
961a8a37 515 if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING)
bd7854cb 516 {
517 syslog(LOG_ERR, "Unable to get list of printers - %s",
518 cupsLastErrorString());
519
520 ippDelete(response);
521
522 return (-1);
523 }
524
525 /*
526 * Scan the response for printers...
527 */
528
529 *dest = '\0';
530 attr = response->attrs;
531
532 while (attr)
533 {
534 /*
535 * Skip to the next printer...
536 */
537
538 while (attr && attr->group_tag != IPP_TAG_PRINTER)
539 attr = attr->next;
540
541 if (!attr)
542 break;
543
544 /*
545 * Get all of the attributes for the current printer...
546 */
547
548 accepting_attr = NULL;
549 info_attr = NULL;
550 name_attr = NULL;
551 shared_attr = NULL;
552 state_attr = NULL;
553
554 while (attr && attr->group_tag == IPP_TAG_PRINTER)
555 {
556 if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
557 attr->value_tag == IPP_TAG_BOOLEAN)
558 accepting_attr = attr;
559 else if (!strcmp(attr->name, "printer-info") &&
560 attr->value_tag == IPP_TAG_TEXT)
561 info_attr = attr;
562 else if (!strcmp(attr->name, "printer-name") &&
563 attr->value_tag == IPP_TAG_NAME)
564 name_attr = attr;
565 else if (!strcmp(attr->name, "printer-is-shared") &&
566 attr->value_tag == IPP_TAG_BOOLEAN)
567 shared_attr = attr;
568 else if (!strcmp(attr->name, "printer-state") &&
569 attr->value_tag == IPP_TAG_ENUM)
570 state_attr = attr;
571
572 attr = attr->next;
573 }
574
575 if (info_attr && name_attr &&
88f9aafc 576 !_cups_strcasecmp(name, info_attr->values[0].string.text))
bd7854cb 577 {
578 /*
579 * Found a match, use this one!
580 */
581
582 strlcpy(dest, name_attr->values[0].string.text, destsize);
583
584 if (accepting && accepting_attr)
585 *accepting = accepting_attr->values[0].boolean;
586
587 if (shared && shared_attr)
588 *shared = shared_attr->values[0].boolean;
589
590 if (state && state_attr)
591 *state = (ipp_pstate_t)state_attr->values[0].integer;
592
593 break;
594 }
595 }
596
597 ippDelete(response);
598
599 if (!*dest)
600 {
601 syslog(LOG_ERR, "Unable to find \"%s\" in list of printers!", name);
602
603 return (-1);
604 }
605
606 name = dest;
607 }
7ff4fea9
MS
608 else
609 {
610 /*
611 * Get values from the response...
612 */
bd7854cb 613
7ff4fea9
MS
614 if (accepting)
615 {
616 if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
617 IPP_TAG_BOOLEAN)) == NULL)
618 syslog(LOG_ERR, "No printer-is-accepting-jobs attribute found in "
619 "response from server!");
620 else
621 *accepting = attr->values[0].boolean;
622 }
bd7854cb 623
7ff4fea9
MS
624 if (shared)
625 {
626 if ((attr = ippFindAttribute(response, "printer-is-shared",
627 IPP_TAG_BOOLEAN)) == NULL)
628 {
629 syslog(LOG_ERR, "No printer-is-shared attribute found in "
630 "response from server!");
631 *shared = 1;
632 }
633 else
634 *shared = attr->values[0].boolean;
635 }
bd7854cb 636
7ff4fea9 637 if (state)
bd7854cb 638 {
7ff4fea9
MS
639 if ((attr = ippFindAttribute(response, "printer-state",
640 IPP_TAG_ENUM)) == NULL)
641 syslog(LOG_ERR, "No printer-state attribute found in "
642 "response from server!");
643 else
644 *state = (ipp_pstate_t)attr->values[0].integer;
bd7854cb 645 }
646
7ff4fea9 647 ippDelete(response);
ef416fc2 648 }
649
bd7854cb 650 /*
651 * Next look for the printer in the lpoptions file...
652 */
ef416fc2 653
bd7854cb 654 num_options = 0;
ef416fc2 655
bd7854cb 656 if (options && shared && accepting)
657 {
658 if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
659 cups_serverroot = CUPS_SERVERROOT;
ef416fc2 660
bd7854cb 661 snprintf(line, sizeof(line), "%s/lpoptions", cups_serverroot);
662 if ((fp = cupsFileOpen(line, "r")) != NULL)
663 {
664 linenum = 0;
665 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
666 {
667 /*
668 * Make sure we have "Dest name options" or "Default name options"...
669 */
ef416fc2 670
88f9aafc 671 if ((_cups_strcasecmp(line, "Dest") && _cups_strcasecmp(line, "Default")) || !value)
bd7854cb 672 continue;
ef416fc2 673
bd7854cb 674 /*
675 * Separate destination name from options...
676 */
ef416fc2 677
bd7854cb 678 for (optptr = value; *optptr && !isspace(*optptr & 255); optptr ++);
ef416fc2 679
bd7854cb 680 while (*optptr == ' ')
681 *optptr++ = '\0';
682
683 /*
684 * If this is our destination, parse the options and break out of
685 * the loop - we're done!
686 */
687
88f9aafc 688 if (!_cups_strcasecmp(value, name))
bd7854cb 689 {
690 num_options = cupsParseOptions(optptr, num_options, options);
691 break;
692 }
693 }
694
695 cupsFileClose(fp);
696 }
697 }
698 else if (options)
699 *options = NULL;
ef416fc2 700
701 /*
bd7854cb 702 * Return the number of options for this destination...
ef416fc2 703 */
704
bd7854cb 705 return (num_options);
706}
707
708
709/*
710 * 'print_file()' - Add a file to the current job.
711 */
712
713static int /* O - 0 on success, -1 on failure */
714print_file(http_t *http, /* I - HTTP connection */
715 int id, /* I - Job ID */
716 const char *filename, /* I - File to print */
717 const char *docname, /* I - document-name */
718 const char *user, /* I - requesting-user-name */
f7deaa1a 719 const char *format, /* I - document-format */
bd7854cb 720 int last) /* I - 1 = last file in job */
721{
722 ipp_t *request; /* IPP request */
723 char uri[HTTP_MAX_URI]; /* Printer URI */
724
725
726 /*
727 * Setup the Send-Document request...
728 */
729
961a8a37 730 request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
bd7854cb 731
732 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", id);
733 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
734
735 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
736 "requesting-user-name", NULL, user);
737
738 if (docname)
739 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
740 "document-name", NULL, docname);
741
f7deaa1a 742 if (format)
743 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
744 "document-format", NULL, format);
745
7e86f2f6 746 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last);
ef416fc2 747
748 /*
749 * Do the request...
750 */
751
bd7854cb 752 snprintf(uri, sizeof(uri), "/jobs/%d", id);
ef416fc2 753
bd7854cb 754 ippDelete(cupsDoFileRequest(http, request, uri, filename));
ef416fc2 755
961a8a37 756 if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
ef416fc2 757 {
bd7854cb 758 syslog(LOG_ERR, "Unable to send document - %s", cupsLastErrorString());
ef416fc2 759
bd7854cb 760 return (-1);
ef416fc2 761 }
762
bd7854cb 763 return (0);
ef416fc2 764}
765
766
767/*
768 * 'recv_print_job()' - Receive a print job from the client.
769 */
770
bd7854cb 771static int /* O - Command status */
772recv_print_job(
773 const char *queue, /* I - Printer name */
774 int num_defaults, /* I - Number of default options */
775 cups_option_t *defaults) /* I - Default options */
ef416fc2 776{
bd7854cb 777 http_t *http; /* HTTP connection */
ef416fc2 778 int i; /* Looping var */
779 int status; /* Command status */
780 int fd; /* Temporary file */
781 FILE *fp; /* File pointer */
782 char filename[1024]; /* Temporary filename */
7e86f2f6
MS
783 ssize_t bytes; /* Bytes received */
784 size_t total; /* Total bytes */
ef416fc2 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
961a8a37 811 http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL);
bd7854cb 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
7e86f2f6 968 for (total = (size_t)strtoll(count, NULL, 10); total > 0; total -= (size_t)bytes)
ef416fc2 969 {
7e86f2f6
MS
970 if (total > sizeof(line))
971 bytes = (ssize_t)sizeof(line);
ef416fc2 972 else
7e86f2f6 973 bytes = (ssize_t)total;
ef416fc2 974
7e86f2f6
MS
975 if ((bytes = (ssize_t)fread(line, 1, (size_t)bytes, stdin)) > 0)
976 bytes = write(fd, line, (size_t)bytes);
ef416fc2 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 */
5eb74392 1056 smart_strlcpy(title, line + 1, sizeof(title));
ef416fc2 1057 break;
1058
c5571a1d 1059 case 'N' : /* Document name */
5eb74392 1060 smart_strlcpy(docname, line + 1, sizeof(docname));
c5571a1d
MS
1061 break;
1062
ef416fc2 1063 case 'P' : /* User identification */
5eb74392 1064 smart_strlcpy(user, line + 1, sizeof(user));
ef416fc2 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
bb94e24c 1126 if ((id = create_job(http, dest, title, user, num_options, options)) < 0)
bd7854cb 1127 status = 1;
1128 else
1129 {
1130 /*
1131 * Then print the job files...
1132 */
ef416fc2 1133
bd7854cb 1134 rewind(fp);
ef416fc2 1135
bd7854cb 1136 docname[0] = '\0';
1137 docnumber = 0;
ef416fc2 1138
bd7854cb 1139 while (smart_gets(line, sizeof(line), fp) != NULL)
1140 {
1141 /*
1142 * Process control lines...
1143 */
1144
1145 switch (line[0])
1146 {
1147 case 'N' : /* Document name */
5eb74392 1148 smart_strlcpy(docname, line + 1, sizeof(docname));
bd7854cb 1149 break;
ef416fc2 1150
bd7854cb 1151 case 'c' : /* Plot CIF file */
1152 case 'd' : /* Print DVI file */
1153 case 'f' : /* Print formatted file */
1154 case 'g' : /* Plot file */
1155 case 'l' : /* Print file leaving control characters (raw) */
1156 case 'n' : /* Print ditroff output file */
1157 case 'o' : /* Print PostScript output file */
1158 case 'p' : /* Print file with 'pr' format (prettyprint) */
1159 case 'r' : /* File to print with FORTRAN carriage control */
1160 case 't' : /* Print troff output file */
1161 case 'v' : /* Print raster file */
1162 /*
1163 * Figure out which file we are printing...
1164 */
1165
1166 for (i = 0; i < num_data; i ++)
1167 if (!strcmp(data[i], line + 1))
1168 break;
1169
1170 if (i >= num_data)
1171 {
1172 status = 1;
ef416fc2 1173 break;
bd7854cb 1174 }
ef416fc2 1175
bd7854cb 1176 /*
1177 * Send the print file...
1178 */
ef416fc2 1179
bd7854cb 1180 docnumber ++;
ef416fc2 1181
bd7854cb 1182 if (print_file(http, id, temp[i], docname, user,
f7deaa1a 1183 cupsGetOption("document-format", num_options,
1184 options),
bd7854cb 1185 docnumber == doccount))
1186 status = 1;
1187 else
1188 status = 0;
ef416fc2 1189
bd7854cb 1190 break;
1191 }
1192
1193 if (status)
1194 break;
ef416fc2 1195 }
ef416fc2 1196 }
91c84a35
MS
1197
1198 fclose(fp);
ef416fc2 1199 }
1200 }
1201
bd7854cb 1202 cupsFreeOptions(num_options, options);
1203
1204 httpClose(http);
1205
ef416fc2 1206 /*
1207 * Clean up all temporary files and return...
1208 */
1209
1210 unlink(control);
1211
1212 for (i = 0; i < num_data; i ++)
1213 unlink(temp[i]);
1214
ef416fc2 1215 return (status);
1216}
1217
1218
1219/*
1220 * 'remove_jobs()' - Cancel one or more jobs.
1221 */
1222
b423cd4c 1223static int /* O - Command status */
ef416fc2 1224remove_jobs(const char *dest, /* I - Destination */
1225 const char *agent, /* I - User agent */
1226 const char *list) /* I - List of jobs or users */
1227{
1228 int id; /* Job ID */
1229 http_t *http; /* HTTP server connection */
bd7854cb 1230 ipp_t *request; /* IPP Request */
ef416fc2 1231 char uri[HTTP_MAX_URI]; /* Job URI */
1232
1233
1234 (void)dest; /* Suppress compiler warnings... */
1235
1236 /*
1237 * Try connecting to the local server...
1238 */
1239
961a8a37 1240 if ((http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL)) == NULL)
ef416fc2 1241 {
1242 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1243 strerror(errno));
1244 return (1);
1245 }
1246
ef416fc2 1247 /*
1248 * Loop for each job...
1249 */
1250
1251 while ((id = atoi(list)) > 0)
1252 {
1253 /*
1254 * Skip job ID in list...
1255 */
1256
1257 while (isdigit(*list & 255))
1258 list ++;
1259 while (isspace(*list & 255))
1260 list ++;
1261
1262 /*
961a8a37 1263 * Build an IPP_OP_CANCEL_JOB request, which requires the following
ef416fc2 1264 * attributes:
1265 *
1266 * attributes-charset
1267 * attributes-natural-language
1268 * job-uri
1269 * requesting-user-name
1270 */
1271
961a8a37 1272 request = ippNewRequest(IPP_OP_CANCEL_JOB);
ef416fc2 1273
1274 sprintf(uri, "ipp://localhost/jobs/%d", id);
1275 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
1276
1277 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1278 "requesting-user-name", NULL, agent);
1279
1280 /*
1281 * Do the request and get back a response...
1282 */
1283
bd7854cb 1284 ippDelete(cupsDoRequest(http, request, "/jobs"));
ef416fc2 1285
961a8a37 1286 if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
ef416fc2 1287 {
1288 syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
bd7854cb 1289 cupsLastErrorString());
ef416fc2 1290 httpClose(http);
1291 return (1);
1292 }
bd7854cb 1293 else
d09495fa 1294 syslog(LOG_INFO, "Job ID %d canceled", id);
ef416fc2 1295 }
1296
ef416fc2 1297 httpClose(http);
1298
1299 return (0);
1300}
1301
1302
1303/*
1304 * 'send_state()' - Send the queue state.
1305 */
1306
b423cd4c 1307static int /* O - Command status */
bd7854cb 1308send_state(const char *queue, /* I - Destination */
ef416fc2 1309 const char *list, /* I - Job or user */
1310 int longstatus) /* I - List of jobs or users */
1311{
1312 int id; /* Job ID from list */
1313 http_t *http; /* HTTP server connection */
1314 ipp_t *request, /* IPP Request */
1315 *response; /* IPP Response */
1316 ipp_attribute_t *attr; /* Current attribute */
ef416fc2 1317 ipp_pstate_t state; /* Printer state */
1318 const char *jobdest, /* Pointer into job-printer-uri */
1319 *jobuser, /* Pointer to job-originating-user-name */
1320 *jobname; /* Pointer to job-name */
1321 ipp_jstate_t jobstate; /* job-state */
1322 int jobid, /* job-id */
1323 jobsize, /* job-k-octets */
1324 jobcount, /* Number of jobs */
1325 jobcopies, /* Number of copies */
1326 rank; /* Rank of job */
1327 char rankstr[255]; /* Rank string */
1328 char namestr[1024]; /* Job name string */
1329 char uri[HTTP_MAX_URI]; /* Printer URI */
bd7854cb 1330 char dest[256]; /* Printer/class queue */
ef416fc2 1331 static const char * const ranks[10] = /* Ranking strings */
1332 {
1333 "th",
1334 "st",
1335 "nd",
1336 "rd",
1337 "th",
1338 "th",
1339 "th",
1340 "th",
1341 "th",
1342 "th"
1343 };
1344 static const char * const requested[] =
1345 { /* Requested attributes */
1346 "job-id",
1347 "job-k-octets",
1348 "job-state",
1349 "job-printer-uri",
1350 "job-originating-user-name",
1351 "job-name",
1352 "copies"
1353 };
1354
1355
ef416fc2 1356 /*
1357 * Try connecting to the local server...
1358 */
1359
961a8a37 1360 if ((http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL)) == NULL)
ef416fc2 1361 {
1362 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1363 strerror(errno));
1364 printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno));
1365 return (1);
1366 }
1367
1368 /*
bd7854cb 1369 * Get the actual destination name and printer state...
ef416fc2 1370 */
1371
bd7854cb 1372 if (get_printer(http, queue, dest, sizeof(dest), NULL, NULL, NULL, &state))
1373 {
1374 syslog(LOG_ERR, "Unable to get printer %s: %s", queue,
1375 cupsLastErrorString());
1376 printf("Unable to get printer %s: %s", queue, cupsLastErrorString());
1377 return (1);
1378 }
ef416fc2 1379
1380 /*
bd7854cb 1381 * Show the queue state...
ef416fc2 1382 */
1383
bd7854cb 1384 switch (state)
ef416fc2 1385 {
961a8a37 1386 case IPP_PSTATE_IDLE :
bd7854cb 1387 printf("%s is ready\n", dest);
1388 break;
961a8a37 1389 case IPP_PSTATE_PROCESSING :
bd7854cb 1390 printf("%s is ready and printing\n", dest);
1391 break;
961a8a37 1392 case IPP_PSTATE_STOPPED :
bd7854cb 1393 printf("%s is not ready\n", dest);
1394 break;
ef416fc2 1395 }
1396
1397 /*
961a8a37 1398 * Build an IPP_OP_GET_JOBS or IPP_OP_GET_JOB_ATTRIBUTES request, which requires
ef416fc2 1399 * the following attributes:
1400 *
1401 * attributes-charset
1402 * attributes-natural-language
1403 * job-uri or printer-uri
1404 */
1405
1406 id = atoi(list);
1407
961a8a37 1408 request = ippNewRequest(id ? IPP_OP_GET_JOB_ATTRIBUTES : IPP_OP_GET_JOBS);
ef416fc2 1409
a4d04587 1410 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
bd7854cb 1411 "localhost", 0, "/printers/%s", dest);
ef416fc2 1412
1413 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1414 NULL, uri);
1415
1416 if (id)
1417 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1418 else
1419 {
1420 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1421 "requesting-user-name", NULL, list);
1422 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
1423 }
1424
1425 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
bd7854cb 1426 "requested-attributes",
1427 sizeof(requested) / sizeof(requested[0]),
ef416fc2 1428 NULL, requested);
1429
1430 /*
1431 * Do the request and get back a response...
1432 */
1433
1434 jobcount = 0;
bd7854cb 1435 response = cupsDoRequest(http, request, "/");
ef416fc2 1436
961a8a37 1437 if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
ef416fc2 1438 {
bd7854cb 1439 printf("get-jobs failed: %s\n", cupsLastErrorString());
1440 ippDelete(response);
1441 return (1);
1442 }
ef416fc2 1443
bd7854cb 1444 /*
1445 * Loop through the job list and display them...
1446 */
ef416fc2 1447
bd7854cb 1448 for (attr = response->attrs, rank = 1; attr; attr = attr->next)
1449 {
ef416fc2 1450 /*
bd7854cb 1451 * Skip leading attributes until we hit a job...
ef416fc2 1452 */
1453
bd7854cb 1454 while (attr && (attr->group_tag != IPP_TAG_JOB || !attr->name))
1455 attr = attr->next;
ef416fc2 1456
bd7854cb 1457 if (!attr)
1458 break;
ef416fc2 1459
bd7854cb 1460 /*
1461 * Pull the needed attributes from this job...
1462 */
ef416fc2 1463
bd7854cb 1464 jobid = 0;
1465 jobsize = 0;
961a8a37 1466 jobstate = IPP_JSTATE_PENDING;
bd7854cb 1467 jobname = "untitled";
1468 jobuser = NULL;
1469 jobdest = NULL;
1470 jobcopies = 1;
ef416fc2 1471
bd7854cb 1472 while (attr && attr->group_tag == IPP_TAG_JOB)
1473 {
1474 if (!strcmp(attr->name, "job-id") &&
1475 attr->value_tag == IPP_TAG_INTEGER)
1476 jobid = attr->values[0].integer;
ef416fc2 1477
bd7854cb 1478 if (!strcmp(attr->name, "job-k-octets") &&
1479 attr->value_tag == IPP_TAG_INTEGER)
1480 jobsize = attr->values[0].integer;
ef416fc2 1481
bd7854cb 1482 if (!strcmp(attr->name, "job-state") &&
1483 attr->value_tag == IPP_TAG_ENUM)
1484 jobstate = (ipp_jstate_t)attr->values[0].integer;
ef416fc2 1485
bd7854cb 1486 if (!strcmp(attr->name, "job-printer-uri") &&
1487 attr->value_tag == IPP_TAG_URI)
1488 if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
1489 jobdest ++;
ef416fc2 1490
bd7854cb 1491 if (!strcmp(attr->name, "job-originating-user-name") &&
1492 attr->value_tag == IPP_TAG_NAME)
1493 jobuser = attr->values[0].string.text;
ef416fc2 1494
bd7854cb 1495 if (!strcmp(attr->name, "job-name") &&
1496 attr->value_tag == IPP_TAG_NAME)
1497 jobname = attr->values[0].string.text;
ef416fc2 1498
bd7854cb 1499 if (!strcmp(attr->name, "copies") &&
1500 attr->value_tag == IPP_TAG_INTEGER)
1501 jobcopies = attr->values[0].integer;
ef416fc2 1502
bd7854cb 1503 attr = attr->next;
1504 }
ef416fc2 1505
bd7854cb 1506 /*
1507 * See if we have everything needed...
1508 */
ef416fc2 1509
bd7854cb 1510 if (!jobdest || !jobid)
1511 {
1512 if (!attr)
1513 break;
1514 else
1515 continue;
1516 }
ef416fc2 1517
bd7854cb 1518 if (!longstatus && jobcount == 0)
1519 puts("Rank Owner Job File(s) Total Size");
ef416fc2 1520
bd7854cb 1521 jobcount ++;
ef416fc2 1522
bd7854cb 1523 /*
1524 * Display the job...
1525 */
ef416fc2 1526
961a8a37 1527 if (jobstate == IPP_JSTATE_PROCESSING)
5a9febac 1528 strlcpy(rankstr, "active", sizeof(rankstr));
bd7854cb 1529 else
1530 {
1531 snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
1532 rank ++;
1533 }
ef416fc2 1534
bd7854cb 1535 if (longstatus)
1536 {
1537 puts("");
ef416fc2 1538
bd7854cb 1539 if (jobcopies > 1)
1540 snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
1541 jobname);
ef416fc2 1542 else
bd7854cb 1543 strlcpy(namestr, jobname, sizeof(namestr));
ef416fc2 1544
bd7854cb 1545 printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid);
1546 printf(" %-39.39s %.0f bytes\n", namestr, 1024.0 * jobsize);
ef416fc2 1547 }
bd7854cb 1548 else
1549 printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr, jobuser,
1550 jobid, jobname, 1024.0 * jobsize);
ef416fc2 1551
bd7854cb 1552 if (!attr)
1553 break;
ef416fc2 1554 }
1555
bd7854cb 1556 ippDelete(response);
1557
ef416fc2 1558 if (jobcount == 0)
1559 puts("no entries");
1560
ef416fc2 1561 httpClose(http);
1562
1563 return (0);
1564}
1565
1566
1567/*
1568 * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1569 */
1570
b423cd4c 1571static char * /* O - Line read or NULL */
ef416fc2 1572smart_gets(char *s, /* I - Pointer to line buffer */
1573 int len, /* I - Size of line buffer */
1574 FILE *fp) /* I - File to read from */
1575{
1576 char *ptr, /* Pointer into line */
1577 *end; /* End of line */
1578 int ch; /* Character from file */
1579
1580
1581 /*
1582 * Read the line; unlike fgets(), we read the entire line but dump
1583 * characters that go past the end of the buffer. Also, we accept
1584 * CR, LF, or CR LF for the line endings to be "safe", although
1585 * RFC 1179 specifically says "just use LF".
1586 */
1587
1588 ptr = s;
1589 end = s + len - 1;
1590
1591 while ((ch = getc(fp)) != EOF)
1592 {
1593 if (ch == '\n')
1594 break;
1595 else if (ch == '\r')
1596 {
1597 /*
1598 * See if a LF follows...
1599 */
1600
1601 ch = getc(fp);
1602
1603 if (ch != '\n')
1604 ungetc(ch, fp);
1605
1606 break;
1607 }
1608 else if (ptr < end)
7e86f2f6 1609 *ptr++ = (char)ch;
ef416fc2 1610 }
1611
1612 *ptr = '\0';
1613
1614 if (ch == EOF && ptr == s)
1615 return (NULL);
1616 else
1617 return (s);
1618}
5eb74392
MS
1619
1620
1621/*
1622 * 'smart_strlcpy()' - Copy a string and convert from ISO-8859-1 to UTF-8 as needed.
1623 */
1624
1625static void
1626smart_strlcpy(char *dst, /* I - Output buffer */
1627 const char *src, /* I - Input string */
1628 size_t dstsize) /* I - Size of output buffer */
1629{
1630 const unsigned char *srcptr; /* Pointer into input string */
1631 unsigned char *dstptr, /* Pointer into output buffer */
1632 *dstend; /* End of output buffer */
1633 int saw_8859 = 0; /* Saw an extended character that was not UTF-8? */
1634
1635
1636 for (srcptr = (unsigned char *)src, dstptr = (unsigned char *)dst, dstend = dstptr + dstsize - 1; *srcptr;)
1637 {
1638 if (*srcptr < 0x80)
1639 *dstptr++ = *srcptr++; /* ASCII */
1640 else if (saw_8859)
1641 {
1642 /*
1643 * Map ISO-8859-1 (most likely character set for legacy LPD clients) to
1644 * UTF-8...
1645 */
1646
1647 if (dstptr > (dstend - 2))
1648 break;
1649
1650 *dstptr++ = 0xc0 | (*srcptr >> 6);
1651 *dstptr++ = 0x80 | (*srcptr++ & 0x3f);
1652 }
5babee86 1653 else if ((*srcptr & 0xe0) == 0xc0 && (srcptr[1] & 0xc0) == 0x80)
5eb74392 1654 {
5babee86
MS
1655 /*
1656 * 2-byte UTF-8 sequence...
1657 */
1658
5eb74392
MS
1659 if (dstptr > (dstend - 2))
1660 break;
1661
1662 *dstptr++ = *srcptr++;
1663 *dstptr++ = *srcptr++;
1664 }
5babee86 1665 else if ((*srcptr & 0xf0) == 0xe0 && (srcptr[1] & 0xc0) == 0x80 && (srcptr[2] & 0xc0) == 0x80)
5eb74392 1666 {
5babee86
MS
1667 /*
1668 * 3-byte UTF-8 sequence...
1669 */
1670
5eb74392
MS
1671 if (dstptr > (dstend - 3))
1672 break;
1673
1674 *dstptr++ = *srcptr++;
1675 *dstptr++ = *srcptr++;
1676 *dstptr++ = *srcptr++;
1677 }
5babee86 1678 else if ((*srcptr & 0xf8) == 0xf0 && (srcptr[1] & 0xc0) == 0x80 && (srcptr[2] & 0xc0) == 0x80 && (srcptr[3] & 0xc0) == 0x80)
5eb74392 1679 {
5babee86
MS
1680 /*
1681 * 4-byte UTF-8 sequence...
1682 */
1683
5eb74392
MS
1684 if (dstptr > (dstend - 4))
1685 break;
1686
1687 *dstptr++ = *srcptr++;
1688 *dstptr++ = *srcptr++;
1689 *dstptr++ = *srcptr++;
1690 *dstptr++ = *srcptr++;
1691 }
1692 else
1693 {
1694 /*
5babee86 1695 * Bad UTF-8 sequence, this must be an ISO-8859-1 string...
5eb74392
MS
1696 */
1697
1698 saw_8859 = 1;
1699
1700 if (dstptr > (dstend - 2))
1701 break;
1702
1703 *dstptr++ = 0xc0 | (*srcptr >> 6);
1704 *dstptr++ = 0x80 | (*srcptr++ & 0x3f);
1705 }
1706 }
1707
1708 *dstptr = '\0';
1709}