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