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