• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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