1/*
2 * "$Id: admin.c 11433 2013-11-20 18:57:44Z 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
55static int	current_device = 0;	/* Current device shown */
56
57
58/*
59 * Local functions...
60 */
61
62static 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);
68static void	do_add_rss_subscription(http_t *http);
69static void	do_am_class(http_t *http, int modify);
70static void	do_am_printer(http_t *http, int modify);
71static void	do_cancel_subscription(http_t *http);
72static void	do_config_server(http_t *http);
73static void	do_delete_class(http_t *http);
74static void	do_delete_printer(http_t *http);
75static void	do_export(http_t *http);
76static void	do_list_printers(http_t *http);
77static void	do_menu(http_t *http);
78static void	do_set_allowed_users(http_t *http);
79static void	do_set_default(http_t *http);
80static void	do_set_options(http_t *http, int is_class);
81static void	do_set_sharing(http_t *http);
82static char	*get_option_value(ppd_file_t *ppd, const char *name,
83		                  char *buffer, size_t bufsize);
84static double	get_points(double number, const char *uval);
85
86
87/*
88 * 'main()' - Main entry for CGI.
89 */
90
91int					/* O - Exit status */
92main(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
307static void
308choose_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
351static void
352do_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
547static void
548do_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
845static void
846do_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        strlcpy(make, "HP", sizeof(make));
966      else if (!_cups_strncasecmp(make, "phaser", 6))
967        strlcpy(make, "Xerox", sizeof(make));
968      else if (!_cups_strncasecmp(make, "stylus", 6))
969        strlcpy(make, "Epson", sizeof(make));
970      else
971        strlcpy(make, "Generic", sizeof(make));
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    if (!file)
1352    {
1353      var = cgiGetVariable("PPD_NAME");
1354      if (strcmp(var, "__no_change__"))
1355	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
1356		     NULL, var);
1357    }
1358
1359    ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location",
1360                 NULL, cgiGetVariable("PRINTER_LOCATION"));
1361
1362    ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
1363                 NULL, cgiGetVariable("PRINTER_INFO"));
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
1464static void
1465do_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
1546static void
1547do_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
2080static void
2081do_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
2165static void
2166do_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
2250static void
2251do_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
2385static void
2386do_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
2589static void
2590do_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
2757static void
2758do_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
3023static void
3024do_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
3106static void
3107do_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
3843static void
3844do_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
3938static char *				/* O - Value string or NULL on error */
3939get_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    memcpy(bufptr, "}", 2);
4191  }
4192
4193  return (buffer);
4194}
4195
4196
4197/*
4198 * 'get_points()' - Get a value in points.
4199 */
4200
4201static double				/* O - Number in points */
4202get_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 11433 2013-11-20 18:57:44Z msweet $".
4222 */
4223