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