1 /*
2  * "lpr" command for CUPS.
3  *
4  * Copyright © 2007-2019 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
8  * information.
9  */
10 
11 /*
12  * Include necessary headers...
13  */
14 
15 #include <cups/cups-private.h>
16 
17 
18 /*
19  * Local functions...
20  */
21 
22 static void	usage(void) _CUPS_NORETURN;
23 
24 
25 /*
26  * 'main()' - Parse options and send files for printing.
27  */
28 
29 int
main(int argc,char * argv[])30 main(int  argc,				/* I - Number of command-line arguments */
31      char *argv[])			/* I - Command-line arguments */
32 {
33   int		i, j;			/* Looping var */
34   int		job_id;			/* Job ID */
35   char		ch;			/* Option character */
36   char		*printer,		/* Destination printer or class */
37 		*instance,		/* Instance */
38 		*opt;			/* Option pointer */
39   const char	*title;			/* Job title */
40   int		num_copies;		/* Number of copies per file */
41   int		num_files;		/* Number of files to print */
42   const char	*files[1000];		/* Files to print */
43   cups_dest_t	*dest;			/* Selected destination */
44   int		num_options;		/* Number of options */
45   cups_option_t	*options;		/* Options */
46   int		deletefile;		/* Delete file after print? */
47   char		buffer[8192];		/* Copy buffer */
48 
49 
50   _cupsSetLocale(argv);
51 
52   deletefile  = 0;
53   printer     = NULL;
54   dest        = NULL;
55   num_options = 0;
56   options     = NULL;
57   num_files   = 0;
58   title       = NULL;
59 
60   for (i = 1; i < argc; i ++)
61   {
62     if (!strcmp(argv[i], "--help"))
63       usage();
64     else if (argv[i][0] == '-')
65     {
66       for (opt = argv[i] + 1; *opt; opt ++)
67       {
68 	switch (ch = *opt)
69 	{
70 	  case 'E' : /* Encrypt */
71 #ifdef HAVE_SSL
72 	      cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
73 #else
74 	      _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
75 #endif /* HAVE_SSL */
76 	      break;
77 
78 	  case 'U' : /* Username */
79 	      if (opt[1] != '\0')
80 	      {
81 		cupsSetUser(opt + 1);
82 		opt += strlen(opt) - 1;
83 	      }
84 	      else
85 	      {
86 		i ++;
87 		if (i >= argc)
88 		{
89 		  _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
90 		  usage();
91 		}
92 
93 		cupsSetUser(argv[i]);
94 	      }
95 	      break;
96 
97 	  case 'H' : /* Connect to host */
98 	      if (opt[1] != '\0')
99 	      {
100 		cupsSetServer(opt + 1);
101 		opt += strlen(opt) - 1;
102 	      }
103 	      else
104 	      {
105 		i ++;
106 
107 		if (i >= argc)
108 		{
109 		  _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-H\" option."), argv[0]);
110 		  usage();
111 		}
112 		else
113 		  cupsSetServer(argv[i]);
114 	      }
115 	      break;
116 
117 	  case '1' : /* TROFF font set 1 */
118 	  case '2' : /* TROFF font set 2 */
119 	  case '3' : /* TROFF font set 3 */
120 	  case '4' : /* TROFF font set 4 */
121 	  case 'i' : /* indent */
122 	  case 'w' : /* width */
123 	      if (opt[1] != '\0')
124 	      {
125 		opt += strlen(opt) - 1;
126 	      }
127 	      else
128 	      {
129 		i ++;
130 
131 		if (i >= argc)
132 		{
133 		  _cupsLangPrintf(stderr,
134 				  _("%s: Error - expected value after \"-%c\" "
135 				    "option."), argv[0], ch);
136 		  usage();
137 		}
138 	      }
139 
140 	  case 'c' : /* CIFPLOT */
141 	  case 'd' : /* DVI */
142 	  case 'f' : /* FORTRAN */
143 	  case 'g' : /* plot */
144 	  case 'n' : /* Ditroff */
145 	  case 't' : /* Troff */
146 	  case 'v' : /* Raster image */
147 	      _cupsLangPrintf(stderr, _("%s: Warning - \"%c\" format modifier not supported - output may not be correct."), argv[0], ch);
148 	      break;
149 
150 	  case 'o' : /* Option */
151 	      if (opt[1] != '\0')
152 	      {
153 		num_options = cupsParseOptions(opt + 1, num_options, &options);
154 		opt += strlen(opt) - 1;
155 	      }
156 	      else
157 	      {
158 		i ++;
159 		if (i >= argc)
160 		{
161 		  _cupsLangPrintf(stderr, _("%s: Error - expected option=value after \"-o\" option."), argv[0]);
162 		  usage();
163 		}
164 
165 		num_options = cupsParseOptions(argv[i], num_options, &options);
166 	      }
167 	      break;
168 
169 	  case 'l' : /* Literal/raw */
170 	      num_options = cupsAddOption("raw", "true", num_options, &options);
171 	      break;
172 
173 	  case 'p' : /* Prettyprint */
174 	      num_options = cupsAddOption("prettyprint", "true", num_options, &options);
175 	      break;
176 
177 	  case 'h' : /* Suppress burst page */
178 	      num_options = cupsAddOption("job-sheets", "none", num_options, &options);
179 	      break;
180 
181 	  case 's' : /* Don't use symlinks */
182 	      break;
183 
184 	  case 'm' : /* Mail on completion */
185 	      {
186 		char	email[1024];	/* EMail address */
187 
188 		snprintf(email, sizeof(email), "mailto:%s@%s", cupsUser(), httpGetHostname(NULL, buffer, sizeof(buffer)));
189 		num_options = cupsAddOption("notify-recipient-uri", email, num_options, &options);
190 	      }
191 	      break;
192 
193 	  case 'q' : /* Queue file but don't print */
194 	      num_options = cupsAddOption("job-hold-until", "indefinite", num_options, &options);
195 	      break;
196 
197 	  case 'r' : /* Remove file after printing */
198 	      deletefile = 1;
199 	      break;
200 
201 	  case 'P' : /* Destination printer or class */
202 	      if (opt[1] != '\0')
203 	      {
204 		printer = opt + 1;
205 		opt += strlen(opt) - 1;
206 	      }
207 	      else
208 	      {
209 		i ++;
210 		if (i >= argc)
211 		{
212 		  _cupsLangPrintf(stderr, _("%s: Error - expected destination after \"-P\" option."), argv[0]);
213 		  usage();
214 		}
215 
216 		printer = argv[i];
217 	      }
218 
219 	      if ((instance = strrchr(printer, '/')) != NULL)
220 		*instance++ = '\0';
221 
222 	      if ((dest = cupsGetNamedDest(NULL, printer, instance)) != NULL)
223 	      {
224 		for (j = 0; j < dest->num_options; j ++)
225 		  if (cupsGetOption(dest->options[j].name, num_options,
226 				    options) == NULL)
227 		    num_options = cupsAddOption(dest->options[j].name,
228 						dest->options[j].value,
229 						num_options, &options);
230 	      }
231 	      else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
232 		       cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
233 	      {
234 		_cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
235 		return (1);
236 	      }
237 	      break;
238 
239 	  case '#' : /* Number of copies */
240 	      if (opt[1] != '\0')
241 	      {
242 		num_copies = atoi(opt + 1);
243 		opt += strlen(opt) - 1;
244 	      }
245 	      else
246 	      {
247 		i ++;
248 		if (i >= argc)
249 		{
250 		  _cupsLangPrintf(stderr, _("%s: Error - expected copies after \"-#\" option."), argv[0]);
251 		  usage();
252 		}
253 
254 		num_copies = atoi(argv[i]);
255 	      }
256 
257 	      if (num_copies < 1)
258 	      {
259 		_cupsLangPrintf(stderr, _("%s: Error - copies must be 1 or more."), argv[0]);
260 		return (1);
261 	      }
262 
263 	      sprintf(buffer, "%d", num_copies);
264 	      num_options = cupsAddOption("copies", buffer, num_options, &options);
265 	      break;
266 
267 	  case 'C' : /* Class */
268 	  case 'J' : /* Job name */
269 	  case 'T' : /* Title */
270 	      if (opt[1] != '\0')
271 	      {
272 		title = opt + 1;
273 		opt += strlen(opt) - 1;
274 	      }
275 	      else
276 	      {
277 		i ++;
278 		if (i >= argc)
279 		{
280 		  _cupsLangPrintf(stderr, _("%s: Error - expected name after \"-%c\" option."), argv[0], ch);
281 		  usage();
282 		}
283 
284 		title = argv[i];
285 	      }
286 	      break;
287 
288 	  default :
289 	      _cupsLangPrintf(stderr, _("%s: Error - unknown option \"%c\"."), argv[0], *opt);
290 	      return (1);
291 	}
292       }
293     }
294     else if (num_files < 1000)
295     {
296      /*
297       * Print a file...
298       */
299 
300       if (access(argv[i], R_OK) != 0)
301       {
302         _cupsLangPrintf(stderr,
303 	                _("%s: Error - unable to access \"%s\" - %s"),
304 		        argv[0], argv[i], strerror(errno));
305         return (1);
306       }
307 
308       files[num_files] = argv[i];
309       num_files ++;
310 
311       if (title == NULL)
312       {
313         if ((title = strrchr(argv[i], '/')) != NULL)
314 	  title ++;
315 	else
316           title = argv[i];
317       }
318     }
319     else
320     {
321       _cupsLangPrintf(stderr, _("%s: Error - too many files - \"%s\"."), argv[0], argv[i]);
322     }
323   }
324 
325  /*
326   * See if we have any files to print; if not, print from stdin...
327   */
328 
329   if (printer == NULL)
330   {
331     if ((dest = cupsGetNamedDest(NULL, NULL, NULL)) != NULL)
332     {
333       printer = dest->name;
334 
335       for (j = 0; j < dest->num_options; j ++)
336 	if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
337 	  num_options = cupsAddOption(dest->options[j].name,
338 		                      dest->options[j].value,
339 				      num_options, &options);
340     }
341     else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
342 	     cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
343     {
344       _cupsLangPrintf(stderr,
345 		      _("%s: Error - add '/version=1.1' to server "
346 			"name."), argv[0]);
347       return (1);
348     }
349   }
350 
351   if (printer == NULL)
352   {
353     if (!cupsGetNamedDest(NULL, NULL, NULL) && cupsLastError() == IPP_STATUS_ERROR_NOT_FOUND)
354       _cupsLangPrintf(stderr, _("%s: Error - %s"), argv[0], cupsLastErrorString());
355     else
356       _cupsLangPrintf(stderr, _("%s: Error - scheduler not responding."), argv[0]);
357 
358     return (1);
359   }
360 
361   if (num_files > 0)
362   {
363     job_id = cupsPrintFiles(printer, num_files, files, title, num_options, options);
364 
365     if (deletefile && job_id > 0)
366     {
367      /*
368       * Delete print files after printing...
369       */
370 
371       for (i = 0; i < num_files; i ++)
372         unlink(files[i]);
373     }
374   }
375   else if ((job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, printer,
376                                    title ? title : "(stdin)",
377                                    num_options, options)) > 0)
378   {
379     http_status_t	status;		/* Write status */
380     const char		*format;	/* Document format */
381     ssize_t		bytes;		/* Bytes read */
382 
383     if (cupsGetOption("raw", num_options, options))
384       format = CUPS_FORMAT_RAW;
385     else if ((format = cupsGetOption("document-format", num_options,
386                                      options)) == NULL)
387       format = CUPS_FORMAT_AUTO;
388 
389     status = cupsStartDocument(CUPS_HTTP_DEFAULT, printer, job_id, NULL,
390                                format, 1);
391 
392     while (status == HTTP_CONTINUE &&
393            (bytes = read(0, buffer, sizeof(buffer))) > 0)
394       status = cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer, (size_t)bytes);
395 
396     if (status != HTTP_CONTINUE)
397     {
398       _cupsLangPrintf(stderr, _("%s: Error - unable to queue from stdin - %s."),
399 		      argv[0], httpStatus(status));
400       cupsFinishDocument(CUPS_HTTP_DEFAULT, printer);
401       cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
402       return (1);
403     }
404 
405     if (cupsFinishDocument(CUPS_HTTP_DEFAULT, printer) != IPP_OK)
406     {
407       _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
408       cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0);
409       return (1);
410     }
411   }
412 
413   if (job_id < 1)
414   {
415     _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString());
416     return (1);
417   }
418 
419   return (0);
420 }
421 
422 
423 /*
424  * 'usage()' - Show program usage and exit.
425  */
426 
427 static void
usage(void)428 usage(void)
429 {
430   _cupsLangPuts(stdout, _("Usage: lpr [options] [file(s)]"));
431   _cupsLangPuts(stdout, _("Options:"));
432   _cupsLangPuts(stdout, _("-# num-copies           Specify the number of copies to print"));
433   _cupsLangPuts(stdout, _("-E                      Encrypt the connection to the server"));
434   _cupsLangPuts(stdout, _("-H server[:port]        Connect to the named server and port"));
435   _cupsLangPuts(stdout, _("-m                      Send an email notification when the job completes"));
436   _cupsLangPuts(stdout, _("-o option[=value]       Specify a printer-specific option"));
437   _cupsLangPuts(stdout, _("-o job-sheets=standard  Print a banner page with the job"));
438   _cupsLangPuts(stdout, _("-o media=size           Specify the media size to use"));
439   _cupsLangPuts(stdout, _("-o number-up=N          Specify that input pages should be printed N-up (1, 2, 4, 6, 9, and 16 are supported)"));
440   _cupsLangPuts(stdout, _("-o orientation-requested=N\n"
441                           "                        Specify portrait (3) or landscape (4) orientation"));
442   _cupsLangPuts(stdout, _("-o print-quality=N      Specify the print quality - draft (3), normal (4), or best (5)"));
443   _cupsLangPuts(stdout, _("-o sides=one-sided      Specify 1-sided printing"));
444   _cupsLangPuts(stdout, _("-o sides=two-sided-long-edge\n"
445                           "                        Specify 2-sided portrait printing"));
446   _cupsLangPuts(stdout, _("-o sides=two-sided-short-edge\n"
447                           "                        Specify 2-sided landscape printing"));
448   _cupsLangPuts(stdout, _("-P destination          Specify the destination"));
449   _cupsLangPuts(stdout, _("-q                      Specify the job should be held for printing"));
450   _cupsLangPuts(stdout, _("-r                      Remove the file(s) after submission"));
451   _cupsLangPuts(stdout, _("-T title                Specify the job title"));
452   _cupsLangPuts(stdout, _("-U username             Specify the username to use for authentication"));
453 
454   exit(1);
455 }
456