1/*
2 * "$Id: admin.c 12131 2014-08-28 23:38:16Z msweet $"
3 *
4 * Administration CGI for CUPS.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2007 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 "cgi-private.h"
21#include <cups/adminutil.h>
22#include <cups/ppd.h>
23#include <errno.h>
24#include <unistd.h>
25#include <fcntl.h>
26#include <sys/wait.h>
27#include <limits.h>
28
29
30/*
31 * Local globals...
32 */
33
34static int	current_device = 0;	/* Current device shown */
35
36
37/*
38 * Local functions...
39 */
40
41static void	choose_device_cb(const char *device_class,
42                                 const char *device_id, const char *device_info,
43                                 const char *device_make_and_model,
44                                 const char *device_uri,
45                                 const char *device_location,
46				 const char *title);
47static void	do_add_rss_subscription(http_t *http);
48static void	do_am_class(http_t *http, int modify);
49static void	do_am_printer(http_t *http, int modify);
50static void	do_cancel_subscription(http_t *http);
51static void	do_config_server(http_t *http);
52static void	do_delete_class(http_t *http);
53static void	do_delete_printer(http_t *http);
54static void	do_export(http_t *http);
55static void	do_list_printers(http_t *http);
56static void	do_menu(http_t *http);
57static void	do_set_allowed_users(http_t *http);
58static void	do_set_default(http_t *http);
59static void	do_set_options(http_t *http, int is_class);
60static void	do_set_sharing(http_t *http);
61static char	*get_option_value(ppd_file_t *ppd, const char *name,
62		                  char *buffer, size_t bufsize);
63static double	get_points(double number, const char *uval);
64
65
66/*
67 * 'main()' - Main entry for CGI.
68 */
69
70int					/* O - Exit status */
71main(void)
72{
73  http_t	*http;			/* Connection to the server */
74  const char	*op;			/* Operation name */
75
76
77 /*
78  * Connect to the HTTP server...
79  */
80
81  fputs("DEBUG: admin.cgi started...\n", stderr);
82
83  http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
84
85  if (!http)
86  {
87    perror("ERROR: Unable to connect to cupsd");
88    fprintf(stderr, "DEBUG: cupsServer()=\"%s\"\n",
89            cupsServer() ? cupsServer() : "(null)");
90    fprintf(stderr, "DEBUG: ippPort()=%d\n", ippPort());
91    fprintf(stderr, "DEBUG: cupsEncryption()=%d\n", cupsEncryption());
92    exit(1);
93  }
94
95  fprintf(stderr, "DEBUG: http=%p\n", http);
96
97 /*
98  * Set the web interface section...
99  */
100
101  cgiSetVariable("SECTION", "admin");
102  cgiSetVariable("REFRESH_PAGE", "");
103
104 /*
105  * See if we have form data...
106  */
107
108  if (!cgiInitialize() || !cgiGetVariable("OP"))
109  {
110   /*
111    * Nope, send the administration menu...
112    */
113
114    fputs("DEBUG: No form data, showing main menu...\n", stderr);
115
116    do_menu(http);
117  }
118  else if ((op = cgiGetVariable("OP")) != NULL && cgiIsPOST())
119  {
120   /*
121    * Do the operation...
122    */
123
124    fprintf(stderr, "DEBUG: op=\"%s\"...\n", op);
125
126    if (!*op)
127    {
128      const char *printer = getenv("PRINTER_NAME"),
129					/* Printer or class name */
130		*server_port = getenv("SERVER_PORT");
131					/* Port number string */
132      int	port = atoi(server_port ? server_port : "0");
133      					/* Port number */
134      char	uri[1024];		/* URL */
135
136      if (printer)
137        httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri),
138	                 getenv("HTTPS") ? "https" : "http", NULL,
139			 getenv("SERVER_NAME"), port, "/%s/%s",
140			 cgiGetVariable("IS_CLASS") ? "classes" : "printers",
141			 printer);
142      else
143        httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri),
144	                getenv("HTTPS") ? "https" : "http", NULL,
145			getenv("SERVER_NAME"), port, "/admin");
146
147      printf("Location: %s\n\n", uri);
148    }
149    else if (!strcmp(op, "set-allowed-users"))
150      do_set_allowed_users(http);
151    else if (!strcmp(op, "set-as-default"))
152      do_set_default(http);
153    else if (!strcmp(op, "set-sharing"))
154      do_set_sharing(http);
155    else if (!strcmp(op, "find-new-printers") ||
156             !strcmp(op, "list-available-printers"))
157      do_list_printers(http);
158    else if (!strcmp(op, "add-class"))
159      do_am_class(http, 0);
160    else if (!strcmp(op, "add-printer"))
161      do_am_printer(http, 0);
162    else if (!strcmp(op, "modify-class"))
163      do_am_class(http, 1);
164    else if (!strcmp(op, "modify-printer"))
165      do_am_printer(http, 1);
166    else if (!strcmp(op, "delete-class"))
167      do_delete_class(http);
168    else if (!strcmp(op, "delete-printer"))
169      do_delete_printer(http);
170    else if (!strcmp(op, "set-class-options"))
171      do_set_options(http, 1);
172    else if (!strcmp(op, "set-printer-options"))
173      do_set_options(http, 0);
174    else if (!strcmp(op, "config-server"))
175      do_config_server(http);
176    else if (!strcmp(op, "export-samba"))
177      do_export(http);
178    else if (!strcmp(op, "add-rss-subscription"))
179      do_add_rss_subscription(http);
180    else if (!strcmp(op, "cancel-subscription"))
181      do_cancel_subscription(http);
182    else
183    {
184     /*
185      * Bad operation code - display an error...
186      */
187
188      cgiStartHTML(cgiText(_("Administration")));
189      cgiCopyTemplateLang("error-op.tmpl");
190      cgiEndHTML();
191    }
192  }
193  else if (op && !strcmp(op, "redirect"))
194  {
195    const char	*url;			/* Redirection URL... */
196    char	prefix[1024];		/* URL prefix */
197
198
199    if (getenv("HTTPS"))
200      snprintf(prefix, sizeof(prefix), "https://%s:%s",
201	       getenv("SERVER_NAME"), getenv("SERVER_PORT"));
202    else
203      snprintf(prefix, sizeof(prefix), "http://%s:%s",
204	       getenv("SERVER_NAME"), getenv("SERVER_PORT"));
205
206    fprintf(stderr, "DEBUG: redirecting with prefix %s!\n", prefix);
207
208    if ((url = cgiGetVariable("URL")) != NULL)
209    {
210      char	encoded[1024],		/* Encoded URL string */
211      		*ptr;			/* Pointer into encoded string */
212
213
214      ptr = encoded;
215      if (*url != '/')
216        *ptr++ = '/';
217
218      for (; *url && ptr < (encoded + sizeof(encoded) - 4); url ++)
219      {
220        if (strchr("%@&+ <>#=", *url) || *url < ' ' || *url & 128)
221	{
222	 /*
223	  * Percent-encode this character; safe because we have at least 4
224	  * bytes left in the array...
225	  */
226
227	  sprintf(ptr, "%%%02X", *url & 255);
228	  ptr += 3;
229	}
230	else
231	  *ptr++ = *url;
232      }
233
234      *ptr = '\0';
235
236      if (*url)
237      {
238       /*
239        * URL was too long, just redirect to the admin page...
240	*/
241
242	printf("Location: %s/admin\n\n", prefix);
243      }
244      else
245      {
246       /*
247        * URL is OK, redirect there...
248	*/
249
250        printf("Location: %s%s\n\n", prefix, encoded);
251      }
252    }
253    else
254      printf("Location: %s/admin\n\n", prefix);
255  }
256  else
257  {
258   /*
259    * Form data but no operation code - display an error...
260    */
261
262    cgiStartHTML(cgiText(_("Administration")));
263    cgiCopyTemplateLang("error-op.tmpl");
264    cgiEndHTML();
265  }
266
267 /*
268  * Close the HTTP server connection...
269  */
270
271  httpClose(http);
272
273 /*
274  * Return with no errors...
275  */
276
277  return (0);
278}
279
280
281/*
282 * 'choose_device_cb()' - Add a device to the device selection page.
283 */
284
285static void
286choose_device_cb(
287    const char *device_class,		/* I - Class */
288    const char *device_id,		/* I - 1284 device ID */
289    const char *device_info,		/* I - Description */
290    const char *device_make_and_model,	/* I - Make and model */
291    const char *device_uri,		/* I - Device URI */
292    const char *device_location,	/* I - Location */
293    const char *title)			/* I - Page title */
294{
295 /*
296  * For modern browsers, start a multi-part page so we can show that something
297  * is happening.  Non-modern browsers just get everything at the end...
298  */
299
300  if (current_device == 0 && cgiSupportsMultipart())
301  {
302    cgiStartMultipart();
303    cgiStartHTML(title);
304    cgiCopyTemplateLang("choose-device.tmpl");
305    cgiEndHTML();
306    fflush(stdout);
307  }
308
309
310 /*
311  * Add the device to the array...
312  */
313
314  cgiSetArray("device_class", current_device, device_class);
315  cgiSetArray("device_id", current_device, device_id);
316  cgiSetArray("device_info", current_device, device_info);
317  cgiSetArray("device_make_and_model", current_device, device_make_and_model);
318  cgiSetArray("device_uri", current_device, device_uri);
319  cgiSetArray("device_location", current_device, device_location);
320
321  current_device ++;
322}
323
324
325/*
326 * 'do_add_rss_subscription()' - Add a RSS subscription.
327 */
328
329static void
330do_add_rss_subscription(http_t *http)	/* I - HTTP connection */
331{
332  ipp_t		*request,		/* IPP request data */
333		*response;		/* IPP response data */
334  char		rss_uri[1024];		/* RSS notify-recipient URI */
335  int		num_events;		/* Number of events */
336  const char	*events[12],		/* Subscribed events */
337		*subscription_name,	/* Subscription name */
338		*printer_uri,		/* Printer URI */
339		*ptr,			/* Pointer into name */
340		*user;			/* Username */
341  int		max_events;		/* Maximum number of events */
342
343
344 /*
345  * See if we have all of the required information...
346  */
347
348  subscription_name = cgiGetVariable("SUBSCRIPTION_NAME");
349  printer_uri       = cgiGetVariable("PRINTER_URI");
350  num_events        = 0;
351
352  if (cgiGetVariable("EVENT_JOB_CREATED"))
353    events[num_events ++] = "job-created";
354  if (cgiGetVariable("EVENT_JOB_COMPLETED"))
355    events[num_events ++] = "job-completed";
356  if (cgiGetVariable("EVENT_JOB_STOPPED"))
357    events[num_events ++] = "job-stopped";
358  if (cgiGetVariable("EVENT_JOB_CONFIG_CHANGED"))
359    events[num_events ++] = "job-config-changed";
360  if (cgiGetVariable("EVENT_PRINTER_STOPPED"))
361    events[num_events ++] = "printer-stopped";
362  if (cgiGetVariable("EVENT_PRINTER_ADDED"))
363    events[num_events ++] = "printer-added";
364  if (cgiGetVariable("EVENT_PRINTER_MODIFIED"))
365    events[num_events ++] = "printer-modified";
366  if (cgiGetVariable("EVENT_PRINTER_DELETED"))
367    events[num_events ++] = "printer-deleted";
368  if (cgiGetVariable("EVENT_SERVER_STARTED"))
369    events[num_events ++] = "server-started";
370  if (cgiGetVariable("EVENT_SERVER_STOPPED"))
371    events[num_events ++] = "server-stopped";
372  if (cgiGetVariable("EVENT_SERVER_RESTARTED"))
373    events[num_events ++] = "server-restarted";
374  if (cgiGetVariable("EVENT_SERVER_AUDIT"))
375    events[num_events ++] = "server-audit";
376
377  if ((ptr = cgiGetVariable("MAX_EVENTS")) != NULL)
378    max_events = atoi(ptr);
379  else
380    max_events = 0;
381
382  if (!subscription_name || !printer_uri || !num_events ||
383      max_events <= 0 || max_events > 9999)
384  {
385   /*
386    * Don't have everything we need, so get the available printers
387    * and classes and (re)show the add page...
388    */
389
390    if (cgiGetVariable("EVENT_JOB_CREATED"))
391      cgiSetVariable("EVENT_JOB_CREATED", "CHECKED");
392    if (cgiGetVariable("EVENT_JOB_COMPLETED"))
393      cgiSetVariable("EVENT_JOB_COMPLETED", "CHECKED");
394    if (cgiGetVariable("EVENT_JOB_STOPPED"))
395      cgiSetVariable("EVENT_JOB_STOPPED", "CHECKED");
396    if (cgiGetVariable("EVENT_JOB_CONFIG_CHANGED"))
397      cgiSetVariable("EVENT_JOB_CONFIG_CHANGED", "CHECKED");
398    if (cgiGetVariable("EVENT_PRINTER_STOPPED"))
399      cgiSetVariable("EVENT_PRINTER_STOPPED", "CHECKED");
400    if (cgiGetVariable("EVENT_PRINTER_ADDED"))
401      cgiSetVariable("EVENT_PRINTER_ADDED", "CHECKED");
402    if (cgiGetVariable("EVENT_PRINTER_MODIFIED"))
403      cgiSetVariable("EVENT_PRINTER_MODIFIED", "CHECKED");
404    if (cgiGetVariable("EVENT_PRINTER_DELETED"))
405      cgiSetVariable("EVENT_PRINTER_DELETED", "CHECKED");
406    if (cgiGetVariable("EVENT_SERVER_STARTED"))
407      cgiSetVariable("EVENT_SERVER_STARTED", "CHECKED");
408    if (cgiGetVariable("EVENT_SERVER_STOPPED"))
409      cgiSetVariable("EVENT_SERVER_STOPPED", "CHECKED");
410    if (cgiGetVariable("EVENT_SERVER_RESTARTED"))
411      cgiSetVariable("EVENT_SERVER_RESTARTED", "CHECKED");
412    if (cgiGetVariable("EVENT_SERVER_AUDIT"))
413      cgiSetVariable("EVENT_SERVER_AUDIT", "CHECKED");
414
415    request  = ippNewRequest(CUPS_GET_PRINTERS);
416    response = cupsDoRequest(http, request, "/");
417
418    cgiSetIPPVars(response, NULL, NULL, NULL, 0);
419
420    ippDelete(response);
421
422    cgiStartHTML(cgiText(_("Add RSS Subscription")));
423
424    cgiCopyTemplateLang("add-rss-subscription.tmpl");
425
426    cgiEndHTML();
427    return;
428  }
429
430 /*
431  * Make sure we have a username...
432  */
433
434  if ((user = getenv("REMOTE_USER")) == NULL)
435  {
436    puts("Status: 401\n");
437    exit(0);
438  }
439
440 /*
441  * Validate the subscription name...
442  */
443
444  for (ptr = subscription_name; *ptr; ptr ++)
445    if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' ||
446        *ptr == '?' || *ptr == '#')
447      break;
448
449  if (*ptr)
450  {
451    cgiSetVariable("ERROR",
452                   cgiText(_("The subscription name may not "
453			     "contain spaces, slashes (/), question marks (?), "
454			     "or the pound sign (#).")));
455    cgiStartHTML(_("Add RSS Subscription"));
456    cgiCopyTemplateLang("error.tmpl");
457    cgiEndHTML();
458    return;
459  }
460
461 /*
462  * Add the subscription...
463  */
464
465  ptr = subscription_name + strlen(subscription_name) - 4;
466  if (ptr < subscription_name || strcmp(ptr, ".rss"))
467    httpAssembleURIf(HTTP_URI_CODING_ALL, rss_uri, sizeof(rss_uri), "rss",
468                     NULL, NULL, 0, "/%s.rss?max_events=%d", subscription_name,
469		     max_events);
470  else
471    httpAssembleURIf(HTTP_URI_CODING_ALL, rss_uri, sizeof(rss_uri), "rss",
472                     NULL, NULL, 0, "/%s?max_events=%d", subscription_name,
473		     max_events);
474
475  request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
476
477  if (!_cups_strcasecmp(printer_uri, "#ALL#"))
478    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
479                 NULL, "ipp://localhost/");
480  else
481    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
482                 NULL, printer_uri);
483
484  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
485               NULL, user);
486
487  ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
488               "notify-recipient-uri", NULL, rss_uri);
489  ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-events",
490                num_events, NULL, events);
491  ippAddInteger(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
492                "notify-lease-duration", 0);
493
494  ippDelete(cupsDoRequest(http, request, "/"));
495
496  if (cupsLastError() == IPP_NOT_AUTHORIZED)
497  {
498    puts("Status: 401\n");
499    exit(0);
500  }
501  else if (cupsLastError() > IPP_OK_CONFLICT)
502  {
503    cgiStartHTML(_("Add RSS Subscription"));
504    cgiShowIPPError(_("Unable to add RSS subscription"));
505  }
506  else
507  {
508   /*
509    * Redirect successful updates back to the admin page...
510    */
511
512    cgiSetVariable("refresh_page", "5;URL=/admin");
513    cgiStartHTML(_("Add RSS Subscription"));
514    cgiCopyTemplateLang("subscription-added.tmpl");
515  }
516
517  cgiEndHTML();
518}
519
520
521/*
522 * 'do_am_class()' - Add or modify a class.
523 */
524
525static void
526do_am_class(http_t *http,		/* I - HTTP connection */
527	    int    modify)		/* I - Modify the printer? */
528{
529  int		i, j;			/* Looping vars */
530  int		element;		/* Element number */
531  int		num_printers;		/* Number of printers */
532  ipp_t		*request,		/* IPP request */
533		*response;		/* IPP response */
534  ipp_attribute_t *attr;		/* member-uris attribute */
535  char		uri[HTTP_MAX_URI];	/* Device or printer URI */
536  const char	*name,			/* Pointer to class name */
537		*op,			/* Operation name */
538		*ptr;			/* Pointer to CGI variable */
539  const char	*title;			/* Title of page */
540  static const char * const pattrs[] =	/* Requested printer attributes */
541		{
542		  "member-names",
543		  "printer-info",
544		  "printer-location"
545		};
546
547
548  title = cgiText(modify ? _("Modify Class") : _("Add Class"));
549  op    = cgiGetVariable("OP");
550  name  = cgiGetVariable("PRINTER_NAME");
551
552  if (cgiGetVariable("PRINTER_LOCATION") == NULL)
553  {
554   /*
555    * Build a CUPS_GET_PRINTERS request, which requires the
556    * following attributes:
557    *
558    *    attributes-charset
559    *    attributes-natural-language
560    */
561
562    request = ippNewRequest(CUPS_GET_PRINTERS);
563
564    ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type",
565		  CUPS_PRINTER_LOCAL);
566    ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask",
567		  CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
568
569   /*
570    * Do the request and get back a response...
571    */
572
573    cgiClearVariables();
574    if (op)
575      cgiSetVariable("OP", op);
576    if (name)
577      cgiSetVariable("PRINTER_NAME", name);
578
579    if ((response = cupsDoRequest(http, request, "/")) != NULL)
580    {
581     /*
582      * Create MEMBER_URIS and MEMBER_NAMES arrays...
583      */
584
585      for (element = 0, attr = response->attrs;
586	   attr != NULL;
587	   attr = attr->next)
588	if (attr->name && !strcmp(attr->name, "printer-uri-supported"))
589	{
590	  if ((ptr = strrchr(attr->values[0].string.text, '/')) != NULL &&
591	      (!name || _cups_strcasecmp(name, ptr + 1)))
592	  {
593	   /*
594	    * Don't show the current class...
595	    */
596
597	    cgiSetArray("MEMBER_URIS", element, attr->values[0].string.text);
598	    element ++;
599	  }
600	}
601
602      for (element = 0, attr = response->attrs;
603	   attr != NULL;
604	   attr = attr->next)
605	if (attr->name && !strcmp(attr->name, "printer-name"))
606	{
607	  if (!name || _cups_strcasecmp(name, attr->values[0].string.text))
608	  {
609	   /*
610	    * Don't show the current class...
611	    */
612
613	    cgiSetArray("MEMBER_NAMES", element, attr->values[0].string.text);
614	    element ++;
615	  }
616	}
617
618      num_printers = cgiGetSize("MEMBER_URIS");
619
620      ippDelete(response);
621    }
622    else
623      num_printers = 0;
624
625    if (modify)
626    {
627     /*
628      * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
629      * following attributes:
630      *
631      *    attributes-charset
632      *    attributes-natural-language
633      *    printer-uri
634      */
635
636      request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
637
638      httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
639                       "localhost", 0, "/classes/%s", name);
640      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
641                   NULL, uri);
642
643      ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
644                    "requested-attributes",
645		    (int)(sizeof(pattrs) / sizeof(pattrs[0])),
646		    NULL, pattrs);
647
648     /*
649      * Do the request and get back a response...
650      */
651
652      if ((response = cupsDoRequest(http, request, "/")) != NULL)
653      {
654	if ((attr = ippFindAttribute(response, "member-names",
655	                             IPP_TAG_NAME)) != NULL)
656	{
657	 /*
658          * Mark any current members in the class...
659	  */
660
661          for (j = 0; j < num_printers; j ++)
662	    cgiSetArray("MEMBER_SELECTED", j, "");
663
664          for (i = 0; i < attr->num_values; i ++)
665	  {
666	    for (j = 0; j < num_printers; j ++)
667	    {
668	      if (!_cups_strcasecmp(attr->values[i].string.text,
669	                      cgiGetArray("MEMBER_NAMES", j)))
670	      {
671		cgiSetArray("MEMBER_SELECTED", j, "SELECTED");
672		break;
673	      }
674            }
675          }
676	}
677
678	if ((attr = ippFindAttribute(response, "printer-info",
679	                             IPP_TAG_TEXT)) != NULL)
680	  cgiSetVariable("PRINTER_INFO", attr->values[0].string.text);
681
682	if ((attr = ippFindAttribute(response, "printer-location",
683	                             IPP_TAG_TEXT)) != NULL)
684	  cgiSetVariable("PRINTER_LOCATION", attr->values[0].string.text);
685
686	ippDelete(response);
687      }
688
689     /*
690      * Update the location and description of an existing printer...
691      */
692
693      cgiStartHTML(title);
694      cgiCopyTemplateLang("modify-class.tmpl");
695    }
696    else
697    {
698     /*
699      * Get the name, location, and description for a new printer...
700      */
701
702      cgiStartHTML(title);
703      cgiCopyTemplateLang("add-class.tmpl");
704    }
705
706    cgiEndHTML();
707
708    return;
709  }
710
711  if (!name)
712  {
713    cgiStartHTML(title);
714    cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
715    cgiCopyTemplateLang("error.tmpl");
716    cgiEndHTML();
717    return;
718  }
719
720  for (ptr = name; *ptr; ptr ++)
721    if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
722      break;
723
724  if (*ptr || ptr == name || strlen(name) > 127)
725  {
726    cgiSetVariable("ERROR",
727                   cgiText(_("The class name may only contain up to "
728			     "127 printable characters and may not "
729			     "contain spaces, slashes (/), or the "
730			     "pound sign (#).")));
731    cgiStartHTML(title);
732    cgiCopyTemplateLang("error.tmpl");
733    cgiEndHTML();
734    return;
735  }
736
737 /*
738  * Build a CUPS_ADD_CLASS request, which requires the following
739  * attributes:
740  *
741  *    attributes-charset
742  *    attributes-natural-language
743  *    printer-uri
744  *    printer-location
745  *    printer-info
746  *    printer-is-accepting-jobs
747  *    printer-state
748  *    member-uris
749  */
750
751  request = ippNewRequest(CUPS_ADD_CLASS);
752
753  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
754                   "localhost", 0, "/classes/%s", name);
755  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
756               NULL, uri);
757
758  ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
759               NULL, cgiGetVariable("PRINTER_LOCATION"));
760
761  ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
762               NULL, cgiGetVariable("PRINTER_INFO"));
763
764  ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
765
766  ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
767                IPP_PRINTER_IDLE);
768
769  if ((num_printers = cgiGetSize("MEMBER_URIS")) > 0)
770  {
771    attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI, "member-uris",
772                         num_printers, NULL, NULL);
773    for (i = 0; i < num_printers; i ++)
774      attr->values[i].string.text = _cupsStrAlloc(cgiGetArray("MEMBER_URIS", i));
775  }
776
777 /*
778  * Do the request and get back a response...
779  */
780
781  ippDelete(cupsDoRequest(http, request, "/admin/"));
782
783  if (cupsLastError() == IPP_NOT_AUTHORIZED)
784  {
785    puts("Status: 401\n");
786    exit(0);
787  }
788  else if (cupsLastError() > IPP_OK_CONFLICT)
789  {
790    cgiStartHTML(title);
791    cgiShowIPPError(modify ? _("Unable to modify class") :
792                             _("Unable to add class"));
793  }
794  else
795  {
796   /*
797    * Redirect successful updates back to the class page...
798    */
799
800    char	refresh[1024];		/* Refresh URL */
801
802    cgiFormEncode(uri, name, sizeof(uri));
803    snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/classes/%s",
804             uri);
805    cgiSetVariable("refresh_page", refresh);
806
807    cgiStartHTML(title);
808
809    if (modify)
810      cgiCopyTemplateLang("class-modified.tmpl");
811    else
812      cgiCopyTemplateLang("class-added.tmpl");
813  }
814
815  cgiEndHTML();
816}
817
818
819/*
820 * 'do_am_printer()' - Add or modify a printer.
821 */
822
823static void
824do_am_printer(http_t *http,		/* I - HTTP connection */
825	      int    modify)		/* I - Modify the printer? */
826{
827  int		i;			/* Looping var */
828  ipp_attribute_t *attr;		/* Current attribute */
829  ipp_t		*request,		/* IPP request */
830		*response,		/* IPP response */
831		*oldinfo;		/* Old printer information */
832  const cgi_file_t *file;		/* Uploaded file, if any */
833  const char	*var;			/* CGI variable */
834  char		uri[HTTP_MAX_URI],	/* Device or printer URI */
835		*uriptr;		/* Pointer into URI */
836  int		maxrate;		/* Maximum baud rate */
837  char		baudrate[255];		/* Baud rate string */
838  const char	*name,			/* Pointer to class name */
839		*ptr;			/* Pointer to CGI variable */
840  const char	*title;			/* Title of page */
841  static int	baudrates[] =		/* Baud rates */
842		{
843		  1200,
844		  2400,
845		  4800,
846		  9600,
847		  19200,
848		  38400,
849		  57600,
850		  115200,
851		  230400,
852		  460800
853		};
854
855
856  ptr = cgiGetVariable("DEVICE_URI");
857  fprintf(stderr, "DEBUG: do_am_printer: DEVICE_URI=\"%s\"\n",
858          ptr ? ptr : "(null)");
859
860  title = cgiText(modify ? _("Modify Printer") : _("Add Printer"));
861
862  if (modify)
863  {
864   /*
865    * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
866    * following attributes:
867    *
868    *    attributes-charset
869    *    attributes-natural-language
870    *    printer-uri
871    */
872
873    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
874
875    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
876                     "localhost", 0, "/printers/%s",
877		     cgiGetVariable("PRINTER_NAME"));
878    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
879                 NULL, uri);
880
881   /*
882    * Do the request and get back a response...
883    */
884
885    oldinfo = cupsDoRequest(http, request, "/");
886  }
887  else
888    oldinfo = NULL;
889
890  file = cgiGetFile();
891
892  if (file)
893  {
894    fprintf(stderr, "DEBUG: file->tempfile=%s\n", file->tempfile);
895    fprintf(stderr, "DEBUG: file->name=%s\n", file->name);
896    fprintf(stderr, "DEBUG: file->filename=%s\n", file->filename);
897    fprintf(stderr, "DEBUG: file->mimetype=%s\n", file->mimetype);
898  }
899
900  if ((name = cgiGetVariable("PRINTER_NAME")) != NULL)
901  {
902    for (ptr = name; *ptr; ptr ++)
903      if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
904	break;
905
906    if (*ptr || ptr == name || strlen(name) > 127)
907    {
908      cgiSetVariable("ERROR",
909		     cgiText(_("The printer name may only contain up to "
910			       "127 printable characters and may not "
911			       "contain spaces, slashes (/), or the "
912			       "pound sign (#).")));
913      cgiStartHTML(title);
914      cgiCopyTemplateLang("error.tmpl");
915      cgiEndHTML();
916      return;
917    }
918  }
919
920  if ((var = cgiGetVariable("DEVICE_URI")) != NULL)
921  {
922    if ((uriptr = strrchr(var, '|')) != NULL)
923    {
924     /*
925      * Extract make and make/model from device URI string...
926      */
927
928      char	make[1024],		/* Make string */
929		*makeptr;		/* Pointer into make */
930
931
932      *uriptr++ = '\0';
933
934      strlcpy(make, uriptr, sizeof(make));
935
936      if ((makeptr = strchr(make, ' ')) != NULL)
937        *makeptr = '\0';
938      else if ((makeptr = strchr(make, '-')) != NULL)
939        *makeptr = '\0';
940      else if (!_cups_strncasecmp(make, "laserjet", 8) ||
941               !_cups_strncasecmp(make, "deskjet", 7) ||
942               !_cups_strncasecmp(make, "designjet", 9))
943        strlcpy(make, "HP", sizeof(make));
944      else if (!_cups_strncasecmp(make, "phaser", 6))
945        strlcpy(make, "Xerox", sizeof(make));
946      else if (!_cups_strncasecmp(make, "stylus", 6))
947        strlcpy(make, "Epson", sizeof(make));
948      else
949        strlcpy(make, "Generic", sizeof(make));
950
951      if (!cgiGetVariable("CURRENT_MAKE"))
952        cgiSetVariable("CURRENT_MAKE", make);
953
954      if (!cgiGetVariable("CURRENT_MAKE_AND_MODEL"))
955        cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr);
956
957      if (!modify)
958      {
959        char	template[128],		/* Template name */
960		*tptr;			/* Pointer into template name */
961
962	cgiSetVariable("PRINTER_INFO", uriptr);
963
964	for (tptr = template;
965	     tptr < (template + sizeof(template) - 1) && *uriptr;
966	     uriptr ++)
967	  if (isalnum(*uriptr & 255) || *uriptr == '_' || *uriptr == '-' ||
968	      *uriptr == '.')
969	    *tptr++ = *uriptr;
970	  else if ((*uriptr == ' ' || *uriptr == '/') && tptr > template &&
971	           tptr[-1] != '_')
972	    *tptr++ = '_';
973	  else if (*uriptr == '?' || *uriptr == '(')
974	    break;
975
976        *tptr = '\0';
977
978        cgiSetVariable("TEMPLATE_NAME", template);
979      }
980    }
981  }
982
983  if (!var)
984  {
985   /*
986    * Look for devices so the user can pick something...
987    */
988
989    if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL)
990    {
991      strlcpy(uri, attr->values[0].string.text, sizeof(uri));
992      if ((uriptr = strchr(uri, ':')) != NULL && strncmp(uriptr, "://", 3) == 0)
993        *uriptr = '\0';
994
995      cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text);
996      cgiSetVariable("CURRENT_DEVICE_SCHEME", uri);
997    }
998
999   /*
1000    * Scan for devices for up to 30 seconds...
1001    */
1002
1003    fputs("DEBUG: Getting list of devices...\n", stderr);
1004
1005    current_device = 0;
1006    if (cupsGetDevices(http, 5, CUPS_INCLUDE_ALL, CUPS_EXCLUDE_NONE,
1007                       (cups_device_cb_t)choose_device_cb,
1008		       (void *)title) == IPP_OK)
1009    {
1010      fputs("DEBUG: Got device list!\n", stderr);
1011
1012      if (cgiSupportsMultipart())
1013        cgiStartMultipart();
1014
1015      cgiSetVariable("CUPS_GET_DEVICES_DONE", "1");
1016      cgiStartHTML(title);
1017      cgiCopyTemplateLang("choose-device.tmpl");
1018      cgiEndHTML();
1019
1020      if (cgiSupportsMultipart())
1021        cgiEndMultipart();
1022    }
1023    else
1024    {
1025      fprintf(stderr,
1026              "ERROR: CUPS-Get-Devices request failed with status %x: %s\n",
1027	      cupsLastError(), cupsLastErrorString());
1028      if (cupsLastError() == IPP_NOT_AUTHORIZED)
1029      {
1030	puts("Status: 401\n");
1031	exit(0);
1032      }
1033      else
1034      {
1035	cgiStartHTML(title);
1036	cgiShowIPPError(modify ? _("Unable to modify printer") :
1037				 _("Unable to add printer"));
1038	cgiEndHTML();
1039        return;
1040      }
1041    }
1042  }
1043  else if (!strchr(var, '/') ||
1044           (!strncmp(var, "lpd://", 6) && !strchr(var + 6, '/')))
1045  {
1046    if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL)
1047    {
1048     /*
1049      * Set the current device URI for the form to the old one...
1050      */
1051
1052      if (strncmp(attr->values[0].string.text, var, strlen(var)) == 0)
1053	cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text);
1054    }
1055
1056   /*
1057    * User needs to set the full URI...
1058    */
1059
1060    cgiStartHTML(title);
1061    cgiCopyTemplateLang("choose-uri.tmpl");
1062    cgiEndHTML();
1063  }
1064  else if (!strncmp(var, "serial:", 7) && !cgiGetVariable("BAUDRATE"))
1065  {
1066   /*
1067    * Need baud rate, parity, etc.
1068    */
1069
1070    if ((var = strchr(var, '?')) != NULL &&
1071        strncmp(var, "?baud=", 6) == 0)
1072      maxrate = atoi(var + 6);
1073    else
1074      maxrate = 19200;
1075
1076    for (i = 0; i < 10; i ++)
1077      if (baudrates[i] > maxrate)
1078        break;
1079      else
1080      {
1081        sprintf(baudrate, "%d", baudrates[i]);
1082	cgiSetArray("BAUDRATES", i, baudrate);
1083      }
1084
1085    cgiStartHTML(title);
1086    cgiCopyTemplateLang("choose-serial.tmpl");
1087    cgiEndHTML();
1088  }
1089  else if (!name || !cgiGetVariable("PRINTER_LOCATION"))
1090  {
1091    cgiStartHTML(title);
1092
1093    if (modify)
1094    {
1095     /*
1096      * Update the location and description of an existing printer...
1097      */
1098
1099      if (oldinfo)
1100      {
1101        if ((attr = ippFindAttribute(oldinfo, "printer-info",
1102	                             IPP_TAG_TEXT)) != NULL)
1103          cgiSetVariable("PRINTER_INFO", attr->values[0].string.text);
1104
1105        if ((attr = ippFindAttribute(oldinfo, "printer-location",
1106	                             IPP_TAG_TEXT)) != NULL)
1107          cgiSetVariable("PRINTER_LOCATION", attr->values[0].string.text);
1108
1109	if ((attr = ippFindAttribute(oldinfo, "printer-is-shared",
1110				     IPP_TAG_BOOLEAN)) != NULL)
1111	  cgiSetVariable("PRINTER_IS_SHARED",
1112			 attr->values[0].boolean ? "1" : "0");
1113      }
1114
1115      cgiCopyTemplateLang("modify-printer.tmpl");
1116    }
1117    else
1118    {
1119     /*
1120      * Get the name, location, and description for a new printer...
1121      */
1122
1123#ifdef __APPLE__
1124      if (!strncmp(var, "usb:", 4))
1125        cgiSetVariable("printer_is_shared", "1");
1126      else
1127#endif /* __APPLE__ */
1128        cgiSetVariable("printer_is_shared", "0");
1129
1130      cgiCopyTemplateLang("add-printer.tmpl");
1131    }
1132
1133    cgiEndHTML();
1134
1135    if (oldinfo)
1136      ippDelete(oldinfo);
1137
1138    return;
1139  }
1140  else if (!file &&
1141           (!cgiGetVariable("PPD_NAME") || cgiGetVariable("SELECT_MAKE")))
1142  {
1143    if (modify && !cgiGetVariable("SELECT_MAKE"))
1144    {
1145     /*
1146      * Get the PPD file...
1147      */
1148
1149      int		fd;		/* PPD file */
1150      char		filename[1024];	/* PPD filename */
1151      ppd_file_t	*ppd;		/* PPD information */
1152      char		buffer[1024];	/* Buffer */
1153      ssize_t		bytes;		/* Number of bytes */
1154      http_status_t	get_status;	/* Status of GET */
1155
1156
1157      /* TODO: Use cupsGetFile() API... */
1158      snprintf(uri, sizeof(uri), "/printers/%s.ppd", name);
1159
1160      if (httpGet(http, uri))
1161        httpGet(http, uri);
1162
1163      while ((get_status = httpUpdate(http)) == HTTP_CONTINUE);
1164
1165      if (get_status != HTTP_OK)
1166      {
1167        httpFlush(http);
1168
1169        fprintf(stderr, "ERROR: Unable to get PPD file %s: %d - %s\n",
1170	        uri, get_status, httpStatus(get_status));
1171      }
1172      else if ((fd = cupsTempFd(filename, sizeof(filename))) >= 0)
1173      {
1174	while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
1175          write(fd, buffer, (size_t)bytes);
1176
1177	close(fd);
1178
1179        if ((ppd = ppdOpenFile(filename)) != NULL)
1180	{
1181	  if (ppd->manufacturer)
1182	    cgiSetVariable("CURRENT_MAKE", ppd->manufacturer);
1183
1184	  if (ppd->nickname)
1185	    cgiSetVariable("CURRENT_MAKE_AND_MODEL", ppd->nickname);
1186
1187          ppdClose(ppd);
1188          unlink(filename);
1189	}
1190	else
1191	{
1192	  int linenum;			/* Line number */
1193
1194	  fprintf(stderr, "ERROR: Unable to open PPD file %s: %s\n",
1195	          filename, ppdErrorString(ppdLastError(&linenum)));
1196	}
1197      }
1198      else
1199      {
1200        httpFlush(http);
1201
1202        fprintf(stderr,
1203	        "ERROR: Unable to create temporary file for PPD file: %s\n",
1204	        strerror(errno));
1205      }
1206    }
1207
1208   /*
1209    * Build a CUPS_GET_PPDS request, which requires the following
1210    * attributes:
1211    *
1212    *    attributes-charset
1213    *    attributes-natural-language
1214    *    printer-uri
1215    */
1216
1217    request = ippNewRequest(CUPS_GET_PPDS);
1218
1219    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1220                 NULL, "ipp://localhost/printers/");
1221
1222    if ((var = cgiGetVariable("PPD_MAKE")) == NULL)
1223      var = cgiGetVariable("CURRENT_MAKE");
1224    if (var && !cgiGetVariable("SELECT_MAKE"))
1225    {
1226      const char *make_model;		/* Make and model */
1227
1228
1229      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1230                   "ppd-make", NULL, var);
1231
1232      if ((make_model = cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL)
1233	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1234		     "ppd-make-and-model", NULL, make_model);
1235    }
1236    else
1237      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1238                   "requested-attributes", NULL, "ppd-make");
1239
1240   /*
1241    * Do the request and get back a response...
1242    */
1243
1244    if ((response = cupsDoRequest(http, request, "/")) != NULL)
1245    {
1246     /*
1247      * Got the list of PPDs, see if the user has selected a make...
1248      */
1249
1250      if (cgiSetIPPVars(response, NULL, NULL, NULL, 0) == 0 && !modify)
1251      {
1252       /*
1253        * No PPD files with this make, try again with all makes...
1254	*/
1255
1256        ippDelete(response);
1257
1258	request = ippNewRequest(CUPS_GET_PPDS);
1259
1260	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1261                     NULL, "ipp://localhost/printers/");
1262
1263	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1264                     "requested-attributes", NULL, "ppd-make");
1265
1266	if ((response = cupsDoRequest(http, request, "/")) != NULL)
1267          cgiSetIPPVars(response, NULL, NULL, NULL, 0);
1268
1269        cgiStartHTML(title);
1270	cgiCopyTemplateLang("choose-make.tmpl");
1271        cgiEndHTML();
1272      }
1273      else if (!var || cgiGetVariable("SELECT_MAKE"))
1274      {
1275        cgiStartHTML(title);
1276	cgiCopyTemplateLang("choose-make.tmpl");
1277        cgiEndHTML();
1278      }
1279      else
1280      {
1281       /*
1282	* Let the user choose a model...
1283	*/
1284
1285        cgiStartHTML(title);
1286	if (!cgiGetVariable("PPD_MAKE"))
1287	  cgiSetVariable("PPD_MAKE", cgiGetVariable("CURRENT_MAKE"));
1288	if (!modify)
1289	  cgiSetVariable("CURRENT_MAKE_AND_MODEL",
1290	                 cgiGetArray("PPD_MAKE_AND_MODEL", 0));
1291	cgiCopyTemplateLang("choose-model.tmpl");
1292        cgiEndHTML();
1293      }
1294
1295      ippDelete(response);
1296    }
1297    else
1298    {
1299      cgiStartHTML(title);
1300      cgiShowIPPError(_("Unable to get list of printer drivers"));
1301      cgiCopyTemplateLang("error.tmpl");
1302      cgiEndHTML();
1303    }
1304  }
1305  else
1306  {
1307   /*
1308    * Build a CUPS_ADD_PRINTER request, which requires the following
1309    * attributes:
1310    *
1311    *    attributes-charset
1312    *    attributes-natural-language
1313    *    printer-uri
1314    *    printer-location
1315    *    printer-info
1316    *    ppd-name
1317    *    device-uri
1318    *    printer-is-accepting-jobs
1319    *    printer-is-shared
1320    *    printer-state
1321    */
1322
1323    request = ippNewRequest(CUPS_ADD_PRINTER);
1324
1325    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1326                     "localhost", 0, "/printers/%s",
1327		     cgiGetVariable("PRINTER_NAME"));
1328    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1329                 NULL, uri);
1330
1331    if (!file)
1332    {
1333      var = cgiGetVariable("PPD_NAME");
1334      if (strcmp(var, "__no_change__"))
1335	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
1336		     NULL, var);
1337    }
1338
1339    ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
1340                 NULL, cgiGetVariable("PRINTER_LOCATION"));
1341
1342    ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
1343                 NULL, cgiGetVariable("PRINTER_INFO"));
1344
1345    strlcpy(uri, cgiGetVariable("DEVICE_URI"), sizeof(uri));
1346
1347   /*
1348    * Strip make and model from URI...
1349    */
1350
1351    if ((uriptr = strrchr(uri, '|')) != NULL)
1352      *uriptr = '\0';
1353
1354    if (!strncmp(uri, "serial:", 7))
1355    {
1356     /*
1357      * Update serial port URI to include baud rate, etc.
1358      */
1359
1360      if ((uriptr = strchr(uri, '?')) == NULL)
1361        uriptr = uri + strlen(uri);
1362
1363      snprintf(uriptr, sizeof(uri) - (size_t)(uriptr - uri),
1364               "?baud=%s+bits=%s+parity=%s+flow=%s",
1365               cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"),
1366	       cgiGetVariable("PARITY"), cgiGetVariable("FLOW"));
1367    }
1368
1369    ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri",
1370                 NULL, uri);
1371
1372    ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
1373
1374    var = cgiGetVariable("printer_is_shared");
1375    ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-shared",
1376                  var && (!strcmp(var, "1") || !strcmp(var, "on")));
1377
1378    ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
1379                  IPP_PRINTER_IDLE);
1380
1381   /*
1382    * Do the request and get back a response...
1383    */
1384
1385    if (file)
1386      ippDelete(cupsDoFileRequest(http, request, "/admin/", file->tempfile));
1387    else
1388      ippDelete(cupsDoRequest(http, request, "/admin/"));
1389
1390    if (cupsLastError() == IPP_NOT_AUTHORIZED)
1391    {
1392      puts("Status: 401\n");
1393      exit(0);
1394    }
1395    else if (cupsLastError() > IPP_OK_CONFLICT)
1396    {
1397      cgiStartHTML(title);
1398      cgiShowIPPError(modify ? _("Unable to modify printer") :
1399                               _("Unable to add printer"));
1400    }
1401    else if (modify)
1402    {
1403     /*
1404      * Redirect successful updates back to the printer page...
1405      */
1406
1407      char	refresh[1024];		/* Refresh URL */
1408
1409
1410      cgiFormEncode(uri, name, sizeof(uri));
1411
1412      snprintf(refresh, sizeof(refresh),
1413	       "5;/admin/?OP=redirect&URL=/printers/%s", uri);
1414
1415      cgiSetVariable("refresh_page", refresh);
1416
1417      cgiStartHTML(title);
1418
1419      cgiCopyTemplateLang("printer-modified.tmpl");
1420    }
1421    else
1422    {
1423     /*
1424      * Set the printer options...
1425      */
1426
1427      cgiSetVariable("OP", "set-printer-options");
1428      do_set_options(http, 0);
1429      return;
1430    }
1431
1432    cgiEndHTML();
1433  }
1434
1435  if (oldinfo)
1436    ippDelete(oldinfo);
1437}
1438
1439
1440/*
1441 * 'do_cancel_subscription()' - Cancel a subscription.
1442 */
1443
1444static void
1445do_cancel_subscription(http_t *http)/* I - HTTP connection */
1446{
1447  ipp_t		*request;		/* IPP request data */
1448  const char	*var,			/* Form variable */
1449		*user;			/* Username */
1450  int		id;			/* Subscription ID */
1451
1452
1453 /*
1454  * See if we have all of the required information...
1455  */
1456
1457  if ((var = cgiGetVariable("NOTIFY_SUBSCRIPTION_ID")) != NULL)
1458    id = atoi(var);
1459  else
1460    id = 0;
1461
1462  if (id <= 0)
1463  {
1464    cgiSetVariable("ERROR", cgiText(_("Bad subscription ID")));
1465    cgiStartHTML(_("Cancel RSS Subscription"));
1466    cgiCopyTemplateLang("error.tmpl");
1467    cgiEndHTML();
1468    return;
1469  }
1470
1471 /*
1472  * Require a username...
1473  */
1474
1475  if ((user = getenv("REMOTE_USER")) == NULL)
1476  {
1477    puts("Status: 401\n");
1478    exit(0);
1479  }
1480
1481 /*
1482  * Cancel the subscription...
1483  */
1484
1485  request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
1486
1487  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1488               NULL, "ipp://localhost/");
1489  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
1490                "notify-subscription-id", id);
1491
1492  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1493               NULL, user);
1494
1495  ippDelete(cupsDoRequest(http, request, "/"));
1496
1497  if (cupsLastError() == IPP_NOT_AUTHORIZED)
1498  {
1499    puts("Status: 401\n");
1500    exit(0);
1501  }
1502  else if (cupsLastError() > IPP_OK_CONFLICT)
1503  {
1504    cgiStartHTML(_("Cancel RSS Subscription"));
1505    cgiShowIPPError(_("Unable to cancel RSS subscription"));
1506  }
1507  else
1508  {
1509   /*
1510    * Redirect successful updates back to the admin page...
1511    */
1512
1513    cgiSetVariable("refresh_page", "5;URL=/admin");
1514    cgiStartHTML(_("Cancel RSS Subscription"));
1515    cgiCopyTemplateLang("subscription-canceled.tmpl");
1516  }
1517
1518  cgiEndHTML();
1519}
1520
1521
1522/*
1523 * 'do_config_server()' - Configure server settings.
1524 */
1525
1526static void
1527do_config_server(http_t *http)		/* I - HTTP connection */
1528{
1529  if (cgiGetVariable("CHANGESETTINGS"))
1530  {
1531   /*
1532    * Save basic setting changes...
1533    */
1534
1535    int			num_settings;	/* Number of server settings */
1536    cups_option_t	*settings;	/* Server settings */
1537    int			advanced,	/* Advanced settings shown? */
1538			changed;	/* Have settings changed? */
1539    const char		*debug_logging,	/* DEBUG_LOGGING value */
1540			*preserve_jobs = NULL,
1541					/* PRESERVE_JOBS value */
1542			*remote_admin,	/* REMOTE_ADMIN value */
1543			*remote_any,	/* REMOTE_ANY value */
1544			*share_printers,/* SHARE_PRINTERS value */
1545			*user_cancel_any,
1546					/* USER_CANCEL_ANY value */
1547			*browse_web_if = NULL,
1548					/* BrowseWebIF value */
1549			*preserve_job_history = NULL,
1550					/* PreserveJobHistory value */
1551			*preserve_job_files = NULL,
1552					/* PreserveJobFiles value */
1553			*max_clients = NULL,
1554					/* MaxClients value */
1555			*max_jobs = NULL,
1556					/* MaxJobs value */
1557			*max_log_size = NULL;
1558					/* MaxLogSize value */
1559    const char		*current_browse_web_if,
1560					/* BrowseWebIF value */
1561			*current_preserve_job_history,
1562					/* PreserveJobHistory value */
1563			*current_preserve_job_files,
1564					/* PreserveJobFiles value */
1565			*current_max_clients,
1566					/* MaxClients value */
1567			*current_max_jobs,
1568					/* MaxJobs value */
1569			*current_max_log_size;
1570					/* MaxLogSize value */
1571#ifdef HAVE_GSSAPI
1572    char		default_auth_type[255];
1573					/* DefaultAuthType value */
1574    const char		*val;		/* Setting value */
1575#endif /* HAVE_GSSAPI */
1576
1577
1578   /*
1579    * Get the checkbox values from the form...
1580    */
1581
1582    debug_logging        = cgiGetVariable("DEBUG_LOGGING") ? "1" : "0";
1583    remote_admin         = cgiGetVariable("REMOTE_ADMIN") ? "1" : "0";
1584    remote_any           = cgiGetVariable("REMOTE_ANY") ? "1" : "0";
1585    share_printers       = cgiGetVariable("SHARE_PRINTERS") ? "1" : "0";
1586    user_cancel_any      = cgiGetVariable("USER_CANCEL_ANY") ? "1" : "0";
1587
1588    advanced = cgiGetVariable("ADVANCEDSETTINGS") != NULL;
1589    if (advanced)
1590    {
1591     /*
1592      * Get advanced settings...
1593      */
1594
1595      browse_web_if        = cgiGetVariable("BROWSE_WEB_IF") ? "Yes" : "No";
1596      max_clients          = cgiGetVariable("MAX_CLIENTS");
1597      max_log_size         = cgiGetVariable("MAX_LOG_SIZE");
1598      preserve_jobs        = cgiGetVariable("PRESERVE_JOBS");
1599
1600      if (preserve_jobs)
1601      {
1602        max_jobs             = cgiGetVariable("MAX_JOBS");
1603	preserve_job_history = cgiGetVariable("PRESERVE_JOB_HISTORY");
1604	preserve_job_files   = cgiGetVariable("PRESERVE_JOB_FILES");
1605
1606	if (!max_jobs || atoi(max_jobs) < 0)
1607	  max_jobs = "500";
1608
1609	if (!preserve_job_history)
1610	  preserve_job_history = "On";
1611
1612	if (!preserve_job_files)
1613	  preserve_job_files = "1d";
1614      }
1615      else
1616      {
1617        max_jobs             = "0";
1618        preserve_job_history = "No";
1619        preserve_job_files   = "No";
1620      }
1621
1622      if (!max_clients || atoi(max_clients) <= 0)
1623	max_clients = "100";
1624
1625      if (!max_log_size || atoi(max_log_size) <= 0.0)
1626	max_log_size = "1m";
1627    }
1628
1629   /*
1630    * Get the current server settings...
1631    */
1632
1633    if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
1634    {
1635      cgiStartHTML(cgiText(_("Change Settings")));
1636      cgiSetVariable("MESSAGE",
1637                     cgiText(_("Unable to change server settings")));
1638      cgiSetVariable("ERROR", cupsLastErrorString());
1639      cgiCopyTemplateLang("error.tmpl");
1640      cgiEndHTML();
1641      return;
1642    }
1643
1644#ifdef HAVE_GSSAPI
1645   /*
1646    * Get authentication settings...
1647    */
1648
1649    if (cgiGetVariable("KERBEROS"))
1650      strlcpy(default_auth_type, "Negotiate", sizeof(default_auth_type));
1651    else
1652    {
1653      val = cupsGetOption("DefaultAuthType", num_settings, settings);
1654
1655      if (!val || !_cups_strcasecmp(val, "Negotiate"))
1656        strlcpy(default_auth_type, "Basic", sizeof(default_auth_type));
1657      else
1658        strlcpy(default_auth_type, val, sizeof(default_auth_type));
1659    }
1660
1661    fprintf(stderr, "DEBUG: DefaultAuthType %s\n", default_auth_type);
1662#endif /* HAVE_GSSAPI */
1663
1664    if ((current_browse_web_if = cupsGetOption("BrowseWebIF", num_settings,
1665                                               settings)) == NULL)
1666      current_browse_web_if = "No";
1667
1668    if ((current_preserve_job_history = cupsGetOption("PreserveJobHistory",
1669                                                      num_settings,
1670						      settings)) == NULL)
1671      current_preserve_job_history = "Yes";
1672
1673    if ((current_preserve_job_files = cupsGetOption("PreserveJobFiles",
1674                                                    num_settings,
1675						    settings)) == NULL)
1676      current_preserve_job_files = "1d";
1677
1678    if ((current_max_clients = cupsGetOption("MaxClients", num_settings,
1679                                             settings)) == NULL)
1680      current_max_clients = "100";
1681
1682    if ((current_max_jobs = cupsGetOption("MaxJobs", num_settings,
1683                                          settings)) == NULL)
1684      current_max_jobs = "500";
1685
1686    if ((current_max_log_size = cupsGetOption("MaxLogSize", num_settings,
1687                                              settings)) == NULL)
1688      current_max_log_size = "1m";
1689
1690   /*
1691    * See if the settings have changed...
1692    */
1693
1694    changed = strcmp(debug_logging, cupsGetOption(CUPS_SERVER_DEBUG_LOGGING,
1695                                                  num_settings, settings)) ||
1696	      strcmp(remote_admin, cupsGetOption(CUPS_SERVER_REMOTE_ADMIN,
1697						 num_settings, settings)) ||
1698	      strcmp(remote_any, cupsGetOption(CUPS_SERVER_REMOTE_ANY,
1699					       num_settings, settings)) ||
1700	      strcmp(share_printers, cupsGetOption(CUPS_SERVER_SHARE_PRINTERS,
1701						   num_settings, settings)) ||
1702#ifdef HAVE_GSSAPI
1703	      !cupsGetOption("DefaultAuthType", num_settings, settings) ||
1704	      strcmp(default_auth_type, cupsGetOption("DefaultAuthType",
1705						      num_settings, settings)) ||
1706#endif /* HAVE_GSSAPI */
1707	      strcmp(user_cancel_any, cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY,
1708						    num_settings, settings));
1709
1710    if (advanced && !changed)
1711      changed = _cups_strcasecmp(browse_web_if, current_browse_web_if) ||
1712		_cups_strcasecmp(preserve_job_history, current_preserve_job_history) ||
1713		_cups_strcasecmp(preserve_job_files, current_preserve_job_files) ||
1714		_cups_strcasecmp(max_clients, current_max_clients) ||
1715		_cups_strcasecmp(max_jobs, current_max_jobs) ||
1716		_cups_strcasecmp(max_log_size, current_max_log_size);
1717
1718    if (changed)
1719    {
1720     /*
1721      * Settings *have* changed, so save the changes...
1722      */
1723
1724      cupsFreeOptions(num_settings, settings);
1725
1726      num_settings = 0;
1727      num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
1728                                   debug_logging, num_settings, &settings);
1729      num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
1730                                   remote_admin, num_settings, &settings);
1731      num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY,
1732                                   remote_any, num_settings, &settings);
1733      num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
1734                                   share_printers, num_settings, &settings);
1735      num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
1736                                   user_cancel_any, num_settings, &settings);
1737#ifdef HAVE_GSSAPI
1738      num_settings = cupsAddOption("DefaultAuthType", default_auth_type,
1739                                   num_settings, &settings);
1740#endif /* HAVE_GSSAPI */
1741
1742      if (advanced)
1743      {
1744       /*
1745        * Add advanced settings...
1746	*/
1747
1748	if (_cups_strcasecmp(browse_web_if, current_browse_web_if))
1749	  num_settings = cupsAddOption("BrowseWebIF", browse_web_if,
1750				       num_settings, &settings);
1751	if (_cups_strcasecmp(preserve_job_history, current_preserve_job_history))
1752	  num_settings = cupsAddOption("PreserveJobHistory",
1753	                               preserve_job_history, num_settings,
1754				       &settings);
1755	if (_cups_strcasecmp(preserve_job_files, current_preserve_job_files))
1756	  num_settings = cupsAddOption("PreserveJobFiles", preserve_job_files,
1757	                               num_settings, &settings);
1758        if (_cups_strcasecmp(max_clients, current_max_clients))
1759	  num_settings = cupsAddOption("MaxClients", max_clients, num_settings,
1760	                               &settings);
1761        if (_cups_strcasecmp(max_jobs, current_max_jobs))
1762	  num_settings = cupsAddOption("MaxJobs", max_jobs, num_settings,
1763	                               &settings);
1764        if (_cups_strcasecmp(max_log_size, current_max_log_size))
1765	  num_settings = cupsAddOption("MaxLogSize", max_log_size, num_settings,
1766	                               &settings);
1767      }
1768
1769      if (!cupsAdminSetServerSettings(http, num_settings, settings))
1770      {
1771        if (cupsLastError() == IPP_NOT_AUTHORIZED)
1772	{
1773	  puts("Status: 401\n");
1774	  exit(0);
1775	}
1776
1777	cgiStartHTML(cgiText(_("Change Settings")));
1778	cgiSetVariable("MESSAGE",
1779                       cgiText(_("Unable to change server settings")));
1780	cgiSetVariable("ERROR", cupsLastErrorString());
1781	cgiCopyTemplateLang("error.tmpl");
1782      }
1783      else
1784      {
1785        if (advanced)
1786	  cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&"
1787	                                 "URL=/admin/?ADVANCEDSETTINGS=YES");
1788        else
1789	  cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
1790	cgiStartHTML(cgiText(_("Change Settings")));
1791	cgiCopyTemplateLang("restart.tmpl");
1792      }
1793    }
1794    else
1795    {
1796     /*
1797      * No changes...
1798      */
1799
1800      cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
1801      cgiStartHTML(cgiText(_("Change Settings")));
1802      cgiCopyTemplateLang("norestart.tmpl");
1803    }
1804
1805    cupsFreeOptions(num_settings, settings);
1806
1807    cgiEndHTML();
1808  }
1809  else if (cgiGetVariable("SAVECHANGES") && cgiGetVariable("CUPSDCONF"))
1810  {
1811   /*
1812    * Save hand-edited config file...
1813    */
1814
1815    http_status_t status;		/* PUT status */
1816    char	tempfile[1024];		/* Temporary new cupsd.conf */
1817    int		tempfd;			/* Temporary file descriptor */
1818    cups_file_t	*temp;			/* Temporary file */
1819    const char	*start,			/* Start of line */
1820		*end;			/* End of line */
1821
1822
1823   /*
1824    * Create a temporary file for the new cupsd.conf file...
1825    */
1826
1827    if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
1828    {
1829      cgiStartHTML(cgiText(_("Edit Configuration File")));
1830      cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file")));
1831      cgiSetVariable("ERROR", strerror(errno));
1832      cgiCopyTemplateLang("error.tmpl");
1833      cgiEndHTML();
1834
1835      perror(tempfile);
1836      return;
1837    }
1838
1839    if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL)
1840    {
1841      cgiStartHTML(cgiText(_("Edit Configuration File")));
1842      cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file")));
1843      cgiSetVariable("ERROR", strerror(errno));
1844      cgiCopyTemplateLang("error.tmpl");
1845      cgiEndHTML();
1846
1847      perror(tempfile);
1848      close(tempfd);
1849      unlink(tempfile);
1850      return;
1851    }
1852
1853   /*
1854    * Copy the cupsd.conf text from the form variable...
1855    */
1856
1857    start = cgiGetVariable("CUPSDCONF");
1858    while (start)
1859    {
1860      if ((end = strstr(start, "\r\n")) == NULL)
1861        if ((end = strstr(start, "\n")) == NULL)
1862	  end = start + strlen(start);
1863
1864      cupsFileWrite(temp, start, (size_t)(end - start));
1865      cupsFilePutChar(temp, '\n');
1866
1867      if (*end == '\r')
1868        start = end + 2;
1869      else if (*end == '\n')
1870        start = end + 1;
1871      else
1872        start = NULL;
1873    }
1874
1875    cupsFileClose(temp);
1876
1877   /*
1878    * Upload the configuration file to the server...
1879    */
1880
1881    status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile);
1882
1883    if (status == HTTP_UNAUTHORIZED)
1884    {
1885      puts("Status: 401\n");
1886      unlink(tempfile);
1887      exit(0);
1888    }
1889    else if (status != HTTP_CREATED)
1890    {
1891      cgiSetVariable("MESSAGE",
1892                     cgiText(_("Unable to upload cupsd.conf file")));
1893      cgiSetVariable("ERROR", httpStatus(status));
1894
1895      cgiStartHTML(cgiText(_("Edit Configuration File")));
1896      cgiCopyTemplateLang("error.tmpl");
1897    }
1898    else
1899    {
1900      cgiSetVariable("refresh_page", "5;URL=/admin/");
1901
1902      cgiStartHTML(cgiText(_("Edit Configuration File")));
1903      cgiCopyTemplateLang("restart.tmpl");
1904    }
1905
1906    cgiEndHTML();
1907
1908    unlink(tempfile);
1909  }
1910  else
1911  {
1912    struct stat	info;			/* cupsd.conf information */
1913    cups_file_t	*cupsd;			/* cupsd.conf file */
1914    char	*buffer,		/* Buffer for entire file */
1915		*bufptr,		/* Pointer into buffer */
1916		*bufend;		/* End of buffer */
1917    int		ch;			/* Character from file */
1918    char	filename[1024];		/* Filename */
1919    const char	*server_root;		/* Location of config files */
1920
1921
1922   /*
1923    * Locate the cupsd.conf file...
1924    */
1925
1926    if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
1927      server_root = CUPS_SERVERROOT;
1928
1929    snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root);
1930
1931   /*
1932    * Figure out the size...
1933    */
1934
1935    if (stat(filename, &info))
1936    {
1937      cgiStartHTML(cgiText(_("Edit Configuration File")));
1938      cgiSetVariable("MESSAGE",
1939                     cgiText(_("Unable to access cupsd.conf file")));
1940      cgiSetVariable("ERROR", strerror(errno));
1941      cgiCopyTemplateLang("error.tmpl");
1942      cgiEndHTML();
1943
1944      perror(filename);
1945      return;
1946    }
1947
1948    if (info.st_size > (1024 * 1024))
1949    {
1950      cgiStartHTML(cgiText(_("Edit Configuration File")));
1951      cgiSetVariable("MESSAGE",
1952                     cgiText(_("Unable to access cupsd.conf file")));
1953      cgiSetVariable("ERROR",
1954                     cgiText(_("Unable to edit cupsd.conf files larger than "
1955		               "1MB")));
1956      cgiCopyTemplateLang("error.tmpl");
1957      cgiEndHTML();
1958
1959      fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename,
1960              (long)info.st_size);
1961      return;
1962    }
1963
1964   /*
1965    * Open the cupsd.conf file...
1966    */
1967
1968    if ((cupsd = cupsFileOpen(filename, "r")) == NULL)
1969    {
1970     /*
1971      * Unable to open - log an error...
1972      */
1973
1974      cgiStartHTML(cgiText(_("Edit Configuration File")));
1975      cgiSetVariable("MESSAGE",
1976                     cgiText(_("Unable to access cupsd.conf file")));
1977      cgiSetVariable("ERROR", strerror(errno));
1978      cgiCopyTemplateLang("error.tmpl");
1979      cgiEndHTML();
1980
1981      perror(filename);
1982      return;
1983    }
1984
1985   /*
1986    * Allocate memory and load the file into a string buffer...
1987    */
1988
1989    if ((buffer = calloc(1, (size_t)info.st_size + 1)) != NULL)
1990    {
1991      cupsFileRead(cupsd, buffer, (size_t)info.st_size);
1992      cgiSetVariable("CUPSDCONF", buffer);
1993      free(buffer);
1994    }
1995
1996    cupsFileClose(cupsd);
1997
1998   /*
1999    * Then get the default cupsd.conf file and put that into a string as
2000    * well...
2001    */
2002
2003    strlcat(filename, ".default", sizeof(filename));
2004
2005    if (!stat(filename, &info) && info.st_size < (1024 * 1024) &&
2006        (cupsd = cupsFileOpen(filename, "r")) != NULL)
2007    {
2008      if ((buffer = calloc(1, 2 * (size_t)info.st_size + 1)) != NULL)
2009      {
2010	bufend = buffer + 2 * info.st_size - 1;
2011
2012	for (bufptr = buffer;
2013	     bufptr < bufend && (ch = cupsFileGetChar(cupsd)) != EOF;)
2014	{
2015	  if (ch == '\\' || ch == '\"')
2016	  {
2017	    *bufptr++ = '\\';
2018	    *bufptr++ = (char)ch;
2019	  }
2020	  else if (ch == '\n')
2021	  {
2022	    *bufptr++ = '\\';
2023	    *bufptr++ = 'n';
2024	  }
2025	  else if (ch == '\t')
2026	  {
2027	    *bufptr++ = '\\';
2028	    *bufptr++ = 't';
2029	  }
2030	  else if (ch >= ' ')
2031	    *bufptr++ = (char)ch;
2032	}
2033
2034	*bufptr = '\0';
2035
2036	cgiSetVariable("CUPSDCONF_DEFAULT", buffer);
2037	free(buffer);
2038      }
2039
2040      cupsFileClose(cupsd);
2041    }
2042
2043   /*
2044    * Show the current config file...
2045    */
2046
2047    cgiStartHTML(cgiText(_("Edit Configuration File")));
2048
2049    cgiCopyTemplateLang("edit-config.tmpl");
2050
2051    cgiEndHTML();
2052  }
2053}
2054
2055
2056/*
2057 * 'do_delete_class()' - Delete a class.
2058 */
2059
2060static void
2061do_delete_class(http_t *http)		/* I - HTTP connection */
2062{
2063  ipp_t		*request;		/* IPP request */
2064  char		uri[HTTP_MAX_URI];	/* Job URI */
2065  const char	*pclass;		/* Printer class name */
2066
2067
2068 /*
2069  * Get form variables...
2070  */
2071
2072  if (cgiGetVariable("CONFIRM") == NULL)
2073  {
2074    cgiStartHTML(cgiText(_("Delete Class")));
2075    cgiCopyTemplateLang("class-confirm.tmpl");
2076    cgiEndHTML();
2077    return;
2078  }
2079
2080  if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL)
2081    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2082                     "localhost", 0, "/classes/%s", pclass);
2083  else
2084  {
2085    cgiStartHTML(cgiText(_("Delete Class")));
2086    cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2087    cgiCopyTemplateLang("error.tmpl");
2088    cgiEndHTML();
2089    return;
2090  }
2091
2092 /*
2093  * Build a CUPS_DELETE_CLASS request, which requires the following
2094  * attributes:
2095  *
2096  *    attributes-charset
2097  *    attributes-natural-language
2098  *    printer-uri
2099  */
2100
2101  request = ippNewRequest(CUPS_DELETE_CLASS);
2102
2103  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2104               NULL, uri);
2105
2106 /*
2107  * Do the request and get back a response...
2108  */
2109
2110  ippDelete(cupsDoRequest(http, request, "/admin/"));
2111
2112 /*
2113  * Show the results...
2114  */
2115
2116  if (cupsLastError() == IPP_NOT_AUTHORIZED)
2117  {
2118    puts("Status: 401\n");
2119    exit(0);
2120  }
2121  else if (cupsLastError() <= IPP_OK_CONFLICT)
2122  {
2123   /*
2124    * Redirect successful updates back to the classes page...
2125    */
2126
2127    cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes");
2128  }
2129
2130  cgiStartHTML(cgiText(_("Delete Class")));
2131
2132  if (cupsLastError() > IPP_OK_CONFLICT)
2133    cgiShowIPPError(_("Unable to delete class"));
2134  else
2135    cgiCopyTemplateLang("class-deleted.tmpl");
2136
2137  cgiEndHTML();
2138}
2139
2140
2141/*
2142 * 'do_delete_printer()' - Delete a printer.
2143 */
2144
2145static void
2146do_delete_printer(http_t *http)		/* I - HTTP connection */
2147{
2148  ipp_t		*request;		/* IPP request */
2149  char		uri[HTTP_MAX_URI];	/* Job URI */
2150  const char	*printer;		/* Printer printer name */
2151
2152
2153 /*
2154  * Get form variables...
2155  */
2156
2157  if (cgiGetVariable("CONFIRM") == NULL)
2158  {
2159    cgiStartHTML(cgiText(_("Delete Printer")));
2160    cgiCopyTemplateLang("printer-confirm.tmpl");
2161    cgiEndHTML();
2162    return;
2163  }
2164
2165  if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
2166    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2167                     "localhost", 0, "/printers/%s", printer);
2168  else
2169  {
2170    cgiStartHTML(cgiText(_("Delete Printer")));
2171    cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2172    cgiCopyTemplateLang("error.tmpl");
2173    cgiEndHTML();
2174    return;
2175  }
2176
2177 /*
2178  * Build a CUPS_DELETE_PRINTER request, which requires the following
2179  * attributes:
2180  *
2181  *    attributes-charset
2182  *    attributes-natural-language
2183  *    printer-uri
2184  */
2185
2186  request = ippNewRequest(CUPS_DELETE_PRINTER);
2187
2188  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2189               NULL, uri);
2190
2191 /*
2192  * Do the request and get back a response...
2193  */
2194
2195  ippDelete(cupsDoRequest(http, request, "/admin/"));
2196
2197 /*
2198  * Show the results...
2199  */
2200
2201  if (cupsLastError() == IPP_NOT_AUTHORIZED)
2202  {
2203    puts("Status: 401\n");
2204    exit(0);
2205  }
2206  else if (cupsLastError() <= IPP_OK_CONFLICT)
2207  {
2208   /*
2209    * Redirect successful updates back to the printers page...
2210    */
2211
2212    cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers");
2213  }
2214
2215  cgiStartHTML(cgiText(_("Delete Printer")));
2216
2217  if (cupsLastError() > IPP_OK_CONFLICT)
2218    cgiShowIPPError(_("Unable to delete printer"));
2219  else
2220    cgiCopyTemplateLang("printer-deleted.tmpl");
2221
2222  cgiEndHTML();
2223}
2224
2225
2226/*
2227 * 'do_export()' - Export printers to Samba.
2228 */
2229
2230static void
2231do_export(http_t *http)			/* I - HTTP connection */
2232{
2233  int		i, j;			/* Looping vars */
2234  ipp_t		*request,		/* IPP request */
2235		*response;		/* IPP response */
2236  const char	*username,		/* Samba username */
2237		*password,		/* Samba password */
2238		*export_all;		/* Export all printers? */
2239  int		export_count,		/* Number of printers to export */
2240		printer_count;		/* Number of available printers */
2241  const char	*name,			/* What name to pull */
2242		*dest;			/* Current destination */
2243  char		ppd[1024];		/* PPD file */
2244
2245
2246 /*
2247  * Get form data...
2248  */
2249
2250  username     = cgiGetVariable("USERNAME");
2251  password     = cgiGetVariable("PASSWORD");
2252  export_all   = cgiGetVariable("EXPORT_ALL");
2253  export_count = cgiGetSize("EXPORT_NAME");
2254
2255 /*
2256  * Get list of available printers...
2257  */
2258
2259  cgiSetSize("PRINTER_NAME", 0);
2260  cgiSetSize("PRINTER_EXPORT", 0);
2261
2262  request = ippNewRequest(CUPS_GET_PRINTERS);
2263
2264  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
2265                "printer-type", 0);
2266
2267  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
2268                "printer-type-mask", CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
2269
2270  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2271               "requested-attributes", NULL, "printer-name");
2272
2273  if ((response = cupsDoRequest(http, request, "/")) != NULL)
2274  {
2275    cgiSetIPPVars(response, NULL, NULL, NULL, 0);
2276    ippDelete(response);
2277
2278    if (!export_all)
2279    {
2280      printer_count = cgiGetSize("PRINTER_NAME");
2281
2282      for (i = 0; i < printer_count; i ++)
2283      {
2284        dest = cgiGetArray("PRINTER_NAME", i);
2285
2286        for (j = 0; j < export_count; j ++)
2287	  if (!_cups_strcasecmp(dest, cgiGetArray("EXPORT_NAME", j)))
2288            break;
2289
2290        cgiSetArray("PRINTER_EXPORT", i, j < export_count ? "Y" : "");
2291      }
2292    }
2293  }
2294
2295 /*
2296  * Export or get the printers to export...
2297  */
2298
2299  if (username && *username && password && *password &&
2300      (export_all || export_count > 0))
2301  {
2302   /*
2303    * Do export...
2304    */
2305
2306    fputs("DEBUG: Export printers...\n", stderr);
2307
2308    if (export_all)
2309    {
2310      name         = "PRINTER_NAME";
2311      export_count = cgiGetSize("PRINTER_NAME");
2312    }
2313    else
2314      name = "EXPORT_NAME";
2315
2316    for (i = 0; i < export_count; i ++)
2317    {
2318      dest = cgiGetArray(name, i);
2319
2320      if (!cupsAdminCreateWindowsPPD(http, dest, ppd, sizeof(ppd)))
2321        break;
2322
2323      j = cupsAdminExportSamba(dest, ppd, "localhost", username, password,
2324                               stderr);
2325
2326      unlink(ppd);
2327
2328      if (!j)
2329        break;
2330    }
2331
2332    if (i < export_count)
2333      cgiSetVariable("ERROR", cupsLastErrorString());
2334    else
2335    {
2336      cgiStartHTML(cgiText(_("Export Printers to Samba")));
2337      cgiCopyTemplateLang("samba-exported.tmpl");
2338      cgiEndHTML();
2339      return;
2340    }
2341  }
2342  else if (username && !*username)
2343    cgiSetVariable("ERROR",
2344                   cgiText(_("A Samba username is required to export "
2345		             "printer drivers")));
2346  else if (username && (!password || !*password))
2347    cgiSetVariable("ERROR",
2348                   cgiText(_("A Samba password is required to export "
2349		             "printer drivers")));
2350
2351 /*
2352  * Show form...
2353  */
2354
2355  cgiStartHTML(cgiText(_("Export Printers to Samba")));
2356  cgiCopyTemplateLang("samba-export.tmpl");
2357  cgiEndHTML();
2358}
2359
2360
2361/*
2362 * 'do_list_printers()' - List available printers.
2363 */
2364
2365static void
2366do_list_printers(http_t *http)		/* I - HTTP connection */
2367{
2368  ipp_t		*request,		/* IPP request */
2369		*response;		/* IPP response */
2370  ipp_attribute_t *attr;		/* IPP attribute */
2371
2372
2373  cgiStartHTML(cgiText(_("List Available Printers")));
2374  fflush(stdout);
2375
2376 /*
2377  * Get the list of printers and their devices...
2378  */
2379
2380  request = ippNewRequest(CUPS_GET_PRINTERS);
2381
2382  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2383               "requested-attributes", NULL, "device-uri");
2384
2385  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type",
2386                CUPS_PRINTER_LOCAL);
2387  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask",
2388                CUPS_PRINTER_LOCAL);
2389
2390  if ((response = cupsDoRequest(http, request, "/")) != NULL)
2391  {
2392   /*
2393    * Got the printer list, now load the devices...
2394    */
2395
2396    int		i;			/* Looping var */
2397    cups_array_t *printer_devices;	/* Printer devices for local printers */
2398    char	*printer_device;	/* Current printer device */
2399
2400
2401   /*
2402    * Allocate an array and copy the device strings...
2403    */
2404
2405    printer_devices = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2406
2407    for (attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI);
2408         attr;
2409	 attr = ippFindNextAttribute(response, "device-uri", IPP_TAG_URI))
2410    {
2411      cupsArrayAdd(printer_devices, _cupsStrAlloc(attr->values[0].string.text));
2412    }
2413
2414   /*
2415    * Free the printer list and get the device list...
2416    */
2417
2418    ippDelete(response);
2419
2420    request = ippNewRequest(CUPS_GET_DEVICES);
2421
2422    if ((response = cupsDoRequest(http, request, "/")) != NULL)
2423    {
2424     /*
2425      * Got the device list, let's parse it...
2426      */
2427
2428      const char *device_uri,		/* device-uri attribute value */
2429		*device_make_and_model,	/* device-make-and-model value */
2430		*device_info;		/* device-info value */
2431
2432
2433      for (i = 0, attr = response->attrs; attr; attr = attr->next)
2434      {
2435       /*
2436        * Skip leading attributes until we hit a device...
2437	*/
2438
2439	while (attr && attr->group_tag != IPP_TAG_PRINTER)
2440          attr = attr->next;
2441
2442	if (!attr)
2443          break;
2444
2445       /*
2446	* Pull the needed attributes from this device...
2447	*/
2448
2449	device_info           = NULL;
2450	device_make_and_model = NULL;
2451	device_uri            = NULL;
2452
2453	while (attr && attr->group_tag == IPP_TAG_PRINTER)
2454	{
2455          if (!strcmp(attr->name, "device-info") &&
2456	      attr->value_tag == IPP_TAG_TEXT)
2457	    device_info = attr->values[0].string.text;
2458
2459          if (!strcmp(attr->name, "device-make-and-model") &&
2460	      attr->value_tag == IPP_TAG_TEXT)
2461	    device_make_and_model = attr->values[0].string.text;
2462
2463          if (!strcmp(attr->name, "device-uri") &&
2464	      attr->value_tag == IPP_TAG_URI)
2465	    device_uri = attr->values[0].string.text;
2466
2467          attr = attr->next;
2468	}
2469
2470       /*
2471	* See if we have everything needed...
2472	*/
2473
2474	if (device_info && device_make_and_model && device_uri &&
2475	    _cups_strcasecmp(device_make_and_model, "unknown") &&
2476	    strchr(device_uri, ':'))
2477	{
2478	 /*
2479	  * Yes, now see if there is already a printer for this
2480	  * device...
2481	  */
2482
2483          if (!cupsArrayFind(printer_devices, (void *)device_uri))
2484          {
2485	   /*
2486	    * Not found, so it must be a new printer...
2487	    */
2488
2489            char	option[1024],	/* Form variables for this device */
2490			*option_ptr;	/* Pointer into string */
2491	    const char	*ptr;		/* Pointer into device string */
2492
2493
2494           /*
2495	    * Format the printer name variable for this device...
2496	    *
2497	    * We use the device-info string first, then device-uri,
2498	    * and finally device-make-and-model to come up with a
2499	    * suitable name.
2500	    */
2501
2502            if (_cups_strncasecmp(device_info, "unknown", 7))
2503	      ptr = device_info;
2504            else if ((ptr = strstr(device_uri, "://")) != NULL)
2505	      ptr += 3;
2506	    else
2507	      ptr = device_make_and_model;
2508
2509	    for (option_ptr = option;
2510	         option_ptr < (option + sizeof(option) - 1) && *ptr;
2511		 ptr ++)
2512	      if (isalnum(*ptr & 255) || *ptr == '_' || *ptr == '-' ||
2513	          *ptr == '.')
2514	        *option_ptr++ = *ptr;
2515	      else if ((*ptr == ' ' || *ptr == '/') && option_ptr > option &&
2516	               option_ptr[-1] != '_')
2517	        *option_ptr++ = '_';
2518	      else if (*ptr == '?' || *ptr == '(')
2519	        break;
2520
2521            *option_ptr = '\0';
2522
2523            cgiSetArray("TEMPLATE_NAME", i, option);
2524
2525           /*
2526	    * Finally, set the form variables for this printer...
2527	    */
2528
2529	    cgiSetArray("device_info", i, device_info);
2530	    cgiSetArray("device_make_and_model", i, device_make_and_model);
2531            cgiSetArray("device_uri", i, device_uri);
2532	    i ++;
2533	  }
2534	}
2535
2536        if (!attr)
2537	  break;
2538      }
2539
2540      ippDelete(response);
2541
2542     /*
2543      * Free the device list...
2544      */
2545
2546      for (printer_device = (char *)cupsArrayFirst(printer_devices);
2547           printer_device;
2548	   printer_device = (char *)cupsArrayNext(printer_devices))
2549        _cupsStrFree(printer_device);
2550
2551      cupsArrayDelete(printer_devices);
2552    }
2553  }
2554
2555 /*
2556  * Finally, show the printer list...
2557  */
2558
2559  cgiCopyTemplateLang("list-available-printers.tmpl");
2560
2561  cgiEndHTML();
2562}
2563
2564
2565/*
2566 * 'do_menu()' - Show the main menu.
2567 */
2568
2569static void
2570do_menu(http_t *http)			/* I - HTTP connection */
2571{
2572  int		num_settings;		/* Number of server settings */
2573  cups_option_t	*settings;		/* Server settings */
2574  const char	*val;			/* Setting value */
2575  char		filename[1024];		/* Temporary filename */
2576  const char	*datadir;		/* Location of data files */
2577  ipp_t		*request,		/* IPP request */
2578		*response;		/* IPP response */
2579
2580
2581 /*
2582  * Get the current server settings...
2583  */
2584
2585  if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
2586  {
2587    cgiSetVariable("SETTINGS_MESSAGE",
2588                   cgiText(_("Unable to open cupsd.conf file:")));
2589    cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString());
2590  }
2591
2592  if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings,
2593                           settings)) != NULL && atoi(val))
2594    cgiSetVariable("DEBUG_LOGGING", "CHECKED");
2595
2596  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings,
2597                           settings)) != NULL && atoi(val))
2598    cgiSetVariable("REMOTE_ADMIN", "CHECKED");
2599
2600  if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings,
2601                           settings)) != NULL && atoi(val))
2602    cgiSetVariable("REMOTE_ANY", "CHECKED");
2603
2604  if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings,
2605                           settings)) != NULL && atoi(val))
2606    cgiSetVariable("SHARE_PRINTERS", "CHECKED");
2607
2608  if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings,
2609                           settings)) != NULL && atoi(val))
2610    cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
2611
2612#ifdef HAVE_GSSAPI
2613  cgiSetVariable("HAVE_GSSAPI", "1");
2614
2615  if ((val = cupsGetOption("DefaultAuthType", num_settings,
2616                           settings)) != NULL && !_cups_strcasecmp(val, "Negotiate"))
2617    cgiSetVariable("KERBEROS", "CHECKED");
2618  else
2619#endif /* HAVE_GSSAPI */
2620  cgiSetVariable("KERBEROS", "");
2621
2622  if ((val = cupsGetOption("BrowseWebIF", num_settings,
2623                           settings)) == NULL)
2624    val = "No";
2625
2626  if (!_cups_strcasecmp(val, "yes") || !_cups_strcasecmp(val, "on") ||
2627      !_cups_strcasecmp(val, "true"))
2628    cgiSetVariable("BROWSE_WEB_IF", "CHECKED");
2629
2630  if ((val = cupsGetOption("PreserveJobHistory", num_settings,
2631                           settings)) == NULL)
2632    val = "Yes";
2633
2634  if (val &&
2635      (!_cups_strcasecmp(val, "0") || !_cups_strcasecmp(val, "no") ||
2636       !_cups_strcasecmp(val, "off") || !_cups_strcasecmp(val, "false") ||
2637       !_cups_strcasecmp(val, "disabled")))
2638  {
2639    cgiSetVariable("PRESERVE_JOB_HISTORY", "0");
2640    cgiSetVariable("PRESERVE_JOB_FILES", "0");
2641  }
2642  else
2643  {
2644    cgiSetVariable("PRESERVE_JOBS", "CHECKED");
2645    cgiSetVariable("PRESERVE_JOB_HISTORY", val);
2646
2647    if ((val = cupsGetOption("PreserveJobFiles", num_settings,
2648			     settings)) == NULL)
2649      val = "1d";
2650
2651    cgiSetVariable("PRESERVE_JOB_FILES", val);
2652
2653  }
2654
2655  if ((val = cupsGetOption("MaxClients", num_settings, settings)) == NULL)
2656    val = "100";
2657
2658  cgiSetVariable("MAX_CLIENTS", val);
2659
2660  if ((val = cupsGetOption("MaxJobs", num_settings, settings)) == NULL)
2661    val = "500";
2662
2663  cgiSetVariable("MAX_JOBS", val);
2664
2665  if ((val = cupsGetOption("MaxLogSize", num_settings, settings)) == NULL)
2666    val = "1m";
2667
2668  cgiSetVariable("MAX_LOG_SIZE", val);
2669
2670  cupsFreeOptions(num_settings, settings);
2671
2672 /*
2673  * See if Samba and the Windows drivers are installed...
2674  */
2675
2676  if ((datadir = getenv("CUPS_DATADIR")) == NULL)
2677    datadir = CUPS_DATADIR;
2678
2679  snprintf(filename, sizeof(filename), "%s/drivers/pscript5.dll", datadir);
2680  if (!access(filename, R_OK))
2681  {
2682   /*
2683    * Found Windows 2000 driver file, see if we have smbclient and
2684    * rpcclient...
2685    */
2686
2687    if (cupsFileFind("smbclient", getenv("PATH"), 1, filename,
2688                     sizeof(filename)) &&
2689        cupsFileFind("rpcclient", getenv("PATH"), 1, filename,
2690	             sizeof(filename)))
2691      cgiSetVariable("HAVE_SAMBA", "Y");
2692    else
2693    {
2694      if (!cupsFileFind("smbclient", getenv("PATH"), 1, filename,
2695                        sizeof(filename)))
2696        fputs("ERROR: smbclient not found!\n", stderr);
2697
2698      if (!cupsFileFind("rpcclient", getenv("PATH"), 1, filename,
2699                        sizeof(filename)))
2700        fputs("ERROR: rpcclient not found!\n", stderr);
2701    }
2702  }
2703  else
2704    perror(filename);
2705
2706 /*
2707  * Subscriptions...
2708  */
2709
2710  request = ippNewRequest(IPP_GET_SUBSCRIPTIONS);
2711
2712  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2713               NULL, "ipp://localhost/");
2714
2715  if ((response = cupsDoRequest(http, request, "/")) != NULL)
2716  {
2717    cgiSetIPPVars(response, NULL, NULL, NULL, 0);
2718    ippDelete(response);
2719  }
2720
2721 /*
2722  * Finally, show the main menu template...
2723  */
2724
2725  cgiStartHTML(cgiText(_("Administration")));
2726
2727  cgiCopyTemplateLang("admin.tmpl");
2728
2729  cgiEndHTML();
2730}
2731
2732
2733/*
2734 * 'do_set_allowed_users()' - Set the allowed/denied users for a queue.
2735 */
2736
2737static void
2738do_set_allowed_users(http_t *http)	/* I - HTTP connection */
2739{
2740  int		i;			/* Looping var */
2741  ipp_t		*request,		/* IPP request */
2742		*response;		/* IPP response */
2743  char		uri[HTTP_MAX_URI];	/* Printer URI */
2744  const char	*printer,		/* Printer name (purge-jobs) */
2745		*is_class,		/* Is a class? */
2746		*users,			/* List of users or groups */
2747		*type;			/* Allow/deny type */
2748  int		num_users;		/* Number of users */
2749  char		*ptr,			/* Pointer into users string */
2750		*end,			/* Pointer to end of users string */
2751		quote;			/* Quote character */
2752  ipp_attribute_t *attr;		/* Attribute */
2753  static const char * const attrs[] =	/* Requested attributes */
2754		{
2755		  "requesting-user-name-allowed",
2756		  "requesting-user-name-denied"
2757		};
2758
2759
2760  is_class = cgiGetVariable("IS_CLASS");
2761  printer  = cgiGetVariable("PRINTER_NAME");
2762
2763  if (!printer)
2764  {
2765    cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2766    cgiStartHTML(cgiText(_("Set Allowed Users")));
2767    cgiCopyTemplateLang("error.tmpl");
2768    cgiEndHTML();
2769    return;
2770  }
2771
2772  users = cgiGetVariable("users");
2773  type  = cgiGetVariable("type");
2774
2775  if (!users || !type ||
2776      (strcmp(type, "requesting-user-name-allowed") &&
2777       strcmp(type, "requesting-user-name-denied")))
2778  {
2779   /*
2780    * Build a Get-Printer-Attributes request, which requires the following
2781    * attributes:
2782    *
2783    *    attributes-charset
2784    *    attributes-natural-language
2785    *    printer-uri
2786    *    requested-attributes
2787    */
2788
2789    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
2790
2791    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2792                     "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2793		     printer);
2794    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2795        	 NULL, uri);
2796
2797    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2798                  "requested-attributes",
2799		  (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
2800
2801   /*
2802    * Do the request and get back a response...
2803    */
2804
2805    if ((response = cupsDoRequest(http, request, "/")) != NULL)
2806    {
2807      cgiSetIPPVars(response, NULL, NULL, NULL, 0);
2808
2809      ippDelete(response);
2810    }
2811
2812    cgiStartHTML(cgiText(_("Set Allowed Users")));
2813
2814    if (cupsLastError() == IPP_NOT_AUTHORIZED)
2815    {
2816      puts("Status: 401\n");
2817      exit(0);
2818    }
2819    else if (cupsLastError() > IPP_OK_CONFLICT)
2820      cgiShowIPPError(_("Unable to get printer attributes"));
2821    else
2822      cgiCopyTemplateLang("users.tmpl");
2823
2824    cgiEndHTML();
2825  }
2826  else
2827  {
2828   /*
2829    * Save the changes...
2830    */
2831
2832    for (num_users = 0, ptr = (char *)users; *ptr; num_users ++)
2833    {
2834     /*
2835      * Skip whitespace and commas...
2836      */
2837
2838      while (*ptr == ',' || isspace(*ptr & 255))
2839	ptr ++;
2840
2841      if (!*ptr)
2842        break;
2843
2844      if (*ptr == '\'' || *ptr == '\"')
2845      {
2846       /*
2847	* Scan quoted name...
2848	*/
2849
2850	quote = *ptr++;
2851
2852	for (end = ptr; *end; end ++)
2853	  if (*end == quote)
2854	    break;
2855      }
2856      else
2857      {
2858       /*
2859	* Scan space or comma-delimited name...
2860	*/
2861
2862        for (end = ptr; *end; end ++)
2863	  if (isspace(*end & 255) || *end == ',')
2864	    break;
2865      }
2866
2867     /*
2868      * Advance to the next name...
2869      */
2870
2871      ptr = end;
2872    }
2873
2874   /*
2875    * Build a CUPS-Add-Printer/Class request, which requires the following
2876    * attributes:
2877    *
2878    *    attributes-charset
2879    *    attributes-natural-language
2880    *    printer-uri
2881    *    requesting-user-name-{allowed,denied}
2882    */
2883
2884    request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
2885
2886    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2887                     "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2888		     printer);
2889    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2890        	 NULL, uri);
2891
2892    if (num_users == 0)
2893      ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2894                   "requesting-user-name-allowed", NULL, "all");
2895    else
2896    {
2897      attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2898                           type, num_users, NULL, NULL);
2899
2900      for (i = 0, ptr = (char *)users; *ptr; i ++)
2901      {
2902       /*
2903        * Skip whitespace and commas...
2904	*/
2905
2906        while (*ptr == ',' || isspace(*ptr & 255))
2907	  ptr ++;
2908
2909        if (!*ptr)
2910	  break;
2911
2912        if (*ptr == '\'' || *ptr == '\"')
2913	{
2914	 /*
2915	  * Scan quoted name...
2916	  */
2917
2918	  quote = *ptr++;
2919
2920	  for (end = ptr; *end; end ++)
2921	    if (*end == quote)
2922	      break;
2923	}
2924	else
2925	{
2926	 /*
2927	  * Scan space or comma-delimited name...
2928	  */
2929
2930          for (end = ptr; *end; end ++)
2931	    if (isspace(*end & 255) || *end == ',')
2932	      break;
2933        }
2934
2935       /*
2936        * Terminate the name...
2937	*/
2938
2939        if (*end)
2940          *end++ = '\0';
2941
2942       /*
2943        * Add the name...
2944	*/
2945
2946        attr->values[i].string.text = _cupsStrAlloc(ptr);
2947
2948       /*
2949        * Advance to the next name...
2950	*/
2951
2952        ptr = end;
2953      }
2954    }
2955
2956   /*
2957    * Do the request and get back a response...
2958    */
2959
2960    ippDelete(cupsDoRequest(http, request, "/admin/"));
2961
2962    if (cupsLastError() == IPP_NOT_AUTHORIZED)
2963    {
2964      puts("Status: 401\n");
2965      exit(0);
2966    }
2967    else if (cupsLastError() > IPP_OK_CONFLICT)
2968    {
2969      cgiStartHTML(cgiText(_("Set Allowed Users")));
2970      cgiShowIPPError(_("Unable to change printer"));
2971    }
2972    else
2973    {
2974     /*
2975      * Redirect successful updates back to the printer page...
2976      */
2977
2978      char	url[1024],		/* Printer/class URL */
2979		refresh[1024];		/* Refresh URL */
2980
2981
2982      cgiRewriteURL(uri, url, sizeof(url), NULL);
2983      cgiFormEncode(uri, url, sizeof(uri));
2984      snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s",
2985               uri);
2986      cgiSetVariable("refresh_page", refresh);
2987
2988      cgiStartHTML(cgiText(_("Set Allowed Users")));
2989
2990      cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
2991                                     "printer-modified.tmpl");
2992    }
2993
2994    cgiEndHTML();
2995  }
2996}
2997
2998
2999/*
3000 * 'do_set_default()' - Set the server default printer/class.
3001 */
3002
3003static void
3004do_set_default(http_t *http)		/* I - HTTP connection */
3005{
3006  const char	*title;			/* Page title */
3007  ipp_t		*request;		/* IPP request */
3008  char		uri[HTTP_MAX_URI];	/* Printer URI */
3009  const char	*printer,		/* Printer name (purge-jobs) */
3010		*is_class;		/* Is a class? */
3011
3012
3013  is_class = cgiGetVariable("IS_CLASS");
3014  printer  = cgiGetVariable("PRINTER_NAME");
3015  title    = cgiText(_("Set As Server Default"));
3016
3017  if (!printer)
3018  {
3019    cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
3020    cgiStartHTML(title);
3021    cgiCopyTemplateLang("error.tmpl");
3022    cgiEndHTML();
3023    return;
3024  }
3025
3026 /*
3027  * Build a printer request, which requires the following
3028  * attributes:
3029  *
3030  *    attributes-charset
3031  *    attributes-natural-language
3032  *    printer-uri
3033  */
3034
3035  request = ippNewRequest(CUPS_SET_DEFAULT);
3036
3037  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3038                   "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
3039		   printer);
3040  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3041               NULL, uri);
3042
3043 /*
3044  * Do the request and get back a response...
3045  */
3046
3047  ippDelete(cupsDoRequest(http, request, "/admin/"));
3048
3049  if (cupsLastError() == IPP_NOT_AUTHORIZED)
3050  {
3051    puts("Status: 401\n");
3052    exit(0);
3053  }
3054  else if (cupsLastError() > IPP_OK_CONFLICT)
3055  {
3056    cgiStartHTML(title);
3057    cgiShowIPPError(_("Unable to set server default"));
3058  }
3059  else
3060  {
3061   /*
3062    * Redirect successful updates back to the printer page...
3063    */
3064
3065    char	url[1024],		/* Printer/class URL */
3066		refresh[1024];		/* Refresh URL */
3067
3068
3069    cgiRewriteURL(uri, url, sizeof(url), NULL);
3070    cgiFormEncode(uri, url, sizeof(uri));
3071    snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri);
3072    cgiSetVariable("refresh_page", refresh);
3073
3074    cgiStartHTML(title);
3075    cgiCopyTemplateLang("printer-default.tmpl");
3076  }
3077
3078  cgiEndHTML();
3079}
3080
3081
3082/*
3083 * 'do_set_options()' - Configure the default options for a queue.
3084 */
3085
3086static void
3087do_set_options(http_t *http,		/* I - HTTP connection */
3088               int    is_class)		/* I - Set options for class? */
3089{
3090  int		i, j, k, m;		/* Looping vars */
3091  int		have_options;		/* Have options? */
3092  ipp_t		*request,		/* IPP request */
3093		*response;		/* IPP response */
3094  ipp_attribute_t *attr;		/* IPP attribute */
3095  char		uri[HTTP_MAX_URI];	/* Job URI */
3096  const char	*var;			/* Variable value */
3097  const char	*printer;		/* Printer printer name */
3098  const char	*filename;		/* PPD filename */
3099  char		tempfile[1024];		/* Temporary filename */
3100  cups_file_t	*in,			/* Input file */
3101		*out;			/* Output file */
3102  char		line[1024],		/* Line from PPD file */
3103		value[1024],		/* Option value */
3104		keyword[1024],		/* Keyword from Default line */
3105		*keyptr;		/* Pointer into keyword... */
3106  ppd_file_t	*ppd;			/* PPD file */
3107  ppd_group_t	*group;			/* Option group */
3108  ppd_option_t	*option;		/* Option */
3109  ppd_coption_t	*coption;		/* Custom option */
3110  ppd_cparam_t	*cparam;		/* Custom parameter */
3111  ppd_attr_t	*ppdattr;		/* PPD attribute */
3112  const char	*title;			/* Page title */
3113
3114
3115  title = cgiText(is_class ? _("Set Class Options") : _("Set Printer Options"));
3116
3117  fprintf(stderr, "DEBUG: do_set_options(http=%p, is_class=%d)\n", http,
3118          is_class);
3119
3120 /*
3121  * Get the printer name...
3122  */
3123
3124  if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
3125    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3126                     "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
3127		     printer);
3128  else
3129  {
3130    cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
3131    cgiStartHTML(title);
3132    cgiCopyTemplateLang("error.tmpl");
3133    cgiEndHTML();
3134    return;
3135  }
3136
3137  fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri);
3138
3139 /*
3140  * If the user clicks on the Auto-Configure button, send an AutoConfigure
3141  * command file to the printer...
3142  */
3143
3144  if (cgiGetVariable("AUTOCONFIGURE"))
3145  {
3146    cgiPrintCommand(http, printer, "AutoConfigure", "Set Default Options");
3147    return;
3148  }
3149
3150 /*
3151  * Get the PPD file...
3152  */
3153
3154  if (is_class)
3155    filename = NULL;
3156  else
3157    filename = cupsGetPPD2(http, printer);
3158
3159  if (filename)
3160  {
3161    fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename);
3162
3163    if ((ppd = ppdOpenFile(filename)) == NULL)
3164    {
3165      cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i)));
3166      cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file")));
3167      cgiStartHTML(title);
3168      cgiCopyTemplateLang("error.tmpl");
3169      cgiEndHTML();
3170      return;
3171    }
3172  }
3173  else
3174  {
3175    fputs("DEBUG: No PPD file\n", stderr);
3176    ppd = NULL;
3177  }
3178
3179  if (cgiGetVariable("job_sheets_start") != NULL ||
3180      cgiGetVariable("job_sheets_end") != NULL)
3181    have_options = 1;
3182  else
3183    have_options = 0;
3184
3185  if (ppd)
3186  {
3187    ppdMarkDefaults(ppd);
3188
3189    for (option = ppdFirstOption(ppd);
3190         option;
3191	 option = ppdNextOption(ppd))
3192    {
3193      if ((var = cgiGetVariable(option->keyword)) != NULL)
3194      {
3195	have_options = 1;
3196	ppdMarkOption(ppd, option->keyword, var);
3197	fprintf(stderr, "DEBUG: Set %s to %s...\n", option->keyword, var);
3198      }
3199      else
3200        fprintf(stderr, "DEBUG: Didn't find %s...\n", option->keyword);
3201    }
3202  }
3203
3204  if (!have_options || ppdConflicts(ppd))
3205  {
3206   /*
3207    * Show the options to the user...
3208    */
3209
3210    fputs("DEBUG: Showing options...\n", stderr);
3211
3212   /*
3213    * Show auto-configure button if supported...
3214    */
3215
3216    if (ppd)
3217    {
3218      if (ppd->num_filters == 0 ||
3219          ((ppdattr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL &&
3220           ppdattr->value && strstr(ppdattr->value, "AutoConfigure")))
3221        cgiSetVariable("HAVE_AUTOCONFIGURE", "YES");
3222      else
3223      {
3224        for (i = 0; i < ppd->num_filters; i ++)
3225	  if (!strncmp(ppd->filters[i], "application/vnd.cups-postscript", 31))
3226	  {
3227	    cgiSetVariable("HAVE_AUTOCONFIGURE", "YES");
3228	    break;
3229	  }
3230      }
3231    }
3232
3233   /*
3234    * Get the printer attributes...
3235    */
3236
3237    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
3238
3239    httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3240                     "localhost", 0, "/printers/%s", printer);
3241    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3242                 NULL, uri);
3243
3244    response = cupsDoRequest(http, request, "/");
3245
3246   /*
3247    * List the groups used as "tabs"...
3248    */
3249
3250    i = 0;
3251
3252    if (ppd)
3253    {
3254      for (group = ppd->groups;
3255	   i < ppd->num_groups;
3256	   i ++, group ++)
3257      {
3258        cgiSetArray("GROUP_ID", i, group->name);
3259
3260	if (!strcmp(group->name, "InstallableOptions"))
3261	  cgiSetArray("GROUP", i, cgiText(_("Options Installed")));
3262	else
3263	  cgiSetArray("GROUP", i, group->text);
3264      }
3265    }
3266
3267    if (ippFindAttribute(response, "job-sheets-supported", IPP_TAG_ZERO))
3268    {
3269      cgiSetArray("GROUP_ID", i, "CUPS_BANNERS");
3270      cgiSetArray("GROUP", i ++, cgiText(_("Banners")));
3271    }
3272
3273    if (ippFindAttribute(response, "printer-error-policy-supported",
3274			 IPP_TAG_ZERO) ||
3275	ippFindAttribute(response, "printer-op-policy-supported",
3276			 IPP_TAG_ZERO))
3277    {
3278      cgiSetArray("GROUP_ID", i, "CUPS_POLICIES");
3279      cgiSetArray("GROUP", i ++, cgiText(_("Policies")));
3280    }
3281
3282    if ((attr = ippFindAttribute(response, "port-monitor-supported",
3283                                 IPP_TAG_NAME)) != NULL && attr->num_values > 1)
3284    {
3285      cgiSetArray("GROUP_ID", i, "CUPS_PORT_MONITOR");
3286      cgiSetArray("GROUP", i, cgiText(_("Port Monitor")));
3287    }
3288
3289    cgiStartHTML(cgiText(_("Set Printer Options")));
3290    cgiCopyTemplateLang("set-printer-options-header.tmpl");
3291
3292    if (ppd)
3293    {
3294      ppdLocalize(ppd);
3295
3296      if (ppdConflicts(ppd))
3297      {
3298	for (i = ppd->num_groups, k = 0, group = ppd->groups;
3299	     i > 0;
3300	     i --, group ++)
3301	  for (j = group->num_options, option = group->options;
3302	       j > 0;
3303	       j --, option ++)
3304	    if (option->conflicted)
3305	    {
3306	      cgiSetArray("ckeyword", k, option->keyword);
3307	      cgiSetArray("ckeytext", k, option->text);
3308
3309	      for (m = 0; m < option->num_choices; m ++)
3310	      {
3311	        if (option->choices[m].marked)
3312	        {
3313	          cgiSetArray("cchoice", k, option->choices[m].text);
3314	          break;
3315	        }
3316              }
3317
3318	      k ++;
3319	    }
3320
3321	cgiCopyTemplateLang("option-conflict.tmpl");
3322      }
3323
3324      for (i = ppd->num_groups, group = ppd->groups;
3325	   i > 0;
3326	   i --, group ++)
3327      {
3328	for (j = group->num_options, option = group->options;
3329	     j > 0;
3330	     j --, option ++)
3331	{
3332	  if (!strcmp(option->keyword, "PageRegion"))
3333	    continue;
3334
3335	  if (option->num_choices > 1)
3336	    break;
3337	}
3338
3339        if (j == 0)
3340	  continue;
3341
3342        cgiSetVariable("GROUP_ID", group->name);
3343
3344	if (!strcmp(group->name, "InstallableOptions"))
3345	  cgiSetVariable("GROUP", cgiText(_("Options Installed")));
3346	else
3347	  cgiSetVariable("GROUP", group->text);
3348
3349	cgiCopyTemplateLang("option-header.tmpl");
3350
3351	for (j = group->num_options, option = group->options;
3352	     j > 0;
3353	     j --, option ++)
3354	{
3355	  if (!strcmp(option->keyword, "PageRegion") || option->num_choices < 2)
3356	    continue;
3357
3358	  cgiSetVariable("KEYWORD", option->keyword);
3359	  cgiSetVariable("KEYTEXT", option->text);
3360
3361	  if (option->conflicted)
3362	    cgiSetVariable("CONFLICTED", "1");
3363	  else
3364	    cgiSetVariable("CONFLICTED", "0");
3365
3366	  cgiSetSize("CHOICES", 0);
3367	  cgiSetSize("TEXT", 0);
3368	  for (k = 0, m = 0; k < option->num_choices; k ++)
3369	  {
3370	    cgiSetArray("CHOICES", m, option->choices[k].choice);
3371	    cgiSetArray("TEXT", m, option->choices[k].text);
3372
3373	    m ++;
3374
3375	    if (option->choices[k].marked)
3376	      cgiSetVariable("DEFCHOICE", option->choices[k].choice);
3377	  }
3378
3379	  cgiSetSize("PARAMS", 0);
3380	  cgiSetSize("PARAMTEXT", 0);
3381	  cgiSetSize("PARAMVALUE", 0);
3382	  cgiSetSize("INPUTTYPE", 0);
3383
3384	  if ((coption = ppdFindCustomOption(ppd, option->keyword)))
3385	  {
3386            const char *units = NULL;	/* Units value, if any */
3387
3388	    cgiSetVariable("ISCUSTOM", "1");
3389
3390	    for (cparam = ppdFirstCustomParam(coption), m = 0;
3391		 cparam;
3392		 cparam = ppdNextCustomParam(coption), m ++)
3393	    {
3394	      if (!_cups_strcasecmp(option->keyword, "PageSize") &&
3395	          _cups_strcasecmp(cparam->name, "Width") &&
3396		  _cups_strcasecmp(cparam->name, "Height"))
3397              {
3398	        m --;
3399		continue;
3400              }
3401
3402	      cgiSetArray("PARAMS", m, cparam->name);
3403	      cgiSetArray("PARAMTEXT", m, cparam->text);
3404	      cgiSetArray("INPUTTYPE", m, "text");
3405
3406	      switch (cparam->type)
3407	      {
3408		case PPD_CUSTOM_POINTS :
3409		    if (!_cups_strncasecmp(option->defchoice, "Custom.", 7))
3410		    {
3411		      units = option->defchoice + strlen(option->defchoice) - 2;
3412
3413		      if (strcmp(units, "mm") && strcmp(units, "cm") &&
3414		          strcmp(units, "in") && strcmp(units, "ft"))
3415		      {
3416		        if (units[1] == 'm')
3417			  units ++;
3418			else
3419			  units = "pt";
3420		      }
3421		    }
3422		    else
3423		      units = "pt";
3424
3425                    if (!strcmp(units, "mm"))
3426		      snprintf(value, sizeof(value), "%g",
3427		               cparam->current.custom_points / 72.0 * 25.4);
3428                    else if (!strcmp(units, "cm"))
3429		      snprintf(value, sizeof(value), "%g",
3430		               cparam->current.custom_points / 72.0 * 2.54);
3431                    else if (!strcmp(units, "in"))
3432		      snprintf(value, sizeof(value), "%g",
3433		               cparam->current.custom_points / 72.0);
3434                    else if (!strcmp(units, "ft"))
3435		      snprintf(value, sizeof(value), "%g",
3436		               cparam->current.custom_points / 72.0 / 12.0);
3437                    else if (!strcmp(units, "m"))
3438		      snprintf(value, sizeof(value), "%g",
3439		               cparam->current.custom_points / 72.0 * 0.0254);
3440                    else
3441		      snprintf(value, sizeof(value), "%g",
3442		               cparam->current.custom_points);
3443		    cgiSetArray("PARAMVALUE", m, value);
3444		    break;
3445
3446		case PPD_CUSTOM_CURVE :
3447		case PPD_CUSTOM_INVCURVE :
3448		case PPD_CUSTOM_REAL :
3449		    snprintf(value, sizeof(value), "%g",
3450		             cparam->current.custom_real);
3451		    cgiSetArray("PARAMVALUE", m, value);
3452		    break;
3453
3454		case PPD_CUSTOM_INT:
3455		    snprintf(value, sizeof(value), "%d",
3456		             cparam->current.custom_int);
3457		    cgiSetArray("PARAMVALUE", m, value);
3458		    break;
3459
3460		case PPD_CUSTOM_PASSCODE:
3461		case PPD_CUSTOM_PASSWORD:
3462		    if (cparam->current.custom_password)
3463		      cgiSetArray("PARAMVALUE", m,
3464		                  cparam->current.custom_password);
3465		    else
3466		      cgiSetArray("PARAMVALUE", m, "");
3467		    cgiSetArray("INPUTTYPE", m, "password");
3468		    break;
3469
3470		case PPD_CUSTOM_STRING:
3471		    if (cparam->current.custom_string)
3472		      cgiSetArray("PARAMVALUE", m,
3473		                  cparam->current.custom_string);
3474		    else
3475		      cgiSetArray("PARAMVALUE", m, "");
3476		    break;
3477	      }
3478	    }
3479
3480            if (units)
3481	    {
3482	      cgiSetArray("PARAMS", m, "Units");
3483	      cgiSetArray("PARAMTEXT", m, cgiText(_("Units")));
3484	      cgiSetArray("PARAMVALUE", m, units);
3485	    }
3486	  }
3487	  else
3488	    cgiSetVariable("ISCUSTOM", "0");
3489
3490	  switch (option->ui)
3491	  {
3492	    case PPD_UI_BOOLEAN :
3493		cgiCopyTemplateLang("option-boolean.tmpl");
3494		break;
3495	    case PPD_UI_PICKONE :
3496		cgiCopyTemplateLang("option-pickone.tmpl");
3497		break;
3498	    case PPD_UI_PICKMANY :
3499		cgiCopyTemplateLang("option-pickmany.tmpl");
3500		break;
3501	  }
3502	}
3503
3504	cgiCopyTemplateLang("option-trailer.tmpl");
3505      }
3506    }
3507
3508    if ((attr = ippFindAttribute(response, "job-sheets-supported",
3509				 IPP_TAG_ZERO)) != NULL)
3510    {
3511     /*
3512      * Add the job sheets options...
3513      */
3514
3515      cgiSetVariable("GROUP_ID", "CUPS_BANNERS");
3516      cgiSetVariable("GROUP", cgiText(_("Banners")));
3517      cgiCopyTemplateLang("option-header.tmpl");
3518
3519      cgiSetSize("CHOICES", attr->num_values);
3520      cgiSetSize("TEXT", attr->num_values);
3521      for (k = 0; k < attr->num_values; k ++)
3522      {
3523	cgiSetArray("CHOICES", k, attr->values[k].string.text);
3524	cgiSetArray("TEXT", k, attr->values[k].string.text);
3525      }
3526
3527      attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO);
3528
3529      cgiSetVariable("KEYWORD", "job_sheets_start");
3530      cgiSetVariable("KEYTEXT",
3531                     /* TRANSLATORS: Banner/cover sheet before the print job. */
3532                     cgiText(_("Starting Banner")));
3533      cgiSetVariable("DEFCHOICE", attr != NULL ?
3534				  attr->values[0].string.text : "");
3535
3536      cgiCopyTemplateLang("option-pickone.tmpl");
3537
3538      cgiSetVariable("KEYWORD", "job_sheets_end");
3539      cgiSetVariable("KEYTEXT",
3540                     /* TRANSLATORS: Banner/cover sheet after the print job. */
3541                     cgiText(_("Ending Banner")));
3542      cgiSetVariable("DEFCHOICE", attr != NULL && attr->num_values > 1 ?
3543				  attr->values[1].string.text : "");
3544
3545      cgiCopyTemplateLang("option-pickone.tmpl");
3546
3547      cgiCopyTemplateLang("option-trailer.tmpl");
3548    }
3549
3550    if (ippFindAttribute(response, "printer-error-policy-supported",
3551			 IPP_TAG_ZERO) ||
3552	ippFindAttribute(response, "printer-op-policy-supported",
3553			 IPP_TAG_ZERO))
3554    {
3555     /*
3556      * Add the error and operation policy options...
3557      */
3558
3559      cgiSetVariable("GROUP_ID", "CUPS_POLICIES");
3560      cgiSetVariable("GROUP", cgiText(_("Policies")));
3561      cgiCopyTemplateLang("option-header.tmpl");
3562
3563     /*
3564      * Error policy...
3565      */
3566
3567      attr = ippFindAttribute(response, "printer-error-policy-supported",
3568			      IPP_TAG_ZERO);
3569
3570      if (attr)
3571      {
3572	cgiSetSize("CHOICES", attr->num_values);
3573	cgiSetSize("TEXT", attr->num_values);
3574	for (k = 0; k < attr->num_values; k ++)
3575	{
3576	  cgiSetArray("CHOICES", k, attr->values[k].string.text);
3577	  cgiSetArray("TEXT", k, attr->values[k].string.text);
3578	}
3579
3580	attr = ippFindAttribute(response, "printer-error-policy",
3581				IPP_TAG_ZERO);
3582
3583	cgiSetVariable("KEYWORD", "printer_error_policy");
3584	cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
3585	cgiSetVariable("DEFCHOICE", attr == NULL ?
3586				    "" : attr->values[0].string.text);
3587      }
3588
3589      cgiCopyTemplateLang("option-pickone.tmpl");
3590
3591     /*
3592      * Operation policy...
3593      */
3594
3595      attr = ippFindAttribute(response, "printer-op-policy-supported",
3596			      IPP_TAG_ZERO);
3597
3598      if (attr)
3599      {
3600	cgiSetSize("CHOICES", attr->num_values);
3601	cgiSetSize("TEXT", attr->num_values);
3602	for (k = 0; k < attr->num_values; k ++)
3603	{
3604	  cgiSetArray("CHOICES", k, attr->values[k].string.text);
3605	  cgiSetArray("TEXT", k, attr->values[k].string.text);
3606	}
3607
3608	attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO);
3609
3610	cgiSetVariable("KEYWORD", "printer_op_policy");
3611	cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy")));
3612	cgiSetVariable("DEFCHOICE", attr == NULL ?
3613				    "" : attr->values[0].string.text);
3614
3615	cgiCopyTemplateLang("option-pickone.tmpl");
3616      }
3617
3618      cgiCopyTemplateLang("option-trailer.tmpl");
3619    }
3620
3621   /*
3622    * Binary protocol support...
3623    */
3624
3625    if ((attr = ippFindAttribute(response, "port-monitor-supported",
3626                                 IPP_TAG_NAME)) != NULL && attr->num_values > 1)
3627    {
3628      cgiSetVariable("GROUP_ID", "CUPS_PORT_MONITOR");
3629      cgiSetVariable("GROUP", cgiText(_("Port Monitor")));
3630
3631      cgiSetSize("CHOICES", attr->num_values);
3632      cgiSetSize("TEXT", attr->num_values);
3633
3634      for (i = 0; i < attr->num_values; i ++)
3635      {
3636        cgiSetArray("CHOICES", i, attr->values[i].string.text);
3637        cgiSetArray("TEXT", i, attr->values[i].string.text);
3638      }
3639
3640      attr = ippFindAttribute(response, "port-monitor", IPP_TAG_NAME);
3641      cgiSetVariable("KEYWORD", "port_monitor");
3642      cgiSetVariable("KEYTEXT", cgiText(_("Port Monitor")));
3643      cgiSetVariable("DEFCHOICE", attr ? attr->values[0].string.text : "none");
3644
3645      cgiCopyTemplateLang("option-header.tmpl");
3646      cgiCopyTemplateLang("option-pickone.tmpl");
3647      cgiCopyTemplateLang("option-trailer.tmpl");
3648    }
3649
3650    cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
3651    cgiEndHTML();
3652
3653    ippDelete(response);
3654  }
3655  else
3656  {
3657   /*
3658    * Set default options...
3659    */
3660
3661    fputs("DEBUG: Setting options...\n", stderr);
3662
3663    if (filename)
3664    {
3665      out = cupsTempFile2(tempfile, sizeof(tempfile));
3666      in  = cupsFileOpen(filename, "r");
3667
3668      if (!in || !out)
3669      {
3670	cgiSetVariable("ERROR", strerror(errno));
3671	cgiStartHTML(cgiText(_("Set Printer Options")));
3672	cgiCopyTemplateLang("error.tmpl");
3673	cgiEndHTML();
3674
3675	if (in)
3676	  cupsFileClose(in);
3677
3678	if (out)
3679	{
3680	  cupsFileClose(out);
3681	  unlink(tempfile);
3682	}
3683
3684	unlink(filename);
3685	return;
3686      }
3687
3688      while (cupsFileGets(in, line, sizeof(line)))
3689      {
3690	if (!strncmp(line, "*cupsProtocol:", 14))
3691	  continue;
3692	else if (strncmp(line, "*Default", 8))
3693	  cupsFilePrintf(out, "%s\n", line);
3694	else
3695	{
3696	 /*
3697	  * Get default option name...
3698	  */
3699
3700	  strlcpy(keyword, line + 8, sizeof(keyword));
3701
3702	  for (keyptr = keyword; *keyptr; keyptr ++)
3703	    if (*keyptr == ':' || isspace(*keyptr & 255))
3704	      break;
3705
3706	  *keyptr = '\0';
3707
3708	  if (!strcmp(keyword, "PageRegion") ||
3709	      !strcmp(keyword, "PaperDimension") ||
3710	      !strcmp(keyword, "ImageableArea"))
3711	    var = get_option_value(ppd, "PageSize", value, sizeof(value));
3712	  else
3713	    var = get_option_value(ppd, keyword, value, sizeof(value));
3714
3715	  if (!var)
3716	    cupsFilePrintf(out, "%s\n", line);
3717	  else
3718	    cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
3719	}
3720      }
3721
3722      cupsFileClose(in);
3723      cupsFileClose(out);
3724    }
3725    else
3726    {
3727     /*
3728      * Make sure temporary filename is cleared when there is no PPD...
3729      */
3730
3731      tempfile[0] = '\0';
3732    }
3733
3734   /*
3735    * Build a CUPS_ADD_MODIFY_CLASS/PRINTER request, which requires the
3736    * following attributes:
3737    *
3738    *    attributes-charset
3739    *    attributes-natural-language
3740    *    printer-uri
3741    *    job-sheets-default
3742    *    printer-error-policy
3743    *    printer-op-policy
3744    *    [ppd file]
3745    */
3746
3747    request = ippNewRequest(is_class ? CUPS_ADD_MODIFY_CLASS :
3748                                       CUPS_ADD_MODIFY_PRINTER);
3749
3750    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3751                 NULL, uri);
3752
3753    attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3754                         "job-sheets-default", 2, NULL, NULL);
3755    attr->values[0].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_start"));
3756    attr->values[1].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_end"));
3757
3758    if ((var = cgiGetVariable("printer_error_policy")) != NULL)
3759      ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3760		   "printer-error-policy", NULL, var);
3761
3762    if ((var = cgiGetVariable("printer_op_policy")) != NULL)
3763      ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3764		   "printer-op-policy", NULL, var);
3765
3766    if ((var = cgiGetVariable("port_monitor")) != NULL)
3767      ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3768		   "port-monitor", NULL, var);
3769
3770   /*
3771    * Do the request and get back a response...
3772    */
3773
3774    if (filename)
3775      ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile));
3776    else
3777      ippDelete(cupsDoRequest(http, request, "/admin/"));
3778
3779    if (cupsLastError() == IPP_NOT_AUTHORIZED)
3780    {
3781      puts("Status: 401\n");
3782      exit(0);
3783    }
3784    else if (cupsLastError() > IPP_OK_CONFLICT)
3785    {
3786      cgiStartHTML(title);
3787      cgiShowIPPError(_("Unable to set options"));
3788    }
3789    else
3790    {
3791     /*
3792      * Redirect successful updates back to the printer page...
3793      */
3794
3795      char	refresh[1024];		/* Refresh URL */
3796
3797
3798      cgiFormEncode(uri, printer, sizeof(uri));
3799      snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/%s/%s",
3800	       is_class ? "classes" : "printers", uri);
3801      cgiSetVariable("refresh_page", refresh);
3802
3803      cgiStartHTML(title);
3804
3805      cgiCopyTemplateLang("printer-configured.tmpl");
3806    }
3807
3808    cgiEndHTML();
3809
3810    if (filename)
3811      unlink(tempfile);
3812  }
3813
3814  if (filename)
3815    unlink(filename);
3816}
3817
3818
3819/*
3820 * 'do_set_sharing()' - Set printer-is-shared value.
3821 */
3822
3823static void
3824do_set_sharing(http_t *http)		/* I - HTTP connection */
3825{
3826  ipp_t		*request,		/* IPP request */
3827		*response;		/* IPP response */
3828  char		uri[HTTP_MAX_URI];	/* Printer URI */
3829  const char	*printer,		/* Printer name */
3830		*is_class,		/* Is a class? */
3831		*shared;		/* Sharing value */
3832
3833
3834  is_class = cgiGetVariable("IS_CLASS");
3835  printer  = cgiGetVariable("PRINTER_NAME");
3836  shared   = cgiGetVariable("SHARED");
3837
3838  if (!printer || !shared)
3839  {
3840    cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
3841    cgiStartHTML(cgiText(_("Set Publishing")));
3842    cgiCopyTemplateLang("error.tmpl");
3843    cgiEndHTML();
3844    return;
3845  }
3846
3847 /*
3848  * Build a CUPS-Add-Printer/CUPS-Add-Class request, which requires the
3849  * following attributes:
3850  *
3851  *    attributes-charset
3852  *    attributes-natural-language
3853  *    printer-uri
3854  *    printer-is-shared
3855  */
3856
3857  request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
3858
3859  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3860                   "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
3861		   printer);
3862  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3863               NULL, uri);
3864
3865  ippAddBoolean(request, IPP_TAG_OPERATION, "printer-is-shared", (char)atoi(shared));
3866
3867 /*
3868  * Do the request and get back a response...
3869  */
3870
3871  if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
3872  {
3873    cgiSetIPPVars(response, NULL, NULL, NULL, 0);
3874
3875    ippDelete(response);
3876  }
3877
3878  if (cupsLastError() == IPP_NOT_AUTHORIZED)
3879  {
3880    puts("Status: 401\n");
3881    exit(0);
3882  }
3883  else if (cupsLastError() > IPP_OK_CONFLICT)
3884  {
3885    cgiStartHTML(cgiText(_("Set Publishing")));
3886    cgiShowIPPError(_("Unable to change printer-is-shared attribute"));
3887  }
3888  else
3889  {
3890   /*
3891    * Redirect successful updates back to the printer page...
3892    */
3893
3894    char	url[1024],		/* Printer/class URL */
3895		refresh[1024];		/* Refresh URL */
3896
3897
3898    cgiRewriteURL(uri, url, sizeof(url), NULL);
3899    cgiFormEncode(uri, url, sizeof(uri));
3900    snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri);
3901    cgiSetVariable("refresh_page", refresh);
3902
3903    cgiStartHTML(cgiText(_("Set Publishing")));
3904    cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
3905                                   "printer-modified.tmpl");
3906  }
3907
3908  cgiEndHTML();
3909}
3910
3911
3912/*
3913 * 'get_option_value()' - Return the value of an option.
3914 *
3915 * This function also handles generation of custom option values.
3916 */
3917
3918static char *				/* O - Value string or NULL on error */
3919get_option_value(
3920    ppd_file_t    *ppd,			/* I - PPD file */
3921    const char    *name,		/* I - Option name */
3922    char          *buffer,		/* I - String buffer */
3923    size_t        bufsize)		/* I - Size of buffer */
3924{
3925  char		*bufptr,		/* Pointer into buffer */
3926		*bufend;		/* End of buffer */
3927  ppd_coption_t *coption;		/* Custom option */
3928  ppd_cparam_t	*cparam;		/* Current custom parameter */
3929  char		keyword[256];		/* Parameter name */
3930  const char	*val,			/* Parameter value */
3931		*uval;			/* Units value */
3932  long		integer;		/* Integer value */
3933  double	number,			/* Number value */
3934		number_points;		/* Number in points */
3935
3936
3937 /*
3938  * See if we have a custom option choice...
3939  */
3940
3941  if ((val = cgiGetVariable(name)) == NULL)
3942  {
3943   /*
3944    * Option not found!
3945    */
3946
3947    return (NULL);
3948  }
3949  else if (_cups_strcasecmp(val, "Custom") ||
3950           (coption = ppdFindCustomOption(ppd, name)) == NULL)
3951  {
3952   /*
3953    * Not a custom choice...
3954    */
3955
3956    strlcpy(buffer, val, bufsize);
3957    return (buffer);
3958  }
3959
3960 /*
3961  * OK, we have a custom option choice, format it...
3962  */
3963
3964  *buffer = '\0';
3965
3966  if (!strcmp(coption->keyword, "PageSize"))
3967  {
3968    const char	*lval;			/* Length string value */
3969    double	width,			/* Width value */
3970		width_points,		/* Width in points */
3971		length,			/* Length value */
3972		length_points;		/* Length in points */
3973
3974
3975    val  = cgiGetVariable("PageSize.Width");
3976    lval = cgiGetVariable("PageSize.Height");
3977    uval = cgiGetVariable("PageSize.Units");
3978
3979    if (!val || !lval || !uval ||
3980        (width = strtod(val, NULL)) == 0.0 ||
3981        (length = strtod(lval, NULL)) == 0.0 ||
3982        (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") &&
3983	 strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m")))
3984      return (NULL);
3985
3986    width_points  = get_points(width, uval);
3987    length_points = get_points(length, uval);
3988
3989    if (width_points < ppd->custom_min[0] ||
3990        width_points > ppd->custom_max[0] ||
3991        length_points < ppd->custom_min[1] ||
3992	length_points > ppd->custom_max[1])
3993      return (NULL);
3994
3995    snprintf(buffer, bufsize, "Custom.%gx%g%s", width, length, uval);
3996  }
3997  else if (cupsArrayCount(coption->params) == 1)
3998  {
3999    cparam = ppdFirstCustomParam(coption);
4000    snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name);
4001
4002    if ((val = cgiGetVariable(keyword)) == NULL)
4003      return (NULL);
4004
4005    switch (cparam->type)
4006    {
4007      case PPD_CUSTOM_CURVE :
4008      case PPD_CUSTOM_INVCURVE :
4009      case PPD_CUSTOM_REAL :
4010	  if ((number = strtod(val, NULL)) == 0.0 ||
4011	      number < cparam->minimum.custom_real ||
4012	      number > cparam->maximum.custom_real)
4013	    return (NULL);
4014
4015          snprintf(buffer, bufsize, "Custom.%g", number);
4016          break;
4017
4018      case PPD_CUSTOM_INT :
4019          if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN ||
4020	      integer == LONG_MAX ||
4021	      integer < cparam->minimum.custom_int ||
4022	      integer > cparam->maximum.custom_int)
4023            return (NULL);
4024
4025          snprintf(buffer, bufsize, "Custom.%ld", integer);
4026          break;
4027
4028      case PPD_CUSTOM_POINTS :
4029          snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);
4030
4031	  if ((number = strtod(val, NULL)) == 0.0 ||
4032	      (uval = cgiGetVariable(keyword)) == NULL ||
4033	      (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") &&
4034	       strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m")))
4035	    return (NULL);
4036
4037	  number_points = get_points(number, uval);
4038	  if (number_points < cparam->minimum.custom_points ||
4039	      number_points > cparam->maximum.custom_points)
4040	    return (NULL);
4041
4042	  snprintf(buffer, bufsize, "Custom.%g%s", number, uval);
4043          break;
4044
4045      case PPD_CUSTOM_PASSCODE :
4046          for (uval = val; *uval; uval ++)
4047	    if (!isdigit(*uval & 255))
4048	      return (NULL);
4049
4050      case PPD_CUSTOM_PASSWORD :
4051      case PPD_CUSTOM_STRING :
4052          integer = (long)strlen(val);
4053	  if (integer < cparam->minimum.custom_string ||
4054	      integer > cparam->maximum.custom_string)
4055	    return (NULL);
4056
4057          snprintf(buffer, bufsize, "Custom.%s", val);
4058	  break;
4059    }
4060  }
4061  else
4062  {
4063    const char *prefix = "{";		/* Prefix string */
4064
4065
4066    bufptr = buffer;
4067    bufend = buffer + bufsize;
4068
4069    for (cparam = ppdFirstCustomParam(coption);
4070	 cparam;
4071	 cparam = ppdNextCustomParam(coption))
4072    {
4073      snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword,
4074               cparam->name);
4075
4076      if ((val = cgiGetVariable(keyword)) == NULL)
4077	return (NULL);
4078
4079      snprintf(bufptr, (size_t)(bufend - bufptr), "%s%s=", prefix, cparam->name);
4080      bufptr += strlen(bufptr);
4081      prefix = " ";
4082
4083      switch (cparam->type)
4084      {
4085	case PPD_CUSTOM_CURVE :
4086	case PPD_CUSTOM_INVCURVE :
4087	case PPD_CUSTOM_REAL :
4088	    if ((number = strtod(val, NULL)) == 0.0 ||
4089		number < cparam->minimum.custom_real ||
4090		number > cparam->maximum.custom_real)
4091	      return (NULL);
4092
4093	    snprintf(bufptr, (size_t)(bufend - bufptr), "%g", number);
4094	    break;
4095
4096	case PPD_CUSTOM_INT :
4097	    if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN ||
4098		integer == LONG_MAX ||
4099		integer < cparam->minimum.custom_int ||
4100		integer > cparam->maximum.custom_int)
4101	      return (NULL);
4102
4103	    snprintf(bufptr, (size_t)(bufend - bufptr), "%ld", integer);
4104	    break;
4105
4106	case PPD_CUSTOM_POINTS :
4107	    snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);
4108
4109	    if ((number = strtod(val, NULL)) == 0.0 ||
4110		(uval = cgiGetVariable(keyword)) == NULL ||
4111		(strcmp(uval, "pt") && strcmp(uval, "in") &&
4112		 strcmp(uval, "ft") && strcmp(uval, "cm") &&
4113		 strcmp(uval, "mm") && strcmp(uval, "m")))
4114	      return (NULL);
4115
4116	    number_points = get_points(number, uval);
4117	    if (number_points < cparam->minimum.custom_points ||
4118		number_points > cparam->maximum.custom_points)
4119	      return (NULL);
4120
4121	    snprintf(bufptr, (size_t)(bufend - bufptr), "%g%s", number, uval);
4122	    break;
4123
4124	case PPD_CUSTOM_PASSCODE :
4125	    for (uval = val; *uval; uval ++)
4126	      if (!isdigit(*uval & 255))
4127		return (NULL);
4128
4129	case PPD_CUSTOM_PASSWORD :
4130	case PPD_CUSTOM_STRING :
4131	    integer = (long)strlen(val);
4132	    if (integer < cparam->minimum.custom_string ||
4133		integer > cparam->maximum.custom_string)
4134	      return (NULL);
4135
4136	    if ((bufptr + 2) > bufend)
4137	      return (NULL);
4138
4139	    bufend --;
4140	    *bufptr++ = '\"';
4141
4142	    while (*val && bufptr < bufend)
4143	    {
4144	      if (*val == '\\' || *val == '\"')
4145	      {
4146		if ((bufptr + 1) >= bufend)
4147		  return (NULL);
4148
4149		*bufptr++ = '\\';
4150	      }
4151
4152	      *bufptr++ = *val++;
4153	    }
4154
4155	    if (bufptr >= bufend)
4156	      return (NULL);
4157
4158	    *bufptr++ = '\"';
4159	    *bufptr   = '\0';
4160	    bufend ++;
4161	    break;
4162      }
4163
4164      bufptr += strlen(bufptr);
4165    }
4166
4167    if (bufptr == buffer || (bufend - bufptr) < 2)
4168      return (NULL);
4169
4170    memcpy(bufptr, "}", 2);
4171  }
4172
4173  return (buffer);
4174}
4175
4176
4177/*
4178 * 'get_points()' - Get a value in points.
4179 */
4180
4181static double				/* O - Number in points */
4182get_points(double     number,		/* I - Original number */
4183           const char *uval)		/* I - Units */
4184{
4185  if (!strcmp(uval, "mm"))		/* Millimeters */
4186    return (number * 72.0 / 25.4);
4187  else if (!strcmp(uval, "cm"))		/* Centimeters */
4188    return (number * 72.0 / 2.54);
4189  else if (!strcmp(uval, "in"))		/* Inches */
4190    return (number * 72.0);
4191  else if (!strcmp(uval, "ft"))		/* Feet */
4192    return (number * 72.0 * 12.0);
4193  else if (!strcmp(uval, "m"))		/* Meters */
4194    return (number * 72.0 / 0.0254);
4195  else					/* Points */
4196    return (number);
4197}
4198
4199
4200/*
4201 * End of "$Id: admin.c 12131 2014-08-28 23:38:16Z msweet $".
4202 */
4203