1/*
2 * "$Id: ippserver.c 11098 2013-07-04 15:55:20Z msweet $"
3 *
4 *   Sample IPP/2.0 server for CUPS.
5 *
6 *   Copyright 2010-2013 by Apple Inc.
7 *
8 *   These coded instructions, statements, and computer programs are the
9 *   property of Apple Inc. and are protected by Federal copyright
10 *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
11 *   which should have been included with this file.  If this file is
12 *   file is missing or damaged, see the license at "http://www.cups.org/".
13 *
14 *   This file is subject to the Apple OS-Developed Software exception.
15 *
16 * Contents:
17 *
18 *   main()			  - Main entry to the sample server.
19 *   clean_jobs()		  - Clean out old (completed) jobs.
20 *   compare_jobs()		  - Compare two jobs.
21 *   copy_attributes()		  - Copy attributes from one request to
22 *				    another.
23 *   copy_job_attrs()		  - Copy job attributes to the response.
24 *   create_client()		  - Accept a new network connection and create
25 *				    a client object.
26 *   create_job()		  - Create a new job object from a Print-Job or
27 *				    Create-Job request.
28 *   create_listener()		  - Create a listener socket.
29 *   create_media_col() 	  - Create a media-col value.
30 *   create_printer()		  - Create, register, and listen for
31 *				    connections to a printer object.
32 *   create_requested_array()	  - Create an array for requested-attributes.
33 *   debug_attributes() 	  - Print attributes in a request or response.
34 *   delete_client()		  - Close the socket and free all memory used
35 *				    by a client object.
36 *   delete_job()		  - Remove from the printer and free all memory
37 *				    used by a job object.
38 *   delete_printer()		  - Unregister, close listen sockets, and free
39 *				    all memory used by a printer object.
40 *   dnssd_callback()		  - Handle Bonjour registration events.
41 *   find_job() 		  - Find a job specified in a request.
42 *   html_escape()		  - Write a HTML-safe string.
43 *   html_printf()		  - Send formatted text to the client, quoting
44 *				    as needed.
45 *   ipp_cancel_job()		  - Cancel a job.
46 *   ipp_create_job()		  - Create a job object.
47 *   ipp_get_job_attributes()	  - Get the attributes for a job object.
48 *   ipp_get_jobs()		  - Get a list of job objects.
49 *   ipp_get_printer_attributes() - Get the attributes for a printer object.
50 *   ipp_print_job()		  - Create a job object with an attached
51 *				    document.
52 *   ipp_print_uri()		  - Create a job object with a referenced
53 *				    document.
54 *   ipp_send_document()	  - Add an attached document to a job object
55 *				    created with Create-Job.
56 *   ipp_send_uri()		  - Add a referenced document to a job object
57 *				    created with Create-Job.
58 *   ipp_validate_job() 	  - Validate job creation attributes.
59 *   process_client()		  - Process client requests on a thread.
60 *   process_http()		  - Process a HTTP request.
61 *   process_ipp()		  - Process an IPP request.
62 *   process_job()		  - Process a print job.
63 *   register_printer() 	  - Register a printer object via Bonjour.
64 *   respond_http()		  - Send a HTTP response.
65 *   respond_ipp()		  - Send an IPP response.
66 *   respond_unsupported()	  - Respond with an unsupported attribute.
67 *   run_printer()		  - Run the printer service.
68 *   usage()			  - Show program usage.
69 *   valid_doc_attributes()	  - Determine whether the document attributes
70 *				    are valid.
71 *   valid_job_attributes()	  - Determine whether the job attributes are
72 *				    valid.
73 */
74
75/*
76 * Disable private and deprecated stuff so we can verify that the public API
77 * is sufficient to implement a server.
78 */
79
80#define _IPP_PRIVATE_STRUCTURES 0	/* Disable private IPP stuff */
81#define _CUPS_NO_DEPRECATED 1		/* Disable deprecated stuff */
82
83
84/*
85 * Include necessary headers...
86 */
87
88#include <cups/cups.h>			/* Public API */
89#include <config.h>			/* CUPS configuration header */
90#include <cups/string-private.h>	/* For string functions */
91#include <cups/thread-private.h>	/* For multithreading functions */
92
93#include <sys/wait.h>
94
95#ifdef HAVE_DNSSD
96#  include <dns_sd.h>
97#endif /* HAVE_DNSSD */
98#include <limits.h>
99#include <sys/stat.h>
100#include <sys/fcntl.h>
101#include <poll.h>
102#ifdef HAVE_SYS_MOUNT_H
103#  include <sys/mount.h>
104#endif /* HAVE_SYS_MOUNT_H */
105#ifdef HAVE_SYS_STATFS_H
106#  include <sys/statfs.h>
107#endif /* HAVE_SYS_STATFS_H */
108#ifdef HAVE_SYS_STATVFS_H
109#  include <sys/statvfs.h>
110#endif /* HAVE_SYS_STATVFS_H */
111#ifdef HAVE_SYS_VFS_H
112#  include <sys/vfs.h>
113#endif /* HAVE_SYS_VFS_H */
114
115
116/*
117 * Constants...
118 */
119
120enum _ipp_preasons_e			/* printer-state-reasons bit values */
121{
122  _IPP_PSTATE_NONE = 0x0000,		/* none */
123  _IPP_PSTATE_OTHER = 0x0001,		/* other */
124  _IPP_PSTATE_COVER_OPEN = 0x0002,	/* cover-open */
125  _IPP_PSTATE_INPUT_TRAY_MISSING = 0x0004,
126					/* input-tray-missing */
127  _IPP_PSTATE_MARKER_SUPPLY_EMPTY = 0x0008,
128					/* marker-supply-empty */
129  _IPP_PSTATE_MARKER_SUPPLY_LOW = 0x0010,
130					/* marker-suply-low */
131  _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL = 0x0020,
132					/* marker-waste-almost-full */
133  _IPP_PSTATE_MARKER_WASTE_FULL = 0x0040,
134					/* marker-waste-full */
135  _IPP_PSTATE_MEDIA_EMPTY = 0x0080,	/* media-empty */
136  _IPP_PSTATE_MEDIA_JAM = 0x0100,	/* media-jam */
137  _IPP_PSTATE_MEDIA_LOW = 0x0200,	/* media-low */
138  _IPP_PSTATE_MEDIA_NEEDED = 0x0400,	/* media-needed */
139  _IPP_PSTATE_MOVING_TO_PAUSED = 0x0800,
140					/* moving-to-paused */
141  _IPP_PSTATE_PAUSED = 0x1000,		/* paused */
142  _IPP_PSTATE_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */
143  _IPP_PSTATE_TONER_EMPTY = 0x4000,	/* toner-empty */
144  _IPP_PSTATE_TONER_LOW = 0x8000	/* toner-low */
145};
146typedef unsigned int _ipp_preasons_t;	/* Bitfield for printer-state-reasons */
147
148typedef enum _ipp_media_class_e
149{
150  _IPP_GENERAL,				/* General-purpose size */
151  _IPP_PHOTO_ONLY,			/* Photo-only size */
152  _IPP_ENV_ONLY				/* Envelope-only size */
153} _ipp_media_class_t;
154
155static const char * const media_supported[] =
156{					/* media-supported values */
157  "iso_a4_210x297mm",			/* A4 */
158  "iso_a5_148x210mm",			/* A5 */
159  "iso_a6_105x148mm",			/* A6 */
160  "iso_dl_110x220mm",			/* DL */
161  "na_legal_8.5x14in",			/* Legal */
162  "na_letter_8.5x11in",			/* Letter */
163  "na_number-10_4.125x9.5in",		/* #10 */
164  "na_index-3x5_3x5in",			/* 3x5 */
165  "oe_photo-l_3.5x5in",			/* L */
166  "na_index-4x6_4x6in",			/* 4x6 */
167  "na_5x7_5x7in"			/* 5x7 aka 2L */
168};
169static const int media_col_sizes[][3] =
170{					/* media-col-database sizes */
171  { 21000, 29700, _IPP_GENERAL },	/* A4 */
172  { 14800, 21000, _IPP_PHOTO_ONLY },	/* A5 */
173  { 10500, 14800, _IPP_PHOTO_ONLY },	/* A6 */
174  { 11000, 22000, _IPP_ENV_ONLY },	/* DL */
175  { 21590, 35560, _IPP_GENERAL },	/* Legal */
176  { 21590, 27940, _IPP_GENERAL },	/* Letter */
177  { 10477, 24130, _IPP_ENV_ONLY },	/* #10 */
178  {  7630, 12700, _IPP_PHOTO_ONLY },	/* 3x5 */
179  {  8890, 12700, _IPP_PHOTO_ONLY },	/* L */
180  { 10160, 15240, _IPP_PHOTO_ONLY },	/* 4x6 */
181  { 12700, 17780, _IPP_PHOTO_ONLY }	/* 5x7 aka 2L */
182};
183static const char * const media_type_supported[] =
184				      /* media-type-supported values */
185{
186  "auto",
187  "cardstock",
188  "envelope",
189  "labels",
190  "other",
191  "photographic-glossy",
192  "photographic-high-gloss",
193  "photographic-matte",
194  "photographic-satin",
195  "photographic-semi-gloss",
196  "stationery",
197  "stationery-letterhead",
198  "transparency"
199};
200
201
202/*
203 * Structures...
204 */
205
206typedef struct _ipp_job_s _ipp_job_t;
207
208typedef struct _ipp_printer_s		/**** Printer data ****/
209{
210  int			ipv4,		/* IPv4 listener */
211			ipv6;		/* IPv6 listener */
212#ifdef HAVE_DNSSD
213  DNSServiceRef		common_ref,	/* Shared service connection */
214			ipp_ref,	/* Bonjour IPP service */
215#  ifdef HAVE_SSL
216			ipps_ref,	/* Bonjour IPPS service */
217#  endif /* HAVE_SSL */
218			http_ref,	/* Bonjour HTTP service */
219			printer_ref;	/* Bonjour LPD service */
220  TXTRecordRef		ipp_txt;	/* Bonjour IPP TXT record */
221  char			*dnssd_name;	/* printer-dnssd-name */
222#endif /* HAVE_DNSSD */
223  char			*name,		/* printer-name */
224			*icon,		/* Icon filename */
225			*directory,	/* Spool directory */
226			*hostname,	/* Hostname */
227			*uri,		/* printer-uri-supported */
228			*command;	/* Command to run with job file */
229  int			port;		/* Port */
230  size_t		urilen;		/* Length of printer URI */
231  ipp_t			*attrs;		/* Static attributes */
232  ipp_pstate_t		state;		/* printer-state value */
233  _ipp_preasons_t	state_reasons;	/* printer-state-reasons values */
234  cups_array_t		*jobs;		/* Jobs */
235  _ipp_job_t		*active_job;	/* Current active/pending job */
236  int			next_job_id;	/* Next job-id value */
237  _cups_rwlock_t	rwlock;		/* Printer lock */
238} _ipp_printer_t;
239
240struct _ipp_job_s			/**** Job data ****/
241{
242  int			id;		/* Job ID */
243  const char		*name,		/* job-name */
244			*username,	/* job-originating-user-name */
245			*format;	/* document-format */
246  ipp_jstate_t		state;		/* job-state value */
247  time_t		processing,	/* time-at-processing value */
248			completed;	/* time-at-completed value */
249  ipp_t			*attrs;		/* Static attributes */
250  int			cancel;		/* Non-zero when job canceled */
251  char			*filename;	/* Print file name */
252  int			fd;		/* Print file descriptor */
253  _ipp_printer_t	*printer;	/* Printer */
254};
255
256typedef struct _ipp_client_s		/**** Client data ****/
257{
258  http_t		*http;		/* HTTP connection */
259  ipp_t			*request,	/* IPP request */
260			*response;	/* IPP response */
261  time_t		start;		/* Request start time */
262  http_state_t		operation;	/* Request operation */
263  ipp_op_t		operation_id;	/* IPP operation-id */
264  char			uri[1024];	/* Request URI */
265  http_addr_t		addr;		/* Client address */
266  char			hostname[256];	/* Client hostname */
267  _ipp_printer_t	*printer;	/* Printer */
268  _ipp_job_t		*job;		/* Current job, if any */
269} _ipp_client_t;
270
271
272/*
273 * Local functions...
274 */
275
276static void		clean_jobs(_ipp_printer_t *printer);
277static int		compare_jobs(_ipp_job_t *a, _ipp_job_t *b);
278static void		copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra,
279			                ipp_tag_t group_tag, int quickcopy);
280static void		copy_job_attributes(_ipp_client_t *client,
281			                    _ipp_job_t *job, cups_array_t *ra);
282static _ipp_client_t	*create_client(_ipp_printer_t *printer, int sock);
283static _ipp_job_t	*create_job(_ipp_client_t *client);
284static int		create_listener(int family, int *port);
285static ipp_t		*create_media_col(const char *media, const char *type,
286					  int width, int length, int margins);
287static ipp_t		*create_media_size(int width, int length);
288static _ipp_printer_t	*create_printer(const char *servername,
289			                const char *name, const char *location,
290			                const char *make, const char *model,
291					const char *icon,
292					const char *docformats, int ppm,
293					int ppm_color, int duplex, int port,
294					int pin,
295#ifdef HAVE_DNSSD
296					const char *subtype,
297#endif /* HAVE_DNSSD */
298					const char *directory,
299					const char *command);
300static void		debug_attributes(const char *title, ipp_t *ipp,
301			                 int response);
302static void		delete_client(_ipp_client_t *client);
303static void		delete_job(_ipp_job_t *job);
304static void		delete_printer(_ipp_printer_t *printer);
305#ifdef HAVE_DNSSD
306static void		dnssd_callback(DNSServiceRef sdRef,
307				       DNSServiceFlags flags,
308				       DNSServiceErrorType errorCode,
309				       const char *name,
310				       const char *regtype,
311				       const char *domain,
312				       _ipp_printer_t *printer);
313#endif /* HAVE_DNSSD */
314static _ipp_job_t	*find_job(_ipp_client_t *client);
315static void		html_escape(_ipp_client_t *client, const char *s,
316			            size_t slen);
317static void		html_printf(_ipp_client_t *client, const char *format,
318			            ...) __attribute__((__format__(__printf__,
319			                                           2, 3)));
320static void		ipp_cancel_job(_ipp_client_t *client);
321static void		ipp_create_job(_ipp_client_t *client);
322static void		ipp_get_job_attributes(_ipp_client_t *client);
323static void		ipp_get_jobs(_ipp_client_t *client);
324static void		ipp_get_printer_attributes(_ipp_client_t *client);
325static void		ipp_print_job(_ipp_client_t *client);
326static void		ipp_print_uri(_ipp_client_t *client);
327static void		ipp_send_document(_ipp_client_t *client);
328static void		ipp_send_uri(_ipp_client_t *client);
329static void		ipp_validate_job(_ipp_client_t *client);
330static void		*process_client(_ipp_client_t *client);
331static int		process_http(_ipp_client_t *client);
332static int		process_ipp(_ipp_client_t *client);
333static void		*process_job(_ipp_job_t *job);
334#ifdef HAVE_DNSSD
335static int		register_printer(_ipp_printer_t *printer,
336			                 const char *location, const char *make,
337					 const char *model, const char *formats,
338					 const char *adminurl, int color,
339					 int duplex, const char *regtype);
340#endif /* HAVE_DNSSD */
341static int		respond_http(_ipp_client_t *client, http_status_t code,
342				     const char *content_coding,
343				     const char *type, size_t length);
344static void		respond_ipp(_ipp_client_t *client, ipp_status_t status,
345			            const char *message, ...)
346			__attribute__ ((__format__ (__printf__, 3, 4)));
347static void		respond_unsupported(_ipp_client_t *client,
348			                    ipp_attribute_t *attr);
349static void		run_printer(_ipp_printer_t *printer);
350static void		usage(int status) __attribute__((noreturn));
351static int		valid_doc_attributes(_ipp_client_t *client);
352static int		valid_job_attributes(_ipp_client_t *client);
353
354
355/*
356 * Globals...
357 */
358
359static int		KeepFiles = 0,
360			Verbosity = 0;
361
362
363/*
364 * 'main()' - Main entry to the sample server.
365 */
366
367int					/* O - Exit status */
368main(int  argc,				/* I - Number of command-line args */
369     char *argv[])			/* I - Command-line arguments */
370{
371  int		i;			/* Looping var */
372  const char	*opt,			/* Current option character */
373		*command = NULL,	/* Command to run with job files */
374		*servername = NULL,	/* Server host name */
375		*name = NULL,		/* Printer name */
376		*location = "",		/* Location of printer */
377		*make = "Test",		/* Manufacturer */
378		*model = "Printer",	/* Model */
379		*icon = "printer.png",	/* Icon file */
380		*formats = "application/pdf,image/jpeg,image/pwg-raster";
381	      				/* Supported formats */
382#ifdef HAVE_DNSSD
383  const char	*subtype = "_print";	/* Bonjour service subtype */
384#endif /* HAVE_DNSSD */
385  int		port = 8631,		/* Port number (0 = auto) */
386		duplex = 0,		/* Duplex mode */
387		ppm = 10,		/* Pages per minute for mono */
388		ppm_color = 0,		/* Pages per minute for color */
389		pin = 0;		/* PIN printing mode? */
390  char		directory[1024] = "";	/* Spool directory */
391  _ipp_printer_t *printer;		/* Printer object */
392
393
394 /*
395  * Parse command-line arguments...
396  */
397
398  for (i = 1; i < argc; i ++)
399    if (argv[i][0] == '-')
400    {
401      for (opt = argv[i] + 1; *opt; opt ++)
402        switch (*opt)
403	{
404	  case '2' : /* -2 (enable 2-sided printing) */
405	      duplex = 1;
406	      break;
407
408	  case 'M' : /* -M manufacturer */
409	      i ++;
410	      if (i >= argc)
411	        usage(1);
412	      make = argv[i];
413	      break;
414
415          case 'P' : /* -P (PIN printing mode) */
416              pin = 1;
417              break;
418
419          case 'c' : /* -c command */
420              i ++;
421	      if (i >= argc)
422	        usage(1);
423
424	      command = argv[i];
425	      break;
426
427	  case 'd' : /* -d spool-directory */
428	      i ++;
429	      if (i >= argc)
430	        usage(1);
431	      strlcpy(directory, argv[i], sizeof(directory));
432	      break;
433
434	  case 'f' : /* -f type/subtype[,...] */
435	      i ++;
436	      if (i >= argc)
437	        usage(1);
438	      formats = argv[i];
439	      break;
440
441          case 'h' : /* -h (show help) */
442	      usage(0);
443	      break;
444
445	  case 'i' : /* -i icon.png */
446	      i ++;
447	      if (i >= argc)
448	        usage(1);
449	      icon = argv[i];
450	      break;
451
452	  case 'k' : /* -k (keep files) */
453	      KeepFiles = 1;
454	      break;
455
456	  case 'l' : /* -l location */
457	      i ++;
458	      if (i >= argc)
459	        usage(1);
460	      location = argv[i];
461	      break;
462
463	  case 'm' : /* -m model */
464	      i ++;
465	      if (i >= argc)
466	        usage(1);
467	      model = argv[i];
468	      break;
469
470	  case 'n' : /* -n hostname */
471	      i ++;
472	      if (i >= argc)
473	        usage(1);
474	      servername = argv[i];
475	      break;
476
477	  case 'p' : /* -p port */
478	      i ++;
479	      if (i >= argc || !isdigit(argv[i][0] & 255))
480	        usage(1);
481	      port = atoi(argv[i]);
482	      break;
483
484#ifdef HAVE_DNSSD
485	  case 'r' : /* -r subtype */
486	      i ++;
487	      if (i >= argc)
488	        usage(1);
489	      subtype = argv[i];
490	      break;
491#endif /* HAVE_DNSSD */
492
493	  case 's' : /* -s speed[,color-speed] */
494	      i ++;
495	      if (i >= argc)
496	        usage(1);
497	      if (sscanf(argv[i], "%d,%d", &ppm, &ppm_color) < 1)
498	        usage(1);
499	      break;
500
501	  case 'v' : /* -v (be verbose) */
502	      Verbosity ++;
503	      break;
504
505          default : /* Unknown */
506	      fprintf(stderr, "Unknown option \"-%c\".\n", *opt);
507	      usage(1);
508	      break;
509	}
510    }
511    else if (!name)
512    {
513      name = argv[i];
514    }
515    else
516    {
517      fprintf(stderr, "Unexpected command-line argument \"%s\"\n", argv[i]);
518      usage(1);
519    }
520
521  if (!name)
522    usage(1);
523
524 /*
525  * Apply defaults as needed...
526  */
527
528  if (!directory[0])
529  {
530    snprintf(directory, sizeof(directory), "/tmp/ippserver.%d", (int)getpid());
531
532    if (mkdir(directory, 0777) && errno != EEXIST)
533    {
534      fprintf(stderr, "Unable to create spool directory \"%s\": %s\n",
535	      directory, strerror(errno));
536      usage(1);
537    }
538
539    if (Verbosity)
540      fprintf(stderr, "Using spool directory \"%s\".\n", directory);
541  }
542
543 /*
544  * Create the printer...
545  */
546
547  if ((printer = create_printer(servername, name, location, make, model, icon,
548                                formats, ppm, ppm_color, duplex, port, pin,
549#ifdef HAVE_DNSSD
550				subtype,
551#endif /* HAVE_DNSSD */
552				directory, command)) == NULL)
553    return (1);
554
555 /*
556  * Run the print service...
557  */
558
559  run_printer(printer);
560
561 /*
562  * Destroy the printer and exit...
563  */
564
565  delete_printer(printer);
566
567  return (0);
568}
569
570
571/*
572 * 'clean_jobs()' - Clean out old (completed) jobs.
573 */
574
575static void
576clean_jobs(_ipp_printer_t *printer)	/* I - Printer */
577{
578  _ipp_job_t	*job;			/* Current job */
579  time_t	cleantime;		/* Clean time */
580
581
582  if (cupsArrayCount(printer->jobs) == 0)
583    return;
584
585  cleantime = time(NULL) - 60;
586
587  _cupsRWLockWrite(&(printer->rwlock));
588  for (job = (_ipp_job_t *)cupsArrayFirst(printer->jobs);
589       job;
590       job = (_ipp_job_t *)cupsArrayNext(printer->jobs))
591    if (job->completed && job->completed < cleantime)
592    {
593      cupsArrayRemove(printer->jobs, job);
594      delete_job(job);
595    }
596    else
597      break;
598  _cupsRWUnlock(&(printer->rwlock));
599}
600
601
602/*
603 * 'compare_jobs()' - Compare two jobs.
604 */
605
606static int				/* O - Result of comparison */
607compare_jobs(_ipp_job_t *a,		/* I - First job */
608             _ipp_job_t *b)		/* I - Second job */
609{
610  return (b->id - a->id);
611}
612
613
614/*
615 * 'copy_attributes()' - Copy attributes from one request to another.
616 */
617
618static void
619copy_attributes(ipp_t        *to,	/* I - Destination request */
620	        ipp_t        *from,	/* I - Source request */
621	        cups_array_t *ra,	/* I - Requested attributes */
622	        ipp_tag_t    group_tag,	/* I - Group to copy */
623	        int          quickcopy)	/* I - Do a quick copy? */
624{
625  ipp_attribute_t	*fromattr;	/* Source attribute */
626
627
628  if (!to || !from)
629    return;
630
631  for (fromattr = ippFirstAttribute(from);
632       fromattr;
633       fromattr = ippNextAttribute(from))
634  {
635   /*
636    * Filter attributes as needed...
637    */
638
639    ipp_tag_t fromgroup = ippGetGroupTag(fromattr);
640    const char *fromname = ippGetName(fromattr);
641
642    if ((group_tag != IPP_TAG_ZERO && fromgroup != group_tag &&
643         fromgroup != IPP_TAG_ZERO) || !fromname)
644      continue;
645
646    if (!ra || cupsArrayFind(ra, (void *)fromname))
647      ippCopyAttribute(to, fromattr, quickcopy);
648  }
649}
650
651
652/*
653 * 'copy_job_attrs()' - Copy job attributes to the response.
654 */
655
656static void
657copy_job_attributes(
658    _ipp_client_t *client,		/* I - Client */
659    _ipp_job_t    *job,			/* I - Job */
660    cups_array_t  *ra)			/* I - requested-attributes */
661{
662  copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0);
663
664  if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
665    ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
666                  "job-printer-up-time", (int)time(NULL));
667
668  if (!ra || cupsArrayFind(ra, "job-state"))
669    ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM,
670		  "job-state", job->state);
671
672  if (!ra || cupsArrayFind(ra, "job-state-reasons"))
673  {
674    switch (job->state)
675    {
676      case IPP_JSTATE_PENDING :
677	  ippAddString(client->response, IPP_TAG_JOB,
678	               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "job-state-reasons",
679		       NULL, "none");
680	  break;
681
682      case IPP_JSTATE_HELD :
683          if (job->fd >= 0)
684	    ippAddString(client->response, IPP_TAG_JOB,
685	                 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
686	                 "job-state-reasons", NULL, "job-incoming");
687	  else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
688	    ippAddString(client->response, IPP_TAG_JOB,
689	                 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
690	                 "job-state-reasons", NULL, "job-hold-until-specified");
691          else
692	    ippAddString(client->response, IPP_TAG_JOB,
693	                 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
694	                 "job-state-reasons", NULL, "job-data-insufficient");
695	  break;
696
697      case IPP_JSTATE_PROCESSING :
698	  if (job->cancel)
699	    ippAddString(client->response, IPP_TAG_JOB,
700	                 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
701	                 "job-state-reasons", NULL, "processing-to-stop-point");
702	  else
703	    ippAddString(client->response, IPP_TAG_JOB,
704	                 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
705	                 "job-state-reasons", NULL, "job-printing");
706	  break;
707
708      case IPP_JSTATE_STOPPED :
709	  ippAddString(client->response, IPP_TAG_JOB,
710	               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "job-state-reasons",
711		       NULL, "job-stopped");
712	  break;
713
714      case IPP_JSTATE_CANCELED :
715	  ippAddString(client->response, IPP_TAG_JOB,
716	               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "job-state-reasons",
717		       NULL, "job-canceled-by-user");
718	  break;
719
720      case IPP_JSTATE_ABORTED :
721	  ippAddString(client->response, IPP_TAG_JOB,
722	               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "job-state-reasons",
723		       NULL, "aborted-by-system");
724	  break;
725
726      case IPP_JSTATE_COMPLETED :
727	  ippAddString(client->response, IPP_TAG_JOB,
728	               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "job-state-reasons",
729		       NULL, "job-completed-successfully");
730	  break;
731    }
732  }
733
734  if (!ra || cupsArrayFind(ra, "time-at-completed"))
735    ippAddInteger(client->response, IPP_TAG_JOB,
736                  job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
737                  "time-at-completed", job->completed);
738
739  if (!ra || cupsArrayFind(ra, "time-at-processing"))
740    ippAddInteger(client->response, IPP_TAG_JOB,
741                  job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
742                  "time-at-processing", job->processing);
743}
744
745
746/*
747 * 'create_client()' - Accept a new network connection and create a client
748 *                     object.
749 */
750
751static _ipp_client_t *			/* O - Client */
752create_client(_ipp_printer_t *printer,	/* I - Printer */
753              int            sock)	/* I - Listen socket */
754{
755  _ipp_client_t	*client;		/* Client */
756
757
758  if ((client = calloc(1, sizeof(_ipp_client_t))) == NULL)
759  {
760    perror("Unable to allocate memory for client");
761    return (NULL);
762  }
763
764  client->printer = printer;
765
766 /*
767  * Accept the client and get the remote address...
768  */
769
770  if ((client->http = httpAcceptConnection(sock, 1)) == NULL)
771  {
772    perror("Unable to accept client connection");
773
774    free(client);
775
776    return (NULL);
777  }
778
779  httpGetHostname(client->http, client->hostname, sizeof(client->hostname));
780
781  if (Verbosity)
782    fprintf(stderr, "Accepted connection from %s\n", client->hostname);
783
784  return (client);
785}
786
787
788/*
789 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
790 *                  request.
791 */
792
793static _ipp_job_t *			/* O - Job */
794create_job(_ipp_client_t *client)	/* I - Client */
795{
796  _ipp_job_t		*job;		/* Job */
797  ipp_attribute_t	*attr;		/* Job attribute */
798  char			uri[1024];	/* job-uri value */
799
800
801  _cupsRWLockWrite(&(client->printer->rwlock));
802  if (client->printer->active_job &&
803      client->printer->active_job->state < IPP_JSTATE_CANCELED)
804  {
805   /*
806    * Only accept a single job at a time...
807    */
808
809    _cupsRWLockWrite(&(client->printer->rwlock));
810    return (NULL);
811  }
812
813 /*
814  * Allocate and initialize the job object...
815  */
816
817  if ((job = calloc(1, sizeof(_ipp_job_t))) == NULL)
818  {
819    perror("Unable to allocate memory for job");
820    return (NULL);
821  }
822
823  job->printer    = client->printer;
824  job->attrs      = client->request;
825  job->state      = IPP_JSTATE_HELD;
826  job->fd         = -1;
827  client->request = NULL;
828
829 /*
830  * Set all but the first two attributes to the job attributes group...
831  */
832
833  for (ippFirstAttribute(job->attrs),
834           ippNextAttribute(job->attrs),
835           attr = ippNextAttribute(job->attrs);
836       attr;
837       attr = ippNextAttribute(job->attrs))
838    ippSetGroupTag(job->attrs, &attr, IPP_TAG_JOB);
839
840 /*
841  * Get the requesting-user-name, document format, and priority...
842  */
843
844  if ((attr = ippFindAttribute(job->attrs, "requesting-user-name",
845                               IPP_TAG_NAME)) != NULL)
846    ippSetName(job->attrs, &attr, "job-originating-user-name");
847  else
848    attr = ippAddString(job->attrs, IPP_TAG_JOB,
849                        IPP_TAG_NAME | IPP_TAG_CUPS_CONST,
850                        "job-originating-user-name", NULL, "anonymous");
851
852  if (attr)
853    job->username = ippGetString(attr, 0, NULL);
854  else
855    job->username = "anonymous";
856
857  if ((attr = ippFindAttribute(job->attrs, "document-format",
858                               IPP_TAG_MIMETYPE)) != NULL)
859    job->format = ippGetString(attr, 0, NULL);
860  else
861    job->format = "application/octet-stream";
862
863 /*
864  * Add job description attributes and add to the jobs array...
865  */
866
867  job->id = client->printer->next_job_id ++;
868
869  snprintf(uri, sizeof(uri), "%s/%d", client->printer->uri, job->id);
870
871  ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
872  ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri);
873  ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
874               client->printer->uri);
875  ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
876                (int)time(NULL));
877
878  cupsArrayAdd(client->printer->jobs, job);
879  client->printer->active_job = job;
880
881  _cupsRWUnlock(&(client->printer->rwlock));
882
883  return (job);
884}
885
886
887/*
888 * 'create_listener()' - Create a listener socket.
889 */
890
891static int				/* O  - Listener socket or -1 on error */
892create_listener(int family,		/* I  - Address family */
893                int *port)		/* IO - Port number */
894{
895  int			sock;		/* Listener socket */
896  http_addrlist_t	*addrlist;	/* Listen address */
897  char			service[255];	/* Service port */
898
899
900  if (!*port)
901  {
902    *port = 8000 + (getuid() % 1000);
903    fprintf(stderr, "Listening on port %d.\n", *port);
904  }
905
906  snprintf(service, sizeof(service), "%d", *port);
907  if ((addrlist = httpAddrGetList(NULL, family, service)) == NULL)
908    return (-1);
909
910  sock = httpAddrListen(&(addrlist->addr), *port);
911
912  httpAddrFreeList(addrlist);
913
914  return (sock);
915}
916
917
918/*
919 * 'create_media_col()' - Create a media-col value.
920 */
921
922static ipp_t *				/* O - media-col collection */
923create_media_col(const char *media,	/* I - Media name */
924		 const char *type,	/* I - Nedua type */
925		 int        width,	/* I - x-dimension in 2540ths */
926		 int        length,	/* I - y-dimension in 2540ths */
927		 int        margins)	/* I - Value for margins */
928{
929  ipp_t	*media_col = ippNew(),		/* media-col value */
930	*media_size = create_media_size(width, length);
931					/* media-size value */
932  char	media_key[256];			/* media-key value */
933
934
935  snprintf(media_key, sizeof(media_key), "%s_%s%s", media, type,
936           margins == 0 ? "_borderless" : "");
937
938  ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL,
939               media_key);
940  ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
941  ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
942                "media-bottom-margin", margins);
943  ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
944                "media-left-margin", margins);
945  ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
946                "media-right-margin", margins);
947  ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
948                "media-top-margin", margins);
949  ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type",
950               NULL, type);
951
952  ippDelete(media_size);
953
954  return (media_col);
955}
956
957
958/*
959 * 'create_media_size()' - Create a media-size value.
960 */
961
962static ipp_t *				/* O - media-col collection */
963create_media_size(int width,		/* I - x-dimension in 2540ths */
964		  int length)		/* I - y-dimension in 2540ths */
965{
966  ipp_t	*media_size = ippNew();		/* media-size value */
967
968
969  ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension",
970                width);
971  ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension",
972                length);
973
974  return (media_size);
975}
976
977
978/*
979 * 'create_printer()' - Create, register, and listen for connections to a
980 *                      printer object.
981 */
982
983static _ipp_printer_t *			/* O - Printer */
984create_printer(const char *servername,	/* I - Server hostname (NULL for default) */
985               const char *name,	/* I - printer-name */
986	       const char *location,	/* I - printer-location */
987	       const char *make,	/* I - printer-make-and-model */
988	       const char *model,	/* I - printer-make-and-model */
989	       const char *icon,	/* I - printer-icons */
990	       const char *docformats,	/* I - document-format-supported */
991	       int        ppm,		/* I - Pages per minute in grayscale */
992	       int        ppm_color,	/* I - Pages per minute in color (0 for gray) */
993	       int        duplex,	/* I - 1 = duplex, 0 = simplex */
994	       int        port,		/* I - Port for listeners or 0 for auto */
995	       int        pin,		/* I - Require PIN printing */
996#ifdef HAVE_DNSSD
997	       const char *subtype,	/* I - Bonjour service subtype */
998#endif /* HAVE_DNSSD */
999	       const char *directory,	/* I - Spool directory */
1000	       const char *command)	/* I - Command to run on job files */
1001{
1002  int			i, j;		/* Looping vars */
1003  _ipp_printer_t	*printer;	/* Printer */
1004  char			hostname[256],	/* Hostname */
1005			uri[1024],	/* Printer URI */
1006			icons[1024],	/* printer-icons URI */
1007			adminurl[1024],	/* printer-more-info URI */
1008			device_id[1024],/* printer-device-id */
1009			make_model[128];/* printer-make-and-model */
1010  int			num_formats;	/* Number of document-format-supported values */
1011  char			*defformat,	/* document-format-default value */
1012			*formats[100],	/* document-format-supported values */
1013			*ptr;		/* Pointer into string */
1014  const char		*prefix;	/* Prefix string */
1015  int			num_database;	/* Number of database values */
1016  ipp_attribute_t	*media_col_database,
1017					/* media-col-database value */
1018			*media_size_supported;
1019					/* media-size-supported value */
1020  ipp_t			*media_col_default;
1021					/* media-col-default value */
1022  int			media_col_index;/* Current media-col-database value */
1023  int			k_supported;	/* Maximum file size supported */
1024#ifdef HAVE_STATVFS
1025  struct statvfs	spoolinfo;	/* FS info for spool directory */
1026  double		spoolsize;	/* FS size */
1027#elif defined(HAVE_STATFS)
1028  struct statfs		spoolinfo;	/* FS info for spool directory */
1029  double		spoolsize;	/* FS size */
1030#endif /* HAVE_STATVFS */
1031  static const int	orients[4] =	/* orientation-requested-supported values */
1032  {
1033    IPP_ORIENT_PORTRAIT,
1034    IPP_ORIENT_LANDSCAPE,
1035    IPP_ORIENT_REVERSE_LANDSCAPE,
1036    IPP_ORIENT_REVERSE_PORTRAIT
1037  };
1038  static const char * const versions[] =/* ipp-versions-supported values */
1039  {
1040    "1.0",
1041    "1.1",
1042    "2.0"
1043  };
1044  static const int	ops[] =		/* operations-supported values */
1045  {
1046    IPP_OP_PRINT_JOB,
1047    IPP_OP_PRINT_URI,
1048    IPP_OP_VALIDATE_JOB,
1049    IPP_OP_CREATE_JOB,
1050    IPP_OP_SEND_DOCUMENT,
1051    IPP_OP_SEND_URI,
1052    IPP_OP_CANCEL_JOB,
1053    IPP_OP_GET_JOB_ATTRIBUTES,
1054    IPP_OP_GET_JOBS,
1055    IPP_OP_GET_PRINTER_ATTRIBUTES
1056  };
1057  static const char * const charsets[] =/* charset-supported values */
1058  {
1059    "us-ascii",
1060    "utf-8"
1061  };
1062  static const char * const compressions[] =/* compression-supported values */
1063  {
1064#ifdef HAVE_LIBZ
1065    "deflate",
1066    "gzip",
1067#endif /* HAVE_LIBZ */
1068    "none"
1069  };
1070  static const char * const job_creation[] =
1071  {					/* job-creation-attributes-supported values */
1072    "copies",
1073    "ipp-attribute-fidelity",
1074    "job-account-id",
1075    "job-accounting-user-id",
1076    "job-name",
1077    "job-password",
1078    "job-priority",
1079    "media",
1080    "media-col",
1081    "multiple-document-handling",
1082    "orientation-requested",
1083    "print-quality",
1084    "sides"
1085  };
1086  static const char * const media_col_supported[] =
1087  {					/* media-col-supported values */
1088    "media-bottom-margin",
1089    "media-left-margin",
1090    "media-right-margin",
1091    "media-size",
1092    "media-top-margin",
1093    "media-type"
1094  };
1095  static const int	media_xxx_margin_supported[] =
1096  {					/* media-xxx-margin-supported values */
1097    0,
1098    635
1099  };
1100  static const char * const multiple_document_handling[] =
1101  {					/* multiple-document-handling-supported values */
1102    "separate-documents-uncollated-copies",
1103    "separate-documents-collated-copies"
1104  };
1105  static const int	print_quality_supported[] =
1106  {					/* print-quality-supported values */
1107    IPP_QUALITY_DRAFT,
1108    IPP_QUALITY_NORMAL,
1109    IPP_QUALITY_HIGH
1110  };
1111  static const int	pwg_raster_document_resolution_supported[] =
1112  {
1113    150,
1114    300,
1115    600
1116  };
1117  static const char * const pwg_raster_document_type_supported[] =
1118  {
1119    "black-1",
1120    "cmyk-8",
1121    "sgray-8",
1122    "srgb-8",
1123    "srgb-16"
1124  };
1125  static const char * const reference_uri_schemes_supported[] =
1126  {					/* reference-uri-schemes-supported */
1127    "file",
1128    "ftp",
1129    "http"
1130#ifdef HAVE_SSL
1131    , "https"
1132#endif /* HAVE_SSL */
1133  };
1134  static const char * const sides_supported[] =
1135  {					/* sides-supported values */
1136    "one-sided",
1137    "two-sided-long-edge",
1138    "two-sided-short-edge"
1139  };
1140  static const char * const which_jobs[] =
1141  {					/* which-jobs-supported values */
1142    "completed",
1143    "not-completed",
1144    "aborted",
1145    "all",
1146    "canceled",
1147    "pending",
1148    "pending-held",
1149    "processing",
1150    "processing-stopped"
1151  };
1152
1153
1154 /*
1155  * Allocate memory for the printer...
1156  */
1157
1158  if ((printer = calloc(1, sizeof(_ipp_printer_t))) == NULL)
1159  {
1160    perror("Unable to allocate memory for printer");
1161    return (NULL);
1162  }
1163
1164  printer->ipv4          = -1;
1165  printer->ipv6          = -1;
1166  printer->name          = strdup(name);
1167#ifdef HAVE_DNSSD
1168  printer->dnssd_name    = strdup(printer->name);
1169#endif /* HAVE_DNSSD */
1170  printer->command       = command ? strdup(command) : NULL;
1171  printer->directory     = strdup(directory);
1172  printer->hostname      = strdup(servername ? servername :
1173                                             httpGetHostname(NULL, hostname,
1174                                                             sizeof(hostname)));
1175  printer->port          = port;
1176  printer->state         = IPP_PSTATE_IDLE;
1177  printer->state_reasons = _IPP_PSTATE_NONE;
1178  printer->jobs          = cupsArrayNew((cups_array_func_t)compare_jobs, NULL);
1179  printer->next_job_id   = 1;
1180
1181  httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1182		  printer->hostname, printer->port, "/ipp/print");
1183  printer->uri    = strdup(uri);
1184  printer->urilen = strlen(uri);
1185
1186  if (icon)
1187    printer->icon = strdup(icon);
1188
1189  _cupsRWInit(&(printer->rwlock));
1190
1191 /*
1192  * Create the listener sockets...
1193  */
1194
1195  if ((printer->ipv4 = create_listener(AF_INET, &(printer->port))) < 0)
1196  {
1197    perror("Unable to create IPv4 listener");
1198    goto bad_printer;
1199  }
1200
1201  if ((printer->ipv6 = create_listener(AF_INET6, &(printer->port))) < 0)
1202  {
1203    perror("Unable to create IPv6 listener");
1204    goto bad_printer;
1205  }
1206
1207 /*
1208  * Prepare values for the printer attributes...
1209  */
1210
1211  httpAssembleURI(HTTP_URI_CODING_ALL, icons, sizeof(icons), "http", NULL,
1212                  printer->hostname, printer->port, "/icon.png");
1213  httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), "http", NULL,
1214                  printer->hostname, printer->port, "/");
1215
1216  if (Verbosity)
1217  {
1218    fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl);
1219    fprintf(stderr, "printer-uri=\"%s\"\n", uri);
1220  }
1221
1222  snprintf(make_model, sizeof(make_model), "%s %s", make, model);
1223
1224  num_formats = 1;
1225  formats[0]  = strdup(docformats);
1226  defformat   = formats[0];
1227  for (ptr = strchr(formats[0], ','); ptr; ptr = strchr(ptr, ','))
1228  {
1229    *ptr++ = '\0';
1230    formats[num_formats++] = ptr;
1231
1232    if (!_cups_strcasecmp(ptr, "application/octet-stream"))
1233      defformat = ptr;
1234  }
1235
1236  snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
1237  ptr    = device_id + strlen(device_id);
1238  prefix = "CMD:";
1239  for (i = 0; i < num_formats; i ++)
1240  {
1241    if (!_cups_strcasecmp(formats[i], "application/pdf"))
1242      snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPDF", prefix);
1243    else if (!_cups_strcasecmp(formats[i], "application/postscript"))
1244      snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPS", prefix);
1245    else if (!_cups_strcasecmp(formats[i], "application/vnd.hp-PCL"))
1246      snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPCL", prefix);
1247    else if (!_cups_strcasecmp(formats[i], "image/jpeg"))
1248      snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sJPEG", prefix);
1249    else if (!_cups_strcasecmp(formats[i], "image/png"))
1250      snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPNG", prefix);
1251    else if (_cups_strcasecmp(formats[i], "application/octet-stream"))
1252      snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%s%s", prefix,
1253               formats[i]);
1254
1255    ptr += strlen(ptr);
1256    prefix = ",";
1257  }
1258  strlcat(device_id, ";", sizeof(device_id));
1259
1260 /*
1261  * Get the maximum spool size based on the size of the filesystem used for
1262  * the spool directory.  If the host OS doesn't support the statfs call
1263  * or the filesystem is larger than 2TiB, always report INT_MAX.
1264  */
1265
1266#ifdef HAVE_STATVFS
1267  if (statvfs(printer->directory, &spoolinfo))
1268    k_supported = INT_MAX;
1269  else if ((spoolsize = (double)spoolinfo.f_frsize *
1270                        spoolinfo.f_blocks / 1024) > INT_MAX)
1271    k_supported = INT_MAX;
1272  else
1273    k_supported = (int)spoolsize;
1274
1275#elif defined(HAVE_STATFS)
1276  if (statfs(printer->directory, &spoolinfo))
1277    k_supported = INT_MAX;
1278  else if ((spoolsize = (double)spoolinfo.f_bsize *
1279                        spoolinfo.f_blocks / 1024) > INT_MAX)
1280    k_supported = INT_MAX;
1281  else
1282    k_supported = (int)spoolsize;
1283
1284#else
1285  k_supported = INT_MAX;
1286#endif /* HAVE_STATVFS */
1287
1288 /*
1289  * Create the printer attributes.  This list of attributes is sorted to improve
1290  * performance when the client provides a requested-attributes attribute...
1291  */
1292
1293  printer->attrs = ippNew();
1294
1295  /* charset-configured */
1296  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1297               IPP_TAG_CHARSET | IPP_TAG_CUPS_CONST,
1298               "charset-configured", NULL, "utf-8");
1299
1300  /* charset-supported */
1301  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1302                IPP_TAG_CHARSET | IPP_TAG_CUPS_CONST,
1303                "charset-supported", sizeof(charsets) / sizeof(charsets[0]),
1304		NULL, charsets);
1305
1306  /* color-supported */
1307  ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "color-supported",
1308                ppm_color > 0);
1309
1310  /* compression-supported */
1311  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1312                IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1313	        "compression-supported",
1314	        (int)(sizeof(compressions) / sizeof(compressions[0])), NULL,
1315	        compressions);
1316
1317  /* copies-default */
1318  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1319                "copies-default", 1);
1320
1321  /* copies-supported */
1322  ippAddRange(printer->attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
1323
1324  /* document-format-default */
1325  ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
1326               "document-format-default", NULL, defformat);
1327
1328  /* document-format-supported */
1329  ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE,
1330                "document-format-supported", num_formats, NULL,
1331		(const char * const *)formats);
1332
1333  /* finishings-default */
1334  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1335                "finishings-default", IPP_FINISHINGS_NONE);
1336
1337  /* finishings-supported */
1338  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1339                "finishings-supported", IPP_FINISHINGS_NONE);
1340
1341  /* generated-natural-language-supported */
1342  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1343               IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST,
1344               "generated-natural-language-supported", NULL, "en");
1345
1346  /* ipp-versions-supported */
1347  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1348                IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1349                "ipp-versions-supported",
1350		sizeof(versions) / sizeof(versions[0]), NULL, versions);
1351
1352  /* job-account-id-supported */
1353  ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-account-id-supported", 1);
1354
1355  /* job-accounting-user-id-supported */
1356  ippAddBoolean(printer->attrs, IPP_TAG_PRINTER,
1357                "job-accounting-user-id-supported", 1);
1358
1359  /* job-creation-attributes-supported */
1360  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1361                IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1362                "job-creation-attributes-supported",
1363		sizeof(job_creation) / sizeof(job_creation[0]),
1364		NULL, job_creation);
1365
1366  /* job-k-octets-supported */
1367  ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0,
1368	      k_supported);
1369
1370  /* job-password-supported */
1371  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1372                "job-password-supported", 4);
1373
1374  /* job-priority-default */
1375  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1376                "job-priority-default", 50);
1377
1378  /* job-priority-supported */
1379  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1380                "job-priority-supported", 100);
1381
1382  /* job-sheets-default */
1383  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1384               IPP_TAG_NAME | IPP_TAG_CUPS_CONST,
1385               "job-sheets-default", NULL, "none");
1386
1387  /* job-sheets-supported */
1388  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1389               IPP_TAG_NAME | IPP_TAG_CUPS_CONST,
1390               "job-sheets-supported", NULL, "none");
1391
1392  /* media-bottom-margin-supported */
1393  ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1394                 "media-bottom-margin-supported",
1395		 (int)(sizeof(media_xxx_margin_supported) /
1396		       sizeof(media_xxx_margin_supported[0])),
1397		 media_xxx_margin_supported);
1398
1399  /* media-col-database */
1400  for (num_database = 0, i = 0;
1401       i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
1402       i ++)
1403  {
1404    if (media_col_sizes[i][2] == _IPP_ENV_ONLY)
1405      num_database += 2;		/* auto + envelope */
1406    else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY)
1407      num_database += 12;		/* auto + photographic-* + borderless */
1408    else
1409      num_database += (int)(sizeof(media_type_supported) /
1410                            sizeof(media_type_supported[0])) + 6;
1411					/* All types + borderless */
1412  }
1413
1414  media_col_database = ippAddCollections(printer->attrs, IPP_TAG_PRINTER,
1415                                         "media-col-database", num_database,
1416					 NULL);
1417  for (media_col_index = 0, i = 0;
1418       i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
1419       i ++)
1420  {
1421    for (j = 0;
1422         j < (int)(sizeof(media_type_supported) /
1423	           sizeof(media_type_supported[0]));
1424         j ++)
1425    {
1426      if (media_col_sizes[i][2] == _IPP_ENV_ONLY &&
1427          strcmp(media_type_supported[j], "auto") &&
1428	  strcmp(media_type_supported[j], "envelope"))
1429	continue;
1430      else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY &&
1431               strcmp(media_type_supported[j], "auto") &&
1432	       strncmp(media_type_supported[j], "photographic-", 13))
1433	continue;
1434
1435      ippSetCollection(printer->attrs, &media_col_database, media_col_index,
1436                       create_media_col(media_supported[i],
1437                                        media_type_supported[j],
1438					media_col_sizes[i][0],
1439					media_col_sizes[i][1],
1440					media_xxx_margin_supported[1]));
1441      media_col_index ++;
1442
1443      if (media_col_sizes[i][2] != _IPP_ENV_ONLY &&
1444	  (!strcmp(media_type_supported[j], "auto") ||
1445	   !strncmp(media_type_supported[j], "photographic-", 13)))
1446      {
1447       /*
1448        * Add borderless version for this combination...
1449	*/
1450
1451	ippSetCollection(printer->attrs, &media_col_database, media_col_index,
1452                         create_media_col(media_supported[i],
1453                                          media_type_supported[j],
1454					  media_col_sizes[i][0],
1455					  media_col_sizes[i][1],
1456					  media_xxx_margin_supported[0]));
1457	media_col_index ++;
1458      }
1459    }
1460  }
1461
1462  /* media-col-default */
1463  media_col_default = create_media_col(media_supported[0],
1464                                       media_type_supported[0],
1465				       media_col_sizes[0][0],
1466				       media_col_sizes[0][1],
1467				       media_xxx_margin_supported[1]);
1468
1469  ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-default",
1470                   media_col_default);
1471  ippDelete(media_col_default);
1472
1473  /* media-col-supported */
1474  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1475                IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1476                "media-col-supported",
1477		(int)(sizeof(media_col_supported) /
1478		      sizeof(media_col_supported[0])), NULL,
1479		media_col_supported);
1480
1481  /* media-default */
1482  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1483               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1484               "media-default", NULL, media_supported[0]);
1485
1486  /* media-left-margin-supported */
1487  ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1488                 "media-left-margin-supported",
1489		 (int)(sizeof(media_xxx_margin_supported) /
1490		       sizeof(media_xxx_margin_supported[0])),
1491		 media_xxx_margin_supported);
1492
1493  /* media-right-margin-supported */
1494  ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1495                 "media-right-margin-supported",
1496		 (int)(sizeof(media_xxx_margin_supported) /
1497		       sizeof(media_xxx_margin_supported[0])),
1498		 media_xxx_margin_supported);
1499
1500  /* media-supported */
1501  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1502                IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1503                "media-supported",
1504		(int)(sizeof(media_supported) / sizeof(media_supported[0])),
1505		NULL, media_supported);
1506
1507  /* media-size-supported */
1508  media_size_supported = ippAddCollections(printer->attrs, IPP_TAG_PRINTER,
1509                                           "media-size-supported",
1510                                           (int)(sizeof(media_col_sizes) /
1511                                                 sizeof(media_col_sizes[0])),
1512                                           NULL);
1513  for (i = 0;
1514       i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0]));
1515       i ++)
1516    ippSetCollection(printer->attrs, &media_size_supported, i,
1517		     create_media_size(media_col_sizes[i][0],
1518		                       media_col_sizes[i][1]));
1519
1520  /* media-top-margin-supported */
1521  ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1522                 "media-top-margin-supported",
1523		 (int)(sizeof(media_xxx_margin_supported) /
1524		       sizeof(media_xxx_margin_supported[0])),
1525		 media_xxx_margin_supported);
1526
1527  /* media-type-supported */
1528  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1529                IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1530                "media-type-supported",
1531		(int)(sizeof(media_type_supported) /
1532		      sizeof(media_type_supported[0])),
1533		NULL, media_type_supported);
1534
1535  /* multiple-document-handling-supported */
1536  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1537                IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1538                "multiple-document-handling-supported",
1539                sizeof(multiple_document_handling) /
1540		    sizeof(multiple_document_handling[0]), NULL,
1541	        multiple_document_handling);
1542
1543  /* multiple-document-jobs-supported */
1544  ippAddBoolean(printer->attrs, IPP_TAG_PRINTER,
1545                "multiple-document-jobs-supported", 0);
1546
1547  /* natural-language-configured */
1548  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1549               IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST,
1550               "natural-language-configured", NULL, "en");
1551
1552  /* number-up-default */
1553  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1554                "number-up-default", 1);
1555
1556  /* number-up-supported */
1557  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1558                "number-up-supported", 1);
1559
1560  /* operations-supported */
1561  ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1562		 "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
1563
1564  /* orientation-requested-default */
1565  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
1566                "orientation-requested-default", 0);
1567
1568  /* orientation-requested-supported */
1569  ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1570                 "orientation-requested-supported", 4, orients);
1571
1572  /* output-bin-default */
1573  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1574               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1575               "output-bin-default", NULL, "face-down");
1576
1577  /* output-bin-supported */
1578  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1579               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1580               "output-bin-supported", NULL, "face-down");
1581
1582  /* pages-per-minute */
1583  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1584                "pages-per-minute", ppm);
1585
1586  /* pages-per-minute-color */
1587  if (ppm_color > 0)
1588    ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1589                  "pages-per-minute-color", ppm_color);
1590
1591  /* pdl-override-supported */
1592  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1593               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1594               "pdl-override-supported", NULL, "attempted");
1595
1596  /* print-quality-default */
1597  ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1598                "print-quality-default", IPP_QUALITY_NORMAL);
1599
1600  /* print-quality-supported */
1601  ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM,
1602                 "print-quality-supported",
1603		 (int)(sizeof(print_quality_supported) /
1604		       sizeof(print_quality_supported[0])),
1605		 print_quality_supported);
1606
1607  /* printer-device-id */
1608  ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1609	       "printer-device-id", NULL, device_id);
1610
1611  /* printer-icons */
1612  ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1613               "printer-icons", NULL, icons);
1614
1615  /* printer-is-accepting-jobs */
1616  ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
1617                1);
1618
1619  /* printer-info */
1620  ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info",
1621               NULL, name);
1622
1623  /* printer-location */
1624  ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1625               "printer-location", NULL, location);
1626
1627  /* printer-make-and-model */
1628  ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT,
1629               "printer-make-and-model", NULL, make_model);
1630
1631  /* printer-mandatory-job-attributes */
1632  if (pin)
1633  {
1634    static const char * const names[] =
1635    {
1636      "job-accounting-user-id",
1637      "job-password"
1638    };
1639
1640    ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1641                  "printer-mandatory-job-attributes",
1642                  (int)(sizeof(names) / sizeof(names[0])), NULL, names);
1643  }
1644
1645  /* printer-more-info */
1646  ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1647               "printer-more-info", NULL, adminurl);
1648
1649  /* printer-name */
1650  ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name",
1651               NULL, name);
1652
1653  /* printer-resolution-default */
1654  ippAddResolution(printer->attrs, IPP_TAG_PRINTER,
1655                   "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
1656
1657  /* printer-resolution-supported */
1658  ippAddResolution(printer->attrs, IPP_TAG_PRINTER,
1659                   "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
1660
1661  /* printer-uri-supported */
1662  ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
1663               "printer-uri-supported", NULL, uri);
1664
1665  /* pwg-raster-document-xxx-supported */
1666  for (i = 0; i < num_formats; i ++)
1667    if (!_cups_strcasecmp(formats[i], "image/pwg-raster"))
1668      break;
1669
1670  if (i < num_formats)
1671  {
1672    ippAddResolutions(printer->attrs, IPP_TAG_PRINTER,
1673                      "pwg-raster-document-resolution-supported",
1674                      (int)(sizeof(pwg_raster_document_resolution_supported) /
1675                            sizeof(pwg_raster_document_resolution_supported[0])),
1676                      IPP_RES_PER_INCH,
1677                      pwg_raster_document_resolution_supported,
1678                      pwg_raster_document_resolution_supported);
1679    ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1680                 "pwg-raster-document-sheet-back", NULL, "normal");
1681    ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1682                  "pwg-raster-document-type-supported",
1683                  (int)(sizeof(pwg_raster_document_type_supported) /
1684                        sizeof(pwg_raster_document_type_supported[0])), NULL,
1685                  pwg_raster_document_type_supported);
1686  }
1687
1688  /* reference-uri-scheme-supported */
1689  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1690                IPP_TAG_URISCHEME | IPP_TAG_CUPS_CONST,
1691                "reference-uri-schemes-supported",
1692                (int)(sizeof(reference_uri_schemes_supported) /
1693                      sizeof(reference_uri_schemes_supported[0])),
1694                NULL, reference_uri_schemes_supported);
1695
1696  /* sides-default */
1697  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1698               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1699               "sides-default", NULL, "one-sided");
1700
1701  /* sides-supported */
1702  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1703                IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1704                "sides-supported", duplex ? 3 : 1, NULL, sides_supported);
1705
1706  /* uri-authentication-supported */
1707  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1708               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1709               "uri-authentication-supported", NULL, "none");
1710
1711  /* uri-security-supported */
1712  ippAddString(printer->attrs, IPP_TAG_PRINTER,
1713               IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1714               "uri-security-supported", NULL, "none");
1715
1716  /* which-jobs-supported */
1717  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
1718                IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
1719                "which-jobs-supported",
1720                sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
1721
1722  free(formats[0]);
1723
1724  debug_attributes("Printer", printer->attrs, 0);
1725
1726#ifdef HAVE_DNSSD
1727 /*
1728  * Register the printer with Bonjour...
1729  */
1730
1731  if (!register_printer(printer, location, make, model, docformats, adminurl,
1732                        ppm_color > 0, duplex, subtype))
1733    goto bad_printer;
1734#endif /* HAVE_DNSSD */
1735
1736 /*
1737  * Return it!
1738  */
1739
1740  return (printer);
1741
1742
1743 /*
1744  * If we get here we were unable to create the printer...
1745  */
1746
1747  bad_printer:
1748
1749  delete_printer(printer);
1750  return (NULL);
1751}
1752
1753
1754/*
1755 * 'debug_attributes()' - Print attributes in a request or response.
1756 */
1757
1758static void
1759debug_attributes(const char *title,	/* I - Title */
1760                 ipp_t      *ipp,	/* I - Request/response */
1761                 int        type)	/* I - 0 = object, 1 = request, 2 = response */
1762{
1763  ipp_tag_t		group_tag;	/* Current group */
1764  ipp_attribute_t	*attr;		/* Current attribute */
1765  char			buffer[2048];	/* String buffer for value */
1766  int			major, minor;	/* Version */
1767
1768
1769  if (Verbosity <= 1)
1770    return;
1771
1772  fprintf(stderr, "%s:\n", title);
1773  major = ippGetVersion(ipp, &minor);
1774  fprintf(stderr, "  version=%d.%d\n", major, minor);
1775  if (type == 1)
1776    fprintf(stderr, "  operation-id=%s(%04x)\n",
1777            ippOpString(ippGetOperation(ipp)), ippGetOperation(ipp));
1778  else if (type == 2)
1779    fprintf(stderr, "  status-code=%s(%04x)\n",
1780            ippErrorString(ippGetStatusCode(ipp)), ippGetStatusCode(ipp));
1781  fprintf(stderr, "  request-id=%d\n\n", ippGetRequestId(ipp));
1782
1783  for (attr = ippFirstAttribute(ipp), group_tag = IPP_TAG_ZERO;
1784       attr;
1785       attr = ippNextAttribute(ipp))
1786  {
1787    if (ippGetGroupTag(attr) != group_tag)
1788    {
1789      group_tag = ippGetGroupTag(attr);
1790      fprintf(stderr, "  %s\n", ippTagString(group_tag));
1791    }
1792
1793    if (ippGetName(attr))
1794    {
1795      ippAttributeString(attr, buffer, sizeof(buffer));
1796      fprintf(stderr, "    %s (%s%s) %s\n", ippGetName(attr),
1797	      ippGetCount(attr) > 1 ? "1setOf " : "",
1798	      ippTagString(ippGetValueTag(attr)), buffer);
1799    }
1800  }
1801}
1802
1803
1804/*
1805 * 'delete_client()' - Close the socket and free all memory used by a client
1806 *                     object.
1807 */
1808
1809static void
1810delete_client(_ipp_client_t *client)	/* I - Client */
1811{
1812  if (Verbosity)
1813    fprintf(stderr, "Closing connection from %s\n", client->hostname);
1814
1815 /*
1816  * Flush pending writes before closing...
1817  */
1818
1819  httpFlushWrite(client->http);
1820
1821 /*
1822  * Free memory...
1823  */
1824
1825  httpClose(client->http);
1826
1827  ippDelete(client->request);
1828  ippDelete(client->response);
1829
1830  free(client);
1831}
1832
1833
1834/*
1835 * 'delete_job()' - Remove from the printer and free all memory used by a job
1836 *                  object.
1837 */
1838
1839static void
1840delete_job(_ipp_job_t *job)		/* I - Job */
1841{
1842  if (Verbosity)
1843    fprintf(stderr, "Removing job #%d from history.\n", job->id);
1844
1845  ippDelete(job->attrs);
1846
1847  if (job->filename)
1848  {
1849    if (!KeepFiles)
1850      unlink(job->filename);
1851
1852    free(job->filename);
1853  }
1854
1855  free(job);
1856}
1857
1858
1859/*
1860 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1861 *                      used by a printer object.
1862 */
1863
1864static void
1865delete_printer(_ipp_printer_t *printer)	/* I - Printer */
1866{
1867  if (printer->ipv4 >= 0)
1868    close(printer->ipv4);
1869
1870  if (printer->ipv6 >= 0)
1871    close(printer->ipv6);
1872
1873#if HAVE_DNSSD
1874  if (printer->printer_ref)
1875    DNSServiceRefDeallocate(printer->printer_ref);
1876
1877  if (printer->ipp_ref)
1878    DNSServiceRefDeallocate(printer->ipp_ref);
1879
1880#  ifdef HAVE_SSL
1881  if (printer->ipps_ref)
1882    DNSServiceRefDeallocate(printer->ipps_ref);
1883#  endif /* HAVE_SSL */
1884  if (printer->http_ref)
1885    DNSServiceRefDeallocate(printer->http_ref);
1886
1887  if (printer->common_ref)
1888    DNSServiceRefDeallocate(printer->common_ref);
1889
1890  TXTRecordDeallocate(&(printer->ipp_txt));
1891
1892  if (printer->dnssd_name)
1893    free(printer->dnssd_name);
1894#endif /* HAVE_DNSSD */
1895
1896  if (printer->name)
1897    free(printer->name);
1898  if (printer->icon)
1899    free(printer->icon);
1900  if (printer->command)
1901    free(printer->command);
1902  if (printer->directory)
1903    free(printer->directory);
1904  if (printer->hostname)
1905    free(printer->hostname);
1906  if (printer->uri)
1907    free(printer->uri);
1908
1909  ippDelete(printer->attrs);
1910  cupsArrayDelete(printer->jobs);
1911
1912  free(printer);
1913}
1914
1915
1916#ifdef HAVE_DNSSD
1917/*
1918 * 'dnssd_callback()' - Handle Bonjour registration events.
1919 */
1920
1921static void
1922dnssd_callback(
1923    DNSServiceRef       sdRef,		/* I - Service reference */
1924    DNSServiceFlags     flags,		/* I - Status flags */
1925    DNSServiceErrorType errorCode,	/* I - Error, if any */
1926    const char          *name,		/* I - Service name */
1927    const char          *regtype,	/* I - Service type */
1928    const char          *domain,	/* I - Domain for service */
1929    _ipp_printer_t      *printer)	/* I - Printer */
1930{
1931  if (errorCode)
1932  {
1933    fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n",
1934            regtype, (int)errorCode);
1935    return;
1936  }
1937  else if (_cups_strcasecmp(name, printer->dnssd_name))
1938  {
1939    if (Verbosity)
1940      fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", name);
1941
1942    /* No lock needed since only the main thread accesses/changes this */
1943    free(printer->dnssd_name);
1944    printer->dnssd_name = strdup(name);
1945  }
1946}
1947#endif /* HAVE_DNSSD */
1948
1949
1950/*
1951 * 'find_job()' - Find a job specified in a request.
1952 */
1953
1954static _ipp_job_t *			/* O - Job or NULL */
1955find_job(_ipp_client_t *client)		/* I - Client */
1956{
1957  ipp_attribute_t	*attr;		/* job-id or job-uri attribute */
1958  _ipp_job_t		key,		/* Job search key */
1959			*job;		/* Matching job, if any */
1960
1961
1962  key.id = 0;
1963
1964  if ((attr = ippFindAttribute(client->request, "job-uri",
1965                               IPP_TAG_URI)) != NULL)
1966  {
1967    const char *uri = ippGetString(attr, 0, NULL);
1968
1969    if (!strncmp(uri, client->printer->uri, client->printer->urilen) &&
1970        uri[client->printer->urilen] == '/')
1971      key.id = atoi(uri + client->printer->urilen + 1);
1972  }
1973  else if ((attr = ippFindAttribute(client->request, "job-id",
1974                                    IPP_TAG_INTEGER)) != NULL)
1975    key.id = ippGetInteger(attr, 0);
1976
1977  _cupsRWLockRead(&(client->printer->rwlock));
1978  job = (_ipp_job_t *)cupsArrayFind(client->printer->jobs, &key);
1979  _cupsRWUnlock(&(client->printer->rwlock));
1980
1981  return (job);
1982}
1983
1984
1985/*
1986 * 'html_escape()' - Write a HTML-safe string.
1987 */
1988
1989static void
1990html_escape(_ipp_client_t *client,	/* I - Client */
1991	    const char    *s,		/* I - String to write */
1992	    size_t        slen)		/* I - Number of characters to write */
1993{
1994  const char	*start,			/* Start of segment */
1995		*end;			/* End of string */
1996
1997
1998  start = s;
1999  end   = s + (slen > 0 ? slen : strlen(s));
2000
2001  while (*s && s < end)
2002  {
2003    if (*s == '&' || *s == '<')
2004    {
2005      if (s > start)
2006        httpWrite2(client->http, start, s - start);
2007
2008      if (*s == '&')
2009        httpWrite2(client->http, "&amp;", 5);
2010      else
2011        httpWrite2(client->http, "&lt;", 4);
2012
2013      start = s + 1;
2014    }
2015
2016    s ++;
2017  }
2018
2019  if (s > start)
2020    httpWrite2(client->http, start, s - start);
2021}
2022
2023
2024/*
2025 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2026 */
2027
2028static void
2029html_printf(_ipp_client_t *client,	/* I - Client */
2030	    const char    *format,	/* I - Printf-style format string */
2031	    ...)			/* I - Additional arguments as needed */
2032{
2033  va_list	ap;			/* Pointer to arguments */
2034  const char	*start;			/* Start of string */
2035  char		size,			/* Size character (h, l, L) */
2036		type;			/* Format type character */
2037  int		width,			/* Width of field */
2038		prec;			/* Number of characters of precision */
2039  char		tformat[100],		/* Temporary format string for sprintf() */
2040		*tptr,			/* Pointer into temporary format */
2041		temp[1024];		/* Buffer for formatted numbers */
2042  char		*s;			/* Pointer to string */
2043
2044
2045 /*
2046  * Loop through the format string, formatting as needed...
2047  */
2048
2049  va_start(ap, format);
2050  start = format;
2051
2052  while (*format)
2053  {
2054    if (*format == '%')
2055    {
2056      if (format > start)
2057        httpWrite2(client->http, start, format - start);
2058
2059      tptr    = tformat;
2060      *tptr++ = *format++;
2061
2062      if (*format == '%')
2063      {
2064        httpWrite2(client->http, "%", 1);
2065        format ++;
2066	continue;
2067      }
2068      else if (strchr(" -+#\'", *format))
2069        *tptr++ = *format++;
2070
2071      if (*format == '*')
2072      {
2073       /*
2074        * Get width from argument...
2075	*/
2076
2077	format ++;
2078	width = va_arg(ap, int);
2079
2080	snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
2081	tptr += strlen(tptr);
2082      }
2083      else
2084      {
2085	width = 0;
2086
2087	while (isdigit(*format & 255))
2088	{
2089	  if (tptr < (tformat + sizeof(tformat) - 1))
2090	    *tptr++ = *format;
2091
2092	  width = width * 10 + *format++ - '0';
2093	}
2094      }
2095
2096      if (*format == '.')
2097      {
2098	if (tptr < (tformat + sizeof(tformat) - 1))
2099	  *tptr++ = *format;
2100
2101        format ++;
2102
2103        if (*format == '*')
2104	{
2105         /*
2106	  * Get precision from argument...
2107	  */
2108
2109	  format ++;
2110	  prec = va_arg(ap, int);
2111
2112	  snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
2113	  tptr += strlen(tptr);
2114	}
2115	else
2116	{
2117	  prec = 0;
2118
2119	  while (isdigit(*format & 255))
2120	  {
2121	    if (tptr < (tformat + sizeof(tformat) - 1))
2122	      *tptr++ = *format;
2123
2124	    prec = prec * 10 + *format++ - '0';
2125	  }
2126	}
2127      }
2128
2129      if (*format == 'l' && format[1] == 'l')
2130      {
2131        size = 'L';
2132
2133	if (tptr < (tformat + sizeof(tformat) - 2))
2134	{
2135	  *tptr++ = 'l';
2136	  *tptr++ = 'l';
2137	}
2138
2139	format += 2;
2140      }
2141      else if (*format == 'h' || *format == 'l' || *format == 'L')
2142      {
2143	if (tptr < (tformat + sizeof(tformat) - 1))
2144	  *tptr++ = *format;
2145
2146        size = *format++;
2147      }
2148      else
2149        size = 0;
2150
2151
2152      if (!*format)
2153      {
2154        start = format;
2155        break;
2156      }
2157
2158      if (tptr < (tformat + sizeof(tformat) - 1))
2159        *tptr++ = *format;
2160
2161      type  = *format++;
2162      *tptr = '\0';
2163      start = format;
2164
2165      switch (type)
2166      {
2167	case 'E' : /* Floating point formats */
2168	case 'G' :
2169	case 'e' :
2170	case 'f' :
2171	case 'g' :
2172	    if ((width + 2) > sizeof(temp))
2173	      break;
2174
2175	    sprintf(temp, tformat, va_arg(ap, double));
2176
2177            httpWrite2(client->http, temp, strlen(temp));
2178	    break;
2179
2180        case 'B' : /* Integer formats */
2181	case 'X' :
2182	case 'b' :
2183        case 'd' :
2184	case 'i' :
2185	case 'o' :
2186	case 'u' :
2187	case 'x' :
2188	    if ((width + 2) > sizeof(temp))
2189	      break;
2190
2191#  ifdef HAVE_LONG_LONG
2192            if (size == 'L')
2193	      sprintf(temp, tformat, va_arg(ap, long long));
2194	    else
2195#  endif /* HAVE_LONG_LONG */
2196            if (size == 'l')
2197	      sprintf(temp, tformat, va_arg(ap, long));
2198	    else
2199	      sprintf(temp, tformat, va_arg(ap, int));
2200
2201            httpWrite2(client->http, temp, strlen(temp));
2202	    break;
2203
2204	case 'p' : /* Pointer value */
2205	    if ((width + 2) > sizeof(temp))
2206	      break;
2207
2208	    sprintf(temp, tformat, va_arg(ap, void *));
2209
2210            httpWrite2(client->http, temp, strlen(temp));
2211	    break;
2212
2213        case 'c' : /* Character or character array */
2214            if (width <= 1)
2215            {
2216              temp[0] = va_arg(ap, int);
2217              temp[1] = '\0';
2218              html_escape(client, temp, 1);
2219            }
2220            else
2221              html_escape(client, va_arg(ap, char *), (size_t)width);
2222	    break;
2223
2224	case 's' : /* String */
2225	    if ((s = va_arg(ap, char *)) == NULL)
2226	      s = "(null)";
2227
2228            html_escape(client, s, strlen(s));
2229	    break;
2230      }
2231    }
2232    else
2233      format ++;
2234  }
2235
2236  if (format > start)
2237    httpWrite2(client->http, start, format - start);
2238
2239  va_end(ap);
2240}
2241
2242
2243/*
2244 * 'ipp_cancel_job()' - Cancel a job.
2245 */
2246
2247static void
2248ipp_cancel_job(_ipp_client_t *client)	/* I - Client */
2249{
2250  _ipp_job_t		*job;		/* Job information */
2251
2252
2253 /*
2254  * Get the job...
2255  */
2256
2257  if ((job = find_job(client)) == NULL)
2258  {
2259    respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
2260    return;
2261  }
2262
2263 /*
2264  * See if the job is already completed, canceled, or aborted; if so,
2265  * we can't cancel...
2266  */
2267
2268  switch (job->state)
2269  {
2270    case IPP_JSTATE_CANCELED :
2271	respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2272		    "Job #%d is already canceled - can\'t cancel.", job->id);
2273        break;
2274
2275    case IPP_JSTATE_ABORTED :
2276	respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2277		    "Job #%d is already aborted - can\'t cancel.", job->id);
2278        break;
2279
2280    case IPP_JSTATE_COMPLETED :
2281	respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
2282		    "Job #%d is already completed - can\'t cancel.", job->id);
2283        break;
2284
2285    default :
2286       /*
2287        * Cancel the job...
2288	*/
2289
2290	_cupsRWLockWrite(&(client->printer->rwlock));
2291
2292	if (job->state == IPP_JSTATE_PROCESSING ||
2293	    (job->state == IPP_JSTATE_HELD && job->fd >= 0))
2294          job->cancel = 1;
2295	else
2296	{
2297	  job->state     = IPP_JSTATE_CANCELED;
2298	  job->completed = time(NULL);
2299	}
2300
2301	_cupsRWUnlock(&(client->printer->rwlock));
2302
2303	respond_ipp(client, IPP_STATUS_OK, NULL);
2304        break;
2305  }
2306}
2307
2308
2309/*
2310 * 'ipp_create_job()' - Create a job object.
2311 */
2312
2313static void
2314ipp_create_job(_ipp_client_t *client)	/* I - Client */
2315{
2316  _ipp_job_t		*job;		/* New job */
2317  cups_array_t		*ra;		/* Attributes to send in response */
2318
2319
2320 /*
2321  * Validate print job attributes...
2322  */
2323
2324  if (!valid_job_attributes(client))
2325  {
2326    httpFlush(client->http);
2327    return;
2328  }
2329
2330 /*
2331  * Do we have a file to print?
2332  */
2333
2334  if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
2335  {
2336    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
2337                "Unexpected document data following request.");
2338    return;
2339  }
2340
2341 /*
2342  * Create the job...
2343  */
2344
2345  if ((job = create_job(client)) == NULL)
2346  {
2347    respond_ipp(client, IPP_STATUS_ERROR_BUSY,
2348                "Currently printing another job.");
2349    return;
2350  }
2351
2352 /*
2353  * Return the job info...
2354  */
2355
2356  respond_ipp(client, IPP_STATUS_OK, NULL);
2357
2358  ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2359  cupsArrayAdd(ra, "job-id");
2360  cupsArrayAdd(ra, "job-state");
2361  cupsArrayAdd(ra, "job-state-reasons");
2362  cupsArrayAdd(ra, "job-uri");
2363
2364  copy_job_attributes(client, job, ra);
2365  cupsArrayDelete(ra);
2366}
2367
2368
2369/*
2370 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2371 */
2372
2373static void
2374ipp_get_job_attributes(
2375    _ipp_client_t *client)		/* I - Client */
2376{
2377  _ipp_job_t	*job;			/* Job */
2378  cups_array_t	*ra;			/* requested-attributes */
2379
2380
2381  if ((job = find_job(client)) == NULL)
2382  {
2383    respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found.");
2384    return;
2385  }
2386
2387  respond_ipp(client, IPP_STATUS_OK, NULL);
2388
2389  ra = ippCreateRequestedArray(client->request);
2390  copy_job_attributes(client, job, ra);
2391  cupsArrayDelete(ra);
2392}
2393
2394
2395/*
2396 * 'ipp_get_jobs()' - Get a list of job objects.
2397 */
2398
2399static void
2400ipp_get_jobs(_ipp_client_t *client)	/* I - Client */
2401{
2402  ipp_attribute_t	*attr;		/* Current attribute */
2403  const char		*which_jobs = NULL;
2404					/* which-jobs values */
2405  int			job_comparison;	/* Job comparison */
2406  ipp_jstate_t		job_state;	/* job-state value */
2407  int			first_job_id,	/* First job ID */
2408			limit,		/* Maximum number of jobs to return */
2409			count;		/* Number of jobs that match */
2410  const char		*username;	/* Username */
2411  _ipp_job_t		*job;		/* Current job pointer */
2412  cups_array_t		*ra;		/* Requested attributes array */
2413
2414
2415 /*
2416  * See if the "which-jobs" attribute have been specified...
2417  */
2418
2419  if ((attr = ippFindAttribute(client->request, "which-jobs",
2420                               IPP_TAG_KEYWORD)) != NULL)
2421  {
2422    which_jobs = ippGetString(attr, 0, NULL);
2423    fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs);
2424  }
2425
2426  if (!which_jobs || !strcmp(which_jobs, "not-completed"))
2427  {
2428    job_comparison = -1;
2429    job_state      = IPP_JSTATE_STOPPED;
2430  }
2431  else if (!strcmp(which_jobs, "completed"))
2432  {
2433    job_comparison = 1;
2434    job_state      = IPP_JSTATE_CANCELED;
2435  }
2436  else if (!strcmp(which_jobs, "aborted"))
2437  {
2438    job_comparison = 0;
2439    job_state      = IPP_JSTATE_ABORTED;
2440  }
2441  else if (!strcmp(which_jobs, "all"))
2442  {
2443    job_comparison = 1;
2444    job_state      = IPP_JSTATE_PENDING;
2445  }
2446  else if (!strcmp(which_jobs, "canceled"))
2447  {
2448    job_comparison = 0;
2449    job_state      = IPP_JSTATE_CANCELED;
2450  }
2451  else if (!strcmp(which_jobs, "pending"))
2452  {
2453    job_comparison = 0;
2454    job_state      = IPP_JSTATE_PENDING;
2455  }
2456  else if (!strcmp(which_jobs, "pending-held"))
2457  {
2458    job_comparison = 0;
2459    job_state      = IPP_JSTATE_HELD;
2460  }
2461  else if (!strcmp(which_jobs, "processing"))
2462  {
2463    job_comparison = 0;
2464    job_state      = IPP_JSTATE_PROCESSING;
2465  }
2466  else if (!strcmp(which_jobs, "processing-stopped"))
2467  {
2468    job_comparison = 0;
2469    job_state      = IPP_JSTATE_STOPPED;
2470  }
2471  else
2472  {
2473    respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
2474                "The which-jobs value \"%s\" is not supported.", which_jobs);
2475    ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
2476                 "which-jobs", NULL, which_jobs);
2477    return;
2478  }
2479
2480 /*
2481  * See if they want to limit the number of jobs reported...
2482  */
2483
2484  if ((attr = ippFindAttribute(client->request, "limit",
2485                               IPP_TAG_INTEGER)) != NULL)
2486  {
2487    limit = ippGetInteger(attr, 0);
2488
2489    fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit);
2490  }
2491  else
2492    limit = 0;
2493
2494  if ((attr = ippFindAttribute(client->request, "first-job-id",
2495                               IPP_TAG_INTEGER)) != NULL)
2496  {
2497    first_job_id = ippGetInteger(attr, 0);
2498
2499    fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname,
2500            first_job_id);
2501  }
2502  else
2503    first_job_id = 1;
2504
2505 /*
2506  * See if we only want to see jobs for a specific user...
2507  */
2508
2509  username = NULL;
2510
2511  if ((attr = ippFindAttribute(client->request, "my-jobs",
2512                               IPP_TAG_BOOLEAN)) != NULL)
2513  {
2514    int my_jobs = ippGetBoolean(attr, 0);
2515
2516    fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname,
2517            my_jobs ? "true" : "false");
2518
2519    if (my_jobs)
2520    {
2521      if ((attr = ippFindAttribute(client->request, "requesting-user-name",
2522					IPP_TAG_NAME)) == NULL)
2523      {
2524	respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
2525	            "Need requesting-user-name with my-jobs.");
2526	return;
2527      }
2528
2529      username = ippGetString(attr, 0, NULL);
2530
2531      fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2532              client->hostname, username);
2533    }
2534  }
2535
2536 /*
2537  * OK, build a list of jobs for this printer...
2538  */
2539
2540  ra = ippCreateRequestedArray(client->request);
2541
2542  respond_ipp(client, IPP_STATUS_OK, NULL);
2543
2544  _cupsRWLockRead(&(client->printer->rwlock));
2545
2546  for (count = 0, job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs);
2547       (limit <= 0 || count < limit) && job;
2548       job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs))
2549  {
2550   /*
2551    * Filter out jobs that don't match...
2552    */
2553
2554    if ((job_comparison < 0 && job->state > job_state) ||
2555	(job_comparison == 0 && job->state != job_state) ||
2556	(job_comparison > 0 && job->state < job_state) ||
2557	job->id < first_job_id ||
2558	(username && job->username &&
2559	 _cups_strcasecmp(username, job->username)))
2560      continue;
2561
2562    if (count > 0)
2563      ippAddSeparator(client->response);
2564
2565    count ++;
2566    copy_job_attributes(client, job, ra);
2567  }
2568
2569  cupsArrayDelete(ra);
2570
2571  _cupsRWUnlock(&(client->printer->rwlock));
2572}
2573
2574
2575/*
2576 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2577 */
2578
2579static void
2580ipp_get_printer_attributes(
2581    _ipp_client_t *client)		/* I - Client */
2582{
2583  cups_array_t		*ra;		/* Requested attributes array */
2584  _ipp_printer_t	*printer;	/* Printer */
2585
2586
2587 /*
2588  * Send the attributes...
2589  */
2590
2591  ra      = ippCreateRequestedArray(client->request);
2592  printer = client->printer;
2593
2594  respond_ipp(client, IPP_STATUS_OK, NULL);
2595
2596  _cupsRWLockRead(&(printer->rwlock));
2597
2598  copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
2599		  IPP_TAG_CUPS_CONST);
2600
2601  if (!ra || cupsArrayFind(ra, "printer-state"))
2602    ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
2603                  "printer-state", printer->state);
2604
2605  if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
2606  {
2607    if (printer->state_reasons == _IPP_PSTATE_NONE)
2608      ippAddString(client->response, IPP_TAG_PRINTER,
2609                   IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
2610                   "printer-state-reasons", NULL, "none");
2611    else
2612    {
2613      int			num_reasons = 0;/* Number of reasons */
2614      const char		*reasons[32];	/* Reason strings */
2615
2616      if (printer->state_reasons & _IPP_PSTATE_OTHER)
2617	reasons[num_reasons ++] = "other";
2618      if (printer->state_reasons & _IPP_PSTATE_COVER_OPEN)
2619	reasons[num_reasons ++] = "cover-open";
2620      if (printer->state_reasons & _IPP_PSTATE_INPUT_TRAY_MISSING)
2621	reasons[num_reasons ++] = "input-tray-missing";
2622      if (printer->state_reasons & _IPP_PSTATE_MARKER_SUPPLY_EMPTY)
2623	reasons[num_reasons ++] = "marker-supply-empty-warning";
2624      if (printer->state_reasons & _IPP_PSTATE_MARKER_SUPPLY_LOW)
2625	reasons[num_reasons ++] = "marker-supply-low-report";
2626      if (printer->state_reasons & _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL)
2627	reasons[num_reasons ++] = "marker-waste-almost-full-report";
2628      if (printer->state_reasons & _IPP_PSTATE_MARKER_WASTE_FULL)
2629	reasons[num_reasons ++] = "marker-waste-full-warning";
2630      if (printer->state_reasons & _IPP_PSTATE_MEDIA_EMPTY)
2631	reasons[num_reasons ++] = "media-empty-warning";
2632      if (printer->state_reasons & _IPP_PSTATE_MEDIA_JAM)
2633	reasons[num_reasons ++] = "media-jam-warning";
2634      if (printer->state_reasons & _IPP_PSTATE_MEDIA_LOW)
2635	reasons[num_reasons ++] = "media-low-report";
2636      if (printer->state_reasons & _IPP_PSTATE_MEDIA_NEEDED)
2637	reasons[num_reasons ++] = "media-needed-report";
2638      if (printer->state_reasons & _IPP_PSTATE_MOVING_TO_PAUSED)
2639	reasons[num_reasons ++] = "moving-to-paused";
2640      if (printer->state_reasons & _IPP_PSTATE_PAUSED)
2641	reasons[num_reasons ++] = "paused";
2642      if (printer->state_reasons & _IPP_PSTATE_SPOOL_AREA_FULL)
2643	reasons[num_reasons ++] = "spool-area-full";
2644      if (printer->state_reasons & _IPP_PSTATE_TONER_EMPTY)
2645	reasons[num_reasons ++] = "toner-empty-warning";
2646      if (printer->state_reasons & _IPP_PSTATE_TONER_LOW)
2647	reasons[num_reasons ++] = "toner-low-report";
2648
2649      ippAddStrings(client->response, IPP_TAG_PRINTER,
2650                    IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST,
2651                    "printer-state-reasons", num_reasons, NULL, reasons);
2652    }
2653  }
2654
2655  if (!ra || cupsArrayFind(ra, "printer-up-time"))
2656    ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2657                  "printer-up-time", (int)time(NULL));
2658
2659  if (!ra || cupsArrayFind(ra, "queued-job-count"))
2660    ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2661                  "queued-job-count",
2662		  printer->active_job &&
2663		      printer->active_job->state < IPP_JSTATE_CANCELED);
2664
2665  _cupsRWUnlock(&(printer->rwlock));
2666
2667  cupsArrayDelete(ra);
2668}
2669
2670
2671/*
2672 * 'ipp_print_job()' - Create a job object with an attached document.
2673 */
2674
2675static void
2676ipp_print_job(_ipp_client_t *client)	/* I - Client */
2677{
2678  _ipp_job_t		*job;		/* New job */
2679  char			filename[1024],	/* Filename buffer */
2680			buffer[4096];	/* Copy buffer */
2681  ssize_t		bytes;		/* Bytes read */
2682  cups_array_t		*ra;		/* Attributes to send in response */
2683
2684
2685 /*
2686  * Validate print job attributes...
2687  */
2688
2689  if (!valid_job_attributes(client))
2690  {
2691    httpFlush(client->http);
2692    return;
2693  }
2694
2695 /*
2696  * Do we have a file to print?
2697  */
2698
2699  if (httpGetState(client->http) == HTTP_STATE_POST_SEND)
2700  {
2701    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request.");
2702    return;
2703  }
2704
2705 /*
2706  * Print the job...
2707  */
2708
2709  if ((job = create_job(client)) == NULL)
2710  {
2711    respond_ipp(client, IPP_STATUS_ERROR_BUSY,
2712                "Currently printing another job.");
2713    return;
2714  }
2715
2716 /*
2717  * Create a file for the request data...
2718  */
2719
2720  if (!_cups_strcasecmp(job->format, "image/jpeg"))
2721    snprintf(filename, sizeof(filename), "%s/%d.jpg",
2722             client->printer->directory, job->id);
2723  else if (!_cups_strcasecmp(job->format, "image/png"))
2724    snprintf(filename, sizeof(filename), "%s/%d.png",
2725             client->printer->directory, job->id);
2726  else if (!_cups_strcasecmp(job->format, "image/pwg-raster"))
2727    snprintf(filename, sizeof(filename), "%s/%d.ras",
2728             client->printer->directory, job->id);
2729  else if (!_cups_strcasecmp(job->format, "application/pdf"))
2730    snprintf(filename, sizeof(filename), "%s/%d.pdf",
2731             client->printer->directory, job->id);
2732  else if (!_cups_strcasecmp(job->format, "application/postscript"))
2733    snprintf(filename, sizeof(filename), "%s/%d.ps",
2734             client->printer->directory, job->id);
2735  else
2736    snprintf(filename, sizeof(filename), "%s/%d.prn",
2737             client->printer->directory, job->id);
2738
2739  if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
2740  {
2741    job->state = IPP_JSTATE_ABORTED;
2742
2743    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2744                "Unable to create print file: %s", strerror(errno));
2745    return;
2746  }
2747
2748  while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
2749  {
2750    if (write(job->fd, buffer, bytes) < bytes)
2751    {
2752      int error = errno;		/* Write error */
2753
2754      job->state = IPP_JSTATE_ABORTED;
2755
2756      close(job->fd);
2757      job->fd = -1;
2758
2759      unlink(filename);
2760
2761      respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2762                  "Unable to write print file: %s", strerror(error));
2763      return;
2764    }
2765  }
2766
2767  if (bytes < 0)
2768  {
2769   /*
2770    * Got an error while reading the print data, so abort this job.
2771    */
2772
2773    job->state = IPP_JSTATE_ABORTED;
2774
2775    close(job->fd);
2776    job->fd = -1;
2777
2778    unlink(filename);
2779
2780    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2781                "Unable to read print file.");
2782    return;
2783  }
2784
2785  if (close(job->fd))
2786  {
2787    int error = errno;		/* Write error */
2788
2789    job->state = IPP_JSTATE_ABORTED;
2790    job->fd    = -1;
2791
2792    unlink(filename);
2793
2794    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2795                "Unable to write print file: %s", strerror(error));
2796    return;
2797  }
2798
2799  job->fd       = -1;
2800  job->filename = strdup(filename);
2801  job->state    = IPP_JSTATE_PENDING;
2802
2803 /*
2804  * Process the job...
2805  */
2806
2807#if 0
2808  if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
2809  {
2810    job->state = IPP_JSTATE_ABORTED;
2811    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
2812    return;
2813  }
2814
2815#else
2816  process_job(job);
2817#endif /* 0 */
2818
2819 /*
2820  * Return the job info...
2821  */
2822
2823  respond_ipp(client, IPP_STATUS_OK, NULL);
2824
2825  ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2826  cupsArrayAdd(ra, "job-id");
2827  cupsArrayAdd(ra, "job-state");
2828  cupsArrayAdd(ra, "job-state-reasons");
2829  cupsArrayAdd(ra, "job-uri");
2830
2831  copy_job_attributes(client, job, ra);
2832  cupsArrayDelete(ra);
2833}
2834
2835
2836/*
2837 * 'ipp_print_uri()' - Create a job object with a referenced document.
2838 */
2839
2840static void
2841ipp_print_uri(_ipp_client_t *client)	/* I - Client */
2842{
2843  _ipp_job_t		*job;		/* New job */
2844  ipp_attribute_t	*uri;		/* document-uri */
2845  char			scheme[256],	/* URI scheme */
2846			userpass[256],	/* Username and password info */
2847			hostname[256],	/* Hostname */
2848			resource[1024];	/* Resource path */
2849  int			port;		/* Port number */
2850  http_uri_status_t	uri_status;	/* URI decode status */
2851  http_encryption_t	encryption;	/* Encryption to use, if any */
2852  http_t		*http;		/* Connection for http/https URIs */
2853  http_status_t		status;		/* Access status for http/https URIs */
2854  int			infile;		/* Input file for local file URIs */
2855  char			filename[1024],	/* Filename buffer */
2856			buffer[4096];	/* Copy buffer */
2857  ssize_t		bytes;		/* Bytes read */
2858  cups_array_t		*ra;		/* Attributes to send in response */
2859  static const char * const uri_status_strings[] =
2860  {					/* URI decode errors */
2861    "URI too large.",
2862    "Bad arguments to function.",
2863    "Bad resource in URI.",
2864    "Bad port number in URI.",
2865    "Bad hostname in URI.",
2866    "Bad username in URI.",
2867    "Bad scheme in URI.",
2868    "Bad/empty URI."
2869  };
2870
2871
2872 /*
2873  * Validate print job attributes...
2874  */
2875
2876  if (!valid_job_attributes(client))
2877  {
2878    httpFlush(client->http);
2879    return;
2880  }
2881
2882 /*
2883  * Do we have a file to print?
2884  */
2885
2886  if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
2887  {
2888    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
2889                "Unexpected document data following request.");
2890    return;
2891  }
2892
2893 /*
2894  * Do we have a document URI?
2895  */
2896
2897  if ((uri = ippFindAttribute(client->request, "document-uri",
2898                              IPP_TAG_URI)) == NULL)
2899  {
2900    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
2901    return;
2902  }
2903
2904  if (ippGetCount(uri) != 1)
2905  {
2906    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
2907                "Too many document-uri values.");
2908    return;
2909  }
2910
2911  uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
2912                               scheme, sizeof(scheme), userpass,
2913                               sizeof(userpass), hostname, sizeof(hostname),
2914                               &port, resource, sizeof(resource));
2915  if (uri_status < HTTP_URI_STATUS_OK)
2916  {
2917    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s",
2918                uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
2919    return;
2920  }
2921
2922  if (strcmp(scheme, "file") &&
2923#ifdef HAVE_SSL
2924      strcmp(scheme, "https") &&
2925#endif /* HAVE_SSL */
2926      strcmp(scheme, "http"))
2927  {
2928    respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
2929                "URI scheme \"%s\" not supported.", scheme);
2930    return;
2931  }
2932
2933  if (!strcmp(scheme, "file") && access(resource, R_OK))
2934  {
2935    respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
2936                "Unable to access URI: %s", strerror(errno));
2937    return;
2938  }
2939
2940 /*
2941  * Print the job...
2942  */
2943
2944  if ((job = create_job(client)) == NULL)
2945  {
2946    respond_ipp(client, IPP_STATUS_ERROR_BUSY,
2947                "Currently printing another job.");
2948    return;
2949  }
2950
2951 /*
2952  * Create a file for the request data...
2953  */
2954
2955  if (!_cups_strcasecmp(job->format, "image/jpeg"))
2956    snprintf(filename, sizeof(filename), "%s/%d.jpg",
2957             client->printer->directory, job->id);
2958  else if (!_cups_strcasecmp(job->format, "image/png"))
2959    snprintf(filename, sizeof(filename), "%s/%d.png",
2960             client->printer->directory, job->id);
2961  else if (!_cups_strcasecmp(job->format, "application/pdf"))
2962    snprintf(filename, sizeof(filename), "%s/%d.pdf",
2963             client->printer->directory, job->id);
2964  else if (!_cups_strcasecmp(job->format, "application/postscript"))
2965    snprintf(filename, sizeof(filename), "%s/%d.ps",
2966             client->printer->directory, job->id);
2967  else
2968    snprintf(filename, sizeof(filename), "%s/%d.prn",
2969             client->printer->directory, job->id);
2970
2971  if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
2972  {
2973    job->state = IPP_JSTATE_ABORTED;
2974
2975    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2976                "Unable to create print file: %s", strerror(errno));
2977    return;
2978  }
2979
2980  if (!strcmp(scheme, "file"))
2981  {
2982    if ((infile = open(resource, O_RDONLY)) < 0)
2983    {
2984      respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
2985                  "Unable to access URI: %s", strerror(errno));
2986      return;
2987    }
2988
2989    do
2990    {
2991      if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
2992          (errno == EAGAIN || errno == EINTR))
2993        bytes = 1;
2994      else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes)
2995      {
2996	int error = errno;		/* Write error */
2997
2998	job->state = IPP_JSTATE_ABORTED;
2999
3000	close(job->fd);
3001	job->fd = -1;
3002
3003	unlink(filename);
3004	close(infile);
3005
3006	respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3007		    "Unable to write print file: %s", strerror(error));
3008	return;
3009      }
3010    }
3011    while (bytes > 0);
3012
3013    close(infile);
3014  }
3015  else
3016  {
3017#ifdef HAVE_SSL
3018    if (port == 443 || !strcmp(scheme, "https"))
3019      encryption = HTTP_ENCRYPTION_ALWAYS;
3020    else
3021#endif /* HAVE_SSL */
3022    encryption = HTTP_ENCRYPTION_IF_REQUESTED;
3023
3024    if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption,
3025                             1, 30000, NULL)) == NULL)
3026    {
3027      respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3028                  "Unable to connect to %s: %s", hostname,
3029		  cupsLastErrorString());
3030      job->state = IPP_JSTATE_ABORTED;
3031
3032      close(job->fd);
3033      job->fd = -1;
3034
3035      unlink(filename);
3036      return;
3037    }
3038
3039    httpClearFields(http);
3040    httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
3041    if (httpGet(http, resource))
3042    {
3043      respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3044                  "Unable to GET URI: %s", strerror(errno));
3045
3046      job->state = IPP_JSTATE_ABORTED;
3047
3048      close(job->fd);
3049      job->fd = -1;
3050
3051      unlink(filename);
3052      httpClose(http);
3053      return;
3054    }
3055
3056    while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
3057
3058    if (status != HTTP_STATUS_OK)
3059    {
3060      respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3061                  "Unable to GET URI: %s", httpStatus(status));
3062
3063      job->state = IPP_JSTATE_ABORTED;
3064
3065      close(job->fd);
3066      job->fd = -1;
3067
3068      unlink(filename);
3069      httpClose(http);
3070      return;
3071    }
3072
3073    while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
3074    {
3075      if (write(job->fd, buffer, bytes) < bytes)
3076      {
3077	int error = errno;		/* Write error */
3078
3079	job->state = IPP_JSTATE_ABORTED;
3080
3081	close(job->fd);
3082	job->fd = -1;
3083
3084	unlink(filename);
3085	httpClose(http);
3086
3087	respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3088		    "Unable to write print file: %s", strerror(error));
3089	return;
3090      }
3091    }
3092
3093    httpClose(http);
3094  }
3095
3096  if (close(job->fd))
3097  {
3098    int error = errno;		/* Write error */
3099
3100    job->state = IPP_JSTATE_ABORTED;
3101    job->fd    = -1;
3102
3103    unlink(filename);
3104
3105    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3106                "Unable to write print file: %s", strerror(error));
3107    return;
3108  }
3109
3110  job->fd       = -1;
3111  job->filename = strdup(filename);
3112  job->state    = IPP_JSTATE_PENDING;
3113
3114 /*
3115  * Process the job...
3116  */
3117
3118#if 0
3119  if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3120  {
3121    job->state = IPP_JSTATE_ABORTED;
3122    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
3123    return;
3124  }
3125
3126#else
3127  process_job(job);
3128#endif /* 0 */
3129
3130 /*
3131  * Return the job info...
3132  */
3133
3134  respond_ipp(client, IPP_STATUS_OK, NULL);
3135
3136  ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3137  cupsArrayAdd(ra, "job-id");
3138  cupsArrayAdd(ra, "job-state");
3139  cupsArrayAdd(ra, "job-state-reasons");
3140  cupsArrayAdd(ra, "job-uri");
3141
3142  copy_job_attributes(client, job, ra);
3143  cupsArrayDelete(ra);
3144}
3145
3146
3147/*
3148 * 'ipp_send_document()' - Add an attached document to a job object created with
3149 *                         Create-Job.
3150 */
3151
3152static void
3153ipp_send_document(_ipp_client_t *client)/* I - Client */
3154{
3155  _ipp_job_t		*job;		/* Job information */
3156  char			filename[1024],	/* Filename buffer */
3157			buffer[4096];	/* Copy buffer */
3158  ssize_t		bytes;		/* Bytes read */
3159  ipp_attribute_t	*attr;		/* Current attribute */
3160  cups_array_t		*ra;		/* Attributes to send in response */
3161
3162
3163 /*
3164  * Get the job...
3165  */
3166
3167  if ((job = find_job(client)) == NULL)
3168  {
3169    respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3170    httpFlush(client->http);
3171    return;
3172  }
3173
3174 /*
3175  * See if we already have a document for this job or the job has already
3176  * in a non-pending state...
3177  */
3178
3179  if (job->state > IPP_JSTATE_HELD)
3180  {
3181    respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3182                "Job is not in a pending state.");
3183    httpFlush(client->http);
3184    return;
3185  }
3186  else if (job->filename || job->fd >= 0)
3187  {
3188    respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
3189                "Multiple document jobs are not supported.");
3190    httpFlush(client->http);
3191    return;
3192  }
3193
3194  if ((attr = ippFindAttribute(client->request, "last-document",
3195                               IPP_TAG_ZERO)) == NULL)
3196  {
3197    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3198                "Missing required last-document attribute.");
3199    httpFlush(client->http);
3200    return;
3201  }
3202  else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
3203           !ippGetBoolean(attr, 0))
3204  {
3205    respond_unsupported(client, attr);
3206    httpFlush(client->http);
3207    return;
3208  }
3209
3210 /*
3211  * Validate document attributes...
3212  */
3213
3214  if (!valid_doc_attributes(client))
3215  {
3216    httpFlush(client->http);
3217    return;
3218  }
3219
3220 /*
3221  * Get the document format for the job...
3222  */
3223
3224  _cupsRWLockWrite(&(client->printer->rwlock));
3225
3226  if ((attr = ippFindAttribute(job->attrs, "document-format",
3227                               IPP_TAG_MIMETYPE)) != NULL)
3228    job->format = ippGetString(attr, 0, NULL);
3229  else
3230    job->format = "application/octet-stream";
3231
3232 /*
3233  * Create a file for the request data...
3234  */
3235
3236  if (!_cups_strcasecmp(job->format, "image/jpeg"))
3237    snprintf(filename, sizeof(filename), "%s/%d.jpg",
3238             client->printer->directory, job->id);
3239  else if (!_cups_strcasecmp(job->format, "image/png"))
3240    snprintf(filename, sizeof(filename), "%s/%d.png",
3241             client->printer->directory, job->id);
3242  else if (!_cups_strcasecmp(job->format, "application/pdf"))
3243    snprintf(filename, sizeof(filename), "%s/%d.pdf",
3244             client->printer->directory, job->id);
3245  else if (!_cups_strcasecmp(job->format, "application/postscript"))
3246    snprintf(filename, sizeof(filename), "%s/%d.ps",
3247             client->printer->directory, job->id);
3248  else
3249    snprintf(filename, sizeof(filename), "%s/%d.prn",
3250             client->printer->directory, job->id);
3251
3252  job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3253
3254  _cupsRWUnlock(&(client->printer->rwlock));
3255
3256  if (job->fd < 0)
3257  {
3258    job->state = IPP_JSTATE_ABORTED;
3259
3260    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3261                "Unable to create print file: %s", strerror(errno));
3262    return;
3263  }
3264
3265  while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
3266  {
3267    if (write(job->fd, buffer, bytes) < bytes)
3268    {
3269      int error = errno;		/* Write error */
3270
3271      job->state = IPP_JSTATE_ABORTED;
3272
3273      close(job->fd);
3274      job->fd = -1;
3275
3276      unlink(filename);
3277
3278      respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3279                  "Unable to write print file: %s", strerror(error));
3280      return;
3281    }
3282  }
3283
3284  if (bytes < 0)
3285  {
3286   /*
3287    * Got an error while reading the print data, so abort this job.
3288    */
3289
3290    job->state = IPP_JSTATE_ABORTED;
3291
3292    close(job->fd);
3293    job->fd = -1;
3294
3295    unlink(filename);
3296
3297    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3298                "Unable to read print file.");
3299    return;
3300  }
3301
3302  if (close(job->fd))
3303  {
3304    int error = errno;			/* Write error */
3305
3306    job->state = IPP_JSTATE_ABORTED;
3307    job->fd    = -1;
3308
3309    unlink(filename);
3310
3311    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3312                "Unable to write print file: %s", strerror(error));
3313    return;
3314  }
3315
3316  _cupsRWLockWrite(&(client->printer->rwlock));
3317
3318  job->fd       = -1;
3319  job->filename = strdup(filename);
3320  job->state    = IPP_JSTATE_PENDING;
3321
3322  _cupsRWUnlock(&(client->printer->rwlock));
3323
3324 /*
3325  * Process the job...
3326  */
3327
3328#if 0
3329  if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3330  {
3331    job->state = IPP_JSTATE_ABORTED;
3332    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
3333    return;
3334  }
3335
3336#else
3337  process_job(job);
3338#endif /* 0 */
3339
3340 /*
3341  * Return the job info...
3342  */
3343
3344  respond_ipp(client, IPP_STATUS_OK, NULL);
3345
3346  ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3347  cupsArrayAdd(ra, "job-id");
3348  cupsArrayAdd(ra, "job-state");
3349  cupsArrayAdd(ra, "job-state-reasons");
3350  cupsArrayAdd(ra, "job-uri");
3351
3352  copy_job_attributes(client, job, ra);
3353  cupsArrayDelete(ra);
3354}
3355
3356
3357/*
3358 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3359 *                    Create-Job.
3360 */
3361
3362static void
3363ipp_send_uri(_ipp_client_t *client)	/* I - Client */
3364{
3365  _ipp_job_t		*job;		/* Job information */
3366  ipp_attribute_t	*uri;		/* document-uri */
3367  char			scheme[256],	/* URI scheme */
3368			userpass[256],	/* Username and password info */
3369			hostname[256],	/* Hostname */
3370			resource[1024];	/* Resource path */
3371  int			port;		/* Port number */
3372  http_uri_status_t	uri_status;	/* URI decode status */
3373  http_encryption_t	encryption;	/* Encryption to use, if any */
3374  http_t		*http;		/* Connection for http/https URIs */
3375  http_status_t		status;		/* Access status for http/https URIs */
3376  int			infile;		/* Input file for local file URIs */
3377  char			filename[1024],	/* Filename buffer */
3378			buffer[4096];	/* Copy buffer */
3379  ssize_t		bytes;		/* Bytes read */
3380  ipp_attribute_t	*attr;		/* Current attribute */
3381  cups_array_t		*ra;		/* Attributes to send in response */
3382  static const char * const uri_status_strings[] =
3383  {					/* URI decode errors */
3384    "URI too large.",
3385    "Bad arguments to function.",
3386    "Bad resource in URI.",
3387    "Bad port number in URI.",
3388    "Bad hostname in URI.",
3389    "Bad username in URI.",
3390    "Bad scheme in URI.",
3391    "Bad/empty URI."
3392  };
3393
3394
3395 /*
3396  * Get the job...
3397  */
3398
3399  if ((job = find_job(client)) == NULL)
3400  {
3401    respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3402    httpFlush(client->http);
3403    return;
3404  }
3405
3406 /*
3407  * See if we already have a document for this job or the job has already
3408  * in a non-pending state...
3409  */
3410
3411  if (job->state > IPP_JSTATE_HELD)
3412  {
3413    respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3414                "Job is not in a pending state.");
3415    httpFlush(client->http);
3416    return;
3417  }
3418  else if (job->filename || job->fd >= 0)
3419  {
3420    respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED,
3421                "Multiple document jobs are not supported.");
3422    httpFlush(client->http);
3423    return;
3424  }
3425
3426  if ((attr = ippFindAttribute(client->request, "last-document",
3427                               IPP_TAG_ZERO)) == NULL)
3428  {
3429    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3430                "Missing required last-document attribute.");
3431    httpFlush(client->http);
3432    return;
3433  }
3434  else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 ||
3435           !ippGetBoolean(attr, 0))
3436  {
3437    respond_unsupported(client, attr);
3438    httpFlush(client->http);
3439    return;
3440  }
3441
3442 /*
3443  * Validate document attributes...
3444  */
3445
3446  if (!valid_doc_attributes(client))
3447  {
3448    httpFlush(client->http);
3449    return;
3450  }
3451
3452 /*
3453  * Do we have a file to print?
3454  */
3455
3456  if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
3457  {
3458    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3459                "Unexpected document data following request.");
3460    return;
3461  }
3462
3463 /*
3464  * Do we have a document URI?
3465  */
3466
3467  if ((uri = ippFindAttribute(client->request, "document-uri",
3468                              IPP_TAG_URI)) == NULL)
3469  {
3470    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
3471    return;
3472  }
3473
3474  if (ippGetCount(uri) != 1)
3475  {
3476    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3477                "Too many document-uri values.");
3478    return;
3479  }
3480
3481  uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
3482                               scheme, sizeof(scheme), userpass,
3483                               sizeof(userpass), hostname, sizeof(hostname),
3484                               &port, resource, sizeof(resource));
3485  if (uri_status < HTTP_URI_STATUS_OK)
3486  {
3487    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s",
3488                uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
3489    return;
3490  }
3491
3492  if (strcmp(scheme, "file") &&
3493#ifdef HAVE_SSL
3494      strcmp(scheme, "https") &&
3495#endif /* HAVE_SSL */
3496      strcmp(scheme, "http"))
3497  {
3498    respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME,
3499                "URI scheme \"%s\" not supported.", scheme);
3500    return;
3501  }
3502
3503  if (!strcmp(scheme, "file") && access(resource, R_OK))
3504  {
3505    respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3506                "Unable to access URI: %s", strerror(errno));
3507    return;
3508  }
3509
3510 /*
3511  * Get the document format for the job...
3512  */
3513
3514  _cupsRWLockWrite(&(client->printer->rwlock));
3515
3516  if ((attr = ippFindAttribute(job->attrs, "document-format",
3517                               IPP_TAG_MIMETYPE)) != NULL)
3518    job->format = ippGetString(attr, 0, NULL);
3519  else
3520    job->format = "application/octet-stream";
3521
3522 /*
3523  * Create a file for the request data...
3524  */
3525
3526  if (!_cups_strcasecmp(job->format, "image/jpeg"))
3527    snprintf(filename, sizeof(filename), "%s/%d.jpg",
3528             client->printer->directory, job->id);
3529  else if (!_cups_strcasecmp(job->format, "image/png"))
3530    snprintf(filename, sizeof(filename), "%s/%d.png",
3531             client->printer->directory, job->id);
3532  else if (!_cups_strcasecmp(job->format, "application/pdf"))
3533    snprintf(filename, sizeof(filename), "%s/%d.pdf",
3534             client->printer->directory, job->id);
3535  else if (!_cups_strcasecmp(job->format, "application/postscript"))
3536    snprintf(filename, sizeof(filename), "%s/%d.ps",
3537             client->printer->directory, job->id);
3538  else
3539    snprintf(filename, sizeof(filename), "%s/%d.prn",
3540             client->printer->directory, job->id);
3541
3542  job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
3543
3544  _cupsRWUnlock(&(client->printer->rwlock));
3545
3546  if (job->fd < 0)
3547  {
3548    job->state = IPP_JSTATE_ABORTED;
3549
3550    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3551                "Unable to create print file: %s", strerror(errno));
3552    return;
3553  }
3554
3555  if (!strcmp(scheme, "file"))
3556  {
3557    if ((infile = open(resource, O_RDONLY)) < 0)
3558    {
3559      respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3560                  "Unable to access URI: %s", strerror(errno));
3561      return;
3562    }
3563
3564    do
3565    {
3566      if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
3567          (errno == EAGAIN || errno == EINTR))
3568        bytes = 1;
3569      else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes)
3570      {
3571	int error = errno;		/* Write error */
3572
3573	job->state = IPP_JSTATE_ABORTED;
3574
3575	close(job->fd);
3576	job->fd = -1;
3577
3578	unlink(filename);
3579	close(infile);
3580
3581	respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3582		    "Unable to write print file: %s", strerror(error));
3583	return;
3584      }
3585    }
3586    while (bytes > 0);
3587
3588    close(infile);
3589  }
3590  else
3591  {
3592#ifdef HAVE_SSL
3593    if (port == 443 || !strcmp(scheme, "https"))
3594      encryption = HTTP_ENCRYPTION_ALWAYS;
3595    else
3596#endif /* HAVE_SSL */
3597    encryption = HTTP_ENCRYPTION_IF_REQUESTED;
3598
3599    if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption,
3600                             1, 30000, NULL)) == NULL)
3601    {
3602      respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3603                  "Unable to connect to %s: %s", hostname,
3604		  cupsLastErrorString());
3605      job->state = IPP_JSTATE_ABORTED;
3606
3607      close(job->fd);
3608      job->fd = -1;
3609
3610      unlink(filename);
3611      return;
3612    }
3613
3614    httpClearFields(http);
3615    httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
3616    if (httpGet(http, resource))
3617    {
3618      respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3619                  "Unable to GET URI: %s", strerror(errno));
3620
3621      job->state = IPP_JSTATE_ABORTED;
3622
3623      close(job->fd);
3624      job->fd = -1;
3625
3626      unlink(filename);
3627      httpClose(http);
3628      return;
3629    }
3630
3631    while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
3632
3633    if (status != HTTP_STATUS_OK)
3634    {
3635      respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS,
3636                  "Unable to GET URI: %s", httpStatus(status));
3637
3638      job->state = IPP_JSTATE_ABORTED;
3639
3640      close(job->fd);
3641      job->fd = -1;
3642
3643      unlink(filename);
3644      httpClose(http);
3645      return;
3646    }
3647
3648    while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
3649    {
3650      if (write(job->fd, buffer, bytes) < bytes)
3651      {
3652	int error = errno;		/* Write error */
3653
3654	job->state = IPP_JSTATE_ABORTED;
3655
3656	close(job->fd);
3657	job->fd = -1;
3658
3659	unlink(filename);
3660	httpClose(http);
3661
3662	respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3663		    "Unable to write print file: %s", strerror(error));
3664	return;
3665      }
3666    }
3667
3668    httpClose(http);
3669  }
3670
3671  if (close(job->fd))
3672  {
3673    int error = errno;		/* Write error */
3674
3675    job->state = IPP_JSTATE_ABORTED;
3676    job->fd    = -1;
3677
3678    unlink(filename);
3679
3680    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
3681                "Unable to write print file: %s", strerror(error));
3682    return;
3683  }
3684
3685  _cupsRWLockWrite(&(client->printer->rwlock));
3686
3687  job->fd       = -1;
3688  job->filename = strdup(filename);
3689  job->state    = IPP_JSTATE_PENDING;
3690
3691  _cupsRWUnlock(&(client->printer->rwlock));
3692
3693 /*
3694  * Process the job...
3695  */
3696
3697#if 0
3698  if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
3699  {
3700    job->state = IPP_JSTATE_ABORTED;
3701    respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
3702    return;
3703  }
3704
3705#else
3706  process_job(job);
3707#endif /* 0 */
3708
3709 /*
3710  * Return the job info...
3711  */
3712
3713  respond_ipp(client, IPP_STATUS_OK, NULL);
3714
3715  ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3716  cupsArrayAdd(ra, "job-id");
3717  cupsArrayAdd(ra, "job-state");
3718  cupsArrayAdd(ra, "job-state-reasons");
3719  cupsArrayAdd(ra, "job-uri");
3720
3721  copy_job_attributes(client, job, ra);
3722  cupsArrayDelete(ra);
3723}
3724
3725
3726/*
3727 * 'ipp_validate_job()' - Validate job creation attributes.
3728 */
3729
3730static void
3731ipp_validate_job(_ipp_client_t *client)	/* I - Client */
3732{
3733  if (valid_job_attributes(client))
3734    respond_ipp(client, IPP_STATUS_OK, NULL);
3735}
3736
3737
3738/*
3739 * 'process_client()' - Process client requests on a thread.
3740 */
3741
3742static void *				/* O - Exit status */
3743process_client(_ipp_client_t *client)	/* I - Client */
3744{
3745 /*
3746  * Loop until we are out of requests or timeout (30 seconds)...
3747  */
3748
3749  while (httpWait(client->http, 30000))
3750    if (!process_http(client))
3751      break;
3752
3753 /*
3754  * Close the conection to the client and return...
3755  */
3756
3757  delete_client(client);
3758
3759  return (NULL);
3760}
3761
3762
3763/*
3764 * 'process_http()' - Process a HTTP request.
3765 */
3766
3767int					/* O - 1 on success, 0 on failure */
3768process_http(_ipp_client_t *client)	/* I - Client connection */
3769{
3770  char			uri[1024];	/* URI */
3771  http_state_t		http_state;	/* HTTP state */
3772  http_status_t		http_status;	/* HTTP status */
3773  ipp_state_t		ipp_state;	/* State of IPP transfer */
3774  char			scheme[32],	/* Method/scheme */
3775			userpass[128],	/* Username:password */
3776			hostname[HTTP_MAX_HOST];
3777					/* Hostname */
3778  int			port;		/* Port number */
3779  const char		*encoding;	/* Content-Encoding value */
3780  static const char * const http_states[] =
3781  {					/* Strings for logging HTTP method */
3782    "WAITING",
3783    "OPTIONS",
3784    "GET",
3785    "GET_SEND",
3786    "HEAD",
3787    "POST",
3788    "POST_RECV",
3789    "POST_SEND",
3790    "PUT",
3791    "PUT_RECV",
3792    "DELETE",
3793    "TRACE",
3794    "CONNECT",
3795    "STATUS",
3796    "UNKNOWN_METHOD",
3797    "UNKNOWN_VERSION"
3798  };
3799
3800
3801 /*
3802  * Clear state variables...
3803  */
3804
3805  ippDelete(client->request);
3806  ippDelete(client->response);
3807
3808  client->request   = NULL;
3809  client->response  = NULL;
3810  client->operation = HTTP_STATE_WAITING;
3811
3812 /*
3813  * Read a request from the connection...
3814  */
3815
3816  while ((http_state = httpReadRequest(client->http, uri,
3817                                       sizeof(uri))) == HTTP_STATE_WAITING)
3818    usleep(1);
3819
3820 /*
3821  * Parse the request line...
3822  */
3823
3824  if (http_state == HTTP_STATE_ERROR)
3825  {
3826    if (httpError(client->http) == EPIPE)
3827      fprintf(stderr, "%s Client closed connection.\n", client->hostname);
3828    else
3829      fprintf(stderr, "%s Bad request line (%s).\n", client->hostname,
3830              strerror(httpError(client->http)));
3831
3832    return (0);
3833  }
3834  else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
3835  {
3836    fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
3837    respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
3838    return (0);
3839  }
3840  else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
3841  {
3842    fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
3843    respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
3844    return (0);
3845  }
3846
3847  fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state],
3848          uri);
3849
3850 /*
3851  * Separate the URI into its components...
3852  */
3853
3854  if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
3855		      userpass, sizeof(userpass),
3856		      hostname, sizeof(hostname), &port,
3857		      client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK)
3858  {
3859    fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
3860    respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
3861    return (0);
3862  }
3863
3864 /*
3865  * Process the request...
3866  */
3867
3868  client->start     = time(NULL);
3869  client->operation = httpGetState(client->http);
3870
3871 /*
3872  * Parse incoming parameters until the status changes...
3873  */
3874
3875  while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
3876
3877  if (http_status != HTTP_STATUS_OK)
3878  {
3879    respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
3880    return (0);
3881  }
3882
3883  if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
3884      httpGetVersion(client->http) >= HTTP_VERSION_1_1)
3885  {
3886   /*
3887    * HTTP/1.1 and higher require the "Host:" field...
3888    */
3889
3890    respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
3891    return (0);
3892  }
3893
3894 /*
3895  * Handle HTTP Upgrade...
3896  */
3897
3898  if (!_cups_strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION),
3899                        "Upgrade"))
3900  {
3901    if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
3902      return (0);
3903  }
3904
3905 /*
3906  * Handle HTTP Expect...
3907  */
3908
3909  if (httpGetExpect(client->http) &&
3910      (client->operation == HTTP_STATE_POST ||
3911       client->operation == HTTP_STATE_PUT))
3912  {
3913    if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
3914    {
3915     /*
3916      * Send 100-continue header...
3917      */
3918
3919      if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
3920	return (0);
3921    }
3922    else
3923    {
3924     /*
3925      * Send 417-expectation-failed header...
3926      */
3927
3928      if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
3929	return (0);
3930    }
3931  }
3932
3933 /*
3934  * Handle new transfers...
3935  */
3936
3937  encoding = httpGetContentEncoding(client->http);
3938
3939  switch (client->operation)
3940  {
3941    case HTTP_STATE_OPTIONS :
3942       /*
3943	* Do HEAD/OPTIONS command...
3944	*/
3945
3946	return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
3947
3948    case HTTP_STATE_HEAD :
3949        if (!strcmp(client->uri, "/icon.png"))
3950	  return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
3951	else if (!strcmp(client->uri, "/"))
3952	  return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
3953	else
3954	  return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
3955	break;
3956
3957    case HTTP_STATE_GET :
3958        if (!strcmp(client->uri, "/icon.png"))
3959	{
3960	 /*
3961	  * Send PNG icon file.
3962	  */
3963
3964          int		fd;		/* Icon file */
3965	  struct stat	fileinfo;	/* Icon file information */
3966	  char		buffer[4096];	/* Copy buffer */
3967	  ssize_t	bytes;		/* Bytes */
3968
3969          fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon);
3970
3971          if (!stat(client->printer->icon, &fileinfo) &&
3972	      (fd = open(client->printer->icon, O_RDONLY)) >= 0)
3973	  {
3974	    if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png",
3975	                      fileinfo.st_size))
3976	    {
3977	      close(fd);
3978	      return (0);
3979	    }
3980
3981	    while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
3982	      httpWrite2(client->http, buffer, bytes);
3983
3984	    httpFlushWrite(client->http);
3985
3986	    close(fd);
3987	  }
3988	  else
3989	    return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
3990	}
3991	else if (!strcmp(client->uri, "/"))
3992	{
3993	 /*
3994	  * Show web status page...
3995	  */
3996
3997          if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0))
3998	    return (0);
3999
4000          html_printf(client,
4001	              "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
4002		      "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
4003		      "<html>\n"
4004		      "<head>\n"
4005		      "<title>%s</title>\n"
4006		      "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
4007		      "type=\"image/png\">\n"
4008		      "</head>\n"
4009		      "<body>\n"
4010		      "</body>\n"
4011		      "<h1><img align=\"right\" src=\"/icon.png\">%s</h1>\n"
4012		      "<p>%s, %d job(s).</p>\n"
4013		      "</body>\n"
4014		      "</html>\n",
4015		      client->printer->name, client->printer->name,
4016		      client->printer->state == IPP_PSTATE_IDLE ? "Idle" :
4017		          client->printer->state == IPP_PSTATE_PROCESSING ?
4018			  "Printing" : "Stopped",
4019		      cupsArrayCount(client->printer->jobs));
4020          httpWrite2(client->http, "", 0);
4021
4022	  return (1);
4023	}
4024	else
4025	  return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
4026	break;
4027
4028    case HTTP_STATE_POST :
4029	if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
4030	           "application/ipp"))
4031        {
4032	 /*
4033	  * Not an IPP request...
4034	  */
4035
4036	  return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
4037	}
4038
4039       /*
4040        * Read the IPP request...
4041	*/
4042
4043	client->request = ippNew();
4044
4045        while ((ipp_state = ippRead(client->http,
4046                                    client->request)) != IPP_STATE_DATA)
4047	{
4048	  if (ipp_state == IPP_STATE_ERROR)
4049	  {
4050            fprintf(stderr, "%s IPP read error (%s).\n", client->hostname,
4051	            cupsLastErrorString());
4052	    respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
4053	    return (0);
4054	  }
4055	}
4056
4057       /*
4058        * Now that we have the IPP request, process the request...
4059	*/
4060
4061        return (process_ipp(client));
4062
4063    default :
4064        break; /* Anti-compiler-warning-code */
4065  }
4066
4067  return (1);
4068}
4069
4070
4071/*
4072 * 'process_ipp()' - Process an IPP request.
4073 */
4074
4075static int				/* O - 1 on success, 0 on error */
4076process_ipp(_ipp_client_t *client)	/* I - Client */
4077{
4078  ipp_tag_t		group;		/* Current group tag */
4079  ipp_attribute_t	*attr;		/* Current attribute */
4080  ipp_attribute_t	*charset;	/* Character set attribute */
4081  ipp_attribute_t	*language;	/* Language attribute */
4082  ipp_attribute_t	*uri;		/* Printer URI attribute */
4083  int			major, minor;	/* Version number */
4084  const char		*name;		/* Name of attribute */
4085
4086
4087  debug_attributes("Request", client->request, 1);
4088
4089 /*
4090  * First build an empty response message for this request...
4091  */
4092
4093  client->operation_id = ippGetOperation(client->request);
4094  client->response     = ippNewResponse(client->request);
4095
4096 /*
4097  * Then validate the request header and required attributes...
4098  */
4099
4100  major = ippGetVersion(client->request, &minor);
4101
4102  if (major < 1 || major > 2)
4103  {
4104   /*
4105    * Return an error, since we only support IPP 1.x and 2.x.
4106    */
4107
4108    respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED,
4109                "Bad request version number %d.%d.", major, minor);
4110  }
4111  else if (ippGetRequestId(client->request) <= 0)
4112    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.",
4113                ippGetRequestId(client->request));
4114  else if (!ippFirstAttribute(client->request))
4115    respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4116                "No attributes in request.");
4117  else
4118  {
4119   /*
4120    * Make sure that the attributes are provided in the correct order and
4121    * don't repeat groups...
4122    */
4123
4124    for (attr = ippFirstAttribute(client->request),
4125             group = ippGetGroupTag(attr);
4126	 attr;
4127	 attr = ippNextAttribute(client->request))
4128    {
4129      if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
4130      {
4131       /*
4132	* Out of order; return an error...
4133	*/
4134
4135	respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4136		    "Attribute groups are out of order (%x < %x).",
4137		    ippGetGroupTag(attr), group);
4138	break;
4139      }
4140      else
4141	group = ippGetGroupTag(attr);
4142    }
4143
4144    if (!attr)
4145    {
4146     /*
4147      * Then make sure that the first three attributes are:
4148      *
4149      *     attributes-charset
4150      *     attributes-natural-language
4151      *     printer-uri/job-uri
4152      */
4153
4154      attr = ippFirstAttribute(client->request);
4155      name = ippGetName(attr);
4156      if (attr && name && !strcmp(name, "attributes-charset") &&
4157	  ippGetValueTag(attr) == IPP_TAG_CHARSET)
4158	charset = attr;
4159      else
4160	charset = NULL;
4161
4162      attr = ippNextAttribute(client->request);
4163      name = ippGetName(attr);
4164
4165      if (attr && name && !strcmp(name, "attributes-natural-language") &&
4166	  ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
4167	language = attr;
4168      else
4169	language = NULL;
4170
4171      if ((attr = ippFindAttribute(client->request, "printer-uri",
4172                                   IPP_TAG_URI)) != NULL)
4173	uri = attr;
4174      else if ((attr = ippFindAttribute(client->request, "job-uri",
4175                                        IPP_TAG_URI)) != NULL)
4176	uri = attr;
4177      else
4178	uri = NULL;
4179
4180      if (charset &&
4181          _cups_strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
4182          _cups_strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
4183      {
4184       /*
4185        * Bad character set...
4186	*/
4187
4188	respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4189	            "Unsupported character set \"%s\".",
4190	            ippGetString(charset, 0, NULL));
4191      }
4192      else if (!charset || !language || !uri)
4193      {
4194       /*
4195	* Return an error, since attributes-charset,
4196	* attributes-natural-language, and printer-uri/job-uri are required
4197	* for all operations.
4198	*/
4199
4200	respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
4201	            "Missing required attributes.");
4202      }
4203      else
4204      {
4205        char		scheme[32],	/* URI scheme */
4206			userpass[32],	/* Username/password in URI */
4207			host[256],	/* Host name in URI */
4208			resource[256];	/* Resource path in URI */
4209	int		port;		/* Port number in URI */
4210
4211        name = ippGetName(uri);
4212
4213        if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
4214                            scheme, sizeof(scheme),
4215                            userpass, sizeof(userpass),
4216                            host, sizeof(host), &port,
4217                            resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
4218	  respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
4219	              "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
4220        else if ((!strcmp(name, "job-uri") &&
4221                  strncmp(resource, "/ipp/print/", 11)) ||
4222                 (!strcmp(name, "printer-uri") &&
4223                  strcmp(resource, "/ipp/print")))
4224	  respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
4225		      name, ippGetString(uri, 0, NULL));
4226	else
4227	{
4228	 /*
4229	  * Try processing the operation...
4230	  */
4231
4232	  switch (ippGetOperation(client->request))
4233	  {
4234	    case IPP_OP_PRINT_JOB :
4235		ipp_print_job(client);
4236		break;
4237
4238	    case IPP_OP_PRINT_URI :
4239		ipp_print_uri(client);
4240		break;
4241
4242	    case IPP_OP_VALIDATE_JOB :
4243		ipp_validate_job(client);
4244		break;
4245
4246	    case IPP_OP_CREATE_JOB :
4247		ipp_create_job(client);
4248		break;
4249
4250	    case IPP_OP_SEND_DOCUMENT :
4251		ipp_send_document(client);
4252		break;
4253
4254	    case IPP_OP_SEND_URI :
4255		ipp_send_uri(client);
4256		break;
4257
4258	    case IPP_OP_CANCEL_JOB :
4259		ipp_cancel_job(client);
4260		break;
4261
4262	    case IPP_OP_GET_JOB_ATTRIBUTES :
4263		ipp_get_job_attributes(client);
4264		break;
4265
4266	    case IPP_OP_GET_JOBS :
4267		ipp_get_jobs(client);
4268		break;
4269
4270	    case IPP_OP_GET_PRINTER_ATTRIBUTES :
4271		ipp_get_printer_attributes(client);
4272		break;
4273
4274	    default :
4275		respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
4276			    "Operation not supported.");
4277		break;
4278	  }
4279	}
4280      }
4281    }
4282  }
4283
4284 /*
4285  * Send the HTTP header and return...
4286  */
4287
4288  if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
4289    httpFlush(client->http);		/* Flush trailing (junk) data */
4290
4291  return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
4292                       ippLength(client->response)));
4293}
4294
4295
4296/*
4297 * 'process_job()' - Process a print job.
4298 */
4299
4300static void *				/* O - Thread exit status */
4301process_job(_ipp_job_t *job)		/* I - Job */
4302{
4303  job->state          = IPP_JSTATE_PROCESSING;
4304  job->printer->state = IPP_PSTATE_PROCESSING;
4305
4306  if (job->printer->command)
4307  {
4308   /*
4309    * Execute a command with the job spool file and wait for it to complete...
4310    */
4311
4312    int 	pid,			/* Process ID */
4313		status;			/* Exit status */
4314    time_t	start,			/* Start time */
4315		end;			/* End time */
4316
4317    fprintf(stderr, "Running command \"%s %s\".\n", job->printer->command,
4318            job->filename);
4319    time(&start);
4320
4321    if ((pid = fork()) == 0)
4322    {
4323     /*
4324      * Child comes here...
4325      */
4326
4327      execlp(job->printer->command, job->printer->command, job->filename,
4328             (void *)NULL);
4329      exit(errno);
4330    }
4331    else if (pid < 0)
4332    {
4333     /*
4334      * Unable to fork process...
4335      */
4336
4337      perror("Unable to start job processing command");
4338    }
4339    else
4340    {
4341     /*
4342      * Wait for child to complete...
4343      */
4344
4345#ifdef HAVE_WAITPID
4346      while (waitpid(pid, &status, 0) < 0);
4347#else
4348      while (wait(&status) < 0);
4349#endif /* HAVE_WAITPID */
4350
4351      if (status)
4352      {
4353        if (WIFEXITED(status))
4354	  fprintf(stderr, "Command \"%s\" exited with status %d.\n",
4355	          job->printer->command, WEXITSTATUS(status));
4356        else
4357	  fprintf(stderr, "Command \"%s\" terminated with signal %d.\n",
4358	          job->printer->command, WTERMSIG(status));
4359      }
4360      else
4361	fprintf(stderr, "Command \"%s\" completed successfully.\n",
4362		job->printer->command);
4363    }
4364
4365   /*
4366    * Make sure processing takes at least 5 seconds...
4367    */
4368
4369    time(&end);
4370    if ((end - start) < 5)
4371      sleep(5);
4372  }
4373  else
4374  {
4375   /*
4376    * Sleep for a random amount of time to simulate job processing.
4377    */
4378
4379    sleep(5 + (rand() % 11));
4380  }
4381
4382  if (job->cancel)
4383    job->state = IPP_JSTATE_CANCELED;
4384  else
4385    job->state = IPP_JSTATE_COMPLETED;
4386
4387  job->completed           = time(NULL);
4388  job->printer->state      = IPP_PSTATE_IDLE;
4389  job->printer->active_job = NULL;
4390
4391  return (NULL);
4392}
4393
4394
4395#ifdef HAVE_DNSSD
4396/*
4397 * 'register_printer()' - Register a printer object via Bonjour.
4398 */
4399
4400static int				/* O - 1 on success, 0 on error */
4401register_printer(
4402    _ipp_printer_t *printer,		/* I - Printer */
4403    const char     *location,		/* I - Location */
4404    const char     *make,		/* I - Manufacturer */
4405    const char     *model,		/* I - Model name */
4406    const char     *formats,		/* I - Supported formats */
4407    const char     *adminurl,		/* I - Web interface URL */
4408    int            color,		/* I - 1 = color, 0 = monochrome */
4409    int            duplex,		/* I - 1 = duplex, 0 = simplex */
4410    const char     *subtype)		/* I - Service subtype */
4411{
4412  DNSServiceErrorType	error;		/* Error from Bonjour */
4413  char			make_model[256],/* Make and model together */
4414			product[256],	/* Product string */
4415			regtype[256];	/* Bonjour service type */
4416
4417
4418 /*
4419  * Build the TXT record for IPP...
4420  */
4421
4422  snprintf(make_model, sizeof(make_model), "%s %s", make, model);
4423  snprintf(product, sizeof(product), "(%s)", model);
4424
4425  TXTRecordCreate(&(printer->ipp_txt), 1024, NULL);
4426  TXTRecordSetValue(&(printer->ipp_txt), "rp", 9, "ipp/print");
4427  TXTRecordSetValue(&(printer->ipp_txt), "ty", (uint8_t)strlen(make_model),
4428                    make_model);
4429  TXTRecordSetValue(&(printer->ipp_txt), "adminurl", (uint8_t)strlen(adminurl),
4430                    adminurl);
4431  if (*location)
4432    TXTRecordSetValue(&(printer->ipp_txt), "note", (uint8_t)strlen(location),
4433		      location);
4434  TXTRecordSetValue(&(printer->ipp_txt), "product", (uint8_t)strlen(product),
4435                    product);
4436  TXTRecordSetValue(&(printer->ipp_txt), "pdl", (uint8_t)strlen(formats),
4437                    formats);
4438  TXTRecordSetValue(&(printer->ipp_txt), "Color", 1, color ? "T" : "F");
4439  TXTRecordSetValue(&(printer->ipp_txt), "Duplex", 1, duplex ? "T" : "F");
4440  TXTRecordSetValue(&(printer->ipp_txt), "usb_MFG", (uint8_t)strlen(make),
4441                    make);
4442  TXTRecordSetValue(&(printer->ipp_txt), "usb_MDL", (uint8_t)strlen(model),
4443                    model);
4444
4445 /*
4446  * Create a shared service reference for Bonjour...
4447  */
4448
4449  if ((error = DNSServiceCreateConnection(&(printer->common_ref)))
4450          != kDNSServiceErr_NoError)
4451  {
4452    fprintf(stderr, "Unable to create mDNSResponder connection: %d\n", error);
4453    return (0);
4454  }
4455
4456 /*
4457  * Register the _printer._tcp (LPD) service type with a port number of 0 to
4458  * defend our service name but not actually support LPD...
4459  */
4460
4461  printer->printer_ref = printer->common_ref;
4462
4463  if ((error = DNSServiceRegister(&(printer->printer_ref),
4464                                  kDNSServiceFlagsShareConnection,
4465                                  0 /* interfaceIndex */, printer->dnssd_name,
4466				  "_printer._tcp", NULL /* domain */,
4467				  NULL /* host */, 0 /* port */, 0 /* txtLen */,
4468				  NULL /* txtRecord */,
4469			          (DNSServiceRegisterReply)dnssd_callback,
4470			          printer)) != kDNSServiceErr_NoError)
4471  {
4472    fprintf(stderr, "Unable to register \"%s._printer._tcp\": %d\n",
4473            printer->dnssd_name, error);
4474    return (0);
4475  }
4476
4477 /*
4478  * Then register the _ipp._tcp (IPP) service type with the real port number to
4479  * advertise our IPP printer...
4480  */
4481
4482  printer->ipp_ref = printer->common_ref;
4483
4484  if (subtype && *subtype)
4485    snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtype);
4486  else
4487    strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
4488
4489  if ((error = DNSServiceRegister(&(printer->ipp_ref),
4490                                  kDNSServiceFlagsShareConnection,
4491                                  0 /* interfaceIndex */, printer->dnssd_name,
4492				  regtype, NULL /* domain */,
4493				  NULL /* host */, htons(printer->port),
4494				  TXTRecordGetLength(&(printer->ipp_txt)),
4495				  TXTRecordGetBytesPtr(&(printer->ipp_txt)),
4496			          (DNSServiceRegisterReply)dnssd_callback,
4497			          printer)) != kDNSServiceErr_NoError)
4498  {
4499    fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
4500            printer->dnssd_name, regtype, error);
4501    return (0);
4502  }
4503
4504#  if 0 /* ifdef HAVE_SSL */
4505 /*
4506  * Then register the _ipps._tcp (IPP) service type with the real port number to
4507  * advertise our IPP printer...
4508  */
4509
4510  printer->ipps_ref = printer->common_ref;
4511
4512  if (subtype && *subtype)
4513    snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtype);
4514  else
4515    strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
4516
4517  if ((error = DNSServiceRegister(&(printer->ipps_ref),
4518                                  kDNSServiceFlagsShareConnection,
4519                                  0 /* interfaceIndex */, printer->dnssd_name,
4520				  regtype, NULL /* domain */,
4521				  NULL /* host */, htons(printer->port),
4522				  TXTRecordGetLength(&(printer->ipp_txt)),
4523				  TXTRecordGetBytesPtr(&(printer->ipp_txt)),
4524			          (DNSServiceRegisterReply)dnssd_callback,
4525			          printer)) != kDNSServiceErr_NoError)
4526  {
4527    fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
4528            printer->dnssd_name, regtype, error);
4529    return (0);
4530  }
4531#  endif /* HAVE_SSL */
4532
4533 /*
4534  * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4535  * real port number to advertise our IPP printer...
4536  */
4537
4538  printer->http_ref = printer->common_ref;
4539
4540  if ((error = DNSServiceRegister(&(printer->http_ref),
4541                                  kDNSServiceFlagsShareConnection,
4542                                  0 /* interfaceIndex */, printer->dnssd_name,
4543				  "_http._tcp,_printer", NULL /* domain */,
4544				  NULL /* host */, htons(printer->port),
4545				  0 /* txtLen */, NULL, /* txtRecord */
4546			          (DNSServiceRegisterReply)dnssd_callback,
4547			          printer)) != kDNSServiceErr_NoError)
4548  {
4549    fprintf(stderr, "Unable to register \"%s.%s\": %d\n",
4550            printer->dnssd_name, regtype, error);
4551    return (0);
4552  }
4553
4554  return (1);
4555}
4556#endif /* HAVE_DNSSD */
4557
4558
4559/*
4560 * 'respond_http()' - Send a HTTP response.
4561 */
4562
4563int					/* O - 1 on success, 0 on failure */
4564respond_http(
4565    _ipp_client_t *client,		/* I - Client */
4566    http_status_t code,			/* I - HTTP status of response */
4567    const char    *content_encoding,	/* I - Content-Encoding of response */
4568    const char    *type,		/* I - MIME media type of response */
4569    size_t        length)		/* I - Length of response */
4570{
4571  char	message[1024];			/* Text message */
4572
4573
4574  fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
4575
4576  if (code == HTTP_STATUS_CONTINUE)
4577  {
4578   /*
4579    * 100-continue doesn't send any headers...
4580    */
4581
4582    return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
4583  }
4584
4585 /*
4586  * Format an error message...
4587  */
4588
4589  if (!type && !length && code != HTTP_STATUS_OK)
4590  {
4591    snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
4592
4593    type   = "text/plain";
4594    length = strlen(message);
4595  }
4596  else
4597    message[0] = '\0';
4598
4599 /*
4600  * Send the HTTP response header...
4601  */
4602
4603  httpClearFields(client->http);
4604
4605  if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
4606      client->operation == HTTP_STATE_OPTIONS)
4607    httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
4608
4609  if (type)
4610  {
4611    if (!strcmp(type, "text/html"))
4612      httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
4613                   "text/html; charset=utf-8");
4614    else
4615      httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
4616
4617    if (content_encoding)
4618      httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
4619  }
4620
4621  httpSetLength(client->http, length);
4622
4623  if (httpWriteResponse(client->http, code) < 0)
4624    return (0);
4625
4626 /*
4627  * Send the response data...
4628  */
4629
4630  if (message[0])
4631  {
4632   /*
4633    * Send a plain text message.
4634    */
4635
4636    if (httpPrintf(client->http, "%s", message) < 0)
4637      return (0);
4638
4639    if (httpWrite2(client->http, "", 0) < 0)
4640      return (0);
4641  }
4642  else if (client->response)
4643  {
4644   /*
4645    * Send an IPP response...
4646    */
4647
4648    debug_attributes("Response", client->response, 2);
4649
4650    ippSetState(client->response, IPP_STATE_IDLE);
4651
4652    if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
4653      return (0);
4654  }
4655
4656  return (1);
4657}
4658
4659
4660/*
4661 * 'respond_ipp()' - Send an IPP response.
4662 */
4663
4664static void
4665respond_ipp(_ipp_client_t *client,	/* I - Client */
4666            ipp_status_t  status,	/* I - status-code */
4667	    const char    *message,	/* I - printf-style status-message */
4668	    ...)			/* I - Additional args as needed */
4669{
4670  const char	*formatted = NULL;	/* Formatted message */
4671
4672
4673  ippSetStatusCode(client->response, status);
4674
4675  if (message)
4676  {
4677    va_list		ap;		/* Pointer to additional args */
4678    ipp_attribute_t	*attr;		/* New status-message attribute */
4679
4680    va_start(ap, message);
4681    if ((attr = ippFindAttribute(client->response, "status-message",
4682				 IPP_TAG_TEXT)) != NULL)
4683      ippSetStringfv(client->response, &attr, 0, message, ap);
4684    else
4685      attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
4686			    "status-message", NULL, message, ap);
4687    va_end(ap);
4688
4689    formatted = ippGetString(attr, 0, NULL);
4690  }
4691
4692  if (formatted)
4693    fprintf(stderr, "%s %s %s (%s)\n", client->hostname,
4694	    ippOpString(client->operation_id), ippErrorString(status),
4695	    formatted);
4696  else
4697    fprintf(stderr, "%s %s %s\n", client->hostname,
4698	    ippOpString(client->operation_id), ippErrorString(status));
4699}
4700
4701
4702/*
4703 * 'respond_unsupported()' - Respond with an unsupported attribute.
4704 */
4705
4706static void
4707respond_unsupported(
4708    _ipp_client_t   *client,		/* I - Client */
4709    ipp_attribute_t *attr)		/* I - Atribute */
4710{
4711  ipp_attribute_t	*temp;		/* Copy of attribute */
4712
4713
4714  respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
4715              "Unsupported %s %s%s value.", ippGetName(attr),
4716              ippGetCount(attr) > 1 ? "1setOf " : "",
4717	      ippTagString(ippGetValueTag(attr)));
4718
4719  temp = ippCopyAttribute(client->response, attr, 0);
4720  ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
4721}
4722
4723
4724/*
4725 * 'run_printer()' - Run the printer service.
4726 */
4727
4728static void
4729run_printer(_ipp_printer_t *printer)	/* I - Printer */
4730{
4731  int		num_fds;		/* Number of file descriptors */
4732  struct pollfd	polldata[3];		/* poll() data */
4733  int		timeout;		/* Timeout for poll() */
4734  _ipp_client_t	*client;		/* New client */
4735
4736
4737 /*
4738  * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
4739  */
4740
4741  polldata[0].fd     = printer->ipv4;
4742  polldata[0].events = POLLIN;
4743
4744  polldata[1].fd     = printer->ipv6;
4745  polldata[1].events = POLLIN;
4746
4747  num_fds = 2;
4748
4749#ifdef HAVE_DNSSD
4750  polldata[num_fds   ].fd     = DNSServiceRefSockFD(printer->common_ref);
4751  polldata[num_fds ++].events = POLLIN;
4752#endif /* HAVE_DNSSD */
4753
4754 /*
4755  * Loop until we are killed or have a hard error...
4756  */
4757
4758  for (;;)
4759  {
4760    if (cupsArrayCount(printer->jobs))
4761      timeout = 10;
4762    else
4763      timeout = -1;
4764
4765    if (poll(polldata, num_fds, timeout) < 0 && errno != EINTR)
4766    {
4767      perror("poll() failed");
4768      break;
4769    }
4770
4771    if (polldata[0].revents & POLLIN)
4772    {
4773      if ((client = create_client(printer, printer->ipv4)) != NULL)
4774      {
4775	if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
4776	{
4777	  perror("Unable to create client thread");
4778	  delete_client(client);
4779	}
4780      }
4781    }
4782
4783    if (polldata[1].revents & POLLIN)
4784    {
4785      if ((client = create_client(printer, printer->ipv6)) != NULL)
4786      {
4787	if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client))
4788	{
4789	  perror("Unable to create client thread");
4790	  delete_client(client);
4791	}
4792      }
4793    }
4794
4795#ifdef HAVE_DNSSD
4796    if (polldata[2].revents & POLLIN)
4797      DNSServiceProcessResult(printer->common_ref);
4798#endif /* HAVE_DNSSD */
4799
4800   /*
4801    * Clean out old jobs...
4802    */
4803
4804    clean_jobs(printer);
4805  }
4806}
4807
4808
4809/*
4810 * 'usage()' - Show program usage.
4811 */
4812
4813static void
4814usage(int status)			/* O - Exit status */
4815{
4816  if (!status)
4817  {
4818    puts(CUPS_SVERSION " - Copyright 2010-2013 by Apple Inc. All rights "
4819         "reserved.");
4820    puts("");
4821  }
4822
4823  puts("Usage: ippserver [options] \"name\"");
4824  puts("");
4825  puts("Options:");
4826  puts("-2                      Supports 2-sided printing (default=1-sided)");
4827  puts("-M manufacturer         Manufacturer name (default=Test)");
4828  puts("-P                      PIN printing mode");
4829  puts("-c command              Run command for every print job");
4830  printf("-d spool-directory      Spool directory "
4831         "(default=/tmp/ippserver.%d)\n", (int)getpid());
4832  puts("-f type/subtype[,...]   List of supported types "
4833       "(default=application/pdf,image/jpeg)");
4834  puts("-h                      Show program help");
4835  puts("-i iconfile.png         PNG icon file (default=printer.png)");
4836  puts("-k                      Keep job spool files");
4837  puts("-l location             Location of printer (default=empty string)");
4838  puts("-m model                Model name (default=Printer)");
4839  puts("-n hostname             Hostname for printer");
4840  puts("-p port                 Port number (default=auto)");
4841#ifdef HAVE_DNSSD
4842  puts("-r subtype              Bonjour service subtype (default=_print)");
4843#endif /* HAVE_DNSSD */
4844  puts("-s speed[,color-speed]  Speed in pages per minute (default=10,0)");
4845  puts("-v[vvv]                 Be (very) verbose");
4846
4847  exit(status);
4848}
4849
4850
4851/*
4852 * 'valid_doc_attributes()' - Determine whether the document attributes are
4853 *                            valid.
4854 *
4855 * When one or more document attributes are invalid, this function adds a
4856 * suitable response and attributes to the unsupported group.
4857 */
4858
4859static int				/* O - 1 if valid, 0 if not */
4860valid_doc_attributes(
4861    _ipp_client_t *client)		/* I - Client */
4862{
4863  int			valid = 1;	/* Valid attributes? */
4864  ipp_op_t		op = ippGetOperation(client->request);
4865					/* IPP operation */
4866  const char		*op_name = ippOpString(op);
4867					/* IPP operation name */
4868  ipp_attribute_t	*attr,		/* Current attribute */
4869			*supported;	/* xxx-supported attribute */
4870  const char		*compression = NULL,
4871					/* compression value */
4872			*format = NULL;	/* document-format value */
4873
4874
4875 /*
4876  * Check operation attributes...
4877  */
4878
4879  if ((attr = ippFindAttribute(client->request, "compression",
4880                               IPP_TAG_ZERO)) != NULL)
4881  {
4882   /*
4883    * If compression is specified, only accept a supported value in a Print-Job
4884    * or Send-Document request...
4885    */
4886
4887    compression = ippGetString(attr, 0, NULL);
4888    supported   = ippFindAttribute(client->printer->attrs,
4889                                   "compression-supported", IPP_TAG_KEYWORD);
4890
4891    if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
4892        ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
4893        (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
4894         op != IPP_OP_VALIDATE_JOB) ||
4895        !ippContainsString(supported, compression))
4896    {
4897      respond_unsupported(client, attr);
4898      valid = 0;
4899    }
4900    else
4901    {
4902      fprintf(stderr, "%s %s compression=\"%s\"\n",
4903              client->hostname, op_name, compression);
4904
4905      if (strcmp(compression, "none"))
4906        httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
4907    }
4908  }
4909
4910 /*
4911  * Is it a format we support?
4912  */
4913
4914  if ((attr = ippFindAttribute(client->request, "document-format",
4915                               IPP_TAG_ZERO)) != NULL)
4916  {
4917    if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
4918        ippGetGroupTag(attr) != IPP_TAG_OPERATION)
4919    {
4920      respond_unsupported(client, attr);
4921      valid = 0;
4922    }
4923    else
4924    {
4925      format = ippGetString(attr, 0, NULL);
4926
4927      fprintf(stderr, "%s %s document-format=\"%s\"\n",
4928	      client->hostname, op_name, format);
4929    }
4930  }
4931  else
4932  {
4933    format = ippGetString(ippFindAttribute(client->printer->attrs,
4934                                           "document-format-default",
4935                                           IPP_TAG_MIMETYPE), 0, NULL);
4936    if (!format)
4937      format = "application/octet-stream"; /* Should never happen */
4938
4939    attr = ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
4940			"document-format", NULL, format);
4941  }
4942
4943  if (!strcmp(format, "application/octet-stream") &&
4944      (ippGetOperation(client->request) == IPP_OP_PRINT_JOB ||
4945       ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
4946  {
4947   /*
4948    * Auto-type the file using the first 4 bytes of the file...
4949    */
4950
4951    unsigned char	header[4];	/* First 4 bytes of file */
4952
4953    memset(header, 0, sizeof(header));
4954    httpPeek(client->http, (char *)header, sizeof(header));
4955
4956    if (!memcmp(header, "%PDF", 4))
4957      format = "application/pdf";
4958    else if (!memcmp(header, "%!", 2))
4959      format = "application/postscript";
4960    else if (!memcmp(header, "\377\330\377", 3) &&
4961	     header[3] >= 0xe0 && header[3] <= 0xef)
4962      format = "image/jpeg";
4963    else if (!memcmp(header, "\211PNG", 4))
4964      format = "image/png";
4965
4966    if (format)
4967      fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n",
4968	      client->hostname, op_name, format);
4969
4970    if (!attr)
4971      attr = ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
4972                          "document-format", NULL, format);
4973    else
4974      ippSetString(client->request, &attr, 0, format);
4975  }
4976
4977  if (op != IPP_OP_CREATE_JOB &&
4978      (supported = ippFindAttribute(client->printer->attrs,
4979                                    "document-format-supported",
4980			            IPP_TAG_MIMETYPE)) != NULL &&
4981      !ippContainsString(supported, format))
4982  {
4983    respond_unsupported(client, attr);
4984    valid = 0;
4985  }
4986
4987  return (valid);
4988}
4989
4990
4991/*
4992 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
4993 *
4994 * When one or more job attributes are invalid, this function adds a suitable
4995 * response and attributes to the unsupported group.
4996 */
4997
4998static int				/* O - 1 if valid, 0 if not */
4999valid_job_attributes(
5000    _ipp_client_t *client)		/* I - Client */
5001{
5002  int			i,		/* Looping var */
5003			valid = 1;	/* Valid attributes? */
5004  ipp_attribute_t	*attr,		/* Current attribute */
5005			*supported;	/* xxx-supported attribute */
5006
5007
5008 /*
5009  * Check operation attributes...
5010  */
5011
5012  valid = valid_doc_attributes(client);
5013
5014 /*
5015  * Check the various job template attributes...
5016  */
5017
5018  if ((attr = ippFindAttribute(client->request, "copies",
5019                               IPP_TAG_ZERO)) != NULL)
5020  {
5021    if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
5022        ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
5023    {
5024      respond_unsupported(client, attr);
5025      valid = 0;
5026    }
5027  }
5028
5029  if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity",
5030                               IPP_TAG_ZERO)) != NULL)
5031  {
5032    if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
5033    {
5034      respond_unsupported(client, attr);
5035      valid = 0;
5036    }
5037  }
5038
5039  if ((attr = ippFindAttribute(client->request, "job-hold-until",
5040                               IPP_TAG_ZERO)) != NULL)
5041  {
5042    if (ippGetCount(attr) != 1 ||
5043        (ippGetValueTag(attr) != IPP_TAG_NAME &&
5044	 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
5045	 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
5046	strcmp(ippGetString(attr, 0, NULL), "no-hold"))
5047    {
5048      respond_unsupported(client, attr);
5049      valid = 0;
5050    }
5051  }
5052
5053  if ((attr = ippFindAttribute(client->request, "job-name",
5054                               IPP_TAG_ZERO)) != NULL)
5055  {
5056    if (ippGetCount(attr) != 1 ||
5057        (ippGetValueTag(attr) != IPP_TAG_NAME &&
5058	 ippGetValueTag(attr) != IPP_TAG_NAMELANG))
5059    {
5060      respond_unsupported(client, attr);
5061      valid = 0;
5062    }
5063  }
5064
5065  if ((attr = ippFindAttribute(client->request, "job-priority",
5066                               IPP_TAG_ZERO)) != NULL)
5067  {
5068    if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
5069        ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
5070    {
5071      respond_unsupported(client, attr);
5072      valid = 0;
5073    }
5074  }
5075
5076  if ((attr = ippFindAttribute(client->request, "job-sheets",
5077                               IPP_TAG_ZERO)) != NULL)
5078  {
5079    if (ippGetCount(attr) != 1 ||
5080        (ippGetValueTag(attr) != IPP_TAG_NAME &&
5081	 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
5082	 ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
5083	strcmp(ippGetString(attr, 0, NULL), "none"))
5084    {
5085      respond_unsupported(client, attr);
5086      valid = 0;
5087    }
5088  }
5089
5090  if ((attr = ippFindAttribute(client->request, "media",
5091                               IPP_TAG_ZERO)) != NULL)
5092  {
5093    if (ippGetCount(attr) != 1 ||
5094        (ippGetValueTag(attr) != IPP_TAG_NAME &&
5095	 ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
5096	 ippGetValueTag(attr) != IPP_TAG_KEYWORD))
5097    {
5098      respond_unsupported(client, attr);
5099      valid = 0;
5100    }
5101    else
5102    {
5103      for (i = 0;
5104           i < (int)(sizeof(media_supported) / sizeof(media_supported[0]));
5105	   i ++)
5106        if (!strcmp(ippGetString(attr, 0, NULL), media_supported[i]))
5107	  break;
5108
5109      if (i >= (int)(sizeof(media_supported) / sizeof(media_supported[0])))
5110      {
5111	respond_unsupported(client, attr);
5112	valid = 0;
5113      }
5114    }
5115  }
5116
5117  if ((attr = ippFindAttribute(client->request, "media-col",
5118                               IPP_TAG_ZERO)) != NULL)
5119  {
5120    if (ippGetCount(attr) != 1 ||
5121        ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
5122    {
5123      respond_unsupported(client, attr);
5124      valid = 0;
5125    }
5126    /* TODO: check for valid media-col */
5127  }
5128
5129  if ((attr = ippFindAttribute(client->request, "multiple-document-handling",
5130                               IPP_TAG_ZERO)) != NULL)
5131  {
5132    if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
5133        (strcmp(ippGetString(attr, 0, NULL),
5134		"separate-documents-uncollated-copies") &&
5135	 strcmp(ippGetString(attr, 0, NULL),
5136		"separate-documents-collated-copies")))
5137    {
5138      respond_unsupported(client, attr);
5139      valid = 0;
5140    }
5141  }
5142
5143  if ((attr = ippFindAttribute(client->request, "orientation-requested",
5144                               IPP_TAG_ZERO)) != NULL)
5145  {
5146    if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
5147        ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
5148        ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
5149    {
5150      respond_unsupported(client, attr);
5151      valid = 0;
5152    }
5153  }
5154
5155  if ((attr = ippFindAttribute(client->request, "page-ranges",
5156                               IPP_TAG_ZERO)) != NULL)
5157  {
5158    respond_unsupported(client, attr);
5159      valid = 0;
5160  }
5161
5162  if ((attr = ippFindAttribute(client->request, "print-quality",
5163                               IPP_TAG_ZERO)) != NULL)
5164  {
5165    if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
5166        ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
5167        ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
5168    {
5169      respond_unsupported(client, attr);
5170      valid = 0;
5171    }
5172  }
5173
5174  if ((attr = ippFindAttribute(client->request, "printer-resolution",
5175                               IPP_TAG_ZERO)) != NULL)
5176  {
5177    respond_unsupported(client, attr);
5178    valid = 0;
5179  }
5180
5181  if ((attr = ippFindAttribute(client->request, "sides",
5182                               IPP_TAG_ZERO)) != NULL)
5183  {
5184    const char *sides = NULL;		/* "sides" value... */
5185
5186    if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
5187    {
5188      respond_unsupported(client, attr);
5189      valid = 0;
5190    }
5191
5192    sides = ippGetString(attr, 0, NULL);
5193
5194    if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported",
5195                                      IPP_TAG_KEYWORD)) != NULL)
5196    {
5197      if (!ippContainsString(supported, sides))
5198      {
5199	respond_unsupported(client, attr);
5200	valid = 0;
5201      }
5202    }
5203    else if (strcmp(sides, "one-sided"))
5204    {
5205      respond_unsupported(client, attr);
5206      valid = 0;
5207    }
5208  }
5209
5210  return (valid);
5211}
5212
5213
5214/*
5215 * End of "$Id: ippserver.c 11098 2013-07-04 15:55:20Z msweet $".
5216 */
5217