1 /*
2 * CGI <-> IPP variable routines for CUPS.
3 *
4 * Copyright 2007-2016 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
8 */
9
10 /*
11 * Include necessary headers...
12 */
13
14 #include "cgi-private.h"
15
16
17 /*
18 * 'cgiGetAttributes()' - Get the list of attributes that are needed
19 * by the template file.
20 */
21
22 void
cgiGetAttributes(ipp_t * request,const char * tmpl)23 cgiGetAttributes(ipp_t *request, /* I - IPP request */
24 const char *tmpl) /* I - Base filename */
25 {
26 int num_attrs; /* Number of attributes */
27 char *attrs[1000]; /* Attributes */
28 int i; /* Looping var */
29 char filename[1024], /* Filename */
30 locale[16]; /* Locale name */
31 const char *directory, /* Directory */
32 *lang; /* Language */
33 FILE *in; /* Input file */
34 int ch; /* Character from file */
35 char name[255], /* Name of variable */
36 *nameptr; /* Pointer into name */
37
38
39 /*
40 * Convert the language to a locale name...
41 */
42
43 if ((lang = getenv("LANG")) != NULL)
44 {
45 for (i = 0; lang[i] && i < 15; i ++)
46 if (isalnum(lang[i] & 255))
47 locale[i] = (char)tolower(lang[i]);
48 else
49 locale[i] = '_';
50
51 locale[i] = '\0';
52 }
53 else
54 locale[0] = '\0';
55
56 /*
57 * See if we have a template file for this language...
58 */
59
60 directory = cgiGetTemplateDir();
61
62 snprintf(filename, sizeof(filename), "%s/%s/%s", directory, locale, tmpl);
63 if (access(filename, 0))
64 {
65 locale[2] = '\0';
66
67 snprintf(filename, sizeof(filename), "%s/%s/%s", directory, locale, tmpl);
68 if (access(filename, 0))
69 snprintf(filename, sizeof(filename), "%s/%s", directory, tmpl);
70 }
71
72 /*
73 * Open the template file...
74 */
75
76 if ((in = fopen(filename, "r")) == NULL)
77 return;
78
79 /*
80 * Loop through the file adding attribute names as needed...
81 */
82
83 num_attrs = 0;
84 attrs[0] = NULL; /* Eliminate compiler warning */
85
86 while ((ch = getc(in)) != EOF)
87 if (ch == '\\')
88 getc(in);
89 else if (ch == '{' && num_attrs < (int)(sizeof(attrs) / sizeof(attrs[0])))
90 {
91 /*
92 * Grab the name...
93 */
94
95 for (nameptr = name; (ch = getc(in)) != EOF;)
96 if (strchr("}]<>=!~ \t\n", ch))
97 break;
98 else if (nameptr > name && ch == '?')
99 break;
100 else if (nameptr < (name + sizeof(name) - 1))
101 {
102 if (ch == '_')
103 *nameptr++ = '-';
104 else
105 *nameptr++ = (char)ch;
106 }
107
108 *nameptr = '\0';
109
110 if (!strncmp(name, "printer_state_history", 21))
111 strlcpy(name, "printer_state_history", sizeof(name));
112
113 /*
114 * Possibly add it to the list of attributes...
115 */
116
117 for (i = 0; i < num_attrs; i ++)
118 if (!strcmp(attrs[i], name))
119 break;
120
121 if (i >= num_attrs)
122 {
123 attrs[num_attrs] = strdup(name);
124 num_attrs ++;
125 }
126 }
127
128 /*
129 * If we have attributes, add a requested-attributes attribute to the
130 * request...
131 */
132
133 if (num_attrs > 0)
134 {
135 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
136 "requested-attributes", num_attrs, NULL, (const char **)attrs);
137
138 for (i = 0; i < num_attrs; i ++)
139 free(attrs[i]);
140 }
141
142 fclose(in);
143 }
144
145
146 /*
147 * 'cgiGetIPPObjects()' - Get the objects in an IPP response.
148 */
149
150 cups_array_t * /* O - Array of objects */
cgiGetIPPObjects(ipp_t * response,void * search)151 cgiGetIPPObjects(ipp_t *response, /* I - IPP response */
152 void *search) /* I - Search filter */
153 {
154 int i; /* Looping var */
155 cups_array_t *objs; /* Array of objects */
156 ipp_attribute_t *attr, /* Current attribute */
157 *first; /* First attribute for object */
158 ipp_tag_t group; /* Current group tag */
159 int add; /* Add this object to the array? */
160
161
162 if (!response)
163 return (0);
164
165 for (add = 0, first = NULL, objs = cupsArrayNew(NULL, NULL),
166 group = IPP_TAG_ZERO, attr = response->attrs;
167 attr;
168 attr = attr->next)
169 {
170 if (attr->group_tag != group)
171 {
172 group = attr->group_tag;
173
174 if (group != IPP_TAG_ZERO && group != IPP_TAG_OPERATION)
175 {
176 first = attr;
177 add = 0;
178 }
179 else if (add && first)
180 {
181 cupsArrayAdd(objs, first);
182
183 add = 0;
184 first = NULL;
185 }
186 }
187
188 if (attr->name && attr->group_tag != IPP_TAG_OPERATION && !add)
189 {
190 if (!search)
191 {
192 /*
193 * Add all objects if there is no search...
194 */
195
196 add = 1;
197 }
198 else
199 {
200 /*
201 * Check the search string against the string and integer values.
202 */
203
204 switch (attr->value_tag)
205 {
206 case IPP_TAG_TEXTLANG :
207 case IPP_TAG_NAMELANG :
208 case IPP_TAG_TEXT :
209 case IPP_TAG_NAME :
210 case IPP_TAG_KEYWORD :
211 case IPP_TAG_URI :
212 case IPP_TAG_MIMETYPE :
213 for (i = 0; !add && i < attr->num_values; i ++)
214 if (cgiDoSearch(search, attr->values[i].string.text))
215 add = 1;
216 break;
217
218 case IPP_TAG_INTEGER :
219 if (!strncmp(ippGetName(attr), "time-at-", 8))
220 break; /* Ignore time-at-xxx */
221
222 for (i = 0; !add && i < attr->num_values; i ++)
223 {
224 char buf[255]; /* Number buffer */
225
226
227 snprintf(buf, sizeof(buf), "%d", attr->values[i].integer);
228
229 if (cgiDoSearch(search, buf))
230 add = 1;
231 }
232 break;
233
234 default :
235 break;
236 }
237 }
238 }
239 }
240
241 if (add && first)
242 cupsArrayAdd(objs, first);
243
244 return (objs);
245 }
246
247
248 /*
249 * 'cgiMoveJobs()' - Move one or more jobs.
250 *
251 * At least one of dest or job_id must be non-zero/NULL.
252 */
253
254 void
cgiMoveJobs(http_t * http,const char * dest,int job_id)255 cgiMoveJobs(http_t *http, /* I - Connection to server */
256 const char *dest, /* I - Destination or NULL */
257 int job_id) /* I - Job ID or 0 for all */
258 {
259 int i; /* Looping var */
260 const char *user; /* Username */
261 ipp_t *request, /* IPP request */
262 *response; /* IPP response */
263 ipp_attribute_t *attr; /* Current attribute */
264 const char *name; /* Destination name */
265 const char *job_printer_uri; /* JOB_PRINTER_URI form variable */
266 char current_dest[1024]; /* Current destination */
267
268
269 /*
270 * Make sure we have a username...
271 */
272
273 if ((user = getenv("REMOTE_USER")) == NULL)
274 {
275 puts("Status: 401\n");
276 exit(0);
277 }
278
279 /*
280 * See if the user has already selected a new destination...
281 */
282
283 if ((job_printer_uri = cgiGetVariable("JOB_PRINTER_URI")) == NULL)
284 {
285 /*
286 * Make sure necessary form variables are set...
287 */
288
289 if (job_id)
290 {
291 char temp[255]; /* Temporary string */
292
293
294 snprintf(temp, sizeof(temp), "%d", job_id);
295 cgiSetVariable("JOB_ID", temp);
296 }
297
298 if (dest)
299 cgiSetVariable("PRINTER_NAME", dest);
300
301 /*
302 * No new destination specified, show the user what the available
303 * printers/classes are...
304 */
305
306 if (!dest)
307 {
308 /*
309 * Get the current destination for job N...
310 */
311
312 char job_uri[1024]; /* Job URI */
313
314
315 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
316
317 snprintf(job_uri, sizeof(job_uri), "ipp://localhost/jobs/%d", job_id);
318 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
319 NULL, job_uri);
320 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
321 "requested-attributes", NULL, "job-printer-uri");
322
323 if ((response = cupsDoRequest(http, request, "/")) != NULL)
324 {
325 if ((attr = ippFindAttribute(response, "job-printer-uri",
326 IPP_TAG_URI)) != NULL)
327 {
328 /*
329 * Pull the name from the URI...
330 */
331
332 strlcpy(current_dest, strrchr(attr->values[0].string.text, '/') + 1,
333 sizeof(current_dest));
334 dest = current_dest;
335 }
336
337 ippDelete(response);
338 }
339
340 if (!dest)
341 {
342 /*
343 * Couldn't get the current destination...
344 */
345
346 cgiStartHTML(cgiText(_("Move Job")));
347 cgiShowIPPError(_("Unable to find destination for job"));
348 cgiEndHTML();
349 return;
350 }
351 }
352
353 /*
354 * Get the list of available destinations...
355 */
356
357 request = ippNewRequest(CUPS_GET_PRINTERS);
358
359 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
360 "requested-attributes", NULL, "printer-uri-supported");
361
362 if (user)
363 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
364 "requesting-user-name", NULL, user);
365
366 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type",
367 CUPS_PRINTER_LOCAL);
368 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask",
369 CUPS_PRINTER_SCANNER);
370
371 if ((response = cupsDoRequest(http, request, "/")) != NULL)
372 {
373 for (i = 0, attr = ippFindAttribute(response, "printer-uri-supported",
374 IPP_TAG_URI);
375 attr;
376 attr = ippFindNextAttribute(response, "printer-uri-supported",
377 IPP_TAG_URI))
378 {
379 /*
380 * Pull the name from the URI...
381 */
382
383 name = strrchr(attr->values[0].string.text, '/') + 1;
384
385 /*
386 * If the name is not the same as the current destination, add it!
387 */
388
389 if (_cups_strcasecmp(name, dest))
390 {
391 cgiSetArray("JOB_PRINTER_URI", i, attr->values[0].string.text);
392 cgiSetArray("JOB_PRINTER_NAME", i, name);
393 i ++;
394 }
395 }
396
397 ippDelete(response);
398 }
399
400 /*
401 * Show the form...
402 */
403
404 if (job_id)
405 cgiStartHTML(cgiText(_("Move Job")));
406 else
407 cgiStartHTML(cgiText(_("Move All Jobs")));
408
409 if (cgiGetSize("JOB_PRINTER_NAME") > 0)
410 cgiCopyTemplateLang("job-move.tmpl");
411 else
412 {
413 if (job_id)
414 cgiSetVariable("MESSAGE", cgiText(_("Unable to move job")));
415 else
416 cgiSetVariable("MESSAGE", cgiText(_("Unable to move jobs")));
417
418 cgiSetVariable("ERROR", cgiText(_("No destinations added.")));
419 cgiCopyTemplateLang("error.tmpl");
420 }
421 }
422 else
423 {
424 /*
425 * Try moving the job or jobs...
426 */
427
428 char uri[1024], /* Job/printer URI */
429 resource[1024], /* Post resource */
430 refresh[1024]; /* Refresh URL */
431 const char *job_printer_name; /* New printer name */
432
433
434 request = ippNewRequest(CUPS_MOVE_JOB);
435
436 if (job_id)
437 {
438 /*
439 * Move 1 job...
440 */
441
442 snprintf(resource, sizeof(resource), "/jobs/%d", job_id);
443
444 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id);
445 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
446 NULL, uri);
447 }
448 else
449 {
450 /*
451 * Move all active jobs on a destination...
452 */
453
454 snprintf(resource, sizeof(resource), "/%s/%s",
455 cgiGetVariable("SECTION"), dest);
456
457 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
458 "localhost", ippPort(), "/%s/%s",
459 cgiGetVariable("SECTION"), dest);
460 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
461 NULL, uri);
462 }
463
464 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-printer-uri",
465 NULL, job_printer_uri);
466
467 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
468 "requesting-user-name", NULL, user);
469
470 ippDelete(cupsDoRequest(http, request, resource));
471
472 /*
473 * Show the results...
474 */
475
476 job_printer_name = strrchr(job_printer_uri, '/') + 1;
477
478 if (cupsLastError() <= IPP_OK_CONFLICT)
479 {
480 const char *path = strstr(job_printer_uri, "/printers/");
481 if (!path)
482 {
483 path = strstr(job_printer_uri, "/classes/");
484 cgiSetVariable("IS_CLASS", "YES");
485 }
486
487 if (path)
488 {
489 cgiFormEncode(uri, path, sizeof(uri));
490 snprintf(refresh, sizeof(refresh), "2;URL=%s", uri);
491 cgiSetVariable("refresh_page", refresh);
492 }
493 }
494
495 if (job_id)
496 cgiStartHTML(cgiText(_("Move Job")));
497 else
498 cgiStartHTML(cgiText(_("Move All Jobs")));
499
500 if (cupsLastError() > IPP_OK_CONFLICT)
501 {
502 if (job_id)
503 cgiShowIPPError(_("Unable to move job"));
504 else
505 cgiShowIPPError(_("Unable to move jobs"));
506 }
507 else
508 {
509 cgiSetVariable("JOB_PRINTER_NAME", job_printer_name);
510 cgiCopyTemplateLang("job-moved.tmpl");
511 }
512 }
513
514 cgiEndHTML();
515 }
516
517
518 /*
519 * 'cgiPrintCommand()' - Print a CUPS command job.
520 */
521
522 void
cgiPrintCommand(http_t * http,const char * dest,const char * command,const char * title)523 cgiPrintCommand(http_t *http, /* I - Connection to server */
524 const char *dest, /* I - Destination printer */
525 const char *command, /* I - Command to send */
526 const char *title) /* I - Page/job title */
527 {
528 int job_id; /* Command file job */
529 char uri[HTTP_MAX_URI], /* Job URI */
530 resource[1024], /* Printer resource path */
531 refresh[1024], /* Refresh URL */
532 command_file[1024]; /* Command "file" */
533 http_status_t status; /* Document status */
534 cups_option_t hold_option; /* job-hold-until option */
535 const char *user; /* User name */
536 ipp_t *request, /* Get-Job-Attributes request */
537 *response; /* Get-Job-Attributes response */
538 ipp_attribute_t *attr; /* Current job attribute */
539 static const char * const job_attrs[] =/* Job attributes we want */
540 {
541 "job-state",
542 "job-printer-state-message"
543 };
544
545
546 /*
547 * Create the CUPS command file...
548 */
549
550 snprintf(command_file, sizeof(command_file), "#CUPS-COMMAND\n%s\n", command);
551
552 /*
553 * Show status...
554 */
555
556 if (cgiSupportsMultipart())
557 {
558 cgiStartMultipart();
559 cgiStartHTML(title);
560 cgiCopyTemplateLang("command.tmpl");
561 cgiEndHTML();
562 fflush(stdout);
563 }
564
565 /*
566 * Send the command file job...
567 */
568
569 hold_option.name = "job-hold-until";
570 hold_option.value = "no-hold";
571
572 if ((user = getenv("REMOTE_USER")) != NULL)
573 cupsSetUser(user);
574 else
575 cupsSetUser("anonymous");
576
577 if ((job_id = cupsCreateJob(http, dest, title,
578 1, &hold_option)) < 1)
579 {
580 cgiSetVariable("MESSAGE", cgiText(_("Unable to send command to printer driver")));
581 cgiSetVariable("ERROR", cupsLastErrorString());
582 cgiStartHTML(title);
583 cgiCopyTemplateLang("error.tmpl");
584 cgiEndHTML();
585
586 if (cgiSupportsMultipart())
587 cgiEndMultipart();
588 return;
589 }
590
591 status = cupsStartDocument(http, dest, job_id, NULL, CUPS_FORMAT_COMMAND, 1);
592 if (status == HTTP_CONTINUE)
593 status = cupsWriteRequestData(http, command_file,
594 strlen(command_file));
595 if (status == HTTP_CONTINUE)
596 cupsFinishDocument(http, dest);
597
598 if (cupsLastError() >= IPP_REDIRECTION_OTHER_SITE)
599 {
600 cgiSetVariable("MESSAGE", cgiText(_("Unable to send command to printer driver")));
601 cgiSetVariable("ERROR", cupsLastErrorString());
602 cgiStartHTML(title);
603 cgiCopyTemplateLang("error.tmpl");
604 cgiEndHTML();
605
606 if (cgiSupportsMultipart())
607 cgiEndMultipart();
608
609 cupsCancelJob(dest, job_id);
610 return;
611 }
612
613 /*
614 * Wait for the job to complete...
615 */
616
617 if (cgiSupportsMultipart())
618 {
619 for (;;)
620 {
621 /*
622 * Get the current job state...
623 */
624
625 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id);
626 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
627 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
628 NULL, uri);
629 if (user)
630 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
631 "requesting-user-name", NULL, user);
632 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
633 "requested-attributes", 2, NULL, job_attrs);
634
635 if ((response = cupsDoRequest(http, request, "/")) != NULL)
636 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
637
638 attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM);
639 if (!attr || attr->values[0].integer >= IPP_JOB_STOPPED ||
640 attr->values[0].integer == IPP_JOB_HELD)
641 {
642 ippDelete(response);
643 break;
644 }
645
646 /*
647 * Job not complete, so update the status...
648 */
649
650 ippDelete(response);
651
652 cgiStartHTML(title);
653 cgiCopyTemplateLang("command.tmpl");
654 cgiEndHTML();
655 fflush(stdout);
656
657 sleep(5);
658 }
659 }
660
661 /*
662 * Send the final page that reloads the printer's page...
663 */
664
665 snprintf(resource, sizeof(resource), "/printers/%s", dest);
666
667 cgiFormEncode(uri, resource, sizeof(uri));
668 snprintf(refresh, sizeof(refresh), "5;URL=%s", uri);
669 cgiSetVariable("refresh_page", refresh);
670
671 cgiStartHTML(title);
672 cgiCopyTemplateLang("command.tmpl");
673 cgiEndHTML();
674
675 if (cgiSupportsMultipart())
676 cgiEndMultipart();
677 }
678
679
680 /*
681 * 'cgiPrintTestPage()' - Print a test page.
682 */
683
684 void
cgiPrintTestPage(http_t * http,const char * dest)685 cgiPrintTestPage(http_t *http, /* I - Connection to server */
686 const char *dest) /* I - Destination printer/class */
687 {
688 ipp_t *request, /* IPP request */
689 *response; /* IPP response */
690 char uri[HTTP_MAX_URI], /* Printer URI */
691 resource[1024], /* POST resource path */
692 refresh[1024], /* Refresh URL */
693 filename[1024]; /* Test page filename */
694 const char *datadir; /* CUPS_DATADIR env var */
695 const char *user; /* Username */
696
697
698 /*
699 * See who is logged in...
700 */
701
702 user = getenv("REMOTE_USER");
703
704 /*
705 * Locate the test page file...
706 */
707
708 if ((datadir = getenv("CUPS_DATADIR")) == NULL)
709 datadir = CUPS_DATADIR;
710
711 snprintf(filename, sizeof(filename), "%s/data/testprint", datadir);
712
713 /*
714 * Point to the printer/class...
715 */
716
717 snprintf(resource, sizeof(resource), "/%s/%s", cgiGetVariable("SECTION"),
718 dest);
719
720 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
721 "localhost", ippPort(), "/%s/%s", cgiGetVariable("SECTION"),
722 dest);
723
724 /*
725 * Build an IPP_PRINT_JOB request, which requires the following
726 * attributes:
727 *
728 * attributes-charset
729 * attributes-natural-language
730 * printer-uri
731 * requesting-user-name
732 */
733
734 request = ippNewRequest(IPP_PRINT_JOB);
735
736 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
737 NULL, uri);
738
739 if (user)
740 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
741 "requesting-user-name", NULL, user);
742
743 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
744 NULL, "Test Page");
745
746 /*
747 * Do the request and get back a response...
748 */
749
750 if ((response = cupsDoFileRequest(http, request, resource,
751 filename)) != NULL)
752 {
753 cgiSetIPPVars(response, NULL, NULL, NULL, 0);
754
755 ippDelete(response);
756 }
757
758 if (cupsLastError() <= IPP_OK_CONFLICT)
759 {
760 /*
761 * Automatically reload the printer status page...
762 */
763
764 cgiFormEncode(uri, resource, sizeof(uri));
765 snprintf(refresh, sizeof(refresh), "2;URL=%s", uri);
766 cgiSetVariable("refresh_page", refresh);
767 }
768 else if (cupsLastError() == IPP_NOT_AUTHORIZED)
769 {
770 puts("Status: 401\n");
771 exit(0);
772 }
773
774 cgiStartHTML(cgiText(_("Print Test Page")));
775
776 if (cupsLastError() > IPP_OK_CONFLICT)
777 cgiShowIPPError(_("Unable to print test page"));
778 else
779 {
780 cgiSetVariable("PRINTER_NAME", dest);
781
782 cgiCopyTemplateLang("test-page.tmpl");
783 }
784
785 cgiEndHTML();
786 }
787
788
789 /*
790 * 'cgiRewriteURL()' - Rewrite a printer URI into a web browser URL...
791 */
792
793 char * /* O - New URL */
cgiRewriteURL(const char * uri,char * url,int urlsize,const char * newresource)794 cgiRewriteURL(const char *uri, /* I - Current URI */
795 char *url, /* O - New URL */
796 int urlsize, /* I - Size of URL buffer */
797 const char *newresource) /* I - Replacement resource */
798 {
799 char scheme[HTTP_MAX_URI],
800 userpass[HTTP_MAX_URI],
801 hostname[HTTP_MAX_URI],
802 rawresource[HTTP_MAX_URI],
803 resource[HTTP_MAX_URI],
804 /* URI components... */
805 *rawptr, /* Pointer into rawresource */
806 *resptr; /* Pointer into resource */
807 int port; /* Port number */
808 static int ishttps = -1; /* Using encryption? */
809 static const char *server; /* Name of server */
810 static char servername[1024];
811 /* Local server name */
812 static const char hexchars[] = "0123456789ABCDEF";
813 /* Hexadecimal conversion characters */
814
815
816 /*
817 * Check if we have been called before...
818 */
819
820 if (ishttps < 0)
821 {
822 /*
823 * No, initialize static vars for the conversion...
824 *
825 * First get the server name associated with the client interface as
826 * well as the locally configured hostname. We'll check *both* of
827 * these to see if the printer URL is local...
828 */
829
830 if ((server = getenv("SERVER_NAME")) == NULL)
831 server = "";
832
833 httpGetHostname(NULL, servername, sizeof(servername));
834
835 /*
836 * Then flag whether we are using SSL on this connection...
837 */
838
839 ishttps = getenv("HTTPS") != NULL;
840 }
841
842 /*
843 * Convert the URI to a URL...
844 */
845
846 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass,
847 sizeof(userpass), hostname, sizeof(hostname), &port,
848 rawresource, sizeof(rawresource));
849
850 if (!strcmp(scheme, "ipp") ||
851 !strcmp(scheme, "http") ||
852 !strcmp(scheme, "https"))
853 {
854 if (newresource)
855 {
856 /*
857 * Force the specified resource name instead of the one in the URL...
858 */
859
860 strlcpy(resource, newresource, sizeof(resource));
861 }
862 else
863 {
864 /*
865 * Rewrite the resource string so it doesn't contain any
866 * illegal chars...
867 */
868
869 for (rawptr = rawresource, resptr = resource; *rawptr; rawptr ++)
870 if ((*rawptr & 128) || *rawptr == '%' || *rawptr == ' ' ||
871 *rawptr == '#' || *rawptr == '?' ||
872 *rawptr == '.') /* For MSIE */
873 {
874 if (resptr < (resource + sizeof(resource) - 3))
875 {
876 *resptr++ = '%';
877 *resptr++ = hexchars[(*rawptr >> 4) & 15];
878 *resptr++ = hexchars[*rawptr & 15];
879 }
880 }
881 else if (resptr < (resource + sizeof(resource) - 1))
882 *resptr++ = *rawptr;
883
884 *resptr = '\0';
885 }
886
887 /*
888 * Map local access to a local URI...
889 */
890
891 if (!_cups_strcasecmp(hostname, "127.0.0.1") ||
892 !_cups_strcasecmp(hostname, "[::1]") ||
893 !_cups_strcasecmp(hostname, "localhost") ||
894 !_cups_strncasecmp(hostname, "localhost.", 10) ||
895 !_cups_strcasecmp(hostname, server) ||
896 !_cups_strcasecmp(hostname, servername))
897 {
898 /*
899 * Make URI relative to the current server...
900 */
901
902 strlcpy(url, resource, (size_t)urlsize);
903 }
904 else
905 {
906 /*
907 * Rewrite URI with HTTP/HTTPS scheme...
908 */
909
910 if (userpass[0])
911 snprintf(url, (size_t)urlsize, "%s://%s@%s:%d%s", ishttps ? "https" : "http", userpass, hostname, port, resource);
912 else
913 snprintf(url, (size_t)urlsize, "%s://%s:%d%s", ishttps ? "https" : "http", hostname, port, resource);
914 }
915 }
916 else
917 strlcpy(url, uri, (size_t)urlsize);
918
919 return (url);
920 }
921
922
923 /*
924 * 'cgiSetIPPObjectVars()' - Set CGI variables from an IPP object.
925 */
926
927 ipp_attribute_t * /* O - Next object */
cgiSetIPPObjectVars(ipp_attribute_t * obj,const char * prefix,int element)928 cgiSetIPPObjectVars(
929 ipp_attribute_t *obj, /* I - Response data to be copied... */
930 const char *prefix, /* I - Prefix for name or NULL */
931 int element) /* I - Parent element number */
932 {
933 ipp_attribute_t *attr; /* Attribute in response... */
934 int i; /* Looping var */
935 char name[1024], /* Name of attribute */
936 *nameptr, /* Pointer into name */
937 value[16384], /* Value(s) */
938 *valptr; /* Pointer into value */
939
940
941 fprintf(stderr, "DEBUG2: cgiSetIPPObjectVars(obj=%p, prefix=\"%s\", "
942 "element=%d)\n",
943 obj, prefix ? prefix : "(null)", element);
944
945 /*
946 * Set common CGI template variables...
947 */
948
949 if (!prefix)
950 cgiSetServerVersion();
951
952 /*
953 * Loop through the attributes and set them for the template...
954 */
955
956 for (attr = obj; attr && attr->group_tag != IPP_TAG_ZERO; attr = attr->next)
957 {
958 /*
959 * Copy the attribute name, substituting "_" for "-"...
960 */
961
962 if (!attr->name)
963 continue;
964
965 if (prefix)
966 {
967 snprintf(name, sizeof(name), "%s.", prefix);
968 nameptr = name + strlen(name);
969 }
970 else
971 nameptr = name;
972
973 for (i = 0; attr->name[i] && nameptr < (name + sizeof(name) - 1); i ++)
974 if (attr->name[i] == '-')
975 *nameptr++ = '_';
976 else
977 *nameptr++ = attr->name[i];
978
979 *nameptr = '\0';
980
981 /*
982 * Add "job_printer_name" variable if we have a "job_printer_uri"
983 * attribute...
984 */
985
986 if (!strcmp(name, "job_printer_uri"))
987 {
988 if ((valptr = strrchr(attr->values[0].string.text, '/')) == NULL)
989 valptr = "unknown";
990 else
991 valptr ++;
992
993 cgiSetArray("job_printer_name", element, valptr);
994 }
995
996 /*
997 * Localize event names in "notify_events" variable...
998 */
999
1000 if (!strcmp(name, "notify_events"))
1001 {
1002 size_t remaining; /* Remaining bytes in buffer */
1003
1004
1005 value[0] = '\0';
1006 valptr = value;
1007
1008 for (i = 0; i < attr->num_values; i ++)
1009 {
1010 if (valptr >= (value + sizeof(value) - 3))
1011 break;
1012
1013 if (i)
1014 {
1015 *valptr++ = ',';
1016 *valptr++ = ' ';
1017 }
1018
1019 remaining = sizeof(value) - (size_t)(valptr - value);
1020
1021 if (!strcmp(attr->values[i].string.text, "printer-stopped"))
1022 strlcpy(valptr, _("Printer Paused"), remaining);
1023 else if (!strcmp(attr->values[i].string.text, "printer-added"))
1024 strlcpy(valptr, _("Printer Added"), remaining);
1025 else if (!strcmp(attr->values[i].string.text, "printer-modified"))
1026 strlcpy(valptr, _("Printer Modified"), remaining);
1027 else if (!strcmp(attr->values[i].string.text, "printer-deleted"))
1028 strlcpy(valptr, _("Printer Deleted"), remaining);
1029 else if (!strcmp(attr->values[i].string.text, "job-created"))
1030 strlcpy(valptr, _("Job Created"), remaining);
1031 else if (!strcmp(attr->values[i].string.text, "job-completed"))
1032 strlcpy(valptr, _("Job Completed"), remaining);
1033 else if (!strcmp(attr->values[i].string.text, "job-stopped"))
1034 strlcpy(valptr, _("Job Stopped"), remaining);
1035 else if (!strcmp(attr->values[i].string.text, "job-config-changed"))
1036 strlcpy(valptr, _("Job Options Changed"), remaining);
1037 else if (!strcmp(attr->values[i].string.text, "server-restarted"))
1038 strlcpy(valptr, _("Server Restarted"), remaining);
1039 else if (!strcmp(attr->values[i].string.text, "server-started"))
1040 strlcpy(valptr, _("Server Started"), remaining);
1041 else if (!strcmp(attr->values[i].string.text, "server-stopped"))
1042 strlcpy(valptr, _("Server Stopped"), remaining);
1043 else if (!strcmp(attr->values[i].string.text, "server-audit"))
1044 strlcpy(valptr, _("Server Security Auditing"), remaining);
1045 else
1046 strlcpy(valptr, attr->values[i].string.text, remaining);
1047
1048 valptr += strlen(valptr);
1049 }
1050
1051 cgiSetArray("notify_events", element, value);
1052 continue;
1053 }
1054
1055 /*
1056 * Add "notify_printer_name" variable if we have a "notify_printer_uri"
1057 * attribute...
1058 */
1059
1060 if (!strcmp(name, "notify_printer_uri"))
1061 {
1062 if ((valptr = strrchr(attr->values[0].string.text, '/')) == NULL)
1063 valptr = "unknown";
1064 else
1065 valptr ++;
1066
1067 cgiSetArray("notify_printer_name", element, valptr);
1068 }
1069
1070 /*
1071 * Add "notify_recipient_name" variable if we have a "notify_recipient_uri"
1072 * attribute, and rewrite recipient URI...
1073 */
1074
1075 if (!strcmp(name, "notify_recipient_uri"))
1076 {
1077 char uri[1024], /* New URI */
1078 scheme[32], /* Scheme portion of URI */
1079 userpass[256], /* Username/password portion of URI */
1080 host[1024], /* Hostname portion of URI */
1081 resource[1024], /* Resource portion of URI */
1082 *options; /* Options in URI */
1083 int port; /* Port number */
1084
1085
1086 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text,
1087 scheme, sizeof(scheme), userpass, sizeof(userpass),
1088 host, sizeof(host), &port, resource, sizeof(resource));
1089
1090 if (!strcmp(scheme, "rss"))
1091 {
1092 /*
1093 * RSS notification...
1094 */
1095
1096 if ((options = strchr(resource, '?')) != NULL)
1097 *options = '\0';
1098
1099 if (host[0])
1100 {
1101 /*
1102 * Link to remote feed...
1103 */
1104
1105 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "http",
1106 userpass, host, port, resource);
1107 strlcpy(name, uri, sizeof(name));
1108 }
1109 else
1110 {
1111 /*
1112 * Link to local feed...
1113 */
1114
1115 snprintf(uri, sizeof(uri), "/rss%s", resource);
1116 strlcpy(name, resource + 1, sizeof(name));
1117 }
1118 }
1119 else
1120 {
1121 /*
1122 * Other...
1123 */
1124
1125 strlcpy(uri, attr->values[0].string.text, sizeof(uri));
1126 strlcpy(name, resource, sizeof(name));
1127 }
1128
1129 cgiSetArray("notify_recipient_uri", element, uri);
1130 cgiSetArray("notify_recipient_name", element, name);
1131 continue;
1132 }
1133
1134 /*
1135 * Add "admin_uri" variable if we have a "printer_uri_supported"
1136 * attribute...
1137 */
1138
1139 if (!strcmp(name, "printer_uri_supported"))
1140 {
1141 cgiRewriteURL(attr->values[0].string.text, value, sizeof(value),
1142 "/admin/");
1143
1144 cgiSetArray("admin_uri", element, value);
1145 }
1146
1147 /*
1148 * Copy values...
1149 */
1150
1151 value[0] = '\0'; /* Initially an empty string */
1152 valptr = value; /* Start at the beginning */
1153
1154 for (i = 0; i < attr->num_values; i ++)
1155 {
1156 if (i)
1157 strlcat(valptr, ", ", sizeof(value) - (size_t)(valptr - value));
1158
1159 valptr += strlen(valptr);
1160
1161 switch (attr->value_tag)
1162 {
1163 case IPP_TAG_INTEGER :
1164 case IPP_TAG_ENUM :
1165 if (strncmp(name, "time_at_", 8) == 0)
1166 _cupsStrDate(valptr, sizeof(value) - (size_t)(valptr - value), (time_t)ippGetInteger(attr, i));
1167 else
1168 snprintf(valptr, sizeof(value) - (size_t)(valptr - value), "%d", ippGetInteger(attr, i));
1169 break;
1170
1171 case IPP_TAG_BOOLEAN :
1172 snprintf(valptr, sizeof(value) - (size_t)(valptr - value),
1173 "%d", attr->values[i].boolean);
1174 break;
1175
1176 case IPP_TAG_NOVALUE :
1177 strlcat(valptr, "novalue", sizeof(value) - (size_t)(valptr - value));
1178 break;
1179
1180 case IPP_TAG_RANGE :
1181 snprintf(valptr, sizeof(value) - (size_t)(valptr - value),
1182 "%d-%d", attr->values[i].range.lower,
1183 attr->values[i].range.upper);
1184 break;
1185
1186 case IPP_TAG_RESOLUTION :
1187 snprintf(valptr, sizeof(value) - (size_t)(valptr - value),
1188 "%dx%d%s", attr->values[i].resolution.xres,
1189 attr->values[i].resolution.yres,
1190 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
1191 "dpi" : "dpcm");
1192 break;
1193
1194 case IPP_TAG_URI :
1195 if (strchr(attr->values[i].string.text, ':') &&
1196 strcmp(name, "device_uri"))
1197 {
1198 /*
1199 * Rewrite URIs...
1200 */
1201
1202 cgiRewriteURL(attr->values[i].string.text, valptr, (int)(sizeof(value) - (size_t)(valptr - value)), NULL);
1203 break;
1204 }
1205
1206 case IPP_TAG_STRING :
1207 case IPP_TAG_TEXT :
1208 case IPP_TAG_NAME :
1209 case IPP_TAG_KEYWORD :
1210 case IPP_TAG_CHARSET :
1211 case IPP_TAG_LANGUAGE :
1212 case IPP_TAG_MIMETYPE :
1213 strlcat(valptr, attr->values[i].string.text,
1214 sizeof(value) - (size_t)(valptr - value));
1215 break;
1216
1217 case IPP_TAG_BEGIN_COLLECTION :
1218 snprintf(value, sizeof(value), "%s%d", name, i + 1);
1219 cgiSetIPPVars(attr->values[i].collection, NULL, NULL, value,
1220 element);
1221 break;
1222
1223 default :
1224 break; /* anti-compiler-warning-code */
1225 }
1226 }
1227
1228 /*
1229 * Add the element...
1230 */
1231
1232 if (attr->value_tag != IPP_TAG_BEGIN_COLLECTION)
1233 {
1234 cgiSetArray(name, element, value);
1235
1236 fprintf(stderr, "DEBUG2: %s[%d]=\"%s\"\n", name, element, value);
1237 }
1238 }
1239
1240 return (attr ? attr->next : NULL);
1241 }
1242
1243
1244 /*
1245 * 'cgiSetIPPVars()' - Set CGI variables from an IPP response.
1246 */
1247
1248 int /* O - Maximum number of elements */
cgiSetIPPVars(ipp_t * response,const char * filter_name,const char * filter_value,const char * prefix,int parent_el)1249 cgiSetIPPVars(ipp_t *response, /* I - Response data to be copied... */
1250 const char *filter_name, /* I - Filter name */
1251 const char *filter_value, /* I - Filter value */
1252 const char *prefix, /* I - Prefix for name or NULL */
1253 int parent_el) /* I - Parent element number */
1254 {
1255 int element; /* Element in CGI array */
1256 ipp_attribute_t *attr, /* Attribute in response... */
1257 *filter; /* Filtering attribute */
1258
1259
1260 fprintf(stderr, "DEBUG2: cgiSetIPPVars(response=%p, filter_name=\"%s\", "
1261 "filter_value=\"%s\", prefix=\"%s\", parent_el=%d)\n",
1262 response, filter_name ? filter_name : "(null)",
1263 filter_value ? filter_value : "(null)",
1264 prefix ? prefix : "(null)", parent_el);
1265
1266 /*
1267 * Set common CGI template variables...
1268 */
1269
1270 if (!prefix)
1271 cgiSetServerVersion();
1272
1273 /*
1274 * Loop through the attributes and set them for the template...
1275 */
1276
1277 attr = response->attrs;
1278
1279 if (!prefix)
1280 while (attr && attr->group_tag == IPP_TAG_OPERATION)
1281 attr = attr->next;
1282
1283 for (element = parent_el; attr; element ++)
1284 {
1285 /*
1286 * Copy attributes to a separator...
1287 */
1288
1289 while (attr && attr->group_tag == IPP_TAG_ZERO)
1290 attr= attr->next;
1291
1292 if (!attr)
1293 break;
1294
1295 if (filter_name)
1296 {
1297 for (filter = attr;
1298 filter != NULL && filter->group_tag != IPP_TAG_ZERO;
1299 filter = filter->next)
1300 if (filter->name && !strcmp(filter->name, filter_name) &&
1301 (filter->value_tag == IPP_TAG_STRING ||
1302 (filter->value_tag >= IPP_TAG_TEXTLANG &&
1303 filter->value_tag <= IPP_TAG_MIMETYPE)) &&
1304 filter->values[0].string.text != NULL &&
1305 !_cups_strcasecmp(filter->values[0].string.text, filter_value))
1306 break;
1307
1308 if (!filter)
1309 return (element + 1);
1310
1311 if (filter->group_tag == IPP_TAG_ZERO)
1312 {
1313 attr = filter;
1314 element --;
1315 continue;
1316 }
1317 }
1318
1319 attr = cgiSetIPPObjectVars(attr, prefix, element);
1320 }
1321
1322 fprintf(stderr, "DEBUG2: Returning %d from cgiSetIPPVars()...\n", element);
1323
1324 return (element);
1325 }
1326
1327
1328 /*
1329 * 'cgiShowIPPError()' - Show the last IPP error message.
1330 *
1331 * The caller must still call cgiStartHTML() and cgiEndHTML().
1332 */
1333
1334 void
cgiShowIPPError(const char * message)1335 cgiShowIPPError(const char *message) /* I - Contextual message */
1336 {
1337 cgiSetVariable("MESSAGE", cgiText(message));
1338 cgiSetVariable("ERROR", cupsLastErrorString());
1339 cgiCopyTemplateLang("error.tmpl");
1340 }
1341
1342
1343 /*
1344 * 'cgiShowJobs()' - Show print jobs.
1345 */
1346
1347 void
cgiShowJobs(http_t * http,const char * dest)1348 cgiShowJobs(http_t *http, /* I - Connection to server */
1349 const char *dest) /* I - Destination name or NULL */
1350 {
1351 int i; /* Looping var */
1352 const char *which_jobs; /* Which jobs to show */
1353 ipp_t *request, /* IPP request */
1354 *response; /* IPP response */
1355 cups_array_t *jobs; /* Array of job objects */
1356 ipp_attribute_t *job; /* Job object */
1357 int first, /* First job to show */
1358 count; /* Number of jobs */
1359 const char *var, /* Form variable */
1360 *query, /* Query string */
1361 *section; /* Section in web interface */
1362 void *search; /* Search data */
1363 char url[1024], /* Printer URI */
1364 val[1024]; /* Form variable */
1365
1366
1367 /*
1368 * Build an IPP_GET_JOBS request, which requires the following
1369 * attributes:
1370 *
1371 * attributes-charset
1372 * attributes-natural-language
1373 * printer-uri
1374 */
1375
1376 request = ippNewRequest(IPP_GET_JOBS);
1377
1378 if (dest)
1379 {
1380 httpAssembleURIf(HTTP_URI_CODING_ALL, url, sizeof(url), "ipp", NULL,
1381 "localhost", ippPort(), "/printers/%s", dest);
1382 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1383 NULL, url);
1384 }
1385 else
1386 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
1387 "ipp://localhost/");
1388
1389 if ((which_jobs = cgiGetVariable("which_jobs")) != NULL && *which_jobs)
1390 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs",
1391 NULL, which_jobs);
1392
1393 if ((var = cgiGetVariable("FIRST")) != NULL)
1394 {
1395 if ((first = atoi(var)) < 0)
1396 first = 0;
1397 }
1398 else
1399 first = 0;
1400
1401 if (first > 0)
1402 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "first-index", first + 1);
1403
1404 cgiGetAttributes(request, "jobs.tmpl");
1405
1406 /*
1407 * Do the request and get back a response...
1408 */
1409
1410 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1411 {
1412 /*
1413 * Get a list of matching job objects.
1414 */
1415
1416 if ((query = cgiGetVariable("QUERY")) != NULL &&
1417 !cgiGetVariable("CLEAR"))
1418 search = cgiCompileSearch(query);
1419 else
1420 {
1421 query = NULL;
1422 search = NULL;
1423 }
1424
1425 jobs = cgiGetIPPObjects(response, search);
1426 count = cupsArrayCount(jobs) + first;
1427
1428 if (search)
1429 cgiFreeSearch(search);
1430
1431 /*
1432 * Figure out which jobs to display...
1433 */
1434
1435 section = cgiGetVariable("SECTION");
1436
1437 cgiClearVariables();
1438
1439 if (query)
1440 cgiSetVariable("QUERY", query);
1441
1442 cgiSetVariable("SECTION", section);
1443
1444 snprintf(val, sizeof(val), "%d", count);
1445 cgiSetVariable("TOTAL", val);
1446
1447 if (which_jobs)
1448 cgiSetVariable("WHICH_JOBS", which_jobs);
1449
1450 for (i = 0, job = (ipp_attribute_t *)cupsArrayFirst(jobs);
1451 i < CUPS_PAGE_MAX && job;
1452 i ++, job = (ipp_attribute_t *)cupsArrayNext(jobs))
1453 cgiSetIPPObjectVars(job, NULL, i);
1454
1455 /*
1456 * Save navigation URLs...
1457 */
1458
1459 if (dest)
1460 {
1461 snprintf(val, sizeof(val), "/%s/%s", section, dest);
1462 cgiSetVariable("PRINTER_NAME", dest);
1463 cgiSetVariable("PRINTER_URI_SUPPORTED", val);
1464 }
1465 else
1466 strlcpy(val, "/jobs/", sizeof(val));
1467
1468 cgiSetVariable("THISURL", val);
1469
1470 if (first > 0)
1471 {
1472 snprintf(val, sizeof(val), "%d", first - CUPS_PAGE_MAX);
1473 cgiSetVariable("PREV", val);
1474 }
1475
1476 if ((first + CUPS_PAGE_MAX) < count)
1477 {
1478 snprintf(val, sizeof(val), "%d", first + CUPS_PAGE_MAX);
1479 cgiSetVariable("NEXT", val);
1480 }
1481
1482 if (count > CUPS_PAGE_MAX)
1483 {
1484 snprintf(val, sizeof(val), "%d", CUPS_PAGE_MAX * (count / CUPS_PAGE_MAX));
1485 cgiSetVariable("LAST", val);
1486 }
1487
1488 /*
1489 * Then show everything...
1490 */
1491
1492 if (dest)
1493 cgiSetVariable("SEARCH_DEST", dest);
1494
1495 cgiCopyTemplateLang("search.tmpl");
1496
1497 cgiCopyTemplateLang("jobs-header.tmpl");
1498
1499 if (count > CUPS_PAGE_MAX)
1500 cgiCopyTemplateLang("pager.tmpl");
1501
1502 cgiCopyTemplateLang("jobs.tmpl");
1503
1504 if (count > CUPS_PAGE_MAX)
1505 cgiCopyTemplateLang("pager.tmpl");
1506
1507 cupsArrayDelete(jobs);
1508 ippDelete(response);
1509 }
1510 }
1511
1512
1513 /*
1514 * 'cgiText()' - Return localized text.
1515 */
1516
1517 const char * /* O - Localized message */
cgiText(const char * message)1518 cgiText(const char *message) /* I - Message */
1519 {
1520 static cups_lang_t *language = NULL;
1521 /* Language */
1522
1523
1524 if (!language)
1525 language = cupsLangDefault();
1526
1527 return (_cupsLangString(language, message));
1528 }
1529