1/*
2 * "$Id: lpadmin.c 11560 2014-02-06 20:10:19Z msweet $"
3 *
4 * "lpadmin" command for CUPS.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file.  If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 */
15
16/*
17 * Include necessary headers...
18 */
19
20#include <cups/cups-private.h>
21
22
23/*
24 * Local functions...
25 */
26
27static int		add_printer_to_class(http_t *http, char *printer, char *pclass);
28static int		default_printer(http_t *http, char *printer);
29static int		delete_printer(http_t *http, char *printer);
30static int		delete_printer_from_class(http_t *http, char *printer,
31			                          char *pclass);
32static int		delete_printer_option(http_t *http, char *printer,
33			                      char *option);
34static int		enable_printer(http_t *http, char *printer);
35static cups_ptype_t	get_printer_type(http_t *http, char *printer, char *uri,
36			                 size_t urisize);
37static int		set_printer_options(http_t *http, char *printer,
38			                    int num_options, cups_option_t *options,
39					    char *file);
40static int		validate_name(const char *name);
41
42
43/*
44 * 'main()' - Parse options and configure the scheduler.
45 */
46
47int
48main(int  argc,			/* I - Number of command-line arguments */
49     char *argv[])		/* I - Command-line arguments */
50{
51  int		i;		/* Looping var */
52  http_t	*http;		/* Connection to server */
53  char		*printer,	/* Destination printer */
54		*pclass,	/* Printer class name */
55		*val;		/* Pointer to allow/deny value */
56  int		num_options;	/* Number of options */
57  cups_option_t	*options;	/* Options */
58  char		*file;		/* New PPD file/interface script */
59
60
61  _cupsSetLocale(argv);
62
63  http        = NULL;
64  printer     = NULL;
65  num_options = 0;
66  options     = NULL;
67  file        = NULL;
68
69  for (i = 1; i < argc; i ++)
70    if (argv[i][0] == '-')
71      switch (argv[i][1])
72      {
73        case 'c' : /* Add printer to class */
74	    if (!http)
75	    {
76              http = httpConnectEncrypt(cupsServer(), ippPort(),
77	                                cupsEncryption());
78
79	      if (http == NULL)
80	      {
81		_cupsLangPrintf(stderr,
82		                _("lpadmin: Unable to connect to server: %s"),
83				strerror(errno));
84		return (1);
85	      }
86            }
87
88	    if (printer == NULL)
89	    {
90	      _cupsLangPuts(stderr,
91	                    _("lpadmin: Unable to add a printer to the class:\n"
92			      "         You must specify a printer name "
93			      "first."));
94	      return (1);
95	    }
96
97	    if (argv[i][2])
98	      pclass = argv[i] + 2;
99	    else
100	    {
101	      i ++;
102
103	      if (i >= argc)
104	      {
105		_cupsLangPuts(stderr,
106		              _("lpadmin: Expected class name after \"-c\" "
107			        "option."));
108		return (1);
109	      }
110
111	      pclass = argv[i];
112	    }
113
114            if (!validate_name(pclass))
115	    {
116	      _cupsLangPuts(stderr,
117	                    _("lpadmin: Class name can only contain printable "
118			      "characters."));
119	      return (1);
120	    }
121
122	    if (add_printer_to_class(http, printer, pclass))
123	      return (1);
124	    break;
125
126        case 'd' : /* Set as default destination */
127	    if (!http)
128	    {
129              http = httpConnectEncrypt(cupsServer(), ippPort(),
130	                                cupsEncryption());
131
132	      if (http == NULL)
133	      {
134		_cupsLangPrintf(stderr,
135		                _("lpadmin: Unable to connect to server: %s"),
136				strerror(errno));
137		return (1);
138	      }
139            }
140
141	    if (argv[i][2])
142	      printer = argv[i] + 2;
143	    else
144	    {
145	      i ++;
146
147	      if (i >= argc)
148	      {
149		_cupsLangPuts(stderr,
150	                      _("lpadmin: Expected printer name after \"-d\" "
151			        "option."));
152		return (1);
153	      }
154
155	      printer = argv[i];
156	    }
157
158            if (!validate_name(printer))
159	    {
160	      _cupsLangPuts(stderr,
161	                    _("lpadmin: Printer name can only contain "
162			      "printable characters."));
163	      return (1);
164	    }
165
166            if (default_printer(http, printer))
167	      return (1);
168
169	    i = argc;
170	    break;
171
172        case 'h' : /* Connect to host */
173	    if (http)
174	    {
175	      httpClose(http);
176	      http = NULL;
177	    }
178
179	    if (argv[i][2] != '\0')
180	      cupsSetServer(argv[i] + 2);
181	    else
182	    {
183	      i ++;
184
185	      if (i >= argc)
186	      {
187	        _cupsLangPuts(stderr,
188	                      _("lpadmin: Expected hostname after \"-h\" "
189			        "option."));
190		return (1);
191              }
192
193              cupsSetServer(argv[i]);
194	    }
195	    break;
196
197        case 'i' : /* Use the specified interface script */
198	    if (argv[i][2])
199	      file = argv[i] + 2;
200	    else
201	    {
202	      i ++;
203
204	      if (i >= argc)
205	      {
206		_cupsLangPuts(stderr,
207	                      _("lpadmin: Expected interface after \"-i\" "
208			        "option."));
209		return (1);
210	      }
211
212	      file = argv[i];
213	    }
214	    break;
215
216        case 'E' : /* Enable the printer */
217	    if (printer == NULL)
218	    {
219#ifdef HAVE_SSL
220	      cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
221
222	      if (http)
223		httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
224#else
225              _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."),
226	                      argv[0]);
227#endif /* HAVE_SSL */
228	      break;
229	    }
230
231	    if (!http)
232	    {
233              http = httpConnectEncrypt(cupsServer(), ippPort(),
234	                                cupsEncryption());
235
236	      if (http == NULL)
237	      {
238		_cupsLangPrintf(stderr,
239		                _("lpadmin: Unable to connect to server: %s"),
240				strerror(errno));
241		return (1);
242	      }
243            }
244
245            if (enable_printer(http, printer))
246	      return (1);
247            break;
248
249        case 'm' : /* Use the specified standard script/PPD file */
250	    if (argv[i][2])
251	      num_options = cupsAddOption("ppd-name", argv[i] + 2, num_options,
252	                                  &options);
253	    else
254	    {
255	      i ++;
256
257	      if (i >= argc)
258	      {
259		_cupsLangPuts(stderr,
260	                      _("lpadmin: Expected model after \"-m\" "
261			        "option."));
262		return (1);
263	      }
264
265	      num_options = cupsAddOption("ppd-name", argv[i], num_options,
266	                                  &options);
267	    }
268	    break;
269
270        case 'o' : /* Set option */
271	    if (argv[i][2])
272	      num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
273	    else
274	    {
275	      i ++;
276
277	      if (i >= argc)
278	      {
279		_cupsLangPuts(stderr,
280	                      _("lpadmin: Expected name=value after \"-o\" "
281			        "option."));
282		return (1);
283	      }
284
285	      num_options = cupsParseOptions(argv[i], num_options, &options);
286	    }
287	    break;
288
289        case 'p' : /* Add/modify a printer */
290	    if (argv[i][2])
291	      printer = argv[i] + 2;
292	    else
293	    {
294	      i ++;
295
296	      if (i >= argc)
297	      {
298		_cupsLangPuts(stderr,
299	                      _("lpadmin: Expected printer after \"-p\" "
300			        "option."));
301		return (1);
302	      }
303
304	      printer = argv[i];
305	    }
306
307            if (!validate_name(printer))
308	    {
309	      _cupsLangPuts(stderr,
310	                    _("lpadmin: Printer name can only contain "
311			      "printable characters."));
312	      return (1);
313	    }
314	    break;
315
316        case 'r' : /* Remove printer from class */
317	    if (!http)
318	    {
319              http = httpConnectEncrypt(cupsServer(), ippPort(),
320	                                cupsEncryption());
321
322	      if (http == NULL)
323	      {
324		_cupsLangPrintf(stderr,
325		                _("lpadmin: Unable to connect to server: %s"),
326				strerror(errno));
327		return (1);
328	      }
329            }
330
331	    if (printer == NULL)
332	    {
333	      _cupsLangPuts(stderr,
334	                    _("lpadmin: Unable to remove a printer from the "
335			      "class:\n"
336			      "         You must specify a printer name "
337			      "first."));
338	      return (1);
339	    }
340
341	    if (argv[i][2])
342	      pclass = argv[i] + 2;
343	    else
344	    {
345	      i ++;
346
347	      if (i >= argc)
348	      {
349		_cupsLangPuts(stderr,
350	                      _("lpadmin: Expected class after \"-r\" "
351			        "option."));
352		return (1);
353	      }
354
355	      pclass = argv[i];
356	    }
357
358            if (!validate_name(pclass))
359	    {
360	      _cupsLangPuts(stderr,
361	                    _("lpadmin: Class name can only contain printable "
362			      "characters."));
363	      return (1);
364	    }
365
366            if (delete_printer_from_class(http, printer, pclass))
367	      return (1);
368	    break;
369
370        case 'R' : /* Remove option */
371	    if (!http)
372	    {
373              http = httpConnectEncrypt(cupsServer(), ippPort(),
374	                                cupsEncryption());
375
376	      if (http == NULL)
377	      {
378		_cupsLangPrintf(stderr,
379		                _("lpadmin: Unable to connect to server: %s"),
380				strerror(errno));
381		return (1);
382	      }
383            }
384
385	    if (printer == NULL)
386	    {
387	      _cupsLangPuts(stderr,
388	                    _("lpadmin: Unable to delete option:\n"
389			      "         You must specify a printer name "
390			      "first."));
391	      return (1);
392	    }
393
394	    if (argv[i][2])
395	      val = argv[i] + 2;
396	    else
397	    {
398	      i ++;
399
400	      if (i >= argc)
401	      {
402		_cupsLangPuts(stderr,
403	                      _("lpadmin: Expected name after \"-R\" "
404			        "option."));
405		return (1);
406	      }
407
408	      val = argv[i];
409	    }
410
411            if (delete_printer_option(http, printer, val))
412	      return (1);
413	    break;
414
415        case 'U' : /* Username */
416	    if (argv[i][2] != '\0')
417	      cupsSetUser(argv[i] + 2);
418	    else
419	    {
420	      i ++;
421	      if (i >= argc)
422	      {
423	        _cupsLangPrintf(stderr,
424		                _("%s: Error - expected username after "
425				  "\"-U\" option."), argv[0]);
426	        return (1);
427	      }
428
429              cupsSetUser(argv[i]);
430	    }
431	    break;
432
433        case 'u' : /* Allow/deny users */
434	    if (argv[i][2])
435	      val = argv[i] + 2;
436	    else
437	    {
438	      i ++;
439
440	      if (i >= argc)
441	      {
442		_cupsLangPuts(stderr,
443	                      _("lpadmin: Expected allow/deny:userlist after "
444			        "\"-u\" option."));
445		return (1);
446	      }
447
448              val = argv[i];
449	    }
450
451            if (!_cups_strncasecmp(val, "allow:", 6))
452	      num_options = cupsAddOption("requesting-user-name-allowed",
453	                                  val + 6, num_options, &options);
454            else if (!_cups_strncasecmp(val, "deny:", 5))
455	      num_options = cupsAddOption("requesting-user-name-denied",
456	                                  val + 5, num_options, &options);
457            else
458	    {
459	      _cupsLangPrintf(stderr,
460	                      _("lpadmin: Unknown allow/deny option \"%s\"."),
461	                      val);
462	      return (1);
463	    }
464	    break;
465
466        case 'v' : /* Set the device-uri attribute */
467	    if (argv[i][2])
468	      num_options = cupsAddOption("device-uri", argv[i] + 2,
469	                                  num_options, &options);
470	    else
471	    {
472	      i ++;
473
474	      if (i >= argc)
475	      {
476		_cupsLangPuts(stderr,
477	                      _("lpadmin: Expected device URI after \"-v\" "
478			        "option."));
479		return (1);
480	      }
481
482	      num_options = cupsAddOption("device-uri", argv[i],
483	                                  num_options, &options);
484	    }
485	    break;
486
487        case 'x' : /* Delete a printer */
488	    if (!http)
489	    {
490              http = httpConnectEncrypt(cupsServer(), ippPort(),
491	                                cupsEncryption());
492
493	      if (http == NULL)
494	      {
495		_cupsLangPrintf(stderr,
496		                _("lpadmin: Unable to connect to server: %s"),
497				strerror(errno));
498		return (1);
499	      }
500            }
501
502	    if (argv[i][2])
503	      printer = argv[i] + 2;
504	    else
505	    {
506	      i ++;
507
508	      if (i >= argc)
509	      {
510		_cupsLangPuts(stderr,
511	                      _("lpadmin: Expected printer or class after "
512			        "\"-x\" option."));
513		return (1);
514	      }
515
516	      printer = argv[i];
517	    }
518
519            if (!validate_name(printer))
520	    {
521	      _cupsLangPuts(stderr,
522	                    _("lpadmin: Printer name can only contain "
523			      "printable characters."));
524	      return (1);
525	    }
526
527            if (delete_printer(http, printer))
528	      return (1);
529
530	    i = argc;
531	    break;
532
533        case 'D' : /* Set the printer-info attribute */
534	    if (argv[i][2])
535	      num_options = cupsAddOption("printer-info", argv[i] + 2,
536	                                  num_options, &options);
537	    else
538	    {
539	      i ++;
540
541	      if (i >= argc)
542	      {
543		_cupsLangPuts(stderr,
544	                      _("lpadmin: Expected description after "
545			        "\"-D\" option."));
546		return (1);
547	      }
548
549	      num_options = cupsAddOption("printer-info", argv[i],
550	                                  num_options, &options);
551	    }
552	    break;
553
554        case 'I' : /* Set the supported file types (ignored) */
555	    i ++;
556
557	    if (i >= argc)
558	    {
559	      _cupsLangPuts(stderr,
560	                    _("lpadmin: Expected file type(s) after \"-I\" "
561			      "option."));
562	      return (1);
563	    }
564
565	    _cupsLangPuts(stderr,
566	                  _("lpadmin: Warning - content type list ignored."));
567	    break;
568
569        case 'L' : /* Set the printer-location attribute */
570	    if (argv[i][2])
571	      num_options = cupsAddOption("printer-location", argv[i] + 2,
572	                                  num_options, &options);
573	    else
574	    {
575	      i ++;
576
577	      if (i >= argc)
578	      {
579		_cupsLangPuts(stderr,
580	                      _("lpadmin: Expected location after \"-L\" "
581			        "option."));
582		return (1);
583	      }
584
585	      num_options = cupsAddOption("printer-location", argv[i],
586	                                  num_options, &options);
587	    }
588	    break;
589
590        case 'P' : /* Use the specified PPD file */
591	    if (argv[i][2])
592	      file = argv[i] + 2;
593	    else
594	    {
595	      i ++;
596
597	      if (i >= argc)
598	      {
599		_cupsLangPuts(stderr,
600	                      _("lpadmin: Expected PPD after \"-P\" option."));
601		return (1);
602	      }
603
604	      file = argv[i];
605	    }
606	    break;
607
608	default :
609	    _cupsLangPrintf(stderr,
610	                    _("lpadmin: Unknown option \"%c\"."), argv[i][1]);
611	    return (1);
612      }
613    else
614    {
615      _cupsLangPrintf(stderr, _("lpadmin: Unknown argument \"%s\"."),
616                      argv[i]);
617      return (1);
618    }
619
620 /*
621  * Set options as needed...
622  */
623
624  if (num_options || file)
625  {
626    if (!http)
627    {
628      http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
629
630      if (http == NULL)
631      {
632	_cupsLangPrintf(stderr,
633			_("lpadmin: Unable to connect to server: %s"),
634			strerror(errno));
635	return (1);
636      }
637    }
638
639    if (printer == NULL)
640    {
641      _cupsLangPuts(stderr,
642                    _("lpadmin: Unable to set the printer options:\n"
643		      "         You must specify a printer name first."));
644      return (1);
645    }
646
647    if (set_printer_options(http, printer, num_options, options, file))
648      return (1);
649  }
650
651  if (printer == NULL)
652  {
653    _cupsLangPuts(stdout,
654	          _("Usage:\n"
655		    "\n"
656		    "    lpadmin [-h server] -d destination\n"
657		    "    lpadmin [-h server] -x destination\n"
658		    "    lpadmin [-h server] -p printer [-c add-class] "
659		    "[-i interface] [-m model]\n"
660		    "                       [-r remove-class] [-v device] "
661		    "[-D description]\n"
662		    "                       [-P ppd-file] [-o name=value]\n"
663		    "                       [-u allow:user,user] "
664		    "[-u deny:user,user]"));
665  }
666
667  if (http)
668    httpClose(http);
669
670  return (0);
671}
672
673
674/*
675 * 'add_printer_to_class()' - Add a printer to a class.
676 */
677
678static int				/* O - 0 on success, 1 on fail */
679add_printer_to_class(http_t *http,	/* I - Server connection */
680                     char   *printer,	/* I - Printer to add */
681		     char   *pclass)	/* I - Class to add to */
682{
683  int		i;			/* Looping var */
684  ipp_t		*request,		/* IPP Request */
685		*response;		/* IPP Response */
686  ipp_attribute_t *attr,		/* Current attribute */
687		*members;		/* Members in class */
688  char		uri[HTTP_MAX_URI];	/* URI for printer/class */
689
690
691  DEBUG_printf(("add_printer_to_class(%p, \"%s\", \"%s\")\n", http,
692                printer, pclass));
693
694 /*
695  * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
696  * attributes:
697  *
698  *    attributes-charset
699  *    attributes-natural-language
700  *    printer-uri
701  *    requesting-user-name
702  */
703
704  request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
705
706  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
707                   "localhost", 0, "/classes/%s", pclass);
708  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
709               "printer-uri", NULL, uri);
710  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
711               NULL, cupsUser());
712
713 /*
714  * Do the request and get back a response...
715  */
716
717  response = cupsDoRequest(http, request, "/");
718
719 /*
720  * Build a CUPS_ADD_MODIFY_CLASS request, which requires the following
721  * attributes:
722  *
723  *    attributes-charset
724  *    attributes-natural-language
725  *    printer-uri
726  *    requesting-user-name
727  *    member-uris
728  */
729
730  request = ippNewRequest(CUPS_ADD_MODIFY_CLASS);
731
732  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
733               "printer-uri", NULL, uri);
734  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
735               NULL, cupsUser());
736
737 /*
738  * See if the printer is already in the class...
739  */
740
741  if (response != NULL &&
742      (members = ippFindAttribute(response, "member-names",
743                                  IPP_TAG_NAME)) != NULL)
744    for (i = 0; i < members->num_values; i ++)
745      if (_cups_strcasecmp(printer, members->values[i].string.text) == 0)
746      {
747        _cupsLangPrintf(stderr,
748	                _("lpadmin: Printer %s is already a member of class "
749			  "%s."), printer, pclass);
750        ippDelete(request);
751	ippDelete(response);
752	return (0);
753      }
754
755 /*
756  * OK, the printer isn't part of the class, so add it...
757  */
758
759  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
760                   "localhost", 0, "/printers/%s", printer);
761
762  if (response != NULL &&
763      (members = ippFindAttribute(response, "member-uris",
764                                  IPP_TAG_URI)) != NULL)
765  {
766   /*
767    * Add the printer to the existing list...
768    */
769
770    attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI,
771                         "member-uris", members->num_values + 1, NULL, NULL);
772    for (i = 0; i < members->num_values; i ++)
773      attr->values[i].string.text =
774          _cupsStrAlloc(members->values[i].string.text);
775
776    attr->values[i].string.text = _cupsStrAlloc(uri);
777  }
778  else
779    ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "member-uris", NULL,
780                 uri);
781
782 /*
783  * Then send the request...
784  */
785
786  ippDelete(response);
787
788  ippDelete(cupsDoRequest(http, request, "/admin/"));
789  if (cupsLastError() > IPP_OK_CONFLICT)
790  {
791    _cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
792
793    return (1);
794  }
795  else
796    return (0);
797}
798
799
800/*
801 * 'default_printer()' - Set the default printing destination.
802 */
803
804static int				/* O - 0 on success, 1 on fail */
805default_printer(http_t *http,		/* I - Server connection */
806                char   *printer)	/* I - Printer name */
807{
808  ipp_t		*request;		/* IPP Request */
809  char		uri[HTTP_MAX_URI];	/* URI for printer/class */
810
811
812  DEBUG_printf(("default_printer(%p, \"%s\")\n", http, printer));
813
814 /*
815  * Build a CUPS_SET_DEFAULT request, which requires the following
816  * attributes:
817  *
818  *    attributes-charset
819  *    attributes-natural-language
820  *    printer-uri
821  *    requesting-user-name
822  */
823
824  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
825                   "localhost", 0, "/printers/%s", printer);
826
827  request = ippNewRequest(CUPS_SET_DEFAULT);
828
829  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
830               "printer-uri", NULL, uri);
831  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
832               NULL, cupsUser());
833
834 /*
835  * Do the request and get back a response...
836  */
837
838  ippDelete(cupsDoRequest(http, request, "/admin/"));
839
840  if (cupsLastError() > IPP_OK_CONFLICT)
841  {
842    _cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
843
844    return (1);
845  }
846  else
847    return (0);
848}
849
850
851/*
852 * 'delete_printer()' - Delete a printer from the system...
853 */
854
855static int				/* O - 0 on success, 1 on fail */
856delete_printer(http_t *http,		/* I - Server connection */
857               char   *printer)		/* I - Printer to delete */
858{
859  ipp_t		*request;		/* IPP Request */
860  char		uri[HTTP_MAX_URI];	/* URI for printer/class */
861
862
863  DEBUG_printf(("delete_printer(%p, \"%s\")\n", http, printer));
864
865 /*
866  * Build a CUPS_DELETE_PRINTER request, which requires the following
867  * attributes:
868  *
869  *    attributes-charset
870  *    attributes-natural-language
871  *    printer-uri
872  *    requesting-user-name
873  */
874
875  request = ippNewRequest(CUPS_DELETE_PRINTER);
876
877  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
878                   "localhost", 0, "/printers/%s", printer);
879  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
880               "printer-uri", NULL, uri);
881  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
882               NULL, cupsUser());
883
884 /*
885  * Do the request and get back a response...
886  */
887
888  ippDelete(cupsDoRequest(http, request, "/admin/"));
889
890  if (cupsLastError() > IPP_OK_CONFLICT)
891  {
892    _cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
893
894    return (1);
895  }
896  else
897    return (0);
898}
899
900
901/*
902 * 'delete_printer_from_class()' - Delete a printer from a class.
903 */
904
905static int				/* O - 0 on success, 1 on fail */
906delete_printer_from_class(
907    http_t *http,			/* I - Server connection */
908    char   *printer,			/* I - Printer to remove */
909    char   *pclass)	  		/* I - Class to remove from */
910{
911  int		i, j, k;		/* Looping vars */
912  ipp_t		*request,		/* IPP Request */
913		*response;		/* IPP Response */
914  ipp_attribute_t *attr,		/* Current attribute */
915		*members;		/* Members in class */
916  char		uri[HTTP_MAX_URI];	/* URI for printer/class */
917
918
919  DEBUG_printf(("delete_printer_from_class(%p, \"%s\", \"%s\")\n", http,
920                printer, pclass));
921
922 /*
923  * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
924  * attributes:
925  *
926  *    attributes-charset
927  *    attributes-natural-language
928  *    printer-uri
929  *    requesting-user-name
930  */
931
932  request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
933
934  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
935                   "localhost", 0, "/classes/%s", pclass);
936  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
937               "printer-uri", NULL, uri);
938  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
939               NULL, cupsUser());
940
941 /*
942  * Do the request and get back a response...
943  */
944
945  if ((response = cupsDoRequest(http, request, "/classes/")) == NULL ||
946      response->request.status.status_code == IPP_NOT_FOUND)
947  {
948    _cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
949
950    ippDelete(response);
951
952    return (1);
953  }
954
955 /*
956  * See if the printer is already in the class...
957  */
958
959  if ((members = ippFindAttribute(response, "member-names", IPP_TAG_NAME)) == NULL)
960  {
961    _cupsLangPuts(stderr, _("lpadmin: No member names were seen."));
962
963    ippDelete(response);
964
965    return (1);
966  }
967
968  for (i = 0; i < members->num_values; i ++)
969    if (!_cups_strcasecmp(printer, members->values[i].string.text))
970      break;
971
972  if (i >= members->num_values)
973  {
974    _cupsLangPrintf(stderr,
975                    _("lpadmin: Printer %s is not a member of class %s."),
976	            printer, pclass);
977
978    ippDelete(response);
979
980    return (1);
981  }
982
983  if (members->num_values == 1)
984  {
985   /*
986    * Build a CUPS_DELETE_CLASS request, which requires the following
987    * attributes:
988    *
989    *    attributes-charset
990    *    attributes-natural-language
991    *    printer-uri
992    *    requesting-user-name
993    */
994
995    request = ippNewRequest(CUPS_DELETE_CLASS);
996
997    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
998        	 "printer-uri", NULL, uri);
999    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1000                 "requesting-user-name", NULL, cupsUser());
1001  }
1002  else
1003  {
1004   /*
1005    * Build a CUPS_ADD_MODIFY_CLASS request, which requires the following
1006    * attributes:
1007    *
1008    *    attributes-charset
1009    *    attributes-natural-language
1010    *    printer-uri
1011    *    requesting-user-name
1012    *    member-uris
1013    */
1014
1015    request = ippNewRequest(CUPS_ADD_MODIFY_CLASS);
1016
1017    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1018        	 "printer-uri", NULL, uri);
1019    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1020                 "requesting-user-name", NULL, cupsUser());
1021
1022   /*
1023    * Delete the printer from the class...
1024    */
1025
1026    members = ippFindAttribute(response, "member-uris", IPP_TAG_URI);
1027    attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI,
1028                         "member-uris", members->num_values - 1, NULL, NULL);
1029
1030    for (j = 0, k = 0; j < members->num_values; j ++)
1031      if (j != i)
1032        attr->values[k ++].string.text =
1033	    _cupsStrAlloc(members->values[j].string.text);
1034  }
1035
1036 /*
1037  * Then send the request...
1038  */
1039
1040  ippDelete(response);
1041
1042  ippDelete(cupsDoRequest(http, request, "/admin/"));
1043
1044  if (cupsLastError() > IPP_OK_CONFLICT)
1045  {
1046    _cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
1047
1048    return (1);
1049  }
1050  else
1051    return (0);
1052}
1053
1054
1055/*
1056 * 'delete_printer_option()' - Delete a printer option.
1057 */
1058
1059static int				/* O - 0 on success, 1 on fail */
1060delete_printer_option(http_t *http,	/* I - Server connection */
1061                      char   *printer,	/* I - Printer */
1062		      char   *option)	/* I - Option to delete */
1063{
1064  ipp_t		*request;		/* IPP request */
1065  char		uri[HTTP_MAX_URI];	/* URI for printer/class */
1066
1067
1068 /*
1069  * Build a CUPS_ADD_MODIFY_PRINTER or CUPS_ADD_MODIFY_CLASS request, which
1070  * requires the following attributes:
1071  *
1072  *    attributes-charset
1073  *    attributes-natural-language
1074  *    printer-uri
1075  *    requesting-user-name
1076  *    option with deleteAttr tag
1077  */
1078
1079  if (get_printer_type(http, printer, uri, sizeof(uri)) & CUPS_PRINTER_CLASS)
1080    request = ippNewRequest(CUPS_ADD_MODIFY_CLASS);
1081  else
1082    request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
1083
1084  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1085               "printer-uri", NULL, uri);
1086  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1087               "requesting-user-name", NULL, cupsUser());
1088  ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_DELETEATTR, option, 0);
1089
1090 /*
1091  * Do the request and get back a response...
1092  */
1093
1094  ippDelete(cupsDoRequest(http, request, "/admin/"));
1095
1096  if (cupsLastError() > IPP_OK_CONFLICT)
1097  {
1098    _cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
1099
1100    return (1);
1101  }
1102  else
1103    return (0);
1104}
1105
1106
1107/*
1108 * 'enable_printer()' - Enable a printer...
1109 */
1110
1111static int				/* O - 0 on success, 1 on fail */
1112enable_printer(http_t *http,		/* I - Server connection */
1113               char   *printer)		/* I - Printer to enable */
1114{
1115  ipp_t		*request;		/* IPP Request */
1116  char		uri[HTTP_MAX_URI];	/* URI for printer/class */
1117
1118
1119  DEBUG_printf(("enable_printer(%p, \"%s\")\n", http, printer));
1120
1121 /*
1122  * Build a CUPS_ADD_MODIFY_PRINTER or CUPS_ADD_MODIFY_CLASS request, which
1123  * require the following attributes:
1124  *
1125  *    attributes-charset
1126  *    attributes-natural-language
1127  *    printer-uri
1128  *    requesting-user-name
1129  *    printer-state
1130  *    printer-is-accepting-jobs
1131  */
1132
1133  if (get_printer_type(http, printer, uri, sizeof(uri)) & CUPS_PRINTER_CLASS)
1134    request = ippNewRequest(CUPS_ADD_MODIFY_CLASS);
1135  else
1136    request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
1137
1138  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1139               "printer-uri", NULL, uri);
1140  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1141               "requesting-user-name", NULL, cupsUser());
1142  ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
1143                IPP_PRINTER_IDLE);
1144  ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
1145
1146 /*
1147  * Do the request and get back a response...
1148  */
1149
1150  ippDelete(cupsDoRequest(http, request, "/admin/"));
1151
1152  if (cupsLastError() > IPP_OK_CONFLICT)
1153  {
1154    _cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
1155
1156    return (1);
1157  }
1158  else
1159    return (0);
1160}
1161
1162
1163/*
1164 * 'get_printer_type()' - Determine the printer type and URI.
1165 */
1166
1167static cups_ptype_t			/* O - printer-type value */
1168get_printer_type(http_t *http,		/* I - Server connection */
1169                 char   *printer,	/* I - Printer name */
1170		 char   *uri,		/* I - URI buffer */
1171                 size_t urisize)	/* I - Size of URI buffer */
1172{
1173  ipp_t			*request,	/* IPP request */
1174			*response;	/* IPP response */
1175  ipp_attribute_t	*attr;		/* printer-type attribute */
1176  cups_ptype_t		type;		/* printer-type value */
1177
1178
1179 /*
1180  * Build a GET_PRINTER_ATTRIBUTES request, which requires the following
1181  * attributes:
1182  *
1183  *    attributes-charset
1184  *    attributes-natural-language
1185  *    printer-uri
1186  *    requested-attributes
1187  *    requesting-user-name
1188  */
1189
1190  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, (int)urisize, "ipp", NULL, "localhost", ippPort(), "/printers/%s", printer);
1191
1192  request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1193  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1194               "printer-uri", NULL, uri);
1195  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1196               "requested-attributes", NULL, "printer-type");
1197  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1198               "requesting-user-name", NULL, cupsUser());
1199
1200 /*
1201  * Do the request...
1202  */
1203
1204  response = cupsDoRequest(http, request, "/");
1205  if ((attr = ippFindAttribute(response, "printer-type",
1206                               IPP_TAG_ENUM)) != NULL)
1207  {
1208    type = (cups_ptype_t)attr->values[0].integer;
1209
1210    if (type & CUPS_PRINTER_CLASS)
1211      httpAssembleURIf(HTTP_URI_CODING_ALL, uri, (int)urisize, "ipp", NULL, "localhost", ippPort(), "/classes/%s", printer);
1212  }
1213  else
1214    type = CUPS_PRINTER_LOCAL;
1215
1216  ippDelete(response);
1217
1218  return (type);
1219}
1220
1221
1222/*
1223 * 'set_printer_options()' - Set the printer options.
1224 */
1225
1226static int				/* O - 0 on success, 1 on fail */
1227set_printer_options(
1228    http_t        *http,		/* I - Server connection */
1229    char          *printer,		/* I - Printer */
1230    int           num_options,		/* I - Number of options */
1231    cups_option_t *options,		/* I - Options */
1232    char          *file)		/* I - PPD file/interface script */
1233{
1234  ipp_t		*request;		/* IPP Request */
1235  const char	*ppdfile;		/* PPD filename */
1236  int		ppdchanged;		/* PPD changed? */
1237  ppd_file_t	*ppd;			/* PPD file */
1238  ppd_choice_t	*choice;		/* Marked choice */
1239  char		uri[HTTP_MAX_URI],	/* URI for printer/class */
1240		line[1024],		/* Line from PPD file */
1241		keyword[1024],		/* Keyword from Default line */
1242		*keyptr,		/* Pointer into keyword... */
1243		tempfile[1024];		/* Temporary filename */
1244  cups_file_t	*in,			/* PPD file */
1245		*out;			/* Temporary file */
1246  const char	*protocol,		/* Old protocol option */
1247		*customval,		/* Custom option value */
1248		*boolval;		/* Boolean value */
1249  int		wrote_ipp_supplies = 0,	/* Wrote cupsIPPSupplies keyword? */
1250		wrote_snmp_supplies = 0;/* Wrote cupsSNMPSupplies keyword? */
1251
1252
1253  DEBUG_printf(("set_printer_options(http=%p, printer=\"%s\", num_options=%d, "
1254                "options=%p, file=\"%s\")\n", http, printer, num_options,
1255		options, file));
1256
1257 /*
1258  * Build a CUPS_ADD_MODIFY_PRINTER or CUPS_ADD_MODIFY_CLASS request, which
1259  * requires the following attributes:
1260  *
1261  *    attributes-charset
1262  *    attributes-natural-language
1263  *    printer-uri
1264  *    requesting-user-name
1265  *    other options
1266  */
1267
1268  if (get_printer_type(http, printer, uri, sizeof(uri)) & CUPS_PRINTER_CLASS)
1269    request = ippNewRequest(CUPS_ADD_MODIFY_CLASS);
1270  else
1271    request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
1272
1273  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1274               "printer-uri", NULL, uri);
1275  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1276               "requesting-user-name", NULL, cupsUser());
1277
1278 /*
1279  * Add the options...
1280  */
1281
1282  cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
1283  cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
1284
1285  if ((protocol = cupsGetOption("protocol", num_options, options)) != NULL)
1286  {
1287    if (!_cups_strcasecmp(protocol, "bcp"))
1288      ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, "port-monitor",
1289                   NULL, "bcp");
1290    else if (!_cups_strcasecmp(protocol, "tbcp"))
1291      ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, "port-monitor",
1292                   NULL, "tbcp");
1293  }
1294
1295  if (file)
1296    ppdfile = file;
1297  else if (request->request.op.operation_id == CUPS_ADD_MODIFY_PRINTER)
1298    ppdfile = cupsGetPPD(printer);
1299  else
1300    ppdfile = NULL;
1301
1302  if (ppdfile != NULL)
1303  {
1304   /*
1305    * Set default options in the PPD file...
1306    */
1307
1308    ppd = ppdOpenFile(ppdfile);
1309    ppdMarkDefaults(ppd);
1310    cupsMarkOptions(ppd, num_options, options);
1311
1312    if ((out = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL)
1313    {
1314      _cupsLangPrintError(NULL, _("lpadmin: Unable to create temporary file"));
1315      ippDelete(request);
1316      if (ppdfile != file)
1317        unlink(ppdfile);
1318      return (1);
1319    }
1320
1321    if ((in = cupsFileOpen(ppdfile, "r")) == NULL)
1322    {
1323      _cupsLangPrintf(stderr,
1324                      _("lpadmin: Unable to open PPD file \"%s\" - %s"),
1325        	      ppdfile, strerror(errno));
1326      ippDelete(request);
1327      if (ppdfile != file)
1328	unlink(ppdfile);
1329      cupsFileClose(out);
1330      unlink(tempfile);
1331      return (1);
1332    }
1333
1334    ppdchanged = 0;
1335
1336    while (cupsFileGets(in, line, sizeof(line)))
1337    {
1338      if (!strncmp(line, "*cupsIPPSupplies:", 17) &&
1339	  (boolval = cupsGetOption("cupsIPPSupplies", num_options,
1340	                           options)) != NULL)
1341      {
1342        wrote_ipp_supplies = 1;
1343        cupsFilePrintf(out, "*cupsIPPSupplies: %s\n",
1344	               (!_cups_strcasecmp(boolval, "true") ||
1345		        !_cups_strcasecmp(boolval, "yes") ||
1346		        !_cups_strcasecmp(boolval, "on")) ? "True" : "False");
1347      }
1348      else if (!strncmp(line, "*cupsSNMPSupplies:", 18) &&
1349	       (boolval = cupsGetOption("cupsSNMPSupplies", num_options,
1350	                                options)) != NULL)
1351      {
1352        wrote_snmp_supplies = 1;
1353        cupsFilePrintf(out, "*cupsSNMPSupplies: %s\n",
1354	               (!_cups_strcasecmp(boolval, "true") ||
1355		        !_cups_strcasecmp(boolval, "yes") ||
1356		        !_cups_strcasecmp(boolval, "on")) ? "True" : "False");
1357      }
1358      else if (strncmp(line, "*Default", 8))
1359        cupsFilePrintf(out, "%s\n", line);
1360      else
1361      {
1362       /*
1363        * Get default option name...
1364	*/
1365
1366        strlcpy(keyword, line + 8, sizeof(keyword));
1367
1368	for (keyptr = keyword; *keyptr; keyptr ++)
1369	  if (*keyptr == ':' || isspace(*keyptr & 255))
1370	    break;
1371
1372        *keyptr++ = '\0';
1373        while (isspace(*keyptr & 255))
1374	  keyptr ++;
1375
1376        if (!strcmp(keyword, "PageRegion") ||
1377	    !strcmp(keyword, "PageSize") ||
1378	    !strcmp(keyword, "PaperDimension") ||
1379	    !strcmp(keyword, "ImageableArea"))
1380	{
1381	  if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) == NULL)
1382	    choice = ppdFindMarkedChoice(ppd, "PageRegion");
1383        }
1384	else
1385	  choice = ppdFindMarkedChoice(ppd, keyword);
1386
1387        if (choice && strcmp(choice->choice, keyptr))
1388	{
1389	  if (strcmp(choice->choice, "Custom"))
1390	  {
1391	    cupsFilePrintf(out, "*Default%s: %s\n", keyword, choice->choice);
1392	    ppdchanged = 1;
1393	  }
1394	  else if ((customval = cupsGetOption(keyword, num_options,
1395	                                      options)) != NULL)
1396	  {
1397	    cupsFilePrintf(out, "*Default%s: %s\n", keyword, customval);
1398	    ppdchanged = 1;
1399	  }
1400	  else
1401	    cupsFilePrintf(out, "%s\n", line);
1402	}
1403	else
1404	  cupsFilePrintf(out, "%s\n", line);
1405      }
1406    }
1407
1408    if (!wrote_ipp_supplies &&
1409	(boolval = cupsGetOption("cupsIPPSupplies", num_options,
1410				 options)) != NULL)
1411    {
1412      cupsFilePrintf(out, "*cupsIPPSupplies: %s\n",
1413		     (!_cups_strcasecmp(boolval, "true") ||
1414		      !_cups_strcasecmp(boolval, "yes") ||
1415		      !_cups_strcasecmp(boolval, "on")) ? "True" : "False");
1416    }
1417
1418    if (!wrote_snmp_supplies &&
1419        (boolval = cupsGetOption("cupsSNMPSupplies", num_options,
1420			         options)) != NULL)
1421    {
1422      cupsFilePrintf(out, "*cupsSNMPSupplies: %s\n",
1423		     (!_cups_strcasecmp(boolval, "true") ||
1424		      !_cups_strcasecmp(boolval, "yes") ||
1425		      !_cups_strcasecmp(boolval, "on")) ? "True" : "False");
1426    }
1427
1428    cupsFileClose(in);
1429    cupsFileClose(out);
1430    ppdClose(ppd);
1431
1432   /*
1433    * Do the request...
1434    */
1435
1436    ippDelete(cupsDoFileRequest(http, request, "/admin/",
1437                                ppdchanged ? tempfile : file));
1438
1439   /*
1440    * Clean up temp files... (TODO: catch signals in case we CTRL-C during
1441    * lpadmin)
1442    */
1443
1444    if (ppdfile != file)
1445      unlink(ppdfile);
1446    unlink(tempfile);
1447  }
1448  else
1449  {
1450   /*
1451    * No PPD file - just set the options...
1452    */
1453
1454    ippDelete(cupsDoRequest(http, request, "/admin/"));
1455  }
1456
1457 /*
1458  * Check the response...
1459  */
1460
1461  if (cupsLastError() > IPP_OK_CONFLICT)
1462  {
1463    _cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
1464
1465    return (1);
1466  }
1467  else
1468    return (0);
1469}
1470
1471
1472/*
1473 * 'validate_name()' - Make sure the printer name only contains valid chars.
1474 */
1475
1476static int				/* O - 0 if name is no good, 1 if name is good */
1477validate_name(const char *name)		/* I - Name to check */
1478{
1479  const char	*ptr;			/* Pointer into name */
1480
1481
1482 /*
1483  * Scan the whole name...
1484  */
1485
1486  for (ptr = name; *ptr; ptr ++)
1487    if (*ptr == '@')
1488      break;
1489    else if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' ||
1490             *ptr == '#')
1491      return (0);
1492
1493 /*
1494  * All the characters are good; validate the length, too...
1495  */
1496
1497  return ((ptr - name) < 128);
1498}
1499
1500
1501/*
1502 * End of "$Id: lpadmin.c 11560 2014-02-06 20:10:19Z msweet $".
1503 */
1504