1 /*
2  * Printer option program for CUPS.
3  *
4  * Copyright © 2007-2018 by Apple Inc.
5  * Copyright © 1997-2006 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 #include <cups/ppd-private.h>
17 
18 
19 /*
20  * Local functions...
21  */
22 
23 static void	list_group(ppd_file_t *ppd, ppd_group_t *group);
24 static void	list_options(cups_dest_t *dest);
25 static void	usage(void) _CUPS_NORETURN;
26 
27 
28 /*
29  * 'main()' - Main entry.
30  */
31 
32 int					/* O - Exit status */
main(int argc,char * argv[])33 main(int  argc,				/* I - Number of command-line arguments */
34      char *argv[])			/* I - Command-line arguments */
35 {
36   int		i, j;			/* Looping vars */
37   int		changes;		/* Did we make changes? */
38   int		num_options;		/* Number of options */
39   cups_option_t	*options;		/* Options */
40   int		num_dests;		/* Number of destinations */
41   cups_dest_t	*dests;			/* Destinations */
42   cups_dest_t	*dest;			/* Current destination */
43   char		*opt,			/* Option pointer */
44 		*printer,		/* Printer name */
45 		*instance,		/* Instance name */
46  		*option;		/* Current option */
47 
48 
49   _cupsSetLocale(argv);
50 
51  /*
52   * Loop through the command-line arguments...
53   */
54 
55   dest        = NULL;
56   num_dests   = 0;
57   dests       = NULL;
58   num_options = 0;
59   options     = NULL;
60   changes     = 0;
61 
62   for (i = 1; i < argc; i ++)
63   {
64     if (!strcmp(argv[i], "--help"))
65       usage();
66     else if (argv[i][0] == '-')
67     {
68       for (opt = argv[i] + 1; *opt; opt ++)
69       {
70 	switch (*opt)
71 	{
72 	  case 'd' : /* -d printer */
73 	      if (opt[1] != '\0')
74 	      {
75 		printer = opt + 1;
76 		opt += strlen(opt) - 1;
77 	      }
78 	      else
79 	      {
80 		i ++;
81 		if (i >= argc)
82 		  usage();
83 
84 		printer = argv[i];
85 	      }
86 
87 	      if ((instance = strrchr(printer, '/')) != NULL)
88 		*instance++ = '\0';
89 
90 	      if (num_dests == 0)
91 		num_dests = cupsGetDests(&dests);
92 
93 	      if (num_dests == 0 || !dests || (dest = cupsGetDest(printer, instance, num_dests, dests)) == NULL)
94 	      {
95 		_cupsLangPuts(stderr, _("lpoptions: Unknown printer or class."));
96 		return (1);
97 	      }
98 
99 	     /*
100 	      * Set the default destination...
101 	      */
102 
103 	      for (j = 0; j < num_dests; j ++)
104 		dests[j].is_default = 0;
105 
106 	      dest->is_default = 1;
107 
108 	      cupsSetDests(num_dests, dests);
109 
110 	      for (j = 0; j < dest->num_options; j ++)
111 		if (cupsGetOption(dest->options[j].name, num_options,
112 				  options) == NULL)
113 		  num_options = cupsAddOption(dest->options[j].name,
114 					      dest->options[j].value,
115 					      num_options, &options);
116 	      break;
117 
118 	  case 'h' : /* -h server */
119 	      if (opt[1] != '\0')
120 	      {
121 		cupsSetServer(opt + 1);
122 		opt += strlen(opt) - 1;
123 	      }
124 	      else
125 	      {
126 		i ++;
127 		if (i >= argc)
128 		  usage();
129 
130 		cupsSetServer(argv[i]);
131 	      }
132 	      break;
133 
134 	  case 'E' : /* Encrypt connection */
135 	      cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
136 	      break;
137 
138 	  case 'l' : /* -l (list options) */
139 	      if (dest == NULL)
140 	      {
141 		if (num_dests == 0)
142 		  num_dests = cupsGetDests(&dests);
143 
144 		if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL)
145 		  dest = dests;
146 	      }
147 
148 	      if (dest == NULL)
149 		_cupsLangPuts(stderr, _("lpoptions: No printers."));
150 	      else
151 		list_options(dest);
152 
153 	      changes = -1;
154 	      break;
155 
156 	  case 'o' : /* -o option[=value] */
157 	      if (dest == NULL)
158 	      {
159 		if (num_dests == 0)
160 		  num_dests = cupsGetDests(&dests);
161 
162 		if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL)
163 		  dest = dests;
164 
165 		if (dest == NULL)
166 		{
167 		  _cupsLangPuts(stderr, _("lpoptions: No printers."));
168 		  return (1);
169 		}
170 
171 		for (j = 0; j < dest->num_options; j ++)
172 		  if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
173 		    num_options = cupsAddOption(dest->options[j].name,
174 						dest->options[j].value,
175 						num_options, &options);
176 	      }
177 
178 	      if (opt[1] != '\0')
179 	      {
180 		num_options = cupsParseOptions(opt + 1, num_options, &options);
181 		opt += strlen(opt) - 1;
182 	      }
183 	      else
184 	      {
185 		i ++;
186 		if (i >= argc)
187 		  usage();
188 
189 		num_options = cupsParseOptions(argv[i], num_options, &options);
190 	      }
191 
192 	      changes = 1;
193 	      break;
194 
195 	  case 'p' : /* -p printer */
196 	      if (opt[1] != '\0')
197 	      {
198 		printer = opt + 1;
199 		opt += strlen(opt) - 1;
200 	      }
201 	      else
202 	      {
203 		i ++;
204 		if (i >= argc)
205 		  usage();
206 
207 		printer = argv[i];
208 	      }
209 
210 	      if ((instance = strrchr(printer, '/')) != NULL)
211 		*instance++ = '\0';
212 
213 	      if (num_dests == 0)
214 		num_dests = cupsGetDests(&dests);
215 
216 	      if ((dest = cupsGetDest(printer, instance, num_dests, dests)) == NULL)
217 	      {
218 		num_dests = cupsAddDest(printer, instance, num_dests, &dests);
219 		dest      = cupsGetDest(printer, instance, num_dests, dests);
220 
221 		if (dest == NULL)
222 		{
223 		  _cupsLangPrintf(stderr, _("lpoptions: Unable to add printer or instance: %s"), strerror(errno));
224 		  return (1);
225 		}
226 	      }
227 
228 	      for (j = 0; j < dest->num_options; j ++)
229 		if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
230 		  num_options = cupsAddOption(dest->options[j].name,
231 					      dest->options[j].value,
232 					      num_options, &options);
233 	      break;
234 
235 	  case 'r' : /* -r option (remove) */
236 	      if (dest == NULL)
237 	      {
238 		if (num_dests == 0)
239 		  num_dests = cupsGetDests(&dests);
240 
241 		if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL)
242 		  dest = dests;
243 
244 		if (dest == NULL)
245 		{
246 		  _cupsLangPuts(stderr, _("lpoptions: No printers."));
247 		  return (1);
248 		}
249 
250 		for (j = 0; j < dest->num_options; j ++)
251 		  if (cupsGetOption(dest->options[j].name, num_options,
252 				    options) == NULL)
253 		    num_options = cupsAddOption(dest->options[j].name,
254 						dest->options[j].value,
255 						num_options, &options);
256 	      }
257 
258 	      if (opt[1] != '\0')
259 	      {
260 		option = opt + 1;
261 		opt += strlen(opt) - 1;
262 	      }
263 	      else
264 	      {
265 		i ++;
266 		if (i >= argc)
267 		  usage();
268 
269 		option = argv[i];
270 	      }
271 
272               num_options = cupsRemoveOption(option, num_options, &options);
273 
274 	      changes = 1;
275 	      break;
276 
277 	  case 'x' : /* -x printer */
278 	      if (opt[1] != '\0')
279 	      {
280 		printer = opt + 1;
281 		opt += strlen(opt) - 1;
282 	      }
283 	      else
284 	      {
285 		i ++;
286 		if (i >= argc)
287 		  usage();
288 
289 		printer = argv[i];
290 	      }
291 
292 	      if ((instance = strrchr(printer, '/')) != NULL)
293 		*instance++ = '\0';
294 
295 	      if (num_dests == 0)
296 		num_dests = cupsGetDests(&dests);
297 
298               num_dests = cupsRemoveDest(printer, instance, num_dests, &dests);
299 
300 	      cupsSetDests(num_dests, dests);
301 	      dest    = NULL;
302 	      changes = -1;
303 	      break;
304 
305 	  default :
306 	      usage();
307 	}
308       }
309     }
310     else
311     {
312       usage();
313     }
314   }
315 
316   if (num_dests == 0)
317     num_dests = cupsGetDests(&dests);
318 
319   if (dest == NULL)
320   {
321     if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
322     {
323       for (j = 0; j < dest->num_options; j ++)
324 	if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
325 	  num_options = cupsAddOption(dest->options[j].name,
326 	                              dest->options[j].value,
327 	                              num_options, &options);
328     }
329   }
330 
331   if (dest == NULL)
332     return (0);
333 
334   if (changes > 0)
335   {
336    /*
337     * Set printer options...
338     */
339 
340     cupsFreeOptions(dest->num_options, dest->options);
341 
342     dest->num_options = num_options;
343     dest->options     = options;
344 
345     cupsSetDests(num_dests, dests);
346   }
347   else if (changes == 0)
348   {
349     char	buffer[10240],		/* String for options */
350 		*ptr;			/* Pointer into string */
351 
352     num_options = dest->num_options;
353     options     = dest->options;
354 
355     for (i = 0, ptr = buffer;
356          ptr < (buffer + sizeof(buffer) - 1) && i < num_options;
357 	 i ++)
358     {
359       if (i)
360         *ptr++ = ' ';
361 
362       if (!options[i].value[0])
363         strlcpy(ptr, options[i].name, sizeof(buffer) - (size_t)(ptr - buffer));
364       else if (strchr(options[i].value, ' ') != NULL ||
365                strchr(options[i].value, '\t') != NULL)
366 	snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s=\'%s\'", options[i].name, options[i].value);
367       else
368 	snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s=%s", options[i].name, options[i].value);
369 
370       ptr += strlen(ptr);
371     }
372 
373     _cupsLangPuts(stdout, buffer);
374   }
375 
376   return (0);
377 }
378 
379 /*
380  * 'list_group()' - List printer-specific options from the PPD group.
381  */
382 
383 static void
list_group(ppd_file_t * ppd,ppd_group_t * group)384 list_group(ppd_file_t  *ppd,		/* I - PPD file */
385            ppd_group_t *group)		/* I - Group to show */
386 {
387   int		i, j;			/* Looping vars */
388   ppd_option_t	*option;		/* Current option */
389   ppd_choice_t	*choice;		/* Current choice */
390   ppd_group_t	*subgroup;		/* Current subgroup */
391   char		buffer[10240],		/* Option string buffer */
392 		*ptr;			/* Pointer into option string */
393 
394 
395   for (i = group->num_options, option = group->options; i > 0; i --, option ++)
396   {
397     if (!_cups_strcasecmp(option->keyword, "PageRegion"))
398       continue;
399 
400     snprintf(buffer, sizeof(buffer), "%s/%s:", option->keyword, option->text);
401 
402     for (j = option->num_choices, choice = option->choices,
403              ptr = buffer + strlen(buffer);
404          j > 0 && ptr < (buffer + sizeof(buffer) - 1);
405 	 j --, choice ++)
406     {
407       if (!_cups_strcasecmp(choice->choice, "Custom"))
408       {
409         ppd_coption_t	*coption;	/* Custom option */
410         ppd_cparam_t	*cparam;	/* Custom parameter */
411 	static const char * const types[] =
412 	{				/* Parameter types */
413 	  "CURVE",
414 	  "INTEGER",
415 	  "INVCURVE",
416 	  "PASSCODE",
417 	  "PASSWORD",
418 	  "POINTS",
419 	  "REAL",
420 	  "STRING"
421 	};
422 
423 
424         if ((coption = ppdFindCustomOption(ppd, option->keyword)) == NULL ||
425 	    cupsArrayCount(coption->params) == 0)
426 	  snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom", choice->marked ? "*" : "");
427         else if (!_cups_strcasecmp(option->keyword, "PageSize") ||
428 	         !_cups_strcasecmp(option->keyword, "PageRegion"))
429 	  snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom.WIDTHxHEIGHT", choice->marked ? "*" : "");
430         else
431 	{
432 	  cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
433 
434 	  if (cupsArrayCount(coption->params) == 1)
435 	    snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom.%s", choice->marked ? "*" : "", types[cparam->type]);
436 	  else
437 	  {
438 	    const char	*prefix;	/* Prefix string */
439 
440 
441             if (choice->marked)
442 	      prefix = " *{";
443 	    else
444 	      prefix = " {";
445 
446 	    while (cparam)
447 	    {
448 	      snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s%s=%s", prefix, cparam->name, types[cparam->type]);
449 	      cparam = (ppd_cparam_t *)cupsArrayNext(coption->params);
450 	      prefix = " ";
451 	      ptr += strlen(ptr);
452 	    }
453 
454             if (ptr < (buffer + sizeof(buffer) - 1))
455 	      strlcpy(ptr, "}", sizeof(buffer) - (size_t)(ptr - buffer));
456 	  }
457 	}
458       }
459       else if (choice->marked)
460         snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " *%s", choice->choice);
461       else
462         snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %s", choice->choice);
463 
464       ptr += strlen(ptr);
465     }
466 
467     _cupsLangPuts(stdout, buffer);
468   }
469 
470   for (i = group->num_subgroups, subgroup = group->subgroups; i > 0; i --, subgroup ++)
471     list_group(ppd, subgroup);
472 }
473 
474 
475 /*
476  * 'list_options()' - List printer-specific options from the PPD file.
477  */
478 
479 static void
list_options(cups_dest_t * dest)480 list_options(cups_dest_t *dest)		/* I - Destination to list */
481 {
482   http_t	*http;			/* Connection to destination */
483   char		resource[1024];		/* Resource path */
484   int		i;			/* Looping var */
485   const char	*filename;		/* PPD filename */
486   ppd_file_t	*ppd;			/* PPD data */
487   ppd_group_t	*group;			/* Current group */
488 
489 
490   if ((http = cupsConnectDest(dest, CUPS_DEST_FLAGS_NONE, 30000, NULL, resource, sizeof(resource), NULL, NULL)) == NULL)
491   {
492     _cupsLangPrintf(stderr, _("lpoptions: Unable to get PPD file for %s: %s"),
493 		    dest->name, cupsLastErrorString());
494     return;
495   }
496 
497   if ((filename = cupsGetPPD2(http, dest->name)) == NULL)
498   {
499     httpClose(http);
500 
501     _cupsLangPrintf(stderr, _("lpoptions: Unable to get PPD file for %s: %s"),
502 		    dest->name, cupsLastErrorString());
503     return;
504   }
505 
506   httpClose(http);
507 
508   if ((ppd = ppdOpenFile(filename)) == NULL)
509   {
510     unlink(filename);
511     _cupsLangPrintf(stderr, _("lpoptions: Unable to open PPD file for %s."),
512 		    dest->name);
513     return;
514   }
515 
516   ppdMarkDefaults(ppd);
517   cupsMarkOptions(ppd, dest->num_options, dest->options);
518 
519   for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
520     list_group(ppd, group);
521 
522   ppdClose(ppd);
523   unlink(filename);
524 }
525 
526 
527 /*
528  * 'usage()' - Show program usage and exit.
529  */
530 
531 static void
usage(void)532 usage(void)
533 {
534   _cupsLangPuts(stdout, _("Usage: lpoptions [options] -d destination\n"
535                           "       lpoptions [options] [-p destination] [-l]\n"
536                           "       lpoptions [options] [-p destination] -o option[=value]\n"
537                           "       lpoptions [options] -x destination"));
538   _cupsLangPuts(stdout, _("Options:"));
539   _cupsLangPuts(stdout, _("-d destination          Set default destination"));
540   _cupsLangPuts(stdout, _("-E                      Encrypt the connection to the server"));
541   _cupsLangPuts(stdout, _("-h server[:port]        Connect to the named server and port"));
542   _cupsLangPuts(stdout, _("-l                      Show supported options and values"));
543   _cupsLangPuts(stdout, _("-o name[=value]         Set default option and value"));
544   _cupsLangPuts(stdout, _("-p destination          Specify a destination"));
545   _cupsLangPuts(stdout, _("-U username             Specify the username to use for authentication"));
546   _cupsLangPuts(stdout, _("-x destination          Remove default options for destination"));
547 
548   exit(1);
549 }
550