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