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