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