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