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