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