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