]>
Commit | Line | Data |
---|---|---|
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 | 59 | static void do_am_class(http_t *http, cups_lang_t *language, int modify); |
60 | static void do_am_printer(http_t *http, cups_lang_t *language, int modify); | |
3d9e2586 | 61 | static void do_config_printer(http_t *http, cups_lang_t *language); |
b5bfe159 | 62 | static void do_config_server(http_t *http, cups_lang_t *language); |
d4c438d4 | 63 | static void do_delete_class(http_t *http, cups_lang_t *language); |
64 | static void do_delete_printer(http_t *http, cups_lang_t *language); | |
2f2420e8 | 65 | static void do_menu(http_t *http, cups_lang_t *language); |
f63a2256 | 66 | static void do_printer_op(http_t *http, cups_lang_t *language, ipp_op_t op); |
707471a3 | 67 | static void do_set_allowed_users(http_t *http, cups_lang_t *language); |
b5bfe159 | 68 | static void form_encode(char *dst, const char *src, size_t dstsize); |
3d9e2586 | 69 | static char *get_line(char *buf, int length, FILE *fp); |
d694b7ed | 70 | static void html_start(void); |
71 | static void html_end(void); | |
b5bfe159 | 72 | static int match_string(const char *a, const char *b); |
e21deeaf | 73 | |
74 | ||
75 | /* | |
76 | * 'main()' - Main entry for CGI. | |
77 | */ | |
78 | ||
707471a3 | 79 | int /* O - Exit status */ |
80 | main(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 | ||
196 | static int /* O - Result of comparison */ | |
197 | compare_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 | ||
208 | static void | |
209 | do_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 | ||
506 | static void | |
507 | do_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 | ||
1040 | static void | |
1041 | do_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 | ||
1485 | static void | |
1486 | do_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 | ||
2181 | static void | |
2182 | do_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 | ||
2265 | static void | |
2266 | do_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 | ||
2349 | static void | |
2350 | do_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 | ||
2765 | static void | |
f63a2256 | 2766 | do_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 | ||
2852 | static void | |
2853 | do_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 | ||
3114 | static void | |
3115 | form_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 | 3185 | static char * /* O - Pointer to buf or NULL on EOF */ |
3186 | get_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 | ||
3233 | static void | |
3234 | html_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 | ||
3248 | static void | |
3249 | html_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 | ||
3272 | static int /* O - Number of matching characters */ | |
3273 | match_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 | */ |