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