]> git.ipfire.org Git - thirdparty/cups.git/blob - systemv/lp.c
bc09b4b20613229dcd0428eaaf4d0a5aca156e05
[thirdparty/cups.git] / systemv / lp.c
1 /*
2 * "lp" command for CUPS.
3 *
4 * Copyright 2007-2016 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products.
6 *
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/".
12 */
13
14 /*
15 * Include necessary headers...
16 */
17
18 #include <cups/cups-private.h>
19
20
21 /*
22 * Local functions.
23 */
24
25 int restart_job(const char *command, int job_id);
26 int set_job_attrs(const char *command, int job_id, int num_options,
27 cups_option_t *options);
28
29
30 /*
31 * 'main()' - Parse options and send files for printing.
32 */
33
34 int
35 main(int argc, /* I - Number of command-line arguments */
36 char *argv[]) /* I - Command-line arguments */
37 {
38 int i, j; /* Looping vars */
39 int job_id; /* Job ID */
40 char *printer, /* Printer name */
41 *instance, /* Instance name */
42 *opt, /* Option pointer */
43 *val, /* Option value */
44 *title; /* Job title */
45 int priority; /* Job priority (1-100) */
46 int num_copies; /* Number of copies per file */
47 int num_files; /* Number of files to print */
48 const char *files[1000]; /* Files to print */
49 cups_dest_t *dest; /* Selected destination */
50 int num_options; /* Number of options */
51 cups_option_t *options; /* Options */
52 int end_options; /* No more options? */
53 int silent; /* Silent or verbose output? */
54 char buffer[8192]; /* Copy buffer */
55
56
57 #ifdef __sun
58 /*
59 * Solaris does some rather strange things to re-queue remote print
60 * jobs. On bootup, the "lp" command is run as "printd" to re-spool
61 * any remote jobs in /var/spool/print. Since CUPS doesn't need this
62 * nonsense, we just need to add the necessary check here to prevent
63 * lp from causing boot problems...
64 */
65
66 if ((val = strrchr(argv[0], '/')) != NULL)
67 val ++;
68 else
69 val = argv[0];
70
71 if (!strcmp(val, "printd"))
72 return (0);
73 #endif /* __sun */
74
75 _cupsSetLocale(argv);
76
77 silent = 0;
78 printer = NULL;
79 dest = NULL;
80 num_options = 0;
81 options = NULL;
82 num_files = 0;
83 title = NULL;
84 job_id = 0;
85 end_options = 0;
86
87 for (i = 1; i < argc; i ++)
88 {
89 if (argv[i][0] == '-' && argv[i][1] && !end_options)
90 {
91 for (opt = argv[i] + 1; *opt; opt ++)
92 {
93 switch (*opt)
94 {
95 case 'E' : /* Encrypt */
96 #ifdef HAVE_SSL
97 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
98 #else
99 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
100 #endif /* HAVE_SSL */
101 break;
102
103 case 'U' : /* Username */
104 if (opt[1] != '\0')
105 {
106 cupsSetUser(opt + 1);
107 opt += strlen(opt) - 1;
108 }
109 else
110 {
111 i ++;
112 if (i >= argc)
113 {
114 _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
115 return (1);
116 }
117
118 cupsSetUser(argv[i]);
119 }
120 break;
121
122 case 'c' : /* Copy to spool dir (always enabled) */
123 break;
124
125 case 'd' : /* Destination printer or class */
126 if (opt[1] != '\0')
127 {
128 printer = opt + 1;
129 opt += strlen(opt) - 1;
130 }
131 else
132 {
133 i ++;
134
135 if (i >= argc)
136 {
137 _cupsLangPrintf(stderr, _("%s: Error - expected destination after \"-d\" option."), argv[0]);
138 return (1);
139 }
140
141 printer = argv[i];
142 }
143
144 if ((instance = strrchr(printer, '/')) != NULL)
145 *instance++ = '\0';
146
147 if ((dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer,
148 instance)) != NULL)
149 {
150 for (j = 0; j < dest->num_options; j ++)
151 if (cupsGetOption(dest->options[j].name, num_options,
152 options) == NULL)
153 num_options = cupsAddOption(dest->options[j].name,
154 dest->options[j].value,
155 num_options, &options);
156 }
157 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
158 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
159 {
160 _cupsLangPrintf(stderr,
161 _("%s: Error - add '/version=1.1' to server "
162 "name."), argv[0]);
163 return (1);
164 }
165 break;
166
167 case 'f' : /* Form */
168 if (opt[1] != '\0')
169 {
170 opt += strlen(opt) - 1;
171 }
172 else
173 {
174 i ++;
175
176 if (i >= argc)
177 {
178 _cupsLangPrintf(stderr, _("%s: Error - expected form after \"-f\" option."), argv[0]);
179 return (1);
180 }
181 }
182
183 _cupsLangPrintf(stderr, _("%s: Warning - form option ignored."), argv[0]);
184 break;
185
186 case 'h' : /* Destination host */
187 if (opt[1] != '\0')
188 {
189 cupsSetServer(opt + 1);
190 opt += strlen(opt) - 1;
191 }
192 else
193 {
194 i ++;
195
196 if (i >= argc)
197 {
198 _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-h\" option."), argv[0]);
199 return (1);
200 }
201
202 cupsSetServer(argv[i]);
203 }
204 break;
205
206 case 'i' : /* Change job */
207 if (opt[1] != '\0')
208 {
209 val = opt + 1;
210 opt += strlen(opt) - 1;
211 }
212 else
213 {
214 i ++;
215
216 if (i >= argc)
217 {
218 _cupsLangPrintf(stderr, _("%s: Expected job ID after \"-i\" option."), argv[0]);
219 return (1);
220 }
221
222 val = argv[i];
223 }
224
225 if (num_files > 0)
226 {
227 _cupsLangPrintf(stderr, _("%s: Error - cannot print files and alter jobs simultaneously."), argv[0]);
228 return (1);
229 }
230
231 if (strrchr(val, '-') != NULL)
232 job_id = atoi(strrchr(val, '-') + 1);
233 else
234 job_id = atoi(val);
235
236 if (job_id < 0)
237 {
238 _cupsLangPrintf(stderr, _("%s: Error - bad job ID."), argv[0]);
239 break;
240 }
241 break;
242
243 case 'm' : /* Send email when job is done */
244 #ifdef __sun
245 case 'p' : /* Notify on completion */
246 #endif /* __sun */
247 case 'w' : /* Write to console or email */
248 {
249 char email[1024]; /* EMail address */
250
251
252 snprintf(email, sizeof(email), "mailto:%s@%s", cupsUser(), httpGetHostname(NULL, buffer, sizeof(buffer)));
253 num_options = cupsAddOption("notify-recipient-uri", email, num_options, &options);
254 }
255
256 silent = 1;
257 break;
258
259 case 'n' : /* Number of copies */
260 if (opt[1] != '\0')
261 {
262 num_copies = atoi(opt + 1);
263 opt += strlen(opt) - 1;
264 }
265 else
266 {
267 i ++;
268
269 if (i >= argc)
270 {
271 _cupsLangPrintf(stderr, _("%s: Error - expected copies after \"-n\" option."), argv[0]);
272 return (1);
273 }
274
275 num_copies = atoi(argv[i]);
276 }
277
278 if (num_copies < 1)
279 {
280 _cupsLangPrintf(stderr, _("%s: Error - copies must be 1 or more."), argv[0]);
281 return (1);
282 }
283
284 sprintf(buffer, "%d", num_copies);
285 num_options = cupsAddOption("copies", buffer, num_options,
286 &options);
287 break;
288
289 case 'o' : /* Option */
290 if (opt[1] != '\0')
291 {
292 num_options = cupsParseOptions(opt + 1, num_options, &options);
293 opt += strlen(opt) - 1;
294 }
295 else
296 {
297 i ++;
298
299 if (i >= argc)
300 {
301 _cupsLangPrintf(stderr, _("%s: Error - expected option=value after \"-o\" option."), argv[0]);
302 return (1);
303 }
304
305 num_options = cupsParseOptions(argv[i], num_options, &options);
306 }
307 break;
308
309 #ifndef __sun
310 case 'p' : /* Queue priority */
311 #endif /* !__sun */
312 case 'q' : /* Queue priority */
313 if (opt[1] != '\0')
314 {
315 priority = atoi(opt + 1);
316 opt += strlen(opt) - 1;
317 }
318 else
319 {
320 if ((i + 1) >= argc)
321 {
322 _cupsLangPrintf(stderr, _("%s: Error - expected priority after \"-%c\" option."), argv[0], *opt);
323 return (1);
324 }
325
326 i ++;
327
328 priority = atoi(argv[i]);
329 }
330
331 /*
332 * For 100% Solaris compatibility, need to add:
333 *
334 * priority = 99 * (39 - priority) / 39 + 1;
335 *
336 * However, to keep CUPS lp the same across all platforms
337 * we will break compatibility this far...
338 */
339
340 if (priority < 1 || priority > 100)
341 {
342 _cupsLangPrintf(stderr, _("%s: Error - priority must be between 1 and 100."), argv[0]);
343 return (1);
344 }
345
346 sprintf(buffer, "%d", priority);
347 num_options = cupsAddOption("job-priority", buffer, num_options,
348 &options);
349 break;
350
351 case 's' : /* Silent */
352 silent = 1;
353 break;
354
355 case 't' : /* Title */
356 if (opt[1] != '\0')
357 {
358 title = opt + 1;
359 opt += strlen(opt) - 1;
360 }
361 else
362 {
363 i ++;
364
365 if (i >= argc)
366 {
367 _cupsLangPrintf(stderr, _("%s: Error - expected title after \"-t\" option."), argv[0]);
368 return (1);
369 }
370
371 title = argv[i];
372 }
373 break;
374
375 case 'y' : /* mode-list */
376 if (opt[1] != '\0')
377 {
378 opt += strlen(opt) - 1;
379 }
380 else
381 {
382 i ++;
383
384 if (i >= argc)
385 {
386 _cupsLangPrintf(stderr, _("%s: Error - expected mode list after \"-y\" option."), argv[0]);
387 return (1);
388 }
389 }
390
391 _cupsLangPrintf(stderr, _("%s: Warning - mode option ignored."), argv[0]);
392 break;
393
394 case 'H' : /* Hold job */
395 if (opt[1] != '\0')
396 {
397 val = opt + 1;
398 opt += strlen(opt) - 1;
399 }
400 else
401 {
402 i ++;
403
404 if (i >= argc)
405 {
406 _cupsLangPrintf(stderr, _("%s: Error - expected hold name after \"-H\" option."), argv[0]);
407 return (1);
408 }
409
410 val = argv[i];
411 }
412
413 if (!strcmp(val, "hold"))
414 num_options = cupsAddOption("job-hold-until", "indefinite", num_options, &options);
415 else if (!strcmp(val, "resume") || !strcmp(val, "release"))
416 num_options = cupsAddOption("job-hold-until", "no-hold", num_options, &options);
417 else if (!strcmp(val, "immediate"))
418 {
419 num_options = cupsAddOption("job-hold-until", "no-hold", num_options, &options);
420 num_options = cupsAddOption("job-priority", "100", num_options, &options);
421 }
422 else if (!strcmp(val, "restart"))
423 {
424 if (job_id < 1)
425 {
426 _cupsLangPrintf(stderr, _("%s: Need job ID (\"-i jobid\") before \"-H restart\"."), argv[0]);
427 return (1);
428 }
429
430 if (restart_job(argv[0], job_id))
431 return (1);
432 }
433 else
434 num_options = cupsAddOption("job-hold-until", val, num_options, &options);
435 break;
436
437 case 'P' : /* Page list */
438 if (opt[1] != '\0')
439 {
440 val = opt + 1;
441 opt += strlen(opt) - 1;
442 }
443 else
444 {
445 i ++;
446
447 if (i >= argc)
448 {
449 _cupsLangPrintf(stderr, _("%s: Error - expected page list after \"-P\" option."), argv[0]);
450 return (1);
451 }
452
453 val = argv[i];
454 }
455
456 num_options = cupsAddOption("page-ranges", val, num_options, &options);
457 break;
458
459 case 'S' : /* character set */
460 if (opt[1] != '\0')
461 {
462 opt += strlen(opt) - 1;
463 }
464 else
465 {
466 i ++;
467
468 if (i >= argc)
469 {
470 _cupsLangPrintf(stderr, _("%s: Error - expected character set after \"-S\" option."), argv[0]);
471 return (1);
472 }
473 }
474
475 _cupsLangPrintf(stderr, _("%s: Warning - character set option ignored."), argv[0]);
476 break;
477
478 case 'T' : /* Content-Type */
479 if (opt[1] != '\0')
480 {
481 opt += strlen(opt) - 1;
482 }
483 else
484 {
485 i ++;
486
487 if (i >= argc)
488 {
489 _cupsLangPrintf(stderr, _("%s: Error - expected content type after \"-T\" option."), argv[0]);
490 return (1);
491 }
492 }
493
494 _cupsLangPrintf(stderr, _("%s: Warning - content type option ignored."), argv[0]);
495 break;
496
497 case '-' : /* Stop processing options */
498 if (opt[1] != '\0')
499 {
500 _cupsLangPrintf(stderr, _("%s: Error - unknown option \"%s\"."), argv[0], argv[i]);
501 return (1);
502 }
503
504 end_options = 1;
505 break;
506
507 default :
508 _cupsLangPrintf(stderr, _("%s: Error - unknown option \"%c\"."), argv[0], *opt);
509 return (1);
510 }
511 }
512 }
513 else if (!strcmp(argv[i], "-"))
514 {
515 if (num_files || job_id)
516 {
517 _cupsLangPrintf(stderr,
518 _("%s: Error - cannot print from stdin if files or a "
519 "job ID are provided."), argv[0]);
520 return (1);
521 }
522
523 break;
524 }
525 else if (num_files < 1000 && job_id == 0)
526 {
527 /*
528 * Print a file...
529 */
530
531 if (access(argv[i], R_OK) != 0)
532 {
533 _cupsLangPrintf(stderr, _("%s: Error - unable to access \"%s\" - %s"), argv[0], argv[i], strerror(errno));
534 return (1);
535 }
536
537 files[num_files] = argv[i];
538 num_files ++;
539
540 if (title == NULL)
541 {
542 if ((title = strrchr(argv[i], '/')) != NULL)
543 title ++;
544 else
545 title = argv[i];
546 }
547 }
548 else
549 {
550 _cupsLangPrintf(stderr, _("%s: Error - too many files - \"%s\"."), argv[0], argv[i]);
551 }
552 }
553
554 /*
555 * See if we are altering an existing job...
556 */
557
558 if (job_id)
559 return (set_job_attrs(argv[0], job_id, num_options, options));
560
561 /*
562 * See if we have any files to print; if not, print from stdin...
563 */
564
565 if (printer == NULL)
566 {
567 if ((dest = cupsGetNamedDest(NULL, NULL, NULL)) != NULL)
568 {
569 printer = dest->name;
570
571 for (j = 0; j < dest->num_options; j ++)
572 if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
573 num_options = cupsAddOption(dest->options[j].name,
574 dest->options[j].value,
575 num_options, &options);
576 }
577 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
578 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
579 {
580 _cupsLangPrintf(stderr,
581 _("%s: Error - add '/version=1.1' to server "
582 "name."), argv[0]);
583 return (1);
584 }
585 }
586
587 if (printer == NULL)
588 {
589 val = NULL;
590
591 if ((printer = getenv("LPDEST")) == NULL)
592 {
593 if ((printer = getenv("PRINTER")) != NULL)
594 {
595 if (!strcmp(printer, "lp"))
596 printer = NULL;
597 else
598 val = "PRINTER";
599 }
600 }
601 else
602 val = "LPDEST";
603
604 if (printer && !cupsGetNamedDest(NULL, printer, NULL))
605 _cupsLangPrintf(stderr,
606 _("%s: Error - %s environment variable names "
607 "non-existent destination \"%s\"."), argv[0], val,
608 printer);
609 else if (cupsLastError() == IPP_NOT_FOUND)
610 _cupsLangPrintf(stderr,
611 _("%s: Error - no default destination available."),
612 argv[0]);
613 else
614 _cupsLangPrintf(stderr, _("%s: Error - scheduler not responding."),
615 argv[0]);
616
617 return (1);
618 }
619
620 if (num_files > 0)
621 job_id = cupsPrintFiles(printer, num_files, files, title, num_options, options);
622 else if ((job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, printer,
623 title ? title : "(stdin)",
624 num_options, options)) > 0)
625 {
626 http_status_t status; /* Write status */
627 const char *format; /* Document format */
628 ssize_t bytes; /* Bytes read */
629
630 if (cupsGetOption("raw", num_options, options))
631 format = CUPS_FORMAT_RAW;
632 else if ((format = cupsGetOption("document-format", num_options,
633 options)) == NULL)
634 format = CUPS_FORMAT_AUTO;
635
636 status = cupsStartDocument(CUPS_HTTP_DEFAULT, printer, job_id, NULL,
637 format, 1);
638
639 while (status == HTTP_CONTINUE &&
640 (bytes = read(0, buffer, sizeof(buffer))) > 0)
641 status = cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer, (size_t)bytes);
642
643 if (status != HTTP_CONTINUE)
644 {
645 _cupsLangPrintf(stderr, _("%s: Error - unable to queue from stdin - %s."),
646 argv[0], httpStatus(status));
647 cupsFinishDocument(CUPS_HTTP_DEFAULT, printer);
648 cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
649 return (1);
650 }
651
652 if (cupsFinishDocument(CUPS_HTTP_DEFAULT, printer) != IPP_OK)
653 {
654 _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
655 cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
656 return (1);
657 }
658 }
659
660 if (job_id < 1)
661 {
662 _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
663 return (1);
664 }
665 else if (!silent)
666 _cupsLangPrintf(stdout, _("request id is %s-%d (%d file(s))"),
667 printer, job_id, num_files);
668
669 return (0);
670 }
671
672
673 /*
674 * 'restart_job()' - Restart a job.
675 */
676
677 int /* O - Exit status */
678 restart_job(const char *command, /* I - Command name */
679 int job_id) /* I - Job ID */
680 {
681 ipp_t *request; /* IPP request */
682 char uri[HTTP_MAX_URI]; /* URI for job */
683
684
685 request = ippNewRequest(IPP_RESTART_JOB);
686
687 sprintf(uri, "ipp://localhost/jobs/%d", job_id);
688
689 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
690 "job-uri", NULL, uri);
691
692 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
693 "requesting-user-name", NULL, cupsUser());
694
695 ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/jobs"));
696
697 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
698 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
699 {
700 _cupsLangPrintf(stderr,
701 _("%s: Error - add '/version=1.1' to server "
702 "name."), command);
703 return (1);
704 }
705 else if (cupsLastError() > IPP_OK_CONFLICT)
706 {
707 _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
708 return (1);
709 }
710
711 return (0);
712 }
713
714
715 /*
716 * 'set_job_attrs()' - Set job attributes.
717 */
718
719 int /* O - Exit status */
720 set_job_attrs(const char *command, /* I - Command name */
721 int job_id, /* I - Job ID */
722 int num_options,/* I - Number of options */
723 cups_option_t *options) /* I - Options */
724 {
725 ipp_t *request; /* IPP request */
726 char uri[HTTP_MAX_URI]; /* URI for job */
727
728
729 if (num_options == 0)
730 return (0);
731
732 request = ippNewRequest(IPP_SET_JOB_ATTRIBUTES);
733
734 sprintf(uri, "ipp://localhost/jobs/%d", job_id);
735
736 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
737 "job-uri", NULL, uri);
738
739 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
740 "requesting-user-name", NULL, cupsUser());
741
742 cupsEncodeOptions(request, num_options, options);
743
744 ippDelete(cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/jobs"));
745
746 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
747 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
748 {
749 _cupsLangPrintf(stderr,
750 _("%s: Error - add '/version=1.1' to server "
751 "name."), command);
752 return (1);
753 }
754 else if (cupsLastError() > IPP_OK_CONFLICT)
755 {
756 _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
757 return (1);
758 }
759
760 return (0);
761 }