• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 	  sprintf(ptr, "%%%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 
769   if (!var)
770   {
771    /*
772     * Look for devices so the user can pick something...
773     */
774 
775     if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL)
776     {
777       strlcpy(uri, attr->values[0].string.text, sizeof(uri));
778       if ((uriptr = strchr(uri, ':')) != NULL && strncmp(uriptr, "://", 3) == 0)
779         *uriptr = '\0';
780 
781       cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text);
782       cgiSetVariable("CURRENT_DEVICE_SCHEME", uri);
783     }
784 
785    /*
786     * Scan for devices for up to 30 seconds...
787     */
788 
789     fputs("DEBUG: Getting list of devices...\n", stderr);
790 
791     current_device = 0;
792     if (cupsGetDevices(http, 5, CUPS_INCLUDE_ALL, CUPS_EXCLUDE_NONE,
793                        (cups_device_cb_t)choose_device_cb,
794 		       (void *)title) == IPP_OK)
795     {
796       fputs("DEBUG: Got device list!\n", stderr);
797 
798       if (cgiSupportsMultipart())
799         cgiStartMultipart();
800 
801       cgiSetVariable("CUPS_GET_DEVICES_DONE", "1");
802       cgiStartHTML(title);
803       cgiCopyTemplateLang("choose-device.tmpl");
804       cgiEndHTML();
805 
806       if (cgiSupportsMultipart())
807         cgiEndMultipart();
808     }
809     else
810     {
811       fprintf(stderr,
812               "ERROR: CUPS-Get-Devices request failed with status %x: %s\n",
813 	      cupsLastError(), cupsLastErrorString());
814       if (cupsLastError() == IPP_NOT_AUTHORIZED)
815       {
816 	puts("Status: 401\n");
817 	exit(0);
818       }
819       else
820       {
821 	cgiStartHTML(title);
822 	cgiShowIPPError(modify ? _("Unable to modify printer") :
823 				 _("Unable to add printer"));
824 	cgiEndHTML();
825         return;
826       }
827     }
828   }
829   else if (!strchr(var, '/') ||
830            (!strncmp(var, "lpd://", 6) && !strchr(var + 6, '/')))
831   {
832     if ((attr = ippFindAttribute(oldinfo, "device-uri", IPP_TAG_URI)) != NULL)
833     {
834      /*
835       * Set the current device URI for the form to the old one...
836       */
837 
838       if (strncmp(attr->values[0].string.text, var, strlen(var)) == 0)
839 	cgiSetVariable("CURRENT_DEVICE_URI", attr->values[0].string.text);
840     }
841 
842    /*
843     * User needs to set the full URI...
844     */
845 
846     cgiStartHTML(title);
847     cgiCopyTemplateLang("choose-uri.tmpl");
848     cgiEndHTML();
849   }
850   else if (!strncmp(var, "serial:", 7) && !cgiGetVariable("BAUDRATE"))
851   {
852    /*
853     * Need baud rate, parity, etc.
854     */
855 
856     if ((var = strchr(var, '?')) != NULL &&
857         strncmp(var, "?baud=", 6) == 0)
858       maxrate = atoi(var + 6);
859     else
860       maxrate = 19200;
861 
862     for (i = 0; i < 10; i ++)
863       if (baudrates[i] > maxrate)
864         break;
865       else
866       {
867         sprintf(baudrate, "%d", baudrates[i]);
868 	cgiSetArray("BAUDRATES", i, baudrate);
869       }
870 
871     cgiStartHTML(title);
872     cgiCopyTemplateLang("choose-serial.tmpl");
873     cgiEndHTML();
874   }
875   else if (!name || !cgiGetVariable("PRINTER_LOCATION"))
876   {
877     cgiStartHTML(title);
878 
879     if (modify)
880     {
881      /*
882       * Update the location and description of an existing printer...
883       */
884 
885       if (oldinfo)
886       {
887         if ((attr = ippFindAttribute(oldinfo, "printer-info",
888 	                             IPP_TAG_TEXT)) != NULL)
889           cgiSetVariable("PRINTER_INFO", attr->values[0].string.text);
890 
891         if ((attr = ippFindAttribute(oldinfo, "printer-location",
892 	                             IPP_TAG_TEXT)) != NULL)
893           cgiSetVariable("PRINTER_LOCATION", attr->values[0].string.text);
894 
895 	if ((attr = ippFindAttribute(oldinfo, "printer-is-shared",
896 				     IPP_TAG_BOOLEAN)) != NULL)
897 	  cgiSetVariable("PRINTER_IS_SHARED",
898 			 attr->values[0].boolean ? "1" : "0");
899       }
900 
901       cgiCopyTemplateLang("modify-printer.tmpl");
902     }
903     else
904     {
905      /*
906       * Get the name, location, and description for a new printer...
907       */
908 
909 #ifdef __APPLE__
910       if (!strncmp(var, "usb:", 4))
911         cgiSetVariable("printer_is_shared", "1");
912       else
913 #endif /* __APPLE__ */
914         cgiSetVariable("printer_is_shared", "0");
915 
916       cgiCopyTemplateLang("add-printer.tmpl");
917     }
918 
919     cgiEndHTML();
920 
921     if (oldinfo)
922       ippDelete(oldinfo);
923 
924     return;
925   }
926   else if (!file &&
927            (!cgiGetVariable("PPD_NAME") || cgiGetVariable("SELECT_MAKE")))
928   {
929     int ipp_everywhere = !strncmp(var, "ipp://", 6) || !strncmp(var, "ipps://", 7) || (!strncmp(var, "dnssd://", 8) && (strstr(var, "_ipp._tcp") || strstr(var, "_ipps._tcp")));
930 
931     if (modify && !cgiGetVariable("SELECT_MAKE"))
932     {
933      /*
934       * Get the PPD file...
935       */
936 
937       int		fd;		/* PPD file */
938       char		filename[1024];	/* PPD filename */
939       ppd_file_t	*ppd;		/* PPD information */
940       char		buffer[1024];	/* Buffer */
941       ssize_t		bytes;		/* Number of bytes */
942       http_status_t	get_status;	/* Status of GET */
943 
944 
945       /* TODO: Use cupsGetFile() API... */
946       snprintf(uri, sizeof(uri), "/printers/%s.ppd", name);
947 
948       if (httpGet(http, uri))
949         httpGet(http, uri);
950 
951       while ((get_status = httpUpdate(http)) == HTTP_CONTINUE);
952 
953       if (get_status != HTTP_OK)
954       {
955         httpFlush(http);
956 
957         fprintf(stderr, "ERROR: Unable to get PPD file %s: %d - %s\n",
958 	        uri, get_status, httpStatus(get_status));
959       }
960       else if ((fd = cupsTempFd(filename, sizeof(filename))) >= 0)
961       {
962 	while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
963           write(fd, buffer, (size_t)bytes);
964 
965 	close(fd);
966 
967         if ((ppd = ppdOpenFile(filename)) != NULL)
968 	{
969 	  if (ppd->manufacturer)
970 	    cgiSetVariable("CURRENT_MAKE", ppd->manufacturer);
971 
972 	  if (ppd->nickname)
973 	    cgiSetVariable("CURRENT_MAKE_AND_MODEL", ppd->nickname);
974 
975           ppdClose(ppd);
976           unlink(filename);
977 	}
978 	else
979 	{
980 	  int linenum;			/* Line number */
981 
982 	  fprintf(stderr, "ERROR: Unable to open PPD file %s: %s\n",
983 	          filename, ppdErrorString(ppdLastError(&linenum)));
984 	}
985       }
986       else
987       {
988         httpFlush(http);
989 
990         fprintf(stderr,
991 	        "ERROR: Unable to create temporary file for PPD file: %s\n",
992 	        strerror(errno));
993       }
994     }
995 
996    /*
997     * Build a CUPS_GET_PPDS request, which requires the following
998     * attributes:
999     *
1000     *    attributes-charset
1001     *    attributes-natural-language
1002     *    printer-uri
1003     */
1004 
1005     request = ippNewRequest(CUPS_GET_PPDS);
1006 
1007     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1008                  NULL, "ipp://localhost/printers/");
1009 
1010     if ((var = cgiGetVariable("PPD_MAKE")) == NULL)
1011       var = cgiGetVariable("CURRENT_MAKE");
1012     if (var && !cgiGetVariable("SELECT_MAKE"))
1013     {
1014       const char *make_model;		/* Make and model */
1015 
1016 
1017       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1018                    "ppd-make", NULL, var);
1019 
1020       if ((make_model = cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL)
1021 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
1022 		     "ppd-make-and-model", NULL, make_model);
1023     }
1024     else
1025       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1026                    "requested-attributes", NULL, "ppd-make");
1027 
1028    /*
1029     * Do the request and get back a response...
1030     */
1031 
1032     if ((response = cupsDoRequest(http, request, "/")) != NULL)
1033     {
1034      /*
1035       * Got the list of PPDs, see if the user has selected a make...
1036       */
1037 
1038       if (cgiSetIPPVars(response, NULL, NULL, NULL, 0) == 0 && !modify)
1039       {
1040        /*
1041         * No PPD files with this make, try again with all makes...
1042 	*/
1043 
1044         ippDelete(response);
1045 
1046 	request = ippNewRequest(CUPS_GET_PPDS);
1047 
1048 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1049                      NULL, "ipp://localhost/printers/");
1050 
1051 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1052                      "requested-attributes", NULL, "ppd-make");
1053 
1054 	if ((response = cupsDoRequest(http, request, "/")) != NULL)
1055           cgiSetIPPVars(response, NULL, NULL, NULL, 0);
1056 
1057         cgiStartHTML(title);
1058 	cgiCopyTemplateLang("choose-make.tmpl");
1059         cgiEndHTML();
1060       }
1061       else if (!var || cgiGetVariable("SELECT_MAKE"))
1062       {
1063         cgiStartHTML(title);
1064 	cgiCopyTemplateLang("choose-make.tmpl");
1065         cgiEndHTML();
1066       }
1067       else
1068       {
1069        /*
1070 	* Let the user choose a model...
1071 	*/
1072 
1073         cgiStartHTML(title);
1074 	if (!cgiGetVariable("PPD_MAKE"))
1075 	  cgiSetVariable("PPD_MAKE", cgiGetVariable("CURRENT_MAKE"));
1076         if (ipp_everywhere)
1077 	  cgiSetVariable("SHOW_IPP_EVERYWHERE", "1");
1078 	cgiCopyTemplateLang("choose-model.tmpl");
1079         cgiEndHTML();
1080       }
1081 
1082       ippDelete(response);
1083     }
1084     else
1085     {
1086       cgiStartHTML(title);
1087       cgiShowIPPError(_("Unable to get list of printer drivers"));
1088       cgiCopyTemplateLang("error.tmpl");
1089       cgiEndHTML();
1090     }
1091   }
1092   else
1093   {
1094    /*
1095     * Build a CUPS_ADD_PRINTER request, which requires the following
1096     * attributes:
1097     *
1098     *    attributes-charset
1099     *    attributes-natural-language
1100     *    printer-uri
1101     *    printer-location
1102     *    printer-info
1103     *    ppd-name
1104     *    device-uri
1105     *    printer-is-accepting-jobs
1106     *    printer-is-shared
1107     *    printer-state
1108     */
1109 
1110     request = ippNewRequest(CUPS_ADD_PRINTER);
1111 
1112     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1113                      "localhost", 0, "/printers/%s",
1114 		     cgiGetVariable("PRINTER_NAME"));
1115     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1116                  NULL, uri);
1117 
1118     if (!file)
1119     {
1120       var = cgiGetVariable("PPD_NAME");
1121       if (!strcmp(var, "everywhere"))
1122         get_printer_ppd(cgiGetVariable("DEVICE_URI"), evefile, sizeof(evefile));
1123       else if (strcmp(var, "__no_change__"))
1124 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
1125 		     NULL, var);
1126     }
1127 
1128     ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
1129                  NULL, cgiGetVariable("PRINTER_LOCATION"));
1130 
1131     ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
1132                  NULL, cgiGetVariable("PRINTER_INFO"));
1133 
1134     strlcpy(uri, cgiGetVariable("DEVICE_URI"), sizeof(uri));
1135 
1136    /*
1137     * Strip make and model from URI...
1138     */
1139 
1140     if ((uriptr = strrchr(uri, '|')) != NULL)
1141       *uriptr = '\0';
1142 
1143     if (!strncmp(uri, "serial:", 7))
1144     {
1145      /*
1146       * Update serial port URI to include baud rate, etc.
1147       */
1148 
1149       if ((uriptr = strchr(uri, '?')) == NULL)
1150         uriptr = uri + strlen(uri);
1151 
1152       snprintf(uriptr, sizeof(uri) - (size_t)(uriptr - uri),
1153                "?baud=%s+bits=%s+parity=%s+flow=%s",
1154                cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"),
1155 	       cgiGetVariable("PARITY"), cgiGetVariable("FLOW"));
1156     }
1157 
1158     ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri",
1159                  NULL, uri);
1160 
1161     ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
1162 
1163     var = cgiGetVariable("printer_is_shared");
1164     ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-shared",
1165                   var && (!strcmp(var, "1") || !strcmp(var, "on")));
1166 
1167     ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
1168                   IPP_PRINTER_IDLE);
1169 
1170    /*
1171     * Do the request and get back a response...
1172     */
1173 
1174     if (file)
1175       ippDelete(cupsDoFileRequest(http, request, "/admin/", file->tempfile));
1176     else if (evefile[0])
1177     {
1178       ippDelete(cupsDoFileRequest(http, request, "/admin/", evefile));
1179       unlink(evefile);
1180     }
1181     else
1182       ippDelete(cupsDoRequest(http, request, "/admin/"));
1183 
1184     if (cupsLastError() == IPP_NOT_AUTHORIZED)
1185     {
1186       puts("Status: 401\n");
1187       exit(0);
1188     }
1189     else if (cupsLastError() > IPP_OK_CONFLICT)
1190     {
1191       cgiStartHTML(title);
1192       cgiShowIPPError(modify ? _("Unable to modify printer") :
1193                                _("Unable to add printer"));
1194     }
1195     else if (modify)
1196     {
1197      /*
1198       * Redirect successful updates back to the printer page...
1199       */
1200 
1201       char	refresh[1024];		/* Refresh URL */
1202 
1203 
1204       cgiFormEncode(uri, name, sizeof(uri));
1205 
1206       snprintf(refresh, sizeof(refresh),
1207 	       "5;/admin/?OP=redirect&URL=/printers/%s", uri);
1208 
1209       cgiSetVariable("refresh_page", refresh);
1210 
1211       cgiStartHTML(title);
1212 
1213       cgiCopyTemplateLang("printer-modified.tmpl");
1214     }
1215     else
1216     {
1217      /*
1218       * Set the printer options...
1219       */
1220 
1221       cgiSetVariable("OP", "set-printer-options");
1222       do_set_options(http, 0);
1223       return;
1224     }
1225 
1226     cgiEndHTML();
1227   }
1228 
1229   if (oldinfo)
1230     ippDelete(oldinfo);
1231 }
1232 
1233 
1234 /*
1235  * 'do_config_server()' - Configure server settings.
1236  */
1237 
1238 static void
do_config_server(http_t * http)1239 do_config_server(http_t *http)		/* I - HTTP connection */
1240 {
1241   if (cgiGetVariable("CHANGESETTINGS"))
1242   {
1243    /*
1244     * Save basic setting changes...
1245     */
1246 
1247     int			num_settings;	/* Number of server settings */
1248     cups_option_t	*settings;	/* Server settings */
1249     int			advanced,	/* Advanced settings shown? */
1250 			changed;	/* Have settings changed? */
1251     const char		*debug_logging,	/* DEBUG_LOGGING value */
1252 			*preserve_jobs = NULL,
1253 					/* PRESERVE_JOBS value */
1254 			*remote_admin,	/* REMOTE_ADMIN value */
1255 			*remote_any,	/* REMOTE_ANY value */
1256 			*share_printers,/* SHARE_PRINTERS value */
1257 			*user_cancel_any,
1258 					/* USER_CANCEL_ANY value */
1259 			*browse_web_if = NULL,
1260 					/* BrowseWebIF value */
1261 			*preserve_job_history = NULL,
1262 					/* PreserveJobHistory value */
1263 			*preserve_job_files = NULL,
1264 					/* PreserveJobFiles value */
1265 			*max_clients = NULL,
1266 					/* MaxClients value */
1267 			*max_jobs = NULL,
1268 					/* MaxJobs value */
1269 			*max_log_size = NULL;
1270 					/* MaxLogSize value */
1271     const char		*current_browse_web_if,
1272 					/* BrowseWebIF value */
1273 			*current_preserve_job_history,
1274 					/* PreserveJobHistory value */
1275 			*current_preserve_job_files,
1276 					/* PreserveJobFiles value */
1277 			*current_max_clients,
1278 					/* MaxClients value */
1279 			*current_max_jobs,
1280 					/* MaxJobs value */
1281 			*current_max_log_size;
1282 					/* MaxLogSize value */
1283 #ifdef HAVE_GSSAPI
1284     char		default_auth_type[255];
1285 					/* DefaultAuthType value */
1286     const char		*val;		/* Setting value */
1287 #endif /* HAVE_GSSAPI */
1288 
1289 
1290    /*
1291     * Get the checkbox values from the form...
1292     */
1293 
1294     debug_logging        = cgiGetVariable("DEBUG_LOGGING") ? "1" : "0";
1295     remote_admin         = cgiGetVariable("REMOTE_ADMIN") ? "1" : "0";
1296     remote_any           = cgiGetVariable("REMOTE_ANY") ? "1" : "0";
1297     share_printers       = cgiGetVariable("SHARE_PRINTERS") ? "1" : "0";
1298     user_cancel_any      = cgiGetVariable("USER_CANCEL_ANY") ? "1" : "0";
1299 
1300     advanced = cgiGetVariable("ADVANCEDSETTINGS") != NULL;
1301     if (advanced)
1302     {
1303      /*
1304       * Get advanced settings...
1305       */
1306 
1307       browse_web_if        = cgiGetVariable("BROWSE_WEB_IF") ? "Yes" : "No";
1308       max_clients          = cgiGetVariable("MAX_CLIENTS");
1309       max_log_size         = cgiGetVariable("MAX_LOG_SIZE");
1310       preserve_jobs        = cgiGetVariable("PRESERVE_JOBS");
1311 
1312       if (preserve_jobs)
1313       {
1314         max_jobs             = cgiGetVariable("MAX_JOBS");
1315 	preserve_job_history = cgiGetVariable("PRESERVE_JOB_HISTORY");
1316 	preserve_job_files   = cgiGetVariable("PRESERVE_JOB_FILES");
1317 
1318 	if (!max_jobs || atoi(max_jobs) < 0)
1319 	  max_jobs = "500";
1320 
1321 	if (!preserve_job_history)
1322 	  preserve_job_history = "On";
1323 
1324 	if (!preserve_job_files)
1325 	  preserve_job_files = "1d";
1326       }
1327       else
1328       {
1329         max_jobs             = "0";
1330         preserve_job_history = "No";
1331         preserve_job_files   = "No";
1332       }
1333 
1334       if (!max_clients || atoi(max_clients) <= 0)
1335 	max_clients = "100";
1336 
1337       if (!max_log_size || atoi(max_log_size) <= 0.0)
1338 	max_log_size = "1m";
1339     }
1340 
1341    /*
1342     * Get the current server settings...
1343     */
1344 
1345     if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
1346     {
1347       cgiStartHTML(cgiText(_("Change Settings")));
1348       cgiSetVariable("MESSAGE",
1349                      cgiText(_("Unable to change server settings")));
1350       cgiSetVariable("ERROR", cupsLastErrorString());
1351       cgiCopyTemplateLang("error.tmpl");
1352       cgiEndHTML();
1353       return;
1354     }
1355 
1356 #ifdef HAVE_GSSAPI
1357    /*
1358     * Get authentication settings...
1359     */
1360 
1361     if (cgiGetVariable("KERBEROS"))
1362       strlcpy(default_auth_type, "Negotiate", sizeof(default_auth_type));
1363     else
1364     {
1365       val = cupsGetOption("DefaultAuthType", num_settings, settings);
1366 
1367       if (!val || !_cups_strcasecmp(val, "Negotiate"))
1368         strlcpy(default_auth_type, "Basic", sizeof(default_auth_type));
1369       else
1370         strlcpy(default_auth_type, val, sizeof(default_auth_type));
1371     }
1372 
1373     fprintf(stderr, "DEBUG: DefaultAuthType %s\n", default_auth_type);
1374 #endif /* HAVE_GSSAPI */
1375 
1376     if ((current_browse_web_if = cupsGetOption("BrowseWebIF", num_settings,
1377                                                settings)) == NULL)
1378       current_browse_web_if = "No";
1379 
1380     if ((current_preserve_job_history = cupsGetOption("PreserveJobHistory",
1381                                                       num_settings,
1382 						      settings)) == NULL)
1383       current_preserve_job_history = "Yes";
1384 
1385     if ((current_preserve_job_files = cupsGetOption("PreserveJobFiles",
1386                                                     num_settings,
1387 						    settings)) == NULL)
1388       current_preserve_job_files = "1d";
1389 
1390     if ((current_max_clients = cupsGetOption("MaxClients", num_settings,
1391                                              settings)) == NULL)
1392       current_max_clients = "100";
1393 
1394     if ((current_max_jobs = cupsGetOption("MaxJobs", num_settings,
1395                                           settings)) == NULL)
1396       current_max_jobs = "500";
1397 
1398     if ((current_max_log_size = cupsGetOption("MaxLogSize", num_settings,
1399                                               settings)) == NULL)
1400       current_max_log_size = "1m";
1401 
1402    /*
1403     * See if the settings have changed...
1404     */
1405 
1406     changed = strcmp(debug_logging, cupsGetOption(CUPS_SERVER_DEBUG_LOGGING,
1407                                                   num_settings, settings)) ||
1408 	      strcmp(remote_admin, cupsGetOption(CUPS_SERVER_REMOTE_ADMIN,
1409 						 num_settings, settings)) ||
1410 	      strcmp(remote_any, cupsGetOption(CUPS_SERVER_REMOTE_ANY,
1411 					       num_settings, settings)) ||
1412 	      strcmp(share_printers, cupsGetOption(CUPS_SERVER_SHARE_PRINTERS,
1413 						   num_settings, settings)) ||
1414 #ifdef HAVE_GSSAPI
1415 	      !cupsGetOption("DefaultAuthType", num_settings, settings) ||
1416 	      strcmp(default_auth_type, cupsGetOption("DefaultAuthType",
1417 						      num_settings, settings)) ||
1418 #endif /* HAVE_GSSAPI */
1419 	      strcmp(user_cancel_any, cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY,
1420 						    num_settings, settings));
1421 
1422     if (advanced && !changed)
1423       changed = _cups_strcasecmp(browse_web_if, current_browse_web_if) ||
1424 		_cups_strcasecmp(preserve_job_history, current_preserve_job_history) ||
1425 		_cups_strcasecmp(preserve_job_files, current_preserve_job_files) ||
1426 		_cups_strcasecmp(max_clients, current_max_clients) ||
1427 		_cups_strcasecmp(max_jobs, current_max_jobs) ||
1428 		_cups_strcasecmp(max_log_size, current_max_log_size);
1429 
1430     if (changed)
1431     {
1432      /*
1433       * Settings *have* changed, so save the changes...
1434       */
1435 
1436       cupsFreeOptions(num_settings, settings);
1437 
1438       num_settings = 0;
1439       num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
1440                                    debug_logging, num_settings, &settings);
1441       num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
1442                                    remote_admin, num_settings, &settings);
1443       num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY,
1444                                    remote_any, num_settings, &settings);
1445       num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
1446                                    share_printers, num_settings, &settings);
1447       num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
1448                                    user_cancel_any, num_settings, &settings);
1449 #ifdef HAVE_GSSAPI
1450       num_settings = cupsAddOption("DefaultAuthType", default_auth_type,
1451                                    num_settings, &settings);
1452 #endif /* HAVE_GSSAPI */
1453 
1454       if (advanced)
1455       {
1456        /*
1457         * Add advanced settings...
1458 	*/
1459 
1460 	if (_cups_strcasecmp(browse_web_if, current_browse_web_if))
1461 	  num_settings = cupsAddOption("BrowseWebIF", browse_web_if,
1462 				       num_settings, &settings);
1463 	if (_cups_strcasecmp(preserve_job_history, current_preserve_job_history))
1464 	  num_settings = cupsAddOption("PreserveJobHistory",
1465 	                               preserve_job_history, num_settings,
1466 				       &settings);
1467 	if (_cups_strcasecmp(preserve_job_files, current_preserve_job_files))
1468 	  num_settings = cupsAddOption("PreserveJobFiles", preserve_job_files,
1469 	                               num_settings, &settings);
1470         if (_cups_strcasecmp(max_clients, current_max_clients))
1471 	  num_settings = cupsAddOption("MaxClients", max_clients, num_settings,
1472 	                               &settings);
1473         if (_cups_strcasecmp(max_jobs, current_max_jobs))
1474 	  num_settings = cupsAddOption("MaxJobs", max_jobs, num_settings,
1475 	                               &settings);
1476         if (_cups_strcasecmp(max_log_size, current_max_log_size))
1477 	  num_settings = cupsAddOption("MaxLogSize", max_log_size, num_settings,
1478 	                               &settings);
1479       }
1480 
1481       if (!cupsAdminSetServerSettings(http, num_settings, settings))
1482       {
1483         if (cupsLastError() == IPP_NOT_AUTHORIZED)
1484 	{
1485 	  puts("Status: 401\n");
1486 	  exit(0);
1487 	}
1488 
1489 	cgiStartHTML(cgiText(_("Change Settings")));
1490 	cgiSetVariable("MESSAGE",
1491                        cgiText(_("Unable to change server settings")));
1492 	cgiSetVariable("ERROR", cupsLastErrorString());
1493 	cgiCopyTemplateLang("error.tmpl");
1494       }
1495       else
1496       {
1497         if (advanced)
1498 	  cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&"
1499 	                                 "URL=/admin/?ADVANCEDSETTINGS=YES");
1500         else
1501 	  cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
1502 	cgiStartHTML(cgiText(_("Change Settings")));
1503 	cgiCopyTemplateLang("restart.tmpl");
1504       }
1505     }
1506     else
1507     {
1508      /*
1509       * No changes...
1510       */
1511 
1512       cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
1513       cgiStartHTML(cgiText(_("Change Settings")));
1514       cgiCopyTemplateLang("norestart.tmpl");
1515     }
1516 
1517     cupsFreeOptions(num_settings, settings);
1518 
1519     cgiEndHTML();
1520   }
1521   else if (cgiGetVariable("SAVECHANGES") && cgiGetVariable("CUPSDCONF"))
1522   {
1523    /*
1524     * Save hand-edited config file...
1525     */
1526 
1527     http_status_t status;		/* PUT status */
1528     char	tempfile[1024];		/* Temporary new cupsd.conf */
1529     int		tempfd;			/* Temporary file descriptor */
1530     cups_file_t	*temp;			/* Temporary file */
1531     const char	*start,			/* Start of line */
1532 		*end;			/* End of line */
1533 
1534 
1535    /*
1536     * Create a temporary file for the new cupsd.conf file...
1537     */
1538 
1539     if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0)
1540     {
1541       cgiStartHTML(cgiText(_("Edit Configuration File")));
1542       cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file")));
1543       cgiSetVariable("ERROR", strerror(errno));
1544       cgiCopyTemplateLang("error.tmpl");
1545       cgiEndHTML();
1546 
1547       perror(tempfile);
1548       return;
1549     }
1550 
1551     if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL)
1552     {
1553       cgiStartHTML(cgiText(_("Edit Configuration File")));
1554       cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file")));
1555       cgiSetVariable("ERROR", strerror(errno));
1556       cgiCopyTemplateLang("error.tmpl");
1557       cgiEndHTML();
1558 
1559       perror(tempfile);
1560       close(tempfd);
1561       unlink(tempfile);
1562       return;
1563     }
1564 
1565    /*
1566     * Copy the cupsd.conf text from the form variable...
1567     */
1568 
1569     start = cgiGetVariable("CUPSDCONF");
1570     while (start)
1571     {
1572       if ((end = strstr(start, "\r\n")) == NULL)
1573         if ((end = strstr(start, "\n")) == NULL)
1574 	  end = start + strlen(start);
1575 
1576       cupsFileWrite(temp, start, (size_t)(end - start));
1577       cupsFilePutChar(temp, '\n');
1578 
1579       if (*end == '\r')
1580         start = end + 2;
1581       else if (*end == '\n')
1582         start = end + 1;
1583       else
1584         start = NULL;
1585     }
1586 
1587     cupsFileClose(temp);
1588 
1589    /*
1590     * Upload the configuration file to the server...
1591     */
1592 
1593     status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile);
1594 
1595     if (status == HTTP_UNAUTHORIZED)
1596     {
1597       puts("Status: 401\n");
1598       unlink(tempfile);
1599       exit(0);
1600     }
1601     else if (status != HTTP_CREATED)
1602     {
1603       cgiSetVariable("MESSAGE",
1604                      cgiText(_("Unable to upload cupsd.conf file")));
1605       cgiSetVariable("ERROR", httpStatus(status));
1606 
1607       cgiStartHTML(cgiText(_("Edit Configuration File")));
1608       cgiCopyTemplateLang("error.tmpl");
1609     }
1610     else
1611     {
1612       cgiSetVariable("refresh_page", "5;URL=/admin/");
1613 
1614       cgiStartHTML(cgiText(_("Edit Configuration File")));
1615       cgiCopyTemplateLang("restart.tmpl");
1616     }
1617 
1618     cgiEndHTML();
1619 
1620     unlink(tempfile);
1621   }
1622   else
1623   {
1624     struct stat	info;			/* cupsd.conf information */
1625     cups_file_t	*cupsd;			/* cupsd.conf file */
1626     char	*buffer,		/* Buffer for entire file */
1627 		*bufptr,		/* Pointer into buffer */
1628 		*bufend;		/* End of buffer */
1629     int		ch;			/* Character from file */
1630     char	filename[1024];		/* Filename */
1631     const char	*server_root;		/* Location of config files */
1632 
1633 
1634    /*
1635     * Locate the cupsd.conf file...
1636     */
1637 
1638     if ((server_root = getenv("CUPS_SERVERROOT")) == NULL)
1639       server_root = CUPS_SERVERROOT;
1640 
1641     snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root);
1642 
1643    /*
1644     * Figure out the size...
1645     */
1646 
1647     if (stat(filename, &info))
1648     {
1649       cgiStartHTML(cgiText(_("Edit Configuration File")));
1650       cgiSetVariable("MESSAGE",
1651                      cgiText(_("Unable to access cupsd.conf file")));
1652       cgiSetVariable("ERROR", strerror(errno));
1653       cgiCopyTemplateLang("error.tmpl");
1654       cgiEndHTML();
1655 
1656       perror(filename);
1657       return;
1658     }
1659 
1660     if (info.st_size > (1024 * 1024))
1661     {
1662       cgiStartHTML(cgiText(_("Edit Configuration File")));
1663       cgiSetVariable("MESSAGE",
1664                      cgiText(_("Unable to access cupsd.conf file")));
1665       cgiSetVariable("ERROR",
1666                      cgiText(_("Unable to edit cupsd.conf files larger than "
1667 		               "1MB")));
1668       cgiCopyTemplateLang("error.tmpl");
1669       cgiEndHTML();
1670 
1671       fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename,
1672               (long)info.st_size);
1673       return;
1674     }
1675 
1676    /*
1677     * Open the cupsd.conf file...
1678     */
1679 
1680     if ((cupsd = cupsFileOpen(filename, "r")) == NULL)
1681     {
1682      /*
1683       * Unable to open - log an error...
1684       */
1685 
1686       cgiStartHTML(cgiText(_("Edit Configuration File")));
1687       cgiSetVariable("MESSAGE",
1688                      cgiText(_("Unable to access cupsd.conf file")));
1689       cgiSetVariable("ERROR", strerror(errno));
1690       cgiCopyTemplateLang("error.tmpl");
1691       cgiEndHTML();
1692 
1693       perror(filename);
1694       return;
1695     }
1696 
1697    /*
1698     * Allocate memory and load the file into a string buffer...
1699     */
1700 
1701     if ((buffer = calloc(1, (size_t)info.st_size + 1)) != NULL)
1702     {
1703       cupsFileRead(cupsd, buffer, (size_t)info.st_size);
1704       cgiSetVariable("CUPSDCONF", buffer);
1705       free(buffer);
1706     }
1707 
1708     cupsFileClose(cupsd);
1709 
1710    /*
1711     * Then get the default cupsd.conf file and put that into a string as
1712     * well...
1713     */
1714 
1715     strlcat(filename, ".default", sizeof(filename));
1716 
1717     if (!stat(filename, &info) && info.st_size < (1024 * 1024) &&
1718         (cupsd = cupsFileOpen(filename, "r")) != NULL)
1719     {
1720       if ((buffer = calloc(1, 2 * (size_t)info.st_size + 1)) != NULL)
1721       {
1722 	bufend = buffer + 2 * info.st_size - 1;
1723 
1724 	for (bufptr = buffer;
1725 	     bufptr < bufend && (ch = cupsFileGetChar(cupsd)) != EOF;)
1726 	{
1727 	  if (ch == '\\' || ch == '\"')
1728 	  {
1729 	    *bufptr++ = '\\';
1730 	    *bufptr++ = (char)ch;
1731 	  }
1732 	  else if (ch == '\n')
1733 	  {
1734 	    *bufptr++ = '\\';
1735 	    *bufptr++ = 'n';
1736 	  }
1737 	  else if (ch == '\t')
1738 	  {
1739 	    *bufptr++ = '\\';
1740 	    *bufptr++ = 't';
1741 	  }
1742 	  else if (ch >= ' ')
1743 	    *bufptr++ = (char)ch;
1744 	}
1745 
1746 	*bufptr = '\0';
1747 
1748 	cgiSetVariable("CUPSDCONF_DEFAULT", buffer);
1749 	free(buffer);
1750       }
1751 
1752       cupsFileClose(cupsd);
1753     }
1754 
1755    /*
1756     * Show the current config file...
1757     */
1758 
1759     cgiStartHTML(cgiText(_("Edit Configuration File")));
1760 
1761     cgiCopyTemplateLang("edit-config.tmpl");
1762 
1763     cgiEndHTML();
1764   }
1765 }
1766 
1767 
1768 /*
1769  * 'do_delete_class()' - Delete a class.
1770  */
1771 
1772 static void
do_delete_class(http_t * http)1773 do_delete_class(http_t *http)		/* I - HTTP connection */
1774 {
1775   ipp_t		*request;		/* IPP request */
1776   char		uri[HTTP_MAX_URI];	/* Job URI */
1777   const char	*pclass;		/* Printer class name */
1778 
1779 
1780  /*
1781   * Get form variables...
1782   */
1783 
1784   if (cgiGetVariable("CONFIRM") == NULL)
1785   {
1786     cgiStartHTML(cgiText(_("Delete Class")));
1787     cgiCopyTemplateLang("class-confirm.tmpl");
1788     cgiEndHTML();
1789     return;
1790   }
1791 
1792   if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL)
1793     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1794                      "localhost", 0, "/classes/%s", pclass);
1795   else
1796   {
1797     cgiStartHTML(cgiText(_("Delete Class")));
1798     cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
1799     cgiCopyTemplateLang("error.tmpl");
1800     cgiEndHTML();
1801     return;
1802   }
1803 
1804  /*
1805   * Build a CUPS_DELETE_CLASS request, which requires the following
1806   * attributes:
1807   *
1808   *    attributes-charset
1809   *    attributes-natural-language
1810   *    printer-uri
1811   */
1812 
1813   request = ippNewRequest(CUPS_DELETE_CLASS);
1814 
1815   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1816                NULL, uri);
1817 
1818  /*
1819   * Do the request and get back a response...
1820   */
1821 
1822   ippDelete(cupsDoRequest(http, request, "/admin/"));
1823 
1824  /*
1825   * Show the results...
1826   */
1827 
1828   if (cupsLastError() == IPP_NOT_AUTHORIZED)
1829   {
1830     puts("Status: 401\n");
1831     exit(0);
1832   }
1833   else if (cupsLastError() <= IPP_OK_CONFLICT)
1834   {
1835    /*
1836     * Redirect successful updates back to the classes page...
1837     */
1838 
1839     cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes");
1840   }
1841 
1842   cgiStartHTML(cgiText(_("Delete Class")));
1843 
1844   if (cupsLastError() > IPP_OK_CONFLICT)
1845     cgiShowIPPError(_("Unable to delete class"));
1846   else
1847     cgiCopyTemplateLang("class-deleted.tmpl");
1848 
1849   cgiEndHTML();
1850 }
1851 
1852 
1853 /*
1854  * 'do_delete_printer()' - Delete a printer.
1855  */
1856 
1857 static void
do_delete_printer(http_t * http)1858 do_delete_printer(http_t *http)		/* I - HTTP connection */
1859 {
1860   ipp_t		*request;		/* IPP request */
1861   char		uri[HTTP_MAX_URI];	/* Job URI */
1862   const char	*printer;		/* Printer printer name */
1863 
1864 
1865  /*
1866   * Get form variables...
1867   */
1868 
1869   if (cgiGetVariable("CONFIRM") == NULL)
1870   {
1871     cgiStartHTML(cgiText(_("Delete Printer")));
1872     cgiCopyTemplateLang("printer-confirm.tmpl");
1873     cgiEndHTML();
1874     return;
1875   }
1876 
1877   if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
1878     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1879                      "localhost", 0, "/printers/%s", printer);
1880   else
1881   {
1882     cgiStartHTML(cgiText(_("Delete Printer")));
1883     cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
1884     cgiCopyTemplateLang("error.tmpl");
1885     cgiEndHTML();
1886     return;
1887   }
1888 
1889  /*
1890   * Build a CUPS_DELETE_PRINTER request, which requires the following
1891   * attributes:
1892   *
1893   *    attributes-charset
1894   *    attributes-natural-language
1895   *    printer-uri
1896   */
1897 
1898   request = ippNewRequest(CUPS_DELETE_PRINTER);
1899 
1900   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1901                NULL, uri);
1902 
1903  /*
1904   * Do the request and get back a response...
1905   */
1906 
1907   ippDelete(cupsDoRequest(http, request, "/admin/"));
1908 
1909  /*
1910   * Show the results...
1911   */
1912 
1913   if (cupsLastError() == IPP_NOT_AUTHORIZED)
1914   {
1915     puts("Status: 401\n");
1916     exit(0);
1917   }
1918   else if (cupsLastError() <= IPP_OK_CONFLICT)
1919   {
1920    /*
1921     * Redirect successful updates back to the printers page...
1922     */
1923 
1924     cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers");
1925   }
1926 
1927   cgiStartHTML(cgiText(_("Delete Printer")));
1928 
1929   if (cupsLastError() > IPP_OK_CONFLICT)
1930     cgiShowIPPError(_("Unable to delete printer"));
1931   else
1932     cgiCopyTemplateLang("printer-deleted.tmpl");
1933 
1934   cgiEndHTML();
1935 }
1936 
1937 
1938 /*
1939  * 'do_list_printers()' - List available printers.
1940  */
1941 
1942 static void
do_list_printers(http_t * http)1943 do_list_printers(http_t *http)		/* I - HTTP connection */
1944 {
1945   ipp_t		*request,		/* IPP request */
1946 		*response;		/* IPP response */
1947   ipp_attribute_t *attr;		/* IPP attribute */
1948 
1949 
1950   cgiStartHTML(cgiText(_("List Available Printers")));
1951   fflush(stdout);
1952 
1953  /*
1954   * Get the list of printers and their devices...
1955   */
1956 
1957   request = ippNewRequest(CUPS_GET_PRINTERS);
1958 
1959   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1960                "requested-attributes", NULL, "device-uri");
1961 
1962   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type",
1963                 CUPS_PRINTER_LOCAL);
1964   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask",
1965                 CUPS_PRINTER_LOCAL);
1966 
1967   if ((response = cupsDoRequest(http, request, "/")) != NULL)
1968   {
1969    /*
1970     * Got the printer list, now load the devices...
1971     */
1972 
1973     int		i;			/* Looping var */
1974     cups_array_t *printer_devices;	/* Printer devices for local printers */
1975     char	*printer_device;	/* Current printer device */
1976 
1977 
1978    /*
1979     * Allocate an array and copy the device strings...
1980     */
1981 
1982     printer_devices = cupsArrayNew((cups_array_func_t)strcmp, NULL);
1983 
1984     for (attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI);
1985          attr;
1986 	 attr = ippFindNextAttribute(response, "device-uri", IPP_TAG_URI))
1987     {
1988       cupsArrayAdd(printer_devices, strdup(attr->values[0].string.text));
1989     }
1990 
1991    /*
1992     * Free the printer list and get the device list...
1993     */
1994 
1995     ippDelete(response);
1996 
1997     request = ippNewRequest(CUPS_GET_DEVICES);
1998 
1999     if ((response = cupsDoRequest(http, request, "/")) != NULL)
2000     {
2001      /*
2002       * Got the device list, let's parse it...
2003       */
2004 
2005       const char *device_uri,		/* device-uri attribute value */
2006 		*device_make_and_model,	/* device-make-and-model value */
2007 		*device_info;		/* device-info value */
2008 
2009 
2010       for (i = 0, attr = response->attrs; attr; attr = attr->next)
2011       {
2012        /*
2013         * Skip leading attributes until we hit a device...
2014 	*/
2015 
2016 	while (attr && attr->group_tag != IPP_TAG_PRINTER)
2017           attr = attr->next;
2018 
2019 	if (!attr)
2020           break;
2021 
2022        /*
2023 	* Pull the needed attributes from this device...
2024 	*/
2025 
2026 	device_info           = NULL;
2027 	device_make_and_model = NULL;
2028 	device_uri            = NULL;
2029 
2030 	while (attr && attr->group_tag == IPP_TAG_PRINTER)
2031 	{
2032           if (!strcmp(attr->name, "device-info") &&
2033 	      attr->value_tag == IPP_TAG_TEXT)
2034 	    device_info = attr->values[0].string.text;
2035 
2036           if (!strcmp(attr->name, "device-make-and-model") &&
2037 	      attr->value_tag == IPP_TAG_TEXT)
2038 	    device_make_and_model = attr->values[0].string.text;
2039 
2040           if (!strcmp(attr->name, "device-uri") &&
2041 	      attr->value_tag == IPP_TAG_URI)
2042 	    device_uri = attr->values[0].string.text;
2043 
2044           attr = attr->next;
2045 	}
2046 
2047        /*
2048 	* See if we have everything needed...
2049 	*/
2050 
2051 	if (device_info && device_make_and_model && device_uri &&
2052 	    _cups_strcasecmp(device_make_and_model, "unknown") &&
2053 	    strchr(device_uri, ':'))
2054 	{
2055 	 /*
2056 	  * Yes, now see if there is already a printer for this
2057 	  * device...
2058 	  */
2059 
2060           if (!cupsArrayFind(printer_devices, (void *)device_uri))
2061           {
2062 	   /*
2063 	    * Not found, so it must be a new printer...
2064 	    */
2065 
2066             char	option[1024],	/* Form variables for this device */
2067 			*option_ptr;	/* Pointer into string */
2068 	    const char	*ptr;		/* Pointer into device string */
2069 
2070 
2071            /*
2072 	    * Format the printer name variable for this device...
2073 	    *
2074 	    * We use the device-info string first, then device-uri,
2075 	    * and finally device-make-and-model to come up with a
2076 	    * suitable name.
2077 	    */
2078 
2079             if (_cups_strncasecmp(device_info, "unknown", 7))
2080 	      ptr = device_info;
2081             else if ((ptr = strstr(device_uri, "://")) != NULL)
2082 	      ptr += 3;
2083 	    else
2084 	      ptr = device_make_and_model;
2085 
2086 	    for (option_ptr = option;
2087 	         option_ptr < (option + sizeof(option) - 1) && *ptr;
2088 		 ptr ++)
2089 	      if (isalnum(*ptr & 255) || *ptr == '_' || *ptr == '-' ||
2090 	          *ptr == '.')
2091 	        *option_ptr++ = *ptr;
2092 	      else if ((*ptr == ' ' || *ptr == '/') && option_ptr > option &&
2093 	               option_ptr[-1] != '_')
2094 	        *option_ptr++ = '_';
2095 	      else if (*ptr == '?' || *ptr == '(')
2096 	        break;
2097 
2098             *option_ptr = '\0';
2099 
2100             cgiSetArray("TEMPLATE_NAME", i, option);
2101 
2102            /*
2103 	    * Finally, set the form variables for this printer...
2104 	    */
2105 
2106 	    cgiSetArray("device_info", i, device_info);
2107 	    cgiSetArray("device_make_and_model", i, device_make_and_model);
2108             cgiSetArray("device_uri", i, device_uri);
2109 	    i ++;
2110 	  }
2111 	}
2112 
2113         if (!attr)
2114 	  break;
2115       }
2116 
2117       ippDelete(response);
2118 
2119      /*
2120       * Free the device list...
2121       */
2122 
2123       for (printer_device = (char *)cupsArrayFirst(printer_devices);
2124            printer_device;
2125 	   printer_device = (char *)cupsArrayNext(printer_devices))
2126         free(printer_device);
2127 
2128       cupsArrayDelete(printer_devices);
2129     }
2130   }
2131 
2132  /*
2133   * Finally, show the printer list...
2134   */
2135 
2136   cgiCopyTemplateLang("list-available-printers.tmpl");
2137 
2138   cgiEndHTML();
2139 }
2140 
2141 
2142 /*
2143  * 'do_menu()' - Show the main menu.
2144  */
2145 
2146 static void
do_menu(http_t * http)2147 do_menu(http_t *http)			/* I - HTTP connection */
2148 {
2149   int		num_settings;		/* Number of server settings */
2150   cups_option_t	*settings;		/* Server settings */
2151   const char	*val;			/* Setting value */
2152 
2153 
2154  /*
2155   * Get the current server settings...
2156   */
2157 
2158   if (!cupsAdminGetServerSettings(http, &num_settings, &settings))
2159   {
2160     cgiSetVariable("SETTINGS_MESSAGE",
2161                    cgiText(_("Unable to open cupsd.conf file:")));
2162     cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString());
2163   }
2164 
2165   if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings,
2166                            settings)) != NULL && atoi(val))
2167     cgiSetVariable("DEBUG_LOGGING", "CHECKED");
2168 
2169   if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings,
2170                            settings)) != NULL && atoi(val))
2171     cgiSetVariable("REMOTE_ADMIN", "CHECKED");
2172 
2173   if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings,
2174                            settings)) != NULL && atoi(val))
2175     cgiSetVariable("REMOTE_ANY", "CHECKED");
2176 
2177   if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings,
2178                            settings)) != NULL && atoi(val))
2179     cgiSetVariable("SHARE_PRINTERS", "CHECKED");
2180 
2181   if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings,
2182                            settings)) != NULL && atoi(val))
2183     cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
2184 
2185 #ifdef HAVE_GSSAPI
2186   cgiSetVariable("HAVE_GSSAPI", "1");
2187 
2188   if ((val = cupsGetOption("DefaultAuthType", num_settings,
2189                            settings)) != NULL && !_cups_strcasecmp(val, "Negotiate"))
2190     cgiSetVariable("KERBEROS", "CHECKED");
2191   else
2192 #endif /* HAVE_GSSAPI */
2193   cgiSetVariable("KERBEROS", "");
2194 
2195   if ((val = cupsGetOption("BrowseWebIF", num_settings,
2196                            settings)) == NULL)
2197     val = "No";
2198 
2199   if (!_cups_strcasecmp(val, "yes") || !_cups_strcasecmp(val, "on") ||
2200       !_cups_strcasecmp(val, "true"))
2201     cgiSetVariable("BROWSE_WEB_IF", "CHECKED");
2202 
2203   if ((val = cupsGetOption("PreserveJobHistory", num_settings,
2204                            settings)) == NULL)
2205     val = "Yes";
2206 
2207   if (val &&
2208       (!_cups_strcasecmp(val, "0") || !_cups_strcasecmp(val, "no") ||
2209        !_cups_strcasecmp(val, "off") || !_cups_strcasecmp(val, "false") ||
2210        !_cups_strcasecmp(val, "disabled")))
2211   {
2212     cgiSetVariable("PRESERVE_JOB_HISTORY", "0");
2213     cgiSetVariable("PRESERVE_JOB_FILES", "0");
2214   }
2215   else
2216   {
2217     cgiSetVariable("PRESERVE_JOBS", "CHECKED");
2218     cgiSetVariable("PRESERVE_JOB_HISTORY", val);
2219 
2220     if ((val = cupsGetOption("PreserveJobFiles", num_settings,
2221 			     settings)) == NULL)
2222       val = "1d";
2223 
2224     cgiSetVariable("PRESERVE_JOB_FILES", val);
2225 
2226   }
2227 
2228   if ((val = cupsGetOption("MaxClients", num_settings, settings)) == NULL)
2229     val = "100";
2230 
2231   cgiSetVariable("MAX_CLIENTS", val);
2232 
2233   if ((val = cupsGetOption("MaxJobs", num_settings, settings)) == NULL)
2234     val = "500";
2235 
2236   cgiSetVariable("MAX_JOBS", val);
2237 
2238   if ((val = cupsGetOption("MaxLogSize", num_settings, settings)) == NULL)
2239     val = "1m";
2240 
2241   cgiSetVariable("MAX_LOG_SIZE", val);
2242 
2243   cupsFreeOptions(num_settings, settings);
2244 
2245  /*
2246   * Finally, show the main menu template...
2247   */
2248 
2249   cgiStartHTML(cgiText(_("Administration")));
2250 
2251   cgiCopyTemplateLang("admin.tmpl");
2252 
2253   cgiEndHTML();
2254 }
2255 
2256 
2257 /*
2258  * 'do_set_allowed_users()' - Set the allowed/denied users for a queue.
2259  */
2260 
2261 static void
do_set_allowed_users(http_t * http)2262 do_set_allowed_users(http_t *http)	/* I - HTTP connection */
2263 {
2264   int		i;			/* Looping var */
2265   ipp_t		*request,		/* IPP request */
2266 		*response;		/* IPP response */
2267   char		uri[HTTP_MAX_URI];	/* Printer URI */
2268   const char	*printer,		/* Printer name (purge-jobs) */
2269 		*is_class,		/* Is a class? */
2270 		*users,			/* List of users or groups */
2271 		*type;			/* Allow/deny type */
2272   int		num_users;		/* Number of users */
2273   char		*ptr,			/* Pointer into users string */
2274 		*end,			/* Pointer to end of users string */
2275 		quote;			/* Quote character */
2276   ipp_attribute_t *attr;		/* Attribute */
2277   static const char * const attrs[] =	/* Requested attributes */
2278 		{
2279 		  "requesting-user-name-allowed",
2280 		  "requesting-user-name-denied"
2281 		};
2282 
2283 
2284   is_class = cgiGetVariable("IS_CLASS");
2285   printer  = cgiGetVariable("PRINTER_NAME");
2286 
2287   if (!printer)
2288   {
2289     cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2290     cgiStartHTML(cgiText(_("Set Allowed Users")));
2291     cgiCopyTemplateLang("error.tmpl");
2292     cgiEndHTML();
2293     return;
2294   }
2295 
2296   users = cgiGetVariable("users");
2297   type  = cgiGetVariable("type");
2298 
2299   if (!users || !type ||
2300       (strcmp(type, "requesting-user-name-allowed") &&
2301        strcmp(type, "requesting-user-name-denied")))
2302   {
2303    /*
2304     * Build a Get-Printer-Attributes request, which requires the following
2305     * attributes:
2306     *
2307     *    attributes-charset
2308     *    attributes-natural-language
2309     *    printer-uri
2310     *    requested-attributes
2311     */
2312 
2313     request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
2314 
2315     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2316                      "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2317 		     printer);
2318     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2319         	 NULL, uri);
2320 
2321     ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2322                   "requested-attributes",
2323 		  (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
2324 
2325    /*
2326     * Do the request and get back a response...
2327     */
2328 
2329     if ((response = cupsDoRequest(http, request, "/")) != NULL)
2330     {
2331       cgiSetIPPVars(response, NULL, NULL, NULL, 0);
2332 
2333       ippDelete(response);
2334     }
2335 
2336     cgiStartHTML(cgiText(_("Set Allowed Users")));
2337 
2338     if (cupsLastError() == IPP_NOT_AUTHORIZED)
2339     {
2340       puts("Status: 401\n");
2341       exit(0);
2342     }
2343     else if (cupsLastError() > IPP_OK_CONFLICT)
2344       cgiShowIPPError(_("Unable to get printer attributes"));
2345     else
2346       cgiCopyTemplateLang("users.tmpl");
2347 
2348     cgiEndHTML();
2349   }
2350   else
2351   {
2352    /*
2353     * Save the changes...
2354     */
2355 
2356     for (num_users = 0, ptr = (char *)users; *ptr; num_users ++)
2357     {
2358      /*
2359       * Skip whitespace and commas...
2360       */
2361 
2362       while (*ptr == ',' || isspace(*ptr & 255))
2363 	ptr ++;
2364 
2365       if (!*ptr)
2366         break;
2367 
2368       if (*ptr == '\'' || *ptr == '\"')
2369       {
2370        /*
2371 	* Scan quoted name...
2372 	*/
2373 
2374 	quote = *ptr++;
2375 
2376 	for (end = ptr; *end; end ++)
2377 	  if (*end == quote)
2378 	    break;
2379       }
2380       else
2381       {
2382        /*
2383 	* Scan space or comma-delimited name...
2384 	*/
2385 
2386         for (end = ptr; *end; end ++)
2387 	  if (isspace(*end & 255) || *end == ',')
2388 	    break;
2389       }
2390 
2391      /*
2392       * Advance to the next name...
2393       */
2394 
2395       ptr = end;
2396     }
2397 
2398    /*
2399     * Build a CUPS-Add-Printer/Class request, which requires the following
2400     * attributes:
2401     *
2402     *    attributes-charset
2403     *    attributes-natural-language
2404     *    printer-uri
2405     *    requesting-user-name-{allowed,denied}
2406     */
2407 
2408     request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
2409 
2410     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2411                      "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2412 		     printer);
2413     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2414         	 NULL, uri);
2415 
2416     if (num_users == 0)
2417       ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2418                    "requesting-user-name-allowed", NULL, "all");
2419     else
2420     {
2421       attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
2422                            type, num_users, NULL, NULL);
2423 
2424       for (i = 0, ptr = (char *)users; *ptr; i ++)
2425       {
2426        /*
2427         * Skip whitespace and commas...
2428 	*/
2429 
2430         while (*ptr == ',' || isspace(*ptr & 255))
2431 	  ptr ++;
2432 
2433         if (!*ptr)
2434 	  break;
2435 
2436         if (*ptr == '\'' || *ptr == '\"')
2437 	{
2438 	 /*
2439 	  * Scan quoted name...
2440 	  */
2441 
2442 	  quote = *ptr++;
2443 
2444 	  for (end = ptr; *end; end ++)
2445 	    if (*end == quote)
2446 	      break;
2447 	}
2448 	else
2449 	{
2450 	 /*
2451 	  * Scan space or comma-delimited name...
2452 	  */
2453 
2454           for (end = ptr; *end; end ++)
2455 	    if (isspace(*end & 255) || *end == ',')
2456 	      break;
2457         }
2458 
2459        /*
2460         * Terminate the name...
2461 	*/
2462 
2463         if (*end)
2464           *end++ = '\0';
2465 
2466        /*
2467         * Add the name...
2468 	*/
2469 
2470         ippSetString(request, &attr, i, ptr);
2471 
2472        /*
2473         * Advance to the next name...
2474 	*/
2475 
2476         ptr = end;
2477       }
2478     }
2479 
2480    /*
2481     * Do the request and get back a response...
2482     */
2483 
2484     ippDelete(cupsDoRequest(http, request, "/admin/"));
2485 
2486     if (cupsLastError() == IPP_NOT_AUTHORIZED)
2487     {
2488       puts("Status: 401\n");
2489       exit(0);
2490     }
2491     else if (cupsLastError() > IPP_OK_CONFLICT)
2492     {
2493       cgiStartHTML(cgiText(_("Set Allowed Users")));
2494       cgiShowIPPError(_("Unable to change printer"));
2495     }
2496     else
2497     {
2498      /*
2499       * Redirect successful updates back to the printer page...
2500       */
2501 
2502       char	url[1024],		/* Printer/class URL */
2503 		refresh[1024];		/* Refresh URL */
2504 
2505 
2506       cgiRewriteURL(uri, url, sizeof(url), NULL);
2507       cgiFormEncode(uri, url, sizeof(uri));
2508       snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s",
2509                uri);
2510       cgiSetVariable("refresh_page", refresh);
2511 
2512       cgiStartHTML(cgiText(_("Set Allowed Users")));
2513 
2514       cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
2515                                      "printer-modified.tmpl");
2516     }
2517 
2518     cgiEndHTML();
2519   }
2520 }
2521 
2522 
2523 /*
2524  * 'do_set_default()' - Set the server default printer/class.
2525  */
2526 
2527 static void
do_set_default(http_t * http)2528 do_set_default(http_t *http)		/* I - HTTP connection */
2529 {
2530   const char	*title;			/* Page title */
2531   ipp_t		*request;		/* IPP request */
2532   char		uri[HTTP_MAX_URI];	/* Printer URI */
2533   const char	*printer,		/* Printer name (purge-jobs) */
2534 		*is_class;		/* Is a class? */
2535 
2536 
2537   is_class = cgiGetVariable("IS_CLASS");
2538   printer  = cgiGetVariable("PRINTER_NAME");
2539   title    = cgiText(_("Set As Server Default"));
2540 
2541   if (!printer)
2542   {
2543     cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2544     cgiStartHTML(title);
2545     cgiCopyTemplateLang("error.tmpl");
2546     cgiEndHTML();
2547     return;
2548   }
2549 
2550  /*
2551   * Build a printer request, which requires the following
2552   * attributes:
2553   *
2554   *    attributes-charset
2555   *    attributes-natural-language
2556   *    printer-uri
2557   */
2558 
2559   request = ippNewRequest(CUPS_SET_DEFAULT);
2560 
2561   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2562                    "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2563 		   printer);
2564   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2565                NULL, uri);
2566 
2567  /*
2568   * Do the request and get back a response...
2569   */
2570 
2571   ippDelete(cupsDoRequest(http, request, "/admin/"));
2572 
2573   if (cupsLastError() == IPP_NOT_AUTHORIZED)
2574   {
2575     puts("Status: 401\n");
2576     exit(0);
2577   }
2578   else if (cupsLastError() > IPP_OK_CONFLICT)
2579   {
2580     cgiStartHTML(title);
2581     cgiShowIPPError(_("Unable to set server default"));
2582   }
2583   else
2584   {
2585    /*
2586     * Redirect successful updates back to the printer page...
2587     */
2588 
2589     char	url[1024],		/* Printer/class URL */
2590 		refresh[1024];		/* Refresh URL */
2591 
2592 
2593     cgiRewriteURL(uri, url, sizeof(url), NULL);
2594     cgiFormEncode(uri, url, sizeof(uri));
2595     snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri);
2596     cgiSetVariable("refresh_page", refresh);
2597 
2598     cgiStartHTML(title);
2599     cgiCopyTemplateLang("printer-default.tmpl");
2600   }
2601 
2602   cgiEndHTML();
2603 }
2604 
2605 
2606 /*
2607  * 'do_set_options()' - Configure the default options for a queue.
2608  */
2609 
2610 static void
do_set_options(http_t * http,int is_class)2611 do_set_options(http_t *http,		/* I - HTTP connection */
2612                int    is_class)		/* I - Set options for class? */
2613 {
2614   int		i, j, k, m;		/* Looping vars */
2615   int		have_options;		/* Have options? */
2616   ipp_t		*request,		/* IPP request */
2617 		*response;		/* IPP response */
2618   ipp_attribute_t *attr;		/* IPP attribute */
2619   char		uri[HTTP_MAX_URI];	/* Job URI */
2620   const char	*var;			/* Variable value */
2621   const char	*printer;		/* Printer printer name */
2622   const char	*filename;		/* PPD filename */
2623   char		tempfile[1024];		/* Temporary filename */
2624   cups_file_t	*in,			/* Input file */
2625 		*out;			/* Output file */
2626   char		line[1024],		/* Line from PPD file */
2627 		value[1024],		/* Option value */
2628 		keyword[1024],		/* Keyword from Default line */
2629 		*keyptr;		/* Pointer into keyword... */
2630   ppd_file_t	*ppd;			/* PPD file */
2631   ppd_group_t	*group;			/* Option group */
2632   ppd_option_t	*option;		/* Option */
2633   ppd_coption_t	*coption;		/* Custom option */
2634   ppd_cparam_t	*cparam;		/* Custom parameter */
2635   ppd_attr_t	*ppdattr;		/* PPD attribute */
2636   const char	*title;			/* Page title */
2637 
2638 
2639   title = cgiText(is_class ? _("Set Class Options") : _("Set Printer Options"));
2640 
2641   fprintf(stderr, "DEBUG: do_set_options(http=%p, is_class=%d)\n", http,
2642           is_class);
2643 
2644  /*
2645   * Get the printer name...
2646   */
2647 
2648   if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL)
2649     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2650                      "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
2651 		     printer);
2652   else
2653   {
2654     cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
2655     cgiStartHTML(title);
2656     cgiCopyTemplateLang("error.tmpl");
2657     cgiEndHTML();
2658     return;
2659   }
2660 
2661   fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri);
2662 
2663  /*
2664   * If the user clicks on the Auto-Configure button, send an AutoConfigure
2665   * command file to the printer...
2666   */
2667 
2668   if (cgiGetVariable("AUTOCONFIGURE"))
2669   {
2670     cgiPrintCommand(http, printer, "AutoConfigure", "Set Default Options");
2671     return;
2672   }
2673 
2674  /*
2675   * Get the PPD file...
2676   */
2677 
2678   if (is_class)
2679     filename = NULL;
2680   else
2681     filename = cupsGetPPD2(http, printer);
2682 
2683   if (filename)
2684   {
2685     fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename);
2686 
2687     if ((ppd = ppdOpenFile(filename)) == NULL)
2688     {
2689       cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i)));
2690       cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file")));
2691       cgiStartHTML(title);
2692       cgiCopyTemplateLang("error.tmpl");
2693       cgiEndHTML();
2694       return;
2695     }
2696   }
2697   else
2698   {
2699     fputs("DEBUG: No PPD file\n", stderr);
2700     ppd = NULL;
2701   }
2702 
2703   if (cgiGetVariable("job_sheets_start") != NULL ||
2704       cgiGetVariable("job_sheets_end") != NULL)
2705     have_options = 1;
2706   else
2707     have_options = 0;
2708 
2709   if (ppd)
2710   {
2711     ppdMarkDefaults(ppd);
2712 
2713     for (option = ppdFirstOption(ppd);
2714          option;
2715 	 option = ppdNextOption(ppd))
2716     {
2717       if ((var = cgiGetVariable(option->keyword)) != NULL)
2718       {
2719 	have_options = 1;
2720 	ppdMarkOption(ppd, option->keyword, var);
2721 	fprintf(stderr, "DEBUG: Set %s to %s...\n", option->keyword, var);
2722       }
2723       else
2724         fprintf(stderr, "DEBUG: Didn't find %s...\n", option->keyword);
2725     }
2726   }
2727 
2728   if (!have_options || ppdConflicts(ppd))
2729   {
2730    /*
2731     * Show the options to the user...
2732     */
2733 
2734     fputs("DEBUG: Showing options...\n", stderr);
2735 
2736    /*
2737     * Show auto-configure button if supported...
2738     */
2739 
2740     if (ppd)
2741     {
2742       if (ppd->num_filters == 0 ||
2743           ((ppdattr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL &&
2744            ppdattr->value && strstr(ppdattr->value, "AutoConfigure")))
2745         cgiSetVariable("HAVE_AUTOCONFIGURE", "YES");
2746       else
2747       {
2748         for (i = 0; i < ppd->num_filters; i ++)
2749 	  if (!strncmp(ppd->filters[i], "application/vnd.cups-postscript", 31))
2750 	  {
2751 	    cgiSetVariable("HAVE_AUTOCONFIGURE", "YES");
2752 	    break;
2753 	  }
2754       }
2755     }
2756 
2757    /*
2758     * Get the printer attributes...
2759     */
2760 
2761     request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
2762 
2763     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
2764                      "localhost", 0, "/printers/%s", printer);
2765     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
2766                  NULL, uri);
2767 
2768     response = cupsDoRequest(http, request, "/");
2769 
2770    /*
2771     * List the groups used as "tabs"...
2772     */
2773 
2774     i = 0;
2775 
2776     if (ppd)
2777     {
2778       for (group = ppd->groups;
2779 	   i < ppd->num_groups;
2780 	   i ++, group ++)
2781       {
2782         cgiSetArray("GROUP_ID", i, group->name);
2783 
2784 	if (!strcmp(group->name, "InstallableOptions"))
2785 	  cgiSetArray("GROUP", i, cgiText(_("Options Installed")));
2786 	else
2787 	  cgiSetArray("GROUP", i, group->text);
2788       }
2789     }
2790 
2791     if (ippFindAttribute(response, "job-sheets-supported", IPP_TAG_ZERO))
2792     {
2793       cgiSetArray("GROUP_ID", i, "CUPS_BANNERS");
2794       cgiSetArray("GROUP", i ++, cgiText(_("Banners")));
2795     }
2796 
2797     if (ippFindAttribute(response, "printer-error-policy-supported",
2798 			 IPP_TAG_ZERO) ||
2799 	ippFindAttribute(response, "printer-op-policy-supported",
2800 			 IPP_TAG_ZERO))
2801     {
2802       cgiSetArray("GROUP_ID", i, "CUPS_POLICIES");
2803       cgiSetArray("GROUP", i ++, cgiText(_("Policies")));
2804     }
2805 
2806     if ((attr = ippFindAttribute(response, "port-monitor-supported",
2807                                  IPP_TAG_NAME)) != NULL && attr->num_values > 1)
2808     {
2809       cgiSetArray("GROUP_ID", i, "CUPS_PORT_MONITOR");
2810       cgiSetArray("GROUP", i, cgiText(_("Port Monitor")));
2811     }
2812 
2813     cgiStartHTML(cgiText(_("Set Printer Options")));
2814     cgiCopyTemplateLang("set-printer-options-header.tmpl");
2815 
2816     if (ppd)
2817     {
2818       ppdLocalize(ppd);
2819 
2820       if (ppdConflicts(ppd))
2821       {
2822 	for (i = ppd->num_groups, k = 0, group = ppd->groups;
2823 	     i > 0;
2824 	     i --, group ++)
2825 	  for (j = group->num_options, option = group->options;
2826 	       j > 0;
2827 	       j --, option ++)
2828 	    if (option->conflicted)
2829 	    {
2830 	      cgiSetArray("ckeyword", k, option->keyword);
2831 	      cgiSetArray("ckeytext", k, option->text);
2832 
2833 	      for (m = 0; m < option->num_choices; m ++)
2834 	      {
2835 	        if (option->choices[m].marked)
2836 	        {
2837 	          cgiSetArray("cchoice", k, option->choices[m].text);
2838 	          break;
2839 	        }
2840               }
2841 
2842 	      k ++;
2843 	    }
2844 
2845 	cgiCopyTemplateLang("option-conflict.tmpl");
2846       }
2847 
2848       for (i = ppd->num_groups, group = ppd->groups;
2849 	   i > 0;
2850 	   i --, group ++)
2851       {
2852 	for (j = group->num_options, option = group->options;
2853 	     j > 0;
2854 	     j --, option ++)
2855 	{
2856 	  if (!strcmp(option->keyword, "PageRegion"))
2857 	    continue;
2858 
2859 	  if (option->num_choices > 1)
2860 	    break;
2861 	}
2862 
2863         if (j == 0)
2864 	  continue;
2865 
2866         cgiSetVariable("GROUP_ID", group->name);
2867 
2868 	if (!strcmp(group->name, "InstallableOptions"))
2869 	  cgiSetVariable("GROUP", cgiText(_("Options Installed")));
2870 	else
2871 	  cgiSetVariable("GROUP", group->text);
2872 
2873 	cgiCopyTemplateLang("option-header.tmpl");
2874 
2875 	for (j = group->num_options, option = group->options;
2876 	     j > 0;
2877 	     j --, option ++)
2878 	{
2879 	  if (!strcmp(option->keyword, "PageRegion") || option->num_choices < 2)
2880 	    continue;
2881 
2882 	  cgiSetVariable("KEYWORD", option->keyword);
2883 	  cgiSetVariable("KEYTEXT", option->text);
2884 
2885 	  if (option->conflicted)
2886 	    cgiSetVariable("CONFLICTED", "1");
2887 	  else
2888 	    cgiSetVariable("CONFLICTED", "0");
2889 
2890 	  cgiSetSize("CHOICES", 0);
2891 	  cgiSetSize("TEXT", 0);
2892 	  for (k = 0, m = 0; k < option->num_choices; k ++)
2893 	  {
2894 	    cgiSetArray("CHOICES", m, option->choices[k].choice);
2895 	    cgiSetArray("TEXT", m, option->choices[k].text);
2896 
2897 	    m ++;
2898 
2899 	    if (option->choices[k].marked)
2900 	      cgiSetVariable("DEFCHOICE", option->choices[k].choice);
2901 	  }
2902 
2903 	  cgiSetSize("PARAMS", 0);
2904 	  cgiSetSize("PARAMTEXT", 0);
2905 	  cgiSetSize("PARAMVALUE", 0);
2906 	  cgiSetSize("INPUTTYPE", 0);
2907 
2908 	  if ((coption = ppdFindCustomOption(ppd, option->keyword)))
2909 	  {
2910             const char *units = NULL;	/* Units value, if any */
2911 
2912 	    cgiSetVariable("ISCUSTOM", "1");
2913 
2914 	    for (cparam = ppdFirstCustomParam(coption), m = 0;
2915 		 cparam;
2916 		 cparam = ppdNextCustomParam(coption), m ++)
2917 	    {
2918 	      if (!_cups_strcasecmp(option->keyword, "PageSize") &&
2919 	          _cups_strcasecmp(cparam->name, "Width") &&
2920 		  _cups_strcasecmp(cparam->name, "Height"))
2921               {
2922 	        m --;
2923 		continue;
2924               }
2925 
2926 	      cgiSetArray("PARAMS", m, cparam->name);
2927 	      cgiSetArray("PARAMTEXT", m, cparam->text);
2928 	      cgiSetArray("INPUTTYPE", m, "text");
2929 
2930 	      switch (cparam->type)
2931 	      {
2932 	        case PPD_CUSTOM_UNKNOWN :
2933 	            break;
2934 
2935 		case PPD_CUSTOM_POINTS :
2936 		    if (!_cups_strncasecmp(option->defchoice, "Custom.", 7))
2937 		    {
2938 		      units = option->defchoice + strlen(option->defchoice) - 2;
2939 
2940 		      if (strcmp(units, "mm") && strcmp(units, "cm") &&
2941 		          strcmp(units, "in") && strcmp(units, "ft"))
2942 		      {
2943 		        if (units[1] == 'm')
2944 			  units ++;
2945 			else
2946 			  units = "pt";
2947 		      }
2948 		    }
2949 		    else
2950 		      units = "pt";
2951 
2952                     if (!strcmp(units, "mm"))
2953 		      snprintf(value, sizeof(value), "%g",
2954 		               cparam->current.custom_points / 72.0 * 25.4);
2955                     else if (!strcmp(units, "cm"))
2956 		      snprintf(value, sizeof(value), "%g",
2957 		               cparam->current.custom_points / 72.0 * 2.54);
2958                     else if (!strcmp(units, "in"))
2959 		      snprintf(value, sizeof(value), "%g",
2960 		               cparam->current.custom_points / 72.0);
2961                     else if (!strcmp(units, "ft"))
2962 		      snprintf(value, sizeof(value), "%g",
2963 		               cparam->current.custom_points / 72.0 / 12.0);
2964                     else if (!strcmp(units, "m"))
2965 		      snprintf(value, sizeof(value), "%g",
2966 		               cparam->current.custom_points / 72.0 * 0.0254);
2967                     else
2968 		      snprintf(value, sizeof(value), "%g",
2969 		               cparam->current.custom_points);
2970 		    cgiSetArray("PARAMVALUE", m, value);
2971 		    break;
2972 
2973 		case PPD_CUSTOM_CURVE :
2974 		case PPD_CUSTOM_INVCURVE :
2975 		case PPD_CUSTOM_REAL :
2976 		    snprintf(value, sizeof(value), "%g",
2977 		             cparam->current.custom_real);
2978 		    cgiSetArray("PARAMVALUE", m, value);
2979 		    break;
2980 
2981 		case PPD_CUSTOM_INT:
2982 		    snprintf(value, sizeof(value), "%d",
2983 		             cparam->current.custom_int);
2984 		    cgiSetArray("PARAMVALUE", m, value);
2985 		    break;
2986 
2987 		case PPD_CUSTOM_PASSCODE:
2988 		case PPD_CUSTOM_PASSWORD:
2989 		    if (cparam->current.custom_password)
2990 		      cgiSetArray("PARAMVALUE", m,
2991 		                  cparam->current.custom_password);
2992 		    else
2993 		      cgiSetArray("PARAMVALUE", m, "");
2994 		    cgiSetArray("INPUTTYPE", m, "password");
2995 		    break;
2996 
2997 		case PPD_CUSTOM_STRING:
2998 		    if (cparam->current.custom_string)
2999 		      cgiSetArray("PARAMVALUE", m,
3000 		                  cparam->current.custom_string);
3001 		    else
3002 		      cgiSetArray("PARAMVALUE", m, "");
3003 		    break;
3004 	      }
3005 	    }
3006 
3007             if (units)
3008 	    {
3009 	      cgiSetArray("PARAMS", m, "Units");
3010 	      cgiSetArray("PARAMTEXT", m, cgiText(_("Units")));
3011 	      cgiSetArray("PARAMVALUE", m, units);
3012 	    }
3013 	  }
3014 	  else
3015 	    cgiSetVariable("ISCUSTOM", "0");
3016 
3017 	  switch (option->ui)
3018 	  {
3019 	    case PPD_UI_BOOLEAN :
3020 		cgiCopyTemplateLang("option-boolean.tmpl");
3021 		break;
3022 	    case PPD_UI_PICKONE :
3023 		cgiCopyTemplateLang("option-pickone.tmpl");
3024 		break;
3025 	    case PPD_UI_PICKMANY :
3026 		cgiCopyTemplateLang("option-pickmany.tmpl");
3027 		break;
3028 	  }
3029 	}
3030 
3031 	cgiCopyTemplateLang("option-trailer.tmpl");
3032       }
3033     }
3034 
3035     if ((attr = ippFindAttribute(response, "job-sheets-supported",
3036 				 IPP_TAG_ZERO)) != NULL)
3037     {
3038      /*
3039       * Add the job sheets options...
3040       */
3041 
3042       cgiSetVariable("GROUP_ID", "CUPS_BANNERS");
3043       cgiSetVariable("GROUP", cgiText(_("Banners")));
3044       cgiCopyTemplateLang("option-header.tmpl");
3045 
3046       cgiSetSize("CHOICES", attr->num_values);
3047       cgiSetSize("TEXT", attr->num_values);
3048       for (k = 0; k < attr->num_values; k ++)
3049       {
3050 	cgiSetArray("CHOICES", k, attr->values[k].string.text);
3051 	cgiSetArray("TEXT", k, attr->values[k].string.text);
3052       }
3053 
3054       attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO);
3055 
3056       cgiSetVariable("KEYWORD", "job_sheets_start");
3057       cgiSetVariable("KEYTEXT",
3058                      /* TRANSLATORS: Banner/cover sheet before the print job. */
3059                      cgiText(_("Starting Banner")));
3060       cgiSetVariable("DEFCHOICE", attr != NULL ?
3061 				  attr->values[0].string.text : "");
3062 
3063       cgiCopyTemplateLang("option-pickone.tmpl");
3064 
3065       cgiSetVariable("KEYWORD", "job_sheets_end");
3066       cgiSetVariable("KEYTEXT",
3067                      /* TRANSLATORS: Banner/cover sheet after the print job. */
3068                      cgiText(_("Ending Banner")));
3069       cgiSetVariable("DEFCHOICE", attr != NULL && attr->num_values > 1 ?
3070 				  attr->values[1].string.text : "");
3071 
3072       cgiCopyTemplateLang("option-pickone.tmpl");
3073 
3074       cgiCopyTemplateLang("option-trailer.tmpl");
3075     }
3076 
3077     if (ippFindAttribute(response, "printer-error-policy-supported",
3078 			 IPP_TAG_ZERO) ||
3079 	ippFindAttribute(response, "printer-op-policy-supported",
3080 			 IPP_TAG_ZERO))
3081     {
3082      /*
3083       * Add the error and operation policy options...
3084       */
3085 
3086       cgiSetVariable("GROUP_ID", "CUPS_POLICIES");
3087       cgiSetVariable("GROUP", cgiText(_("Policies")));
3088       cgiCopyTemplateLang("option-header.tmpl");
3089 
3090      /*
3091       * Error policy...
3092       */
3093 
3094       attr = ippFindAttribute(response, "printer-error-policy-supported",
3095 			      IPP_TAG_ZERO);
3096 
3097       if (attr)
3098       {
3099 	cgiSetSize("CHOICES", attr->num_values);
3100 	cgiSetSize("TEXT", attr->num_values);
3101 	for (k = 0; k < attr->num_values; k ++)
3102 	{
3103 	  cgiSetArray("CHOICES", k, attr->values[k].string.text);
3104 	  cgiSetArray("TEXT", k, attr->values[k].string.text);
3105 	}
3106 
3107 	attr = ippFindAttribute(response, "printer-error-policy",
3108 				IPP_TAG_ZERO);
3109 
3110 	cgiSetVariable("KEYWORD", "printer_error_policy");
3111 	cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
3112 	cgiSetVariable("DEFCHOICE", attr == NULL ?
3113 				    "" : attr->values[0].string.text);
3114       }
3115 
3116       cgiCopyTemplateLang("option-pickone.tmpl");
3117 
3118      /*
3119       * Operation policy...
3120       */
3121 
3122       attr = ippFindAttribute(response, "printer-op-policy-supported",
3123 			      IPP_TAG_ZERO);
3124 
3125       if (attr)
3126       {
3127 	cgiSetSize("CHOICES", attr->num_values);
3128 	cgiSetSize("TEXT", attr->num_values);
3129 	for (k = 0; k < attr->num_values; k ++)
3130 	{
3131 	  cgiSetArray("CHOICES", k, attr->values[k].string.text);
3132 	  cgiSetArray("TEXT", k, attr->values[k].string.text);
3133 	}
3134 
3135 	attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO);
3136 
3137 	cgiSetVariable("KEYWORD", "printer_op_policy");
3138 	cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy")));
3139 	cgiSetVariable("DEFCHOICE", attr == NULL ?
3140 				    "" : attr->values[0].string.text);
3141 
3142 	cgiCopyTemplateLang("option-pickone.tmpl");
3143       }
3144 
3145       cgiCopyTemplateLang("option-trailer.tmpl");
3146     }
3147 
3148    /*
3149     * Binary protocol support...
3150     */
3151 
3152     if ((attr = ippFindAttribute(response, "port-monitor-supported",
3153                                  IPP_TAG_NAME)) != NULL && attr->num_values > 1)
3154     {
3155       cgiSetVariable("GROUP_ID", "CUPS_PORT_MONITOR");
3156       cgiSetVariable("GROUP", cgiText(_("Port Monitor")));
3157 
3158       cgiSetSize("CHOICES", attr->num_values);
3159       cgiSetSize("TEXT", attr->num_values);
3160 
3161       for (i = 0; i < attr->num_values; i ++)
3162       {
3163         cgiSetArray("CHOICES", i, attr->values[i].string.text);
3164         cgiSetArray("TEXT", i, attr->values[i].string.text);
3165       }
3166 
3167       attr = ippFindAttribute(response, "port-monitor", IPP_TAG_NAME);
3168       cgiSetVariable("KEYWORD", "port_monitor");
3169       cgiSetVariable("KEYTEXT", cgiText(_("Port Monitor")));
3170       cgiSetVariable("DEFCHOICE", attr ? attr->values[0].string.text : "none");
3171 
3172       cgiCopyTemplateLang("option-header.tmpl");
3173       cgiCopyTemplateLang("option-pickone.tmpl");
3174       cgiCopyTemplateLang("option-trailer.tmpl");
3175     }
3176 
3177     cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
3178     cgiEndHTML();
3179 
3180     ippDelete(response);
3181   }
3182   else
3183   {
3184    /*
3185     * Set default options...
3186     */
3187 
3188     fputs("DEBUG: Setting options...\n", stderr);
3189 
3190     if (filename)
3191     {
3192       out = cupsTempFile2(tempfile, sizeof(tempfile));
3193       in  = cupsFileOpen(filename, "r");
3194 
3195       if (!in || !out)
3196       {
3197 	cgiSetVariable("ERROR", strerror(errno));
3198 	cgiStartHTML(cgiText(_("Set Printer Options")));
3199 	cgiCopyTemplateLang("error.tmpl");
3200 	cgiEndHTML();
3201 
3202 	if (in)
3203 	  cupsFileClose(in);
3204 
3205 	if (out)
3206 	{
3207 	  cupsFileClose(out);
3208 	  unlink(tempfile);
3209 	}
3210 
3211 	unlink(filename);
3212 	return;
3213       }
3214 
3215       while (cupsFileGets(in, line, sizeof(line)))
3216       {
3217 	if (!strncmp(line, "*cupsProtocol:", 14))
3218 	  continue;
3219 	else if (strncmp(line, "*Default", 8))
3220 	  cupsFilePrintf(out, "%s\n", line);
3221 	else
3222 	{
3223 	 /*
3224 	  * Get default option name...
3225 	  */
3226 
3227 	  strlcpy(keyword, line + 8, sizeof(keyword));
3228 
3229 	  for (keyptr = keyword; *keyptr; keyptr ++)
3230 	    if (*keyptr == ':' || isspace(*keyptr & 255))
3231 	      break;
3232 
3233 	  *keyptr = '\0';
3234 
3235 	  if (!strcmp(keyword, "PageRegion") ||
3236 	      !strcmp(keyword, "PaperDimension") ||
3237 	      !strcmp(keyword, "ImageableArea"))
3238 	    var = get_option_value(ppd, "PageSize", value, sizeof(value));
3239 	  else
3240 	    var = get_option_value(ppd, keyword, value, sizeof(value));
3241 
3242 	  if (!var)
3243 	    cupsFilePrintf(out, "%s\n", line);
3244 	  else
3245 	    cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
3246 	}
3247       }
3248 
3249       cupsFileClose(in);
3250       cupsFileClose(out);
3251     }
3252     else
3253     {
3254      /*
3255       * Make sure temporary filename is cleared when there is no PPD...
3256       */
3257 
3258       tempfile[0] = '\0';
3259     }
3260 
3261    /*
3262     * Build a CUPS_ADD_MODIFY_CLASS/PRINTER request, which requires the
3263     * following attributes:
3264     *
3265     *    attributes-charset
3266     *    attributes-natural-language
3267     *    printer-uri
3268     *    job-sheets-default
3269     *    printer-error-policy
3270     *    printer-op-policy
3271     *    [ppd file]
3272     */
3273 
3274     request = ippNewRequest(is_class ? CUPS_ADD_MODIFY_CLASS :
3275                                        CUPS_ADD_MODIFY_PRINTER);
3276 
3277     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3278                  NULL, uri);
3279 
3280     attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3281                          "job-sheets-default", 2, NULL, NULL);
3282     ippSetString(request, &attr, 0, cgiGetVariable("job_sheets_start"));
3283     ippSetString(request, &attr, 1, cgiGetVariable("job_sheets_end"));
3284 
3285     if ((var = cgiGetVariable("printer_error_policy")) != NULL)
3286       ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3287 		   "printer-error-policy", NULL, var);
3288 
3289     if ((var = cgiGetVariable("printer_op_policy")) != NULL)
3290       ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3291 		   "printer-op-policy", NULL, var);
3292 
3293     if ((var = cgiGetVariable("port_monitor")) != NULL)
3294       ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME,
3295 		   "port-monitor", NULL, var);
3296 
3297    /*
3298     * Do the request and get back a response...
3299     */
3300 
3301     if (filename)
3302       ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile));
3303     else
3304       ippDelete(cupsDoRequest(http, request, "/admin/"));
3305 
3306     if (cupsLastError() == IPP_NOT_AUTHORIZED)
3307     {
3308       puts("Status: 401\n");
3309       exit(0);
3310     }
3311     else if (cupsLastError() > IPP_OK_CONFLICT)
3312     {
3313       cgiStartHTML(title);
3314       cgiShowIPPError(_("Unable to set options"));
3315     }
3316     else
3317     {
3318      /*
3319       * Redirect successful updates back to the printer page...
3320       */
3321 
3322       char	refresh[1024];		/* Refresh URL */
3323 
3324 
3325       cgiFormEncode(uri, printer, sizeof(uri));
3326       snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/%s/%s",
3327 	       is_class ? "classes" : "printers", uri);
3328       cgiSetVariable("refresh_page", refresh);
3329 
3330       cgiStartHTML(title);
3331 
3332       cgiCopyTemplateLang("printer-configured.tmpl");
3333     }
3334 
3335     cgiEndHTML();
3336 
3337     if (filename)
3338       unlink(tempfile);
3339   }
3340 
3341   if (filename)
3342     unlink(filename);
3343 }
3344 
3345 
3346 /*
3347  * 'do_set_sharing()' - Set printer-is-shared value.
3348  */
3349 
3350 static void
do_set_sharing(http_t * http)3351 do_set_sharing(http_t *http)		/* I - HTTP connection */
3352 {
3353   ipp_t		*request,		/* IPP request */
3354 		*response;		/* IPP response */
3355   char		uri[HTTP_MAX_URI];	/* Printer URI */
3356   const char	*printer,		/* Printer name */
3357 		*is_class,		/* Is a class? */
3358 		*shared;		/* Sharing value */
3359 
3360 
3361   is_class = cgiGetVariable("IS_CLASS");
3362   printer  = cgiGetVariable("PRINTER_NAME");
3363   shared   = cgiGetVariable("SHARED");
3364 
3365   if (!printer || !shared)
3366   {
3367     cgiSetVariable("ERROR", cgiText(_("Missing form variable")));
3368     cgiStartHTML(cgiText(_("Set Publishing")));
3369     cgiCopyTemplateLang("error.tmpl");
3370     cgiEndHTML();
3371     return;
3372   }
3373 
3374  /*
3375   * Build a CUPS-Add-Printer/CUPS-Add-Class request, which requires the
3376   * following attributes:
3377   *
3378   *    attributes-charset
3379   *    attributes-natural-language
3380   *    printer-uri
3381   *    printer-is-shared
3382   */
3383 
3384   request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER);
3385 
3386   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3387                    "localhost", 0, is_class ? "/classes/%s" : "/printers/%s",
3388 		   printer);
3389   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
3390                NULL, uri);
3391 
3392   ippAddBoolean(request, IPP_TAG_OPERATION, "printer-is-shared", (char)atoi(shared));
3393 
3394  /*
3395   * Do the request and get back a response...
3396   */
3397 
3398   if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
3399   {
3400     cgiSetIPPVars(response, NULL, NULL, NULL, 0);
3401 
3402     ippDelete(response);
3403   }
3404 
3405   if (cupsLastError() == IPP_NOT_AUTHORIZED)
3406   {
3407     puts("Status: 401\n");
3408     exit(0);
3409   }
3410   else if (cupsLastError() > IPP_OK_CONFLICT)
3411   {
3412     cgiStartHTML(cgiText(_("Set Publishing")));
3413     cgiShowIPPError(_("Unable to change printer-is-shared attribute"));
3414   }
3415   else
3416   {
3417    /*
3418     * Redirect successful updates back to the printer page...
3419     */
3420 
3421     char	url[1024],		/* Printer/class URL */
3422 		refresh[1024];		/* Refresh URL */
3423 
3424 
3425     cgiRewriteURL(uri, url, sizeof(url), NULL);
3426     cgiFormEncode(uri, url, sizeof(uri));
3427     snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri);
3428     cgiSetVariable("refresh_page", refresh);
3429 
3430     cgiStartHTML(cgiText(_("Set Publishing")));
3431     cgiCopyTemplateLang(is_class ? "class-modified.tmpl" :
3432                                    "printer-modified.tmpl");
3433   }
3434 
3435   cgiEndHTML();
3436 }
3437 
3438 
3439 /*
3440  * 'get_option_value()' - Return the value of an option.
3441  *
3442  * This function also handles generation of custom option values.
3443  */
3444 
3445 static char *				/* O - Value string or NULL on error */
get_option_value(ppd_file_t * ppd,const char * name,char * buffer,size_t bufsize)3446 get_option_value(
3447     ppd_file_t    *ppd,			/* I - PPD file */
3448     const char    *name,		/* I - Option name */
3449     char          *buffer,		/* I - String buffer */
3450     size_t        bufsize)		/* I - Size of buffer */
3451 {
3452   char		*bufptr,		/* Pointer into buffer */
3453 		*bufend;		/* End of buffer */
3454   ppd_coption_t *coption;		/* Custom option */
3455   ppd_cparam_t	*cparam;		/* Current custom parameter */
3456   char		keyword[256];		/* Parameter name */
3457   const char	*val,			/* Parameter value */
3458 		*uval;			/* Units value */
3459   long		integer;		/* Integer value */
3460   double	number,			/* Number value */
3461 		number_points;		/* Number in points */
3462 
3463 
3464  /*
3465   * See if we have a custom option choice...
3466   */
3467 
3468   if ((val = cgiGetVariable(name)) == NULL)
3469   {
3470    /*
3471     * Option not found!
3472     */
3473 
3474     return (NULL);
3475   }
3476   else if (_cups_strcasecmp(val, "Custom") ||
3477            (coption = ppdFindCustomOption(ppd, name)) == NULL)
3478   {
3479    /*
3480     * Not a custom choice...
3481     */
3482 
3483     strlcpy(buffer, val, bufsize);
3484     return (buffer);
3485   }
3486 
3487  /*
3488   * OK, we have a custom option choice, format it...
3489   */
3490 
3491   *buffer = '\0';
3492 
3493   if (!strcmp(coption->keyword, "PageSize"))
3494   {
3495     const char	*lval;			/* Length string value */
3496     double	width,			/* Width value */
3497 		width_points,		/* Width in points */
3498 		length,			/* Length value */
3499 		length_points;		/* Length in points */
3500 
3501 
3502     val  = cgiGetVariable("PageSize.Width");
3503     lval = cgiGetVariable("PageSize.Height");
3504     uval = cgiGetVariable("PageSize.Units");
3505 
3506     if (!val || !lval || !uval ||
3507         (width = strtod(val, NULL)) == 0.0 ||
3508         (length = strtod(lval, NULL)) == 0.0 ||
3509         (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") &&
3510 	 strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m")))
3511       return (NULL);
3512 
3513     width_points  = get_points(width, uval);
3514     length_points = get_points(length, uval);
3515 
3516     if (width_points < ppd->custom_min[0] ||
3517         width_points > ppd->custom_max[0] ||
3518         length_points < ppd->custom_min[1] ||
3519 	length_points > ppd->custom_max[1])
3520       return (NULL);
3521 
3522     snprintf(buffer, bufsize, "Custom.%gx%g%s", width, length, uval);
3523   }
3524   else if (cupsArrayCount(coption->params) == 1)
3525   {
3526     cparam = ppdFirstCustomParam(coption);
3527     snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name);
3528 
3529     if ((val = cgiGetVariable(keyword)) == NULL)
3530       return (NULL);
3531 
3532     switch (cparam->type)
3533     {
3534       case PPD_CUSTOM_UNKNOWN :
3535 	  break;
3536 
3537       case PPD_CUSTOM_CURVE :
3538       case PPD_CUSTOM_INVCURVE :
3539       case PPD_CUSTOM_REAL :
3540 	  if ((number = strtod(val, NULL)) == 0.0 ||
3541 	      number < cparam->minimum.custom_real ||
3542 	      number > cparam->maximum.custom_real)
3543 	    return (NULL);
3544 
3545           snprintf(buffer, bufsize, "Custom.%g", number);
3546           break;
3547 
3548       case PPD_CUSTOM_INT :
3549           if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN ||
3550 	      integer == LONG_MAX ||
3551 	      integer < cparam->minimum.custom_int ||
3552 	      integer > cparam->maximum.custom_int)
3553             return (NULL);
3554 
3555           snprintf(buffer, bufsize, "Custom.%ld", integer);
3556           break;
3557 
3558       case PPD_CUSTOM_POINTS :
3559           snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);
3560 
3561 	  if ((number = strtod(val, NULL)) == 0.0 ||
3562 	      (uval = cgiGetVariable(keyword)) == NULL ||
3563 	      (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") &&
3564 	       strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m")))
3565 	    return (NULL);
3566 
3567 	  number_points = get_points(number, uval);
3568 	  if (number_points < cparam->minimum.custom_points ||
3569 	      number_points > cparam->maximum.custom_points)
3570 	    return (NULL);
3571 
3572 	  snprintf(buffer, bufsize, "Custom.%g%s", number, uval);
3573           break;
3574 
3575       case PPD_CUSTOM_PASSCODE :
3576           for (uval = val; *uval; uval ++)
3577 	    if (!isdigit(*uval & 255))
3578 	      return (NULL);
3579 
3580       case PPD_CUSTOM_PASSWORD :
3581       case PPD_CUSTOM_STRING :
3582           integer = (long)strlen(val);
3583 	  if (integer < cparam->minimum.custom_string ||
3584 	      integer > cparam->maximum.custom_string)
3585 	    return (NULL);
3586 
3587           snprintf(buffer, bufsize, "Custom.%s", val);
3588 	  break;
3589     }
3590   }
3591   else
3592   {
3593     const char *prefix = "{";		/* Prefix string */
3594 
3595 
3596     bufptr = buffer;
3597     bufend = buffer + bufsize;
3598 
3599     for (cparam = ppdFirstCustomParam(coption);
3600 	 cparam;
3601 	 cparam = ppdNextCustomParam(coption))
3602     {
3603       snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword,
3604                cparam->name);
3605 
3606       if ((val = cgiGetVariable(keyword)) == NULL)
3607 	return (NULL);
3608 
3609       snprintf(bufptr, (size_t)(bufend - bufptr), "%s%s=", prefix, cparam->name);
3610       bufptr += strlen(bufptr);
3611       prefix = " ";
3612 
3613       switch (cparam->type)
3614       {
3615 	case PPD_CUSTOM_UNKNOWN :
3616 	    break;
3617 
3618 	case PPD_CUSTOM_CURVE :
3619 	case PPD_CUSTOM_INVCURVE :
3620 	case PPD_CUSTOM_REAL :
3621 	    if ((number = strtod(val, NULL)) == 0.0 ||
3622 		number < cparam->minimum.custom_real ||
3623 		number > cparam->maximum.custom_real)
3624 	      return (NULL);
3625 
3626 	    snprintf(bufptr, (size_t)(bufend - bufptr), "%g", number);
3627 	    break;
3628 
3629 	case PPD_CUSTOM_INT :
3630 	    if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN ||
3631 		integer == LONG_MAX ||
3632 		integer < cparam->minimum.custom_int ||
3633 		integer > cparam->maximum.custom_int)
3634 	      return (NULL);
3635 
3636 	    snprintf(bufptr, (size_t)(bufend - bufptr), "%ld", integer);
3637 	    break;
3638 
3639 	case PPD_CUSTOM_POINTS :
3640 	    snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);
3641 
3642 	    if ((number = strtod(val, NULL)) == 0.0 ||
3643 		(uval = cgiGetVariable(keyword)) == NULL ||
3644 		(strcmp(uval, "pt") && strcmp(uval, "in") &&
3645 		 strcmp(uval, "ft") && strcmp(uval, "cm") &&
3646 		 strcmp(uval, "mm") && strcmp(uval, "m")))
3647 	      return (NULL);
3648 
3649 	    number_points = get_points(number, uval);
3650 	    if (number_points < cparam->minimum.custom_points ||
3651 		number_points > cparam->maximum.custom_points)
3652 	      return (NULL);
3653 
3654 	    snprintf(bufptr, (size_t)(bufend - bufptr), "%g%s", number, uval);
3655 	    break;
3656 
3657 	case PPD_CUSTOM_PASSCODE :
3658 	    for (uval = val; *uval; uval ++)
3659 	      if (!isdigit(*uval & 255))
3660 		return (NULL);
3661 
3662 	case PPD_CUSTOM_PASSWORD :
3663 	case PPD_CUSTOM_STRING :
3664 	    integer = (long)strlen(val);
3665 	    if (integer < cparam->minimum.custom_string ||
3666 		integer > cparam->maximum.custom_string)
3667 	      return (NULL);
3668 
3669 	    if ((bufptr + 2) > bufend)
3670 	      return (NULL);
3671 
3672 	    bufend --;
3673 	    *bufptr++ = '\"';
3674 
3675 	    while (*val && bufptr < bufend)
3676 	    {
3677 	      if (*val == '\\' || *val == '\"')
3678 	      {
3679 		if ((bufptr + 1) >= bufend)
3680 		  return (NULL);
3681 
3682 		*bufptr++ = '\\';
3683 	      }
3684 
3685 	      *bufptr++ = *val++;
3686 	    }
3687 
3688 	    if (bufptr >= bufend)
3689 	      return (NULL);
3690 
3691 	    *bufptr++ = '\"';
3692 	    *bufptr   = '\0';
3693 	    bufend ++;
3694 	    break;
3695       }
3696 
3697       bufptr += strlen(bufptr);
3698     }
3699 
3700     if (bufptr == buffer || (bufend - bufptr) < 2)
3701       return (NULL);
3702 
3703     memcpy(bufptr, "}", 2);
3704   }
3705 
3706   return (buffer);
3707 }
3708 
3709 
3710 /*
3711  * 'get_points()' - Get a value in points.
3712  */
3713 
3714 static double				/* O - Number in points */
get_points(double number,const char * uval)3715 get_points(double     number,		/* I - Original number */
3716            const char *uval)		/* I - Units */
3717 {
3718   if (!strcmp(uval, "mm"))		/* Millimeters */
3719     return (number * 72.0 / 25.4);
3720   else if (!strcmp(uval, "cm"))		/* Centimeters */
3721     return (number * 72.0 / 2.54);
3722   else if (!strcmp(uval, "in"))		/* Inches */
3723     return (number * 72.0);
3724   else if (!strcmp(uval, "ft"))		/* Feet */
3725     return (number * 72.0 * 12.0);
3726   else if (!strcmp(uval, "m"))		/* Meters */
3727     return (number * 72.0 / 0.0254);
3728   else					/* Points */
3729     return (number);
3730 }
3731 
3732 
3733 /*
3734  * 'get_printer_ppd()' - Get an IPP Everywhere PPD file for the given URI.
3735  */
3736 
3737 static char *				/* O - Filename or NULL */
get_printer_ppd(const char * uri,char * buffer,size_t bufsize)3738 get_printer_ppd(const char *uri,	/* I - Printer URI */
3739                 char       *buffer,	/* I - Filename buffer */
3740 		size_t     bufsize)	/* I - Size of filename buffer */
3741 {
3742   http_t	*http;			/* Connection to printer */
3743   ipp_t		*request,		/* Get-Printer-Attributes request */
3744 		*response;		/* Get-Printer-Attributes response */
3745   char		resolved[1024],		/* Resolved URI */
3746 		scheme[32],		/* URI scheme */
3747 		userpass[256],		/* Username:password */
3748 		host[256],		/* Hostname */
3749 		resource[256];		/* Resource path */
3750   int		port;			/* Port number */
3751   static const char * const pattrs[] =	/* Printer attributes we need */
3752   {
3753     "all",
3754     "media-col-database"
3755   };
3756 
3757 
3758  /*
3759   * Connect to the printer...
3760   */
3761 
3762   if (strstr(uri, "._tcp"))
3763   {
3764    /*
3765     * Resolve URI...
3766     */
3767 
3768     if (!_httpResolveURI(uri, resolved, sizeof(resolved), _HTTP_RESOLVE_DEFAULT, NULL, NULL))
3769     {
3770       fprintf(stderr, "ERROR: Unable to resolve \"%s\".\n", uri);
3771       return (NULL);
3772     }
3773 
3774     uri = resolved;
3775   }
3776 
3777   if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
3778   {
3779     fprintf(stderr, "ERROR: Bad printer URI \"%s\".\n", uri);
3780     return (NULL);
3781   }
3782 
3783   http = httpConnect2(host, port, NULL, AF_UNSPEC, !strcmp(scheme, "ipps") ? HTTP_ENCRYPTION_ALWAYS : HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
3784   if (!http)
3785   {
3786     fprintf(stderr, "ERROR: Unable to connect to \"%s:%d\": %s\n", host, port, cupsLastErrorString());
3787     return (NULL);
3788   }
3789 
3790  /*
3791   * Send a Get-Printer-Attributes request...
3792   */
3793 
3794   request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
3795   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
3796   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes",  (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
3797   response = cupsDoRequest(http, request, resource);
3798 
3799   if (!_ppdCreateFromIPP(buffer, bufsize, response))
3800     fprintf(stderr, "ERROR: Unable to create PPD file: %s\n", strerror(errno));
3801 
3802   ippDelete(response);
3803   httpClose(http);
3804 
3805   if (buffer[0])
3806     return (buffer);
3807   else
3808     return (NULL);
3809 }
3810