4 * Sample IPP/2.0 server for CUPS.
6 * Copyright 2010-2014 by Apple Inc.
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/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * Disable private and deprecated stuff so we can verify that the public API
19 * is sufficient to implement a server.
22 #define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */
23 #define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */
27 * Include necessary headers...
30 #include <cups/cups.h> /* Public API */
31 #include <config.h> /* CUPS configuration header */
32 #include <cups/string-private.h> /* For string functions */
33 #include <cups/thread-private.h> /* For multithreading functions */
39 #endif /* HAVE_DNSSD */
42 #include <sys/fcntl.h>
44 #ifdef HAVE_SYS_MOUNT_H
45 # include <sys/mount.h>
46 #endif /* HAVE_SYS_MOUNT_H */
47 #ifdef HAVE_SYS_STATFS_H
48 # include <sys/statfs.h>
49 #endif /* HAVE_SYS_STATFS_H */
50 #ifdef HAVE_SYS_STATVFS_H
51 # include <sys/statvfs.h>
52 #endif /* HAVE_SYS_STATVFS_H */
55 #endif /* HAVE_SYS_VFS_H */
62 enum _ipp_preasons_e
/* printer-state-reasons bit values */
64 _IPP_PSTATE_NONE
= 0x0000, /* none */
65 _IPP_PSTATE_OTHER
= 0x0001, /* other */
66 _IPP_PSTATE_COVER_OPEN
= 0x0002, /* cover-open */
67 _IPP_PSTATE_INPUT_TRAY_MISSING
= 0x0004,
68 /* input-tray-missing */
69 _IPP_PSTATE_MARKER_SUPPLY_EMPTY
= 0x0008,
70 /* marker-supply-empty */
71 _IPP_PSTATE_MARKER_SUPPLY_LOW
= 0x0010,
72 /* marker-suply-low */
73 _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL
= 0x0020,
74 /* marker-waste-almost-full */
75 _IPP_PSTATE_MARKER_WASTE_FULL
= 0x0040,
76 /* marker-waste-full */
77 _IPP_PSTATE_MEDIA_EMPTY
= 0x0080, /* media-empty */
78 _IPP_PSTATE_MEDIA_JAM
= 0x0100, /* media-jam */
79 _IPP_PSTATE_MEDIA_LOW
= 0x0200, /* media-low */
80 _IPP_PSTATE_MEDIA_NEEDED
= 0x0400, /* media-needed */
81 _IPP_PSTATE_MOVING_TO_PAUSED
= 0x0800,
82 /* moving-to-paused */
83 _IPP_PSTATE_PAUSED
= 0x1000, /* paused */
84 _IPP_PSTATE_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
85 _IPP_PSTATE_TONER_EMPTY
= 0x4000, /* toner-empty */
86 _IPP_PSTATE_TONER_LOW
= 0x8000 /* toner-low */
88 typedef unsigned int _ipp_preasons_t
; /* Bitfield for printer-state-reasons */
90 typedef enum _ipp_media_class_e
92 _IPP_GENERAL
, /* General-purpose size */
93 _IPP_PHOTO_ONLY
, /* Photo-only size */
94 _IPP_ENV_ONLY
/* Envelope-only size */
97 static const char * const media_supported
[] =
98 { /* media-supported values */
99 "iso_a4_210x297mm", /* A4 */
100 "iso_a5_148x210mm", /* A5 */
101 "iso_a6_105x148mm", /* A6 */
102 "iso_dl_110x220mm", /* DL */
103 "na_legal_8.5x14in", /* Legal */
104 "na_letter_8.5x11in", /* Letter */
105 "na_number-10_4.125x9.5in", /* #10 */
106 "na_index-3x5_3x5in", /* 3x5 */
107 "oe_photo-l_3.5x5in", /* L */
108 "na_index-4x6_4x6in", /* 4x6 */
109 "na_5x7_5x7in" /* 5x7 aka 2L */
111 static const int media_col_sizes
[][3] =
112 { /* media-col-database sizes */
113 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
114 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
115 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
116 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
117 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
118 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
119 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
120 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
121 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
122 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
123 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
125 static const char * const media_type_supported
[] =
126 /* media-type-supported values */
133 "photographic-glossy",
134 "photographic-high-gloss",
135 "photographic-matte",
136 "photographic-satin",
137 "photographic-semi-gloss",
139 "stationery-letterhead",
148 typedef struct _ipp_job_s _ipp_job_t
;
150 typedef struct _ipp_printer_s
/**** Printer data ****/
152 int ipv4
, /* IPv4 listener */
153 ipv6
; /* IPv6 listener */
155 DNSServiceRef common_ref
, /* Shared service connection */
156 ipp_ref
, /* Bonjour IPP service */
158 ipps_ref
, /* Bonjour IPPS service */
159 # endif /* HAVE_SSL */
160 http_ref
, /* Bonjour HTTP service */
161 printer_ref
; /* Bonjour LPD service */
162 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
163 char *dnssd_name
; /* printer-dnssd-name */
164 #endif /* HAVE_DNSSD */
165 char *name
, /* printer-name */
166 *icon
, /* Icon filename */
167 *directory
, /* Spool directory */
168 *hostname
, /* Hostname */
169 *uri
, /* printer-uri-supported */
170 *command
; /* Command to run with job file */
172 size_t urilen
; /* Length of printer URI */
173 ipp_t
*attrs
; /* Static attributes */
174 ipp_pstate_t state
; /* printer-state value */
175 _ipp_preasons_t state_reasons
; /* printer-state-reasons values */
176 cups_array_t
*jobs
; /* Jobs */
177 _ipp_job_t
*active_job
; /* Current active/pending job */
178 int next_job_id
; /* Next job-id value */
179 _cups_rwlock_t rwlock
; /* Printer lock */
182 struct _ipp_job_s
/**** Job data ****/
185 const char *name
, /* job-name */
186 *username
, /* job-originating-user-name */
187 *format
; /* document-format */
188 ipp_jstate_t state
; /* job-state value */
189 time_t processing
, /* time-at-processing value */
190 completed
; /* time-at-completed value */
191 ipp_t
*attrs
; /* Static attributes */
192 int cancel
; /* Non-zero when job canceled */
193 char *filename
; /* Print file name */
194 int fd
; /* Print file descriptor */
195 _ipp_printer_t
*printer
; /* Printer */
198 typedef struct _ipp_client_s
/**** Client data ****/
200 http_t
*http
; /* HTTP connection */
201 ipp_t
*request
, /* IPP request */
202 *response
; /* IPP response */
203 time_t start
; /* Request start time */
204 http_state_t operation
; /* Request operation */
205 ipp_op_t operation_id
; /* IPP operation-id */
206 char uri
[1024]; /* Request URI */
207 http_addr_t addr
; /* Client address */
208 char hostname
[256]; /* Client hostname */
209 _ipp_printer_t
*printer
; /* Printer */
210 _ipp_job_t
*job
; /* Current job, if any */
218 static void clean_jobs(_ipp_printer_t
*printer
);
219 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
220 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
221 ipp_tag_t group_tag
, int quickcopy
);
222 static void copy_job_attributes(_ipp_client_t
*client
,
223 _ipp_job_t
*job
, cups_array_t
*ra
);
224 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
225 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
226 static int create_listener(int family
, int *port
);
227 static ipp_t
*create_media_col(const char *media
, const char *type
,
228 int width
, int length
, int margins
);
229 static ipp_t
*create_media_size(int width
, int length
);
230 static _ipp_printer_t
*create_printer(const char *servername
,
231 const char *name
, const char *location
,
232 const char *make
, const char *model
,
234 const char *docformats
, int ppm
,
235 int ppm_color
, int duplex
, int port
,
239 #endif /* HAVE_DNSSD */
240 const char *directory
,
241 const char *command
);
242 static void debug_attributes(const char *title
, ipp_t
*ipp
,
244 static void delete_client(_ipp_client_t
*client
);
245 static void delete_job(_ipp_job_t
*job
);
246 static void delete_printer(_ipp_printer_t
*printer
);
248 static void dnssd_callback(DNSServiceRef sdRef
,
249 DNSServiceFlags flags
,
250 DNSServiceErrorType errorCode
,
254 _ipp_printer_t
*printer
);
255 #endif /* HAVE_DNSSD */
256 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
257 static void html_escape(_ipp_client_t
*client
, const char *s
,
259 static void html_printf(_ipp_client_t
*client
, const char *format
,
260 ...) __attribute__((__format__(__printf__
,
262 static void ipp_cancel_job(_ipp_client_t
*client
);
263 static void ipp_create_job(_ipp_client_t
*client
);
264 static void ipp_get_job_attributes(_ipp_client_t
*client
);
265 static void ipp_get_jobs(_ipp_client_t
*client
);
266 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
267 static void ipp_print_job(_ipp_client_t
*client
);
268 static void ipp_print_uri(_ipp_client_t
*client
);
269 static void ipp_send_document(_ipp_client_t
*client
);
270 static void ipp_send_uri(_ipp_client_t
*client
);
271 static void ipp_validate_job(_ipp_client_t
*client
);
272 static void *process_client(_ipp_client_t
*client
);
273 static int process_http(_ipp_client_t
*client
);
274 static int process_ipp(_ipp_client_t
*client
);
275 static void *process_job(_ipp_job_t
*job
);
277 static int register_printer(_ipp_printer_t
*printer
,
278 const char *location
, const char *make
,
279 const char *model
, const char *formats
,
280 const char *adminurl
, int color
,
281 int duplex
, const char *regtype
);
282 #endif /* HAVE_DNSSD */
283 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
284 const char *content_coding
,
285 const char *type
, size_t length
);
286 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
287 const char *message
, ...)
288 __attribute__ ((__format__ (__printf__
, 3, 4)));
289 static void respond_unsupported(_ipp_client_t
*client
,
290 ipp_attribute_t
*attr
);
291 static void run_printer(_ipp_printer_t
*printer
);
292 static void usage(int status
) __attribute__((noreturn
));
293 static int valid_doc_attributes(_ipp_client_t
*client
);
294 static int valid_job_attributes(_ipp_client_t
*client
);
301 static int KeepFiles
= 0,
306 * 'main()' - Main entry to the sample server.
309 int /* O - Exit status */
310 main(int argc
, /* I - Number of command-line args */
311 char *argv
[]) /* I - Command-line arguments */
313 int i
; /* Looping var */
314 const char *opt
, /* Current option character */
315 *command
= NULL
, /* Command to run with job files */
316 *servername
= NULL
, /* Server host name */
317 *name
= NULL
, /* Printer name */
318 *location
= "", /* Location of printer */
319 *make
= "Test", /* Manufacturer */
320 *model
= "Printer", /* Model */
321 *icon
= "printer.png", /* Icon file */
322 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
323 /* Supported formats */
325 const char *subtype
= "_print"; /* Bonjour service subtype */
326 #endif /* HAVE_DNSSD */
327 int port
= 8631, /* Port number (0 = auto) */
328 duplex
= 0, /* Duplex mode */
329 ppm
= 10, /* Pages per minute for mono */
330 ppm_color
= 0, /* Pages per minute for color */
331 pin
= 0; /* PIN printing mode? */
332 char directory
[1024] = ""; /* Spool directory */
333 _ipp_printer_t
*printer
; /* Printer object */
337 * Parse command-line arguments...
340 for (i
= 1; i
< argc
; i
++)
341 if (argv
[i
][0] == '-')
343 for (opt
= argv
[i
] + 1; *opt
; opt
++)
346 case '2' : /* -2 (enable 2-sided printing) */
350 case 'M' : /* -M manufacturer */
357 case 'P' : /* -P (PIN printing mode) */
361 case 'c' : /* -c command */
369 case 'd' : /* -d spool-directory */
373 strlcpy(directory
, argv
[i
], sizeof(directory
));
376 case 'f' : /* -f type/subtype[,...] */
383 case 'h' : /* -h (show help) */
386 case 'i' : /* -i icon.png */
393 case 'k' : /* -k (keep files) */
397 case 'l' : /* -l location */
404 case 'm' : /* -m model */
411 case 'n' : /* -n hostname */
415 servername
= argv
[i
];
418 case 'p' : /* -p port */
420 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
422 port
= atoi(argv
[i
]);
426 case 'r' : /* -r subtype */
432 #endif /* HAVE_DNSSD */
434 case 's' : /* -s speed[,color-speed] */
438 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
442 case 'v' : /* -v (be verbose) */
446 default : /* Unknown */
447 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
457 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
465 * Apply defaults as needed...
470 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
472 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
474 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
475 directory
, strerror(errno
));
480 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
484 * Create the printer...
487 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
488 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
491 #endif /* HAVE_DNSSD */
492 directory
, command
)) == NULL
)
496 * Run the print service...
499 run_printer(printer
);
502 * Destroy the printer and exit...
505 delete_printer(printer
);
512 * 'clean_jobs()' - Clean out old (completed) jobs.
516 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
518 _ipp_job_t
*job
; /* Current job */
519 time_t cleantime
; /* Clean time */
522 if (cupsArrayCount(printer
->jobs
) == 0)
525 cleantime
= time(NULL
) - 60;
527 _cupsRWLockWrite(&(printer
->rwlock
));
528 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
530 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
531 if (job
->completed
&& job
->completed
< cleantime
)
533 cupsArrayRemove(printer
->jobs
, job
);
538 _cupsRWUnlock(&(printer
->rwlock
));
543 * 'compare_jobs()' - Compare two jobs.
546 static int /* O - Result of comparison */
547 compare_jobs(_ipp_job_t
*a
, /* I - First job */
548 _ipp_job_t
*b
) /* I - Second job */
550 return (b
->id
- a
->id
);
555 * 'copy_attributes()' - Copy attributes from one request to another.
559 copy_attributes(ipp_t
*to
, /* I - Destination request */
560 ipp_t
*from
, /* I - Source request */
561 cups_array_t
*ra
, /* I - Requested attributes */
562 ipp_tag_t group_tag
, /* I - Group to copy */
563 int quickcopy
) /* I - Do a quick copy? */
565 ipp_attribute_t
*fromattr
; /* Source attribute */
571 for (fromattr
= ippFirstAttribute(from
);
573 fromattr
= ippNextAttribute(from
))
576 * Filter attributes as needed...
579 ipp_tag_t fromgroup
= ippGetGroupTag(fromattr
);
580 const char *fromname
= ippGetName(fromattr
);
582 if ((group_tag
!= IPP_TAG_ZERO
&& fromgroup
!= group_tag
&&
583 fromgroup
!= IPP_TAG_ZERO
) || !fromname
)
586 if (!ra
|| cupsArrayFind(ra
, (void *)fromname
))
587 ippCopyAttribute(to
, fromattr
, quickcopy
);
593 * 'copy_job_attrs()' - Copy job attributes to the response.
598 _ipp_client_t
*client
, /* I - Client */
599 _ipp_job_t
*job
, /* I - Job */
600 cups_array_t
*ra
) /* I - requested-attributes */
602 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
604 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
605 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
606 "job-printer-up-time", (int)time(NULL
));
608 if (!ra
|| cupsArrayFind(ra
, "job-state"))
609 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
610 "job-state", job
->state
);
612 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
616 case IPP_JSTATE_PENDING
:
617 ippAddString(client
->response
, IPP_TAG_JOB
,
618 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
622 case IPP_JSTATE_HELD
:
624 ippAddString(client
->response
, IPP_TAG_JOB
,
625 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
626 "job-state-reasons", NULL
, "job-incoming");
627 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
628 ippAddString(client
->response
, IPP_TAG_JOB
,
629 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
630 "job-state-reasons", NULL
, "job-hold-until-specified");
632 ippAddString(client
->response
, IPP_TAG_JOB
,
633 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
634 "job-state-reasons", NULL
, "job-data-insufficient");
637 case IPP_JSTATE_PROCESSING
:
639 ippAddString(client
->response
, IPP_TAG_JOB
,
640 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
641 "job-state-reasons", NULL
, "processing-to-stop-point");
643 ippAddString(client
->response
, IPP_TAG_JOB
,
644 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
645 "job-state-reasons", NULL
, "job-printing");
648 case IPP_JSTATE_STOPPED
:
649 ippAddString(client
->response
, IPP_TAG_JOB
,
650 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
651 NULL
, "job-stopped");
654 case IPP_JSTATE_CANCELED
:
655 ippAddString(client
->response
, IPP_TAG_JOB
,
656 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
657 NULL
, "job-canceled-by-user");
660 case IPP_JSTATE_ABORTED
:
661 ippAddString(client
->response
, IPP_TAG_JOB
,
662 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
663 NULL
, "aborted-by-system");
666 case IPP_JSTATE_COMPLETED
:
667 ippAddString(client
->response
, IPP_TAG_JOB
,
668 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
669 NULL
, "job-completed-successfully");
674 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
675 ippAddInteger(client
->response
, IPP_TAG_JOB
,
676 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
677 "time-at-completed", (int)job
->completed
);
679 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
680 ippAddInteger(client
->response
, IPP_TAG_JOB
,
681 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
682 "time-at-processing", (int)job
->processing
);
687 * 'create_client()' - Accept a new network connection and create a client
691 static _ipp_client_t
* /* O - Client */
692 create_client(_ipp_printer_t
*printer
, /* I - Printer */
693 int sock
) /* I - Listen socket */
695 _ipp_client_t
*client
; /* Client */
698 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
700 perror("Unable to allocate memory for client");
704 client
->printer
= printer
;
707 * Accept the client and get the remote address...
710 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
712 perror("Unable to accept client connection");
719 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
722 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
729 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
733 static _ipp_job_t
* /* O - Job */
734 create_job(_ipp_client_t
*client
) /* I - Client */
736 _ipp_job_t
*job
; /* Job */
737 ipp_attribute_t
*attr
; /* Job attribute */
738 char uri
[1024]; /* job-uri value */
741 _cupsRWLockWrite(&(client
->printer
->rwlock
));
742 if (client
->printer
->active_job
&&
743 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
746 * Only accept a single job at a time...
749 _cupsRWLockWrite(&(client
->printer
->rwlock
));
754 * Allocate and initialize the job object...
757 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
759 perror("Unable to allocate memory for job");
763 job
->printer
= client
->printer
;
764 job
->attrs
= client
->request
;
765 job
->state
= IPP_JSTATE_HELD
;
767 client
->request
= NULL
;
770 * Set all but the first two attributes to the job attributes group...
773 for (ippFirstAttribute(job
->attrs
),
774 ippNextAttribute(job
->attrs
),
775 attr
= ippNextAttribute(job
->attrs
);
777 attr
= ippNextAttribute(job
->attrs
))
778 ippSetGroupTag(job
->attrs
, &attr
, IPP_TAG_JOB
);
781 * Get the requesting-user-name, document format, and priority...
784 if ((attr
= ippFindAttribute(job
->attrs
, "requesting-user-name",
785 IPP_TAG_NAME
)) != NULL
)
786 ippSetName(job
->attrs
, &attr
, "job-originating-user-name");
788 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
,
789 IPP_CONST_TAG(IPP_TAG_NAME
),
790 "job-originating-user-name", NULL
, "anonymous");
793 job
->username
= ippGetString(attr
, 0, NULL
);
795 job
->username
= "anonymous";
797 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
798 IPP_TAG_MIMETYPE
)) != NULL
)
799 job
->format
= ippGetString(attr
, 0, NULL
);
801 job
->format
= "application/octet-stream";
804 * Add job description attributes and add to the jobs array...
807 job
->id
= client
->printer
->next_job_id
++;
809 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
811 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
812 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
813 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
814 client
->printer
->uri
);
815 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
818 cupsArrayAdd(client
->printer
->jobs
, job
);
819 client
->printer
->active_job
= job
;
821 _cupsRWUnlock(&(client
->printer
->rwlock
));
828 * 'create_listener()' - Create a listener socket.
831 static int /* O - Listener socket or -1 on error */
832 create_listener(int family
, /* I - Address family */
833 int *port
) /* IO - Port number */
835 int sock
; /* Listener socket */
836 http_addrlist_t
*addrlist
; /* Listen address */
837 char service
[255]; /* Service port */
842 *port
= 8000 + (getuid() % 1000);
843 fprintf(stderr
, "Listening on port %d.\n", *port
);
846 snprintf(service
, sizeof(service
), "%d", *port
);
847 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
850 sock
= httpAddrListen(&(addrlist
->addr
), *port
);
852 httpAddrFreeList(addrlist
);
859 * 'create_media_col()' - Create a media-col value.
862 static ipp_t
* /* O - media-col collection */
863 create_media_col(const char *media
, /* I - Media name */
864 const char *type
, /* I - Nedua type */
865 int width
, /* I - x-dimension in 2540ths */
866 int length
, /* I - y-dimension in 2540ths */
867 int margins
) /* I - Value for margins */
869 ipp_t
*media_col
= ippNew(), /* media-col value */
870 *media_size
= create_media_size(width
, length
);
871 /* media-size value */
872 char media_key
[256]; /* media-key value */
875 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, type
,
876 margins
== 0 ? "_borderless" : "");
878 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
880 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
881 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
882 "media-bottom-margin", margins
);
883 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
884 "media-left-margin", margins
);
885 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
886 "media-right-margin", margins
);
887 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
888 "media-top-margin", margins
);
889 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type",
892 ippDelete(media_size
);
899 * 'create_media_size()' - Create a media-size value.
902 static ipp_t
* /* O - media-col collection */
903 create_media_size(int width
, /* I - x-dimension in 2540ths */
904 int length
) /* I - y-dimension in 2540ths */
906 ipp_t
*media_size
= ippNew(); /* media-size value */
909 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
911 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
919 * 'create_printer()' - Create, register, and listen for connections to a
923 static _ipp_printer_t
* /* O - Printer */
924 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
925 const char *name
, /* I - printer-name */
926 const char *location
, /* I - printer-location */
927 const char *make
, /* I - printer-make-and-model */
928 const char *model
, /* I - printer-make-and-model */
929 const char *icon
, /* I - printer-icons */
930 const char *docformats
, /* I - document-format-supported */
931 int ppm
, /* I - Pages per minute in grayscale */
932 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
933 int duplex
, /* I - 1 = duplex, 0 = simplex */
934 int port
, /* I - Port for listeners or 0 for auto */
935 int pin
, /* I - Require PIN printing */
937 const char *subtype
, /* I - Bonjour service subtype */
938 #endif /* HAVE_DNSSD */
939 const char *directory
, /* I - Spool directory */
940 const char *command
) /* I - Command to run on job files */
942 int i
, j
; /* Looping vars */
943 _ipp_printer_t
*printer
; /* Printer */
944 char hostname
[256], /* Hostname */
945 uri
[1024], /* Printer URI */
946 icons
[1024], /* printer-icons URI */
947 adminurl
[1024], /* printer-more-info URI */
948 device_id
[1024],/* printer-device-id */
949 make_model
[128];/* printer-make-and-model */
950 int num_formats
; /* Number of document-format-supported values */
951 char *defformat
, /* document-format-default value */
952 *formats
[100], /* document-format-supported values */
953 *ptr
; /* Pointer into string */
954 const char *prefix
; /* Prefix string */
955 int num_database
; /* Number of database values */
956 ipp_attribute_t
*media_col_database
,
957 /* media-col-database value */
958 *media_size_supported
;
959 /* media-size-supported value */
960 ipp_t
*media_col_default
;
961 /* media-col-default value */
962 int media_col_index
;/* Current media-col-database value */
963 int k_supported
; /* Maximum file size supported */
965 struct statvfs spoolinfo
; /* FS info for spool directory */
966 double spoolsize
; /* FS size */
967 #elif defined(HAVE_STATFS)
968 struct statfs spoolinfo
; /* FS info for spool directory */
969 double spoolsize
; /* FS size */
970 #endif /* HAVE_STATVFS */
971 static const int orients
[4] = /* orientation-requested-supported values */
974 IPP_ORIENT_LANDSCAPE
,
975 IPP_ORIENT_REVERSE_LANDSCAPE
,
976 IPP_ORIENT_REVERSE_PORTRAIT
978 static const char * const versions
[] =/* ipp-versions-supported values */
984 static const int ops
[] = /* operations-supported values */
990 IPP_OP_SEND_DOCUMENT
,
993 IPP_OP_GET_JOB_ATTRIBUTES
,
995 IPP_OP_GET_PRINTER_ATTRIBUTES
997 static const char * const charsets
[] =/* charset-supported values */
1002 static const char * const compressions
[] =/* compression-supported values */
1007 #endif /* HAVE_LIBZ */
1010 static const char * const job_creation
[] =
1011 { /* job-creation-attributes-supported values */
1013 "ipp-attribute-fidelity",
1015 "job-accounting-user-id",
1021 "multiple-document-handling",
1022 "orientation-requested",
1026 static const char * const media_col_supported
[] =
1027 { /* media-col-supported values */
1028 "media-bottom-margin",
1029 "media-left-margin",
1030 "media-right-margin",
1035 static const int media_xxx_margin_supported
[] =
1036 { /* media-xxx-margin-supported values */
1040 static const char * const multiple_document_handling
[] =
1041 { /* multiple-document-handling-supported values */
1042 "separate-documents-uncollated-copies",
1043 "separate-documents-collated-copies"
1045 static const int print_quality_supported
[] =
1046 { /* print-quality-supported values */
1051 static const int pwg_raster_document_resolution_supported
[] =
1057 static const char * const pwg_raster_document_type_supported
[] =
1065 static const char * const reference_uri_schemes_supported
[] =
1066 { /* reference-uri-schemes-supported */
1072 #endif /* HAVE_SSL */
1074 static const char * const sides_supported
[] =
1075 { /* sides-supported values */
1077 "two-sided-long-edge",
1078 "two-sided-short-edge"
1080 static const char * const which_jobs
[] =
1081 { /* which-jobs-supported values */
1090 "processing-stopped"
1095 * Allocate memory for the printer...
1098 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1100 perror("Unable to allocate memory for printer");
1106 printer
->name
= strdup(name
);
1108 printer
->dnssd_name
= strdup(printer
->name
);
1109 #endif /* HAVE_DNSSD */
1110 printer
->command
= command
? strdup(command
) : NULL
;
1111 printer
->directory
= strdup(directory
);
1112 printer
->hostname
= strdup(servername
? servername
:
1113 httpGetHostname(NULL
, hostname
,
1115 printer
->port
= port
;
1116 printer
->state
= IPP_PSTATE_IDLE
;
1117 printer
->state_reasons
= _IPP_PSTATE_NONE
;
1118 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1119 printer
->next_job_id
= 1;
1121 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1122 printer
->hostname
, printer
->port
, "/ipp/print");
1123 printer
->uri
= strdup(uri
);
1124 printer
->urilen
= strlen(uri
);
1127 printer
->icon
= strdup(icon
);
1129 _cupsRWInit(&(printer
->rwlock
));
1132 * Create the listener sockets...
1135 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1137 perror("Unable to create IPv4 listener");
1141 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1143 perror("Unable to create IPv6 listener");
1148 * Prepare values for the printer attributes...
1151 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1152 printer
->hostname
, printer
->port
, "/icon.png");
1153 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
,
1154 printer
->hostname
, printer
->port
, "/");
1158 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1159 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1162 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1165 formats
[0] = strdup(docformats
);
1166 defformat
= formats
[0];
1167 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1170 formats
[num_formats
++] = ptr
;
1172 if (!_cups_strcasecmp(ptr
, "application/octet-stream"))
1176 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1177 ptr
= device_id
+ strlen(device_id
);
1179 for (i
= 0; i
< num_formats
; i
++)
1181 if (!_cups_strcasecmp(formats
[i
], "application/pdf"))
1182 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1183 else if (!_cups_strcasecmp(formats
[i
], "application/postscript"))
1184 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1185 else if (!_cups_strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1186 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1187 else if (!_cups_strcasecmp(formats
[i
], "image/jpeg"))
1188 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1189 else if (!_cups_strcasecmp(formats
[i
], "image/png"))
1190 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1191 else if (_cups_strcasecmp(formats
[i
], "application/octet-stream"))
1192 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1197 strlcat(device_id
, ";", sizeof(device_id
));
1200 * Get the maximum spool size based on the size of the filesystem used for
1201 * the spool directory. If the host OS doesn't support the statfs call
1202 * or the filesystem is larger than 2TiB, always report INT_MAX.
1206 if (statvfs(printer
->directory
, &spoolinfo
))
1207 k_supported
= INT_MAX
;
1208 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1209 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1210 k_supported
= INT_MAX
;
1212 k_supported
= (int)spoolsize
;
1214 #elif defined(HAVE_STATFS)
1215 if (statfs(printer
->directory
, &spoolinfo
))
1216 k_supported
= INT_MAX
;
1217 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1218 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1219 k_supported
= INT_MAX
;
1221 k_supported
= (int)spoolsize
;
1224 k_supported
= INT_MAX
;
1225 #endif /* HAVE_STATVFS */
1228 * Create the printer attributes. This list of attributes is sorted to improve
1229 * performance when the client provides a requested-attributes attribute...
1232 printer
->attrs
= ippNew();
1234 /* charset-configured */
1235 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1236 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1237 "charset-configured", NULL
, "utf-8");
1239 /* charset-supported */
1240 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1241 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1242 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1245 /* color-supported */
1246 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1249 /* compression-supported */
1250 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1251 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1252 "compression-supported",
1253 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1256 /* copies-default */
1257 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1258 "copies-default", 1);
1260 /* copies-supported */
1261 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1263 /* document-format-default */
1264 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1265 "document-format-default", NULL
, defformat
);
1267 /* document-format-supported */
1268 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1269 "document-format-supported", num_formats
, NULL
,
1270 (const char * const *)formats
);
1272 /* finishings-default */
1273 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1274 "finishings-default", IPP_FINISHINGS_NONE
);
1276 /* finishings-supported */
1277 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1278 "finishings-supported", IPP_FINISHINGS_NONE
);
1280 /* generated-natural-language-supported */
1281 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1282 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1283 "generated-natural-language-supported", NULL
, "en");
1285 /* ipp-versions-supported */
1286 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1287 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1288 "ipp-versions-supported",
1289 sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1291 /* job-account-id-supported */
1292 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1294 /* job-accounting-user-id-supported */
1295 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1296 "job-accounting-user-id-supported", 1);
1298 /* job-creation-attributes-supported */
1299 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1300 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1301 "job-creation-attributes-supported",
1302 sizeof(job_creation
) / sizeof(job_creation
[0]),
1303 NULL
, job_creation
);
1305 /* job-k-octets-supported */
1306 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1309 /* job-password-supported */
1310 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1311 "job-password-supported", 4);
1313 /* job-priority-default */
1314 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1315 "job-priority-default", 50);
1317 /* job-priority-supported */
1318 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1319 "job-priority-supported", 100);
1321 /* job-sheets-default */
1322 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1323 IPP_CONST_TAG(IPP_TAG_NAME
),
1324 "job-sheets-default", NULL
, "none");
1326 /* job-sheets-supported */
1327 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1328 IPP_CONST_TAG(IPP_TAG_NAME
),
1329 "job-sheets-supported", NULL
, "none");
1331 /* media-bottom-margin-supported */
1332 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1333 "media-bottom-margin-supported",
1334 (int)(sizeof(media_xxx_margin_supported
) /
1335 sizeof(media_xxx_margin_supported
[0])),
1336 media_xxx_margin_supported
);
1338 /* media-col-database */
1339 for (num_database
= 0, i
= 0;
1340 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1343 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1344 num_database
+= 2; /* auto + envelope */
1345 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1346 num_database
+= 12; /* auto + photographic-* + borderless */
1348 num_database
+= (int)(sizeof(media_type_supported
) /
1349 sizeof(media_type_supported
[0])) + 6;
1350 /* All types + borderless */
1353 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1354 "media-col-database", num_database
,
1356 for (media_col_index
= 0, i
= 0;
1357 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1361 j
< (int)(sizeof(media_type_supported
) /
1362 sizeof(media_type_supported
[0]));
1365 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
&&
1366 strcmp(media_type_supported
[j
], "auto") &&
1367 strcmp(media_type_supported
[j
], "envelope"))
1369 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
&&
1370 strcmp(media_type_supported
[j
], "auto") &&
1371 strncmp(media_type_supported
[j
], "photographic-", 13))
1374 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1375 create_media_col(media_supported
[i
],
1376 media_type_supported
[j
],
1377 media_col_sizes
[i
][0],
1378 media_col_sizes
[i
][1],
1379 media_xxx_margin_supported
[1]));
1382 if (media_col_sizes
[i
][2] != _IPP_ENV_ONLY
&&
1383 (!strcmp(media_type_supported
[j
], "auto") ||
1384 !strncmp(media_type_supported
[j
], "photographic-", 13)))
1387 * Add borderless version for this combination...
1390 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1391 create_media_col(media_supported
[i
],
1392 media_type_supported
[j
],
1393 media_col_sizes
[i
][0],
1394 media_col_sizes
[i
][1],
1395 media_xxx_margin_supported
[0]));
1401 /* media-col-default */
1402 media_col_default
= create_media_col(media_supported
[0],
1403 media_type_supported
[0],
1404 media_col_sizes
[0][0],
1405 media_col_sizes
[0][1],
1406 media_xxx_margin_supported
[1]);
1408 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1410 ippDelete(media_col_default
);
1412 /* media-col-supported */
1413 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1414 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1415 "media-col-supported",
1416 (int)(sizeof(media_col_supported
) /
1417 sizeof(media_col_supported
[0])), NULL
,
1418 media_col_supported
);
1421 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1422 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1423 "media-default", NULL
, media_supported
[0]);
1425 /* media-left-margin-supported */
1426 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1427 "media-left-margin-supported",
1428 (int)(sizeof(media_xxx_margin_supported
) /
1429 sizeof(media_xxx_margin_supported
[0])),
1430 media_xxx_margin_supported
);
1432 /* media-right-margin-supported */
1433 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1434 "media-right-margin-supported",
1435 (int)(sizeof(media_xxx_margin_supported
) /
1436 sizeof(media_xxx_margin_supported
[0])),
1437 media_xxx_margin_supported
);
1439 /* media-supported */
1440 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1441 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1443 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1444 NULL
, media_supported
);
1446 /* media-size-supported */
1447 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1448 "media-size-supported",
1449 (int)(sizeof(media_col_sizes
) /
1450 sizeof(media_col_sizes
[0])),
1453 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1455 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1456 create_media_size(media_col_sizes
[i
][0],
1457 media_col_sizes
[i
][1]));
1459 /* media-top-margin-supported */
1460 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1461 "media-top-margin-supported",
1462 (int)(sizeof(media_xxx_margin_supported
) /
1463 sizeof(media_xxx_margin_supported
[0])),
1464 media_xxx_margin_supported
);
1466 /* media-type-supported */
1467 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1468 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1469 "media-type-supported",
1470 (int)(sizeof(media_type_supported
) /
1471 sizeof(media_type_supported
[0])),
1472 NULL
, media_type_supported
);
1474 /* multiple-document-handling-supported */
1475 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1476 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1477 "multiple-document-handling-supported",
1478 sizeof(multiple_document_handling
) /
1479 sizeof(multiple_document_handling
[0]), NULL
,
1480 multiple_document_handling
);
1482 /* multiple-document-jobs-supported */
1483 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1484 "multiple-document-jobs-supported", 0);
1486 /* natural-language-configured */
1487 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1488 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1489 "natural-language-configured", NULL
, "en");
1491 /* number-up-default */
1492 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1493 "number-up-default", 1);
1495 /* number-up-supported */
1496 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1497 "number-up-supported", 1);
1499 /* operations-supported */
1500 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1501 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1503 /* orientation-requested-default */
1504 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1505 "orientation-requested-default", 0);
1507 /* orientation-requested-supported */
1508 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1509 "orientation-requested-supported", 4, orients
);
1511 /* output-bin-default */
1512 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1513 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1514 "output-bin-default", NULL
, "face-down");
1516 /* output-bin-supported */
1517 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1518 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1519 "output-bin-supported", NULL
, "face-down");
1521 /* pages-per-minute */
1522 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1523 "pages-per-minute", ppm
);
1525 /* pages-per-minute-color */
1527 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1528 "pages-per-minute-color", ppm_color
);
1530 /* pdl-override-supported */
1531 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1532 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1533 "pdl-override-supported", NULL
, "attempted");
1535 /* print-quality-default */
1536 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1537 "print-quality-default", IPP_QUALITY_NORMAL
);
1539 /* print-quality-supported */
1540 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1541 "print-quality-supported",
1542 (int)(sizeof(print_quality_supported
) /
1543 sizeof(print_quality_supported
[0])),
1544 print_quality_supported
);
1546 /* printer-device-id */
1547 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1548 "printer-device-id", NULL
, device_id
);
1551 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1552 "printer-icons", NULL
, icons
);
1554 /* printer-is-accepting-jobs */
1555 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1559 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1562 /* printer-location */
1563 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1564 "printer-location", NULL
, location
);
1566 /* printer-make-and-model */
1567 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1568 "printer-make-and-model", NULL
, make_model
);
1570 /* printer-mandatory-job-attributes */
1573 static const char * const names
[] =
1575 "job-accounting-user-id",
1579 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1580 "printer-mandatory-job-attributes",
1581 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1584 /* printer-more-info */
1585 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1586 "printer-more-info", NULL
, adminurl
);
1589 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1592 /* printer-resolution-default */
1593 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1594 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1596 /* printer-resolution-supported */
1597 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1598 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1600 /* printer-uri-supported */
1601 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1602 "printer-uri-supported", NULL
, uri
);
1604 /* pwg-raster-document-xxx-supported */
1605 for (i
= 0; i
< num_formats
; i
++)
1606 if (!_cups_strcasecmp(formats
[i
], "image/pwg-raster"))
1609 if (i
< num_formats
)
1611 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
1612 "pwg-raster-document-resolution-supported",
1613 (int)(sizeof(pwg_raster_document_resolution_supported
) /
1614 sizeof(pwg_raster_document_resolution_supported
[0])),
1616 pwg_raster_document_resolution_supported
,
1617 pwg_raster_document_resolution_supported
);
1618 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1619 "pwg-raster-document-sheet-back", NULL
, "normal");
1620 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1621 "pwg-raster-document-type-supported",
1622 (int)(sizeof(pwg_raster_document_type_supported
) /
1623 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
1624 pwg_raster_document_type_supported
);
1627 /* reference-uri-scheme-supported */
1628 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1629 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
1630 "reference-uri-schemes-supported",
1631 (int)(sizeof(reference_uri_schemes_supported
) /
1632 sizeof(reference_uri_schemes_supported
[0])),
1633 NULL
, reference_uri_schemes_supported
);
1636 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1637 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1638 "sides-default", NULL
, "one-sided");
1640 /* sides-supported */
1641 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1642 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1643 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1645 /* uri-authentication-supported */
1646 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1647 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1648 "uri-authentication-supported", NULL
, "none");
1650 /* uri-security-supported */
1651 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1652 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1653 "uri-security-supported", NULL
, "none");
1655 /* which-jobs-supported */
1656 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1657 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1658 "which-jobs-supported",
1659 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1663 debug_attributes("Printer", printer
->attrs
, 0);
1667 * Register the printer with Bonjour...
1670 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
,
1671 ppm_color
> 0, duplex
, subtype
))
1673 #endif /* HAVE_DNSSD */
1683 * If we get here we were unable to create the printer...
1688 delete_printer(printer
);
1694 * 'debug_attributes()' - Print attributes in a request or response.
1698 debug_attributes(const char *title
, /* I - Title */
1699 ipp_t
*ipp
, /* I - Request/response */
1700 int type
) /* I - 0 = object, 1 = request, 2 = response */
1702 ipp_tag_t group_tag
; /* Current group */
1703 ipp_attribute_t
*attr
; /* Current attribute */
1704 char buffer
[2048]; /* String buffer for value */
1705 int major
, minor
; /* Version */
1711 fprintf(stderr
, "%s:\n", title
);
1712 major
= ippGetVersion(ipp
, &minor
);
1713 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1715 fprintf(stderr
, " operation-id=%s(%04x)\n",
1716 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1718 fprintf(stderr
, " status-code=%s(%04x)\n",
1719 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1720 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1722 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1724 attr
= ippNextAttribute(ipp
))
1726 if (ippGetGroupTag(attr
) != group_tag
)
1728 group_tag
= ippGetGroupTag(attr
);
1729 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1732 if (ippGetName(attr
))
1734 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1735 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1736 ippGetCount(attr
) > 1 ? "1setOf " : "",
1737 ippTagString(ippGetValueTag(attr
)), buffer
);
1744 * 'delete_client()' - Close the socket and free all memory used by a client
1749 delete_client(_ipp_client_t
*client
) /* I - Client */
1752 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1755 * Flush pending writes before closing...
1758 httpFlushWrite(client
->http
);
1764 httpClose(client
->http
);
1766 ippDelete(client
->request
);
1767 ippDelete(client
->response
);
1774 * 'delete_job()' - Remove from the printer and free all memory used by a job
1779 delete_job(_ipp_job_t
*job
) /* I - Job */
1782 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
1784 ippDelete(job
->attrs
);
1789 unlink(job
->filename
);
1791 free(job
->filename
);
1799 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1800 * used by a printer object.
1804 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
1806 if (printer
->ipv4
>= 0)
1807 close(printer
->ipv4
);
1809 if (printer
->ipv6
>= 0)
1810 close(printer
->ipv6
);
1813 if (printer
->printer_ref
)
1814 DNSServiceRefDeallocate(printer
->printer_ref
);
1816 if (printer
->ipp_ref
)
1817 DNSServiceRefDeallocate(printer
->ipp_ref
);
1820 if (printer
->ipps_ref
)
1821 DNSServiceRefDeallocate(printer
->ipps_ref
);
1822 # endif /* HAVE_SSL */
1823 if (printer
->http_ref
)
1824 DNSServiceRefDeallocate(printer
->http_ref
);
1826 if (printer
->common_ref
)
1827 DNSServiceRefDeallocate(printer
->common_ref
);
1829 TXTRecordDeallocate(&(printer
->ipp_txt
));
1831 if (printer
->dnssd_name
)
1832 free(printer
->dnssd_name
);
1833 #endif /* HAVE_DNSSD */
1836 free(printer
->name
);
1838 free(printer
->icon
);
1839 if (printer
->command
)
1840 free(printer
->command
);
1841 if (printer
->directory
)
1842 free(printer
->directory
);
1843 if (printer
->hostname
)
1844 free(printer
->hostname
);
1848 ippDelete(printer
->attrs
);
1849 cupsArrayDelete(printer
->jobs
);
1857 * 'dnssd_callback()' - Handle Bonjour registration events.
1862 DNSServiceRef sdRef
, /* I - Service reference */
1863 DNSServiceFlags flags
, /* I - Status flags */
1864 DNSServiceErrorType errorCode
, /* I - Error, if any */
1865 const char *name
, /* I - Service name */
1866 const char *regtype
, /* I - Service type */
1867 const char *domain
, /* I - Domain for service */
1868 _ipp_printer_t
*printer
) /* I - Printer */
1876 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
1877 regtype
, (int)errorCode
);
1880 else if (_cups_strcasecmp(name
, printer
->dnssd_name
))
1883 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
1885 /* No lock needed since only the main thread accesses/changes this */
1886 free(printer
->dnssd_name
);
1887 printer
->dnssd_name
= strdup(name
);
1890 #endif /* HAVE_DNSSD */
1894 * 'find_job()' - Find a job specified in a request.
1897 static _ipp_job_t
* /* O - Job or NULL */
1898 find_job(_ipp_client_t
*client
) /* I - Client */
1900 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
1901 _ipp_job_t key
, /* Job search key */
1902 *job
; /* Matching job, if any */
1907 if ((attr
= ippFindAttribute(client
->request
, "job-uri",
1908 IPP_TAG_URI
)) != NULL
)
1910 const char *uri
= ippGetString(attr
, 0, NULL
);
1912 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
1913 uri
[client
->printer
->urilen
] == '/')
1914 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
1916 else if ((attr
= ippFindAttribute(client
->request
, "job-id",
1917 IPP_TAG_INTEGER
)) != NULL
)
1918 key
.id
= ippGetInteger(attr
, 0);
1920 _cupsRWLockRead(&(client
->printer
->rwlock
));
1921 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
1922 _cupsRWUnlock(&(client
->printer
->rwlock
));
1929 * 'html_escape()' - Write a HTML-safe string.
1933 html_escape(_ipp_client_t
*client
, /* I - Client */
1934 const char *s
, /* I - String to write */
1935 size_t slen
) /* I - Number of characters to write */
1937 const char *start
, /* Start of segment */
1938 *end
; /* End of string */
1942 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
1944 while (*s
&& s
< end
)
1946 if (*s
== '&' || *s
== '<')
1949 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
1952 httpWrite2(client
->http
, "&", 5);
1954 httpWrite2(client
->http
, "<", 4);
1963 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
1968 * 'html_printf()' - Send formatted text to the client, quoting as needed.
1972 html_printf(_ipp_client_t
*client
, /* I - Client */
1973 const char *format
, /* I - Printf-style format string */
1974 ...) /* I - Additional arguments as needed */
1976 va_list ap
; /* Pointer to arguments */
1977 const char *start
; /* Start of string */
1978 char size
, /* Size character (h, l, L) */
1979 type
; /* Format type character */
1980 int width
, /* Width of field */
1981 prec
; /* Number of characters of precision */
1982 char tformat
[100], /* Temporary format string for sprintf() */
1983 *tptr
, /* Pointer into temporary format */
1984 temp
[1024]; /* Buffer for formatted numbers */
1985 char *s
; /* Pointer to string */
1989 * Loop through the format string, formatting as needed...
1992 va_start(ap
, format
);
2000 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2003 *tptr
++ = *format
++;
2007 httpWrite2(client
->http
, "%", 1);
2011 else if (strchr(" -+#\'", *format
))
2012 *tptr
++ = *format
++;
2017 * Get width from argument...
2021 width
= va_arg(ap
, int);
2023 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2024 tptr
+= strlen(tptr
);
2030 while (isdigit(*format
& 255))
2032 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2035 width
= width
* 10 + *format
++ - '0';
2041 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2049 * Get precision from argument...
2053 prec
= va_arg(ap
, int);
2055 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2056 tptr
+= strlen(tptr
);
2062 while (isdigit(*format
& 255))
2064 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2067 prec
= prec
* 10 + *format
++ - '0';
2072 if (*format
== 'l' && format
[1] == 'l')
2076 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2084 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2086 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2101 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2110 case 'E' : /* Floating point formats */
2115 if ((size_t)(width
+ 2) > sizeof(temp
))
2118 sprintf(temp
, tformat
, va_arg(ap
, double));
2120 httpWrite2(client
->http
, temp
, strlen(temp
));
2123 case 'B' : /* Integer formats */
2131 if ((size_t)(width
+ 2) > sizeof(temp
))
2134 # ifdef HAVE_LONG_LONG
2136 sprintf(temp
, tformat
, va_arg(ap
, long long));
2138 # endif /* HAVE_LONG_LONG */
2140 sprintf(temp
, tformat
, va_arg(ap
, long));
2142 sprintf(temp
, tformat
, va_arg(ap
, int));
2144 httpWrite2(client
->http
, temp
, strlen(temp
));
2147 case 'p' : /* Pointer value */
2148 if ((size_t)(width
+ 2) > sizeof(temp
))
2151 sprintf(temp
, tformat
, va_arg(ap
, void *));
2153 httpWrite2(client
->http
, temp
, strlen(temp
));
2156 case 'c' : /* Character or character array */
2159 temp
[0] = (char)va_arg(ap
, int);
2161 html_escape(client
, temp
, 1);
2164 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2167 case 's' : /* String */
2168 if ((s
= va_arg(ap
, char *)) == NULL
)
2171 html_escape(client
, s
, strlen(s
));
2180 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2187 * 'ipp_cancel_job()' - Cancel a job.
2191 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2193 _ipp_job_t
*job
; /* Job information */
2200 if ((job
= find_job(client
)) == NULL
)
2202 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2207 * See if the job is already completed, canceled, or aborted; if so,
2208 * we can't cancel...
2213 case IPP_JSTATE_CANCELED
:
2214 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2215 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2218 case IPP_JSTATE_ABORTED
:
2219 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2220 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2223 case IPP_JSTATE_COMPLETED
:
2224 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2225 "Job #%d is already completed - can\'t cancel.", job
->id
);
2233 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2235 if (job
->state
== IPP_JSTATE_PROCESSING
||
2236 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2240 job
->state
= IPP_JSTATE_CANCELED
;
2241 job
->completed
= time(NULL
);
2244 _cupsRWUnlock(&(client
->printer
->rwlock
));
2246 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2253 * 'ipp_create_job()' - Create a job object.
2257 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2259 _ipp_job_t
*job
; /* New job */
2260 cups_array_t
*ra
; /* Attributes to send in response */
2264 * Validate print job attributes...
2267 if (!valid_job_attributes(client
))
2269 httpFlush(client
->http
);
2274 * Do we have a file to print?
2277 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2279 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2280 "Unexpected document data following request.");
2288 if ((job
= create_job(client
)) == NULL
)
2290 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2291 "Currently printing another job.");
2296 * Return the job info...
2299 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2301 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2302 cupsArrayAdd(ra
, "job-id");
2303 cupsArrayAdd(ra
, "job-state");
2304 cupsArrayAdd(ra
, "job-state-reasons");
2305 cupsArrayAdd(ra
, "job-uri");
2307 copy_job_attributes(client
, job
, ra
);
2308 cupsArrayDelete(ra
);
2313 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2317 ipp_get_job_attributes(
2318 _ipp_client_t
*client
) /* I - Client */
2320 _ipp_job_t
*job
; /* Job */
2321 cups_array_t
*ra
; /* requested-attributes */
2324 if ((job
= find_job(client
)) == NULL
)
2326 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2330 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2332 ra
= ippCreateRequestedArray(client
->request
);
2333 copy_job_attributes(client
, job
, ra
);
2334 cupsArrayDelete(ra
);
2339 * 'ipp_get_jobs()' - Get a list of job objects.
2343 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2345 ipp_attribute_t
*attr
; /* Current attribute */
2346 const char *which_jobs
= NULL
;
2347 /* which-jobs values */
2348 int job_comparison
; /* Job comparison */
2349 ipp_jstate_t job_state
; /* job-state value */
2350 int first_job_id
, /* First job ID */
2351 limit
, /* Maximum number of jobs to return */
2352 count
; /* Number of jobs that match */
2353 const char *username
; /* Username */
2354 _ipp_job_t
*job
; /* Current job pointer */
2355 cups_array_t
*ra
; /* Requested attributes array */
2359 * See if the "which-jobs" attribute have been specified...
2362 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2363 IPP_TAG_KEYWORD
)) != NULL
)
2365 which_jobs
= ippGetString(attr
, 0, NULL
);
2366 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2369 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2371 job_comparison
= -1;
2372 job_state
= IPP_JSTATE_STOPPED
;
2374 else if (!strcmp(which_jobs
, "completed"))
2377 job_state
= IPP_JSTATE_CANCELED
;
2379 else if (!strcmp(which_jobs
, "aborted"))
2382 job_state
= IPP_JSTATE_ABORTED
;
2384 else if (!strcmp(which_jobs
, "all"))
2387 job_state
= IPP_JSTATE_PENDING
;
2389 else if (!strcmp(which_jobs
, "canceled"))
2392 job_state
= IPP_JSTATE_CANCELED
;
2394 else if (!strcmp(which_jobs
, "pending"))
2397 job_state
= IPP_JSTATE_PENDING
;
2399 else if (!strcmp(which_jobs
, "pending-held"))
2402 job_state
= IPP_JSTATE_HELD
;
2404 else if (!strcmp(which_jobs
, "processing"))
2407 job_state
= IPP_JSTATE_PROCESSING
;
2409 else if (!strcmp(which_jobs
, "processing-stopped"))
2412 job_state
= IPP_JSTATE_STOPPED
;
2416 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2417 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2418 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2419 "which-jobs", NULL
, which_jobs
);
2424 * See if they want to limit the number of jobs reported...
2427 if ((attr
= ippFindAttribute(client
->request
, "limit",
2428 IPP_TAG_INTEGER
)) != NULL
)
2430 limit
= ippGetInteger(attr
, 0);
2432 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2437 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2438 IPP_TAG_INTEGER
)) != NULL
)
2440 first_job_id
= ippGetInteger(attr
, 0);
2442 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2449 * See if we only want to see jobs for a specific user...
2454 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2455 IPP_TAG_BOOLEAN
)) != NULL
)
2457 int my_jobs
= ippGetBoolean(attr
, 0);
2459 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2460 my_jobs
? "true" : "false");
2464 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2465 IPP_TAG_NAME
)) == NULL
)
2467 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2468 "Need requesting-user-name with my-jobs.");
2472 username
= ippGetString(attr
, 0, NULL
);
2474 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2475 client
->hostname
, username
);
2480 * OK, build a list of jobs for this printer...
2483 ra
= ippCreateRequestedArray(client
->request
);
2485 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2487 _cupsRWLockRead(&(client
->printer
->rwlock
));
2489 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2490 (limit
<= 0 || count
< limit
) && job
;
2491 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2494 * Filter out jobs that don't match...
2497 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2498 (job_comparison
== 0 && job
->state
!= job_state
) ||
2499 (job_comparison
> 0 && job
->state
< job_state
) ||
2500 job
->id
< first_job_id
||
2501 (username
&& job
->username
&&
2502 _cups_strcasecmp(username
, job
->username
)))
2506 ippAddSeparator(client
->response
);
2509 copy_job_attributes(client
, job
, ra
);
2512 cupsArrayDelete(ra
);
2514 _cupsRWUnlock(&(client
->printer
->rwlock
));
2519 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2523 ipp_get_printer_attributes(
2524 _ipp_client_t
*client
) /* I - Client */
2526 cups_array_t
*ra
; /* Requested attributes array */
2527 _ipp_printer_t
*printer
; /* Printer */
2531 * Send the attributes...
2534 ra
= ippCreateRequestedArray(client
->request
);
2535 printer
= client
->printer
;
2537 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2539 _cupsRWLockRead(&(printer
->rwlock
));
2541 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2542 IPP_TAG_CUPS_CONST
);
2544 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
2545 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2546 "printer-state", printer
->state
);
2548 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
2550 if (printer
->state_reasons
== _IPP_PSTATE_NONE
)
2551 ippAddString(client
->response
, IPP_TAG_PRINTER
,
2552 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2553 "printer-state-reasons", NULL
, "none");
2556 int num_reasons
= 0;/* Number of reasons */
2557 const char *reasons
[32]; /* Reason strings */
2559 if (printer
->state_reasons
& _IPP_PSTATE_OTHER
)
2560 reasons
[num_reasons
++] = "other";
2561 if (printer
->state_reasons
& _IPP_PSTATE_COVER_OPEN
)
2562 reasons
[num_reasons
++] = "cover-open";
2563 if (printer
->state_reasons
& _IPP_PSTATE_INPUT_TRAY_MISSING
)
2564 reasons
[num_reasons
++] = "input-tray-missing";
2565 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_EMPTY
)
2566 reasons
[num_reasons
++] = "marker-supply-empty-warning";
2567 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_LOW
)
2568 reasons
[num_reasons
++] = "marker-supply-low-report";
2569 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL
)
2570 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
2571 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_FULL
)
2572 reasons
[num_reasons
++] = "marker-waste-full-warning";
2573 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_EMPTY
)
2574 reasons
[num_reasons
++] = "media-empty-warning";
2575 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_JAM
)
2576 reasons
[num_reasons
++] = "media-jam-warning";
2577 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_LOW
)
2578 reasons
[num_reasons
++] = "media-low-report";
2579 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_NEEDED
)
2580 reasons
[num_reasons
++] = "media-needed-report";
2581 if (printer
->state_reasons
& _IPP_PSTATE_MOVING_TO_PAUSED
)
2582 reasons
[num_reasons
++] = "moving-to-paused";
2583 if (printer
->state_reasons
& _IPP_PSTATE_PAUSED
)
2584 reasons
[num_reasons
++] = "paused";
2585 if (printer
->state_reasons
& _IPP_PSTATE_SPOOL_AREA_FULL
)
2586 reasons
[num_reasons
++] = "spool-area-full";
2587 if (printer
->state_reasons
& _IPP_PSTATE_TONER_EMPTY
)
2588 reasons
[num_reasons
++] = "toner-empty-warning";
2589 if (printer
->state_reasons
& _IPP_PSTATE_TONER_LOW
)
2590 reasons
[num_reasons
++] = "toner-low-report";
2592 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
2593 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2594 "printer-state-reasons", num_reasons
, NULL
, reasons
);
2598 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
2599 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2600 "printer-up-time", (int)time(NULL
));
2602 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
2603 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2605 printer
->active_job
&&
2606 printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
2608 _cupsRWUnlock(&(printer
->rwlock
));
2610 cupsArrayDelete(ra
);
2615 * 'ipp_print_job()' - Create a job object with an attached document.
2619 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
2621 _ipp_job_t
*job
; /* New job */
2622 char filename
[1024], /* Filename buffer */
2623 buffer
[4096]; /* Copy buffer */
2624 ssize_t bytes
; /* Bytes read */
2625 cups_array_t
*ra
; /* Attributes to send in response */
2629 * Validate print job attributes...
2632 if (!valid_job_attributes(client
))
2634 httpFlush(client
->http
);
2639 * Do we have a file to print?
2642 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
2644 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
2652 if ((job
= create_job(client
)) == NULL
)
2654 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2655 "Currently printing another job.");
2660 * Create a file for the request data...
2663 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
2664 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2665 client
->printer
->directory
, job
->id
);
2666 else if (!_cups_strcasecmp(job
->format
, "image/png"))
2667 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2668 client
->printer
->directory
, job
->id
);
2669 else if (!_cups_strcasecmp(job
->format
, "image/pwg-raster"))
2670 snprintf(filename
, sizeof(filename
), "%s/%d.ras",
2671 client
->printer
->directory
, job
->id
);
2672 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
2673 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2674 client
->printer
->directory
, job
->id
);
2675 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
2676 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2677 client
->printer
->directory
, job
->id
);
2679 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2680 client
->printer
->directory
, job
->id
);
2682 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2684 job
->state
= IPP_JSTATE_ABORTED
;
2686 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2687 "Unable to create print file: %s", strerror(errno
));
2691 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2693 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2695 int error
= errno
; /* Write error */
2697 job
->state
= IPP_JSTATE_ABORTED
;
2704 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2705 "Unable to write print file: %s", strerror(error
));
2713 * Got an error while reading the print data, so abort this job.
2716 job
->state
= IPP_JSTATE_ABORTED
;
2723 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2724 "Unable to read print file.");
2730 int error
= errno
; /* Write error */
2732 job
->state
= IPP_JSTATE_ABORTED
;
2737 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2738 "Unable to write print file: %s", strerror(error
));
2743 job
->filename
= strdup(filename
);
2744 job
->state
= IPP_JSTATE_PENDING
;
2747 * Process the job...
2751 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
2753 job
->state
= IPP_JSTATE_ABORTED
;
2754 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
2763 * Return the job info...
2766 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2768 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2769 cupsArrayAdd(ra
, "job-id");
2770 cupsArrayAdd(ra
, "job-state");
2771 cupsArrayAdd(ra
, "job-state-reasons");
2772 cupsArrayAdd(ra
, "job-uri");
2774 copy_job_attributes(client
, job
, ra
);
2775 cupsArrayDelete(ra
);
2780 * 'ipp_print_uri()' - Create a job object with a referenced document.
2784 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
2786 _ipp_job_t
*job
; /* New job */
2787 ipp_attribute_t
*uri
; /* document-uri */
2788 char scheme
[256], /* URI scheme */
2789 userpass
[256], /* Username and password info */
2790 hostname
[256], /* Hostname */
2791 resource
[1024]; /* Resource path */
2792 int port
; /* Port number */
2793 http_uri_status_t uri_status
; /* URI decode status */
2794 http_encryption_t encryption
; /* Encryption to use, if any */
2795 http_t
*http
; /* Connection for http/https URIs */
2796 http_status_t status
; /* Access status for http/https URIs */
2797 int infile
; /* Input file for local file URIs */
2798 char filename
[1024], /* Filename buffer */
2799 buffer
[4096]; /* Copy buffer */
2800 ssize_t bytes
; /* Bytes read */
2801 cups_array_t
*ra
; /* Attributes to send in response */
2802 static const char * const uri_status_strings
[] =
2803 { /* URI decode errors */
2805 "Bad arguments to function.",
2806 "Bad resource in URI.",
2807 "Bad port number in URI.",
2808 "Bad hostname in URI.",
2809 "Bad username in URI.",
2810 "Bad scheme in URI.",
2816 * Validate print job attributes...
2819 if (!valid_job_attributes(client
))
2821 httpFlush(client
->http
);
2826 * Do we have a file to print?
2829 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2831 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2832 "Unexpected document data following request.");
2837 * Do we have a document URI?
2840 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
2841 IPP_TAG_URI
)) == NULL
)
2843 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
2847 if (ippGetCount(uri
) != 1)
2849 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2850 "Too many document-uri values.");
2854 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
2855 scheme
, sizeof(scheme
), userpass
,
2856 sizeof(userpass
), hostname
, sizeof(hostname
),
2857 &port
, resource
, sizeof(resource
));
2858 if (uri_status
< HTTP_URI_STATUS_OK
)
2860 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
2861 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
2865 if (strcmp(scheme
, "file") &&
2867 strcmp(scheme
, "https") &&
2868 #endif /* HAVE_SSL */
2869 strcmp(scheme
, "http"))
2871 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
2872 "URI scheme \"%s\" not supported.", scheme
);
2876 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
2878 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
2879 "Unable to access URI: %s", strerror(errno
));
2887 if ((job
= create_job(client
)) == NULL
)
2889 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2890 "Currently printing another job.");
2895 * Create a file for the request data...
2898 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
2899 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2900 client
->printer
->directory
, job
->id
);
2901 else if (!_cups_strcasecmp(job
->format
, "image/png"))
2902 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2903 client
->printer
->directory
, job
->id
);
2904 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
2905 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2906 client
->printer
->directory
, job
->id
);
2907 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
2908 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2909 client
->printer
->directory
, job
->id
);
2911 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2912 client
->printer
->directory
, job
->id
);
2914 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2916 job
->state
= IPP_JSTATE_ABORTED
;
2918 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2919 "Unable to create print file: %s", strerror(errno
));
2923 if (!strcmp(scheme
, "file"))
2925 if ((infile
= open(resource
, O_RDONLY
)) < 0)
2927 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
2928 "Unable to access URI: %s", strerror(errno
));
2934 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
2935 (errno
== EAGAIN
|| errno
== EINTR
))
2937 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2939 int error
= errno
; /* Write error */
2941 job
->state
= IPP_JSTATE_ABORTED
;
2949 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2950 "Unable to write print file: %s", strerror(error
));
2961 if (port
== 443 || !strcmp(scheme
, "https"))
2962 encryption
= HTTP_ENCRYPTION_ALWAYS
;
2964 #endif /* HAVE_SSL */
2965 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
2967 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
2968 1, 30000, NULL
)) == NULL
)
2970 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
2971 "Unable to connect to %s: %s", hostname
,
2972 cupsLastErrorString());
2973 job
->state
= IPP_JSTATE_ABORTED
;
2982 httpClearFields(http
);
2983 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
2984 if (httpGet(http
, resource
))
2986 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
2987 "Unable to GET URI: %s", strerror(errno
));
2989 job
->state
= IPP_JSTATE_ABORTED
;
2999 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3001 if (status
!= HTTP_STATUS_OK
)
3003 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3004 "Unable to GET URI: %s", httpStatus(status
));
3006 job
->state
= IPP_JSTATE_ABORTED
;
3016 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3018 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3020 int error
= errno
; /* Write error */
3022 job
->state
= IPP_JSTATE_ABORTED
;
3030 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3031 "Unable to write print file: %s", strerror(error
));
3041 int error
= errno
; /* Write error */
3043 job
->state
= IPP_JSTATE_ABORTED
;
3048 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3049 "Unable to write print file: %s", strerror(error
));
3054 job
->filename
= strdup(filename
);
3055 job
->state
= IPP_JSTATE_PENDING
;
3058 * Process the job...
3062 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3064 job
->state
= IPP_JSTATE_ABORTED
;
3065 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3074 * Return the job info...
3077 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3079 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3080 cupsArrayAdd(ra
, "job-id");
3081 cupsArrayAdd(ra
, "job-state");
3082 cupsArrayAdd(ra
, "job-state-reasons");
3083 cupsArrayAdd(ra
, "job-uri");
3085 copy_job_attributes(client
, job
, ra
);
3086 cupsArrayDelete(ra
);
3091 * 'ipp_send_document()' - Add an attached document to a job object created with
3096 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3098 _ipp_job_t
*job
; /* Job information */
3099 char filename
[1024], /* Filename buffer */
3100 buffer
[4096]; /* Copy buffer */
3101 ssize_t bytes
; /* Bytes read */
3102 ipp_attribute_t
*attr
; /* Current attribute */
3103 cups_array_t
*ra
; /* Attributes to send in response */
3110 if ((job
= find_job(client
)) == NULL
)
3112 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3113 httpFlush(client
->http
);
3118 * See if we already have a document for this job or the job has already
3119 * in a non-pending state...
3122 if (job
->state
> IPP_JSTATE_HELD
)
3124 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3125 "Job is not in a pending state.");
3126 httpFlush(client
->http
);
3129 else if (job
->filename
|| job
->fd
>= 0)
3131 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3132 "Multiple document jobs are not supported.");
3133 httpFlush(client
->http
);
3137 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3138 IPP_TAG_ZERO
)) == NULL
)
3140 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3141 "Missing required last-document attribute.");
3142 httpFlush(client
->http
);
3145 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3146 !ippGetBoolean(attr
, 0))
3148 respond_unsupported(client
, attr
);
3149 httpFlush(client
->http
);
3154 * Validate document attributes...
3157 if (!valid_doc_attributes(client
))
3159 httpFlush(client
->http
);
3164 * Get the document format for the job...
3167 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3169 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3170 IPP_TAG_MIMETYPE
)) != NULL
)
3171 job
->format
= ippGetString(attr
, 0, NULL
);
3173 job
->format
= "application/octet-stream";
3176 * Create a file for the request data...
3179 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3180 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3181 client
->printer
->directory
, job
->id
);
3182 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3183 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3184 client
->printer
->directory
, job
->id
);
3185 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3186 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3187 client
->printer
->directory
, job
->id
);
3188 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3189 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3190 client
->printer
->directory
, job
->id
);
3192 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3193 client
->printer
->directory
, job
->id
);
3195 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3197 _cupsRWUnlock(&(client
->printer
->rwlock
));
3201 job
->state
= IPP_JSTATE_ABORTED
;
3203 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3204 "Unable to create print file: %s", strerror(errno
));
3208 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3210 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3212 int error
= errno
; /* Write error */
3214 job
->state
= IPP_JSTATE_ABORTED
;
3221 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3222 "Unable to write print file: %s", strerror(error
));
3230 * Got an error while reading the print data, so abort this job.
3233 job
->state
= IPP_JSTATE_ABORTED
;
3240 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3241 "Unable to read print file.");
3247 int error
= errno
; /* Write error */
3249 job
->state
= IPP_JSTATE_ABORTED
;
3254 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3255 "Unable to write print file: %s", strerror(error
));
3259 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3262 job
->filename
= strdup(filename
);
3263 job
->state
= IPP_JSTATE_PENDING
;
3265 _cupsRWUnlock(&(client
->printer
->rwlock
));
3268 * Process the job...
3272 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3274 job
->state
= IPP_JSTATE_ABORTED
;
3275 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3284 * Return the job info...
3287 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3289 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3290 cupsArrayAdd(ra
, "job-id");
3291 cupsArrayAdd(ra
, "job-state");
3292 cupsArrayAdd(ra
, "job-state-reasons");
3293 cupsArrayAdd(ra
, "job-uri");
3295 copy_job_attributes(client
, job
, ra
);
3296 cupsArrayDelete(ra
);
3301 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3306 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3308 _ipp_job_t
*job
; /* Job information */
3309 ipp_attribute_t
*uri
; /* document-uri */
3310 char scheme
[256], /* URI scheme */
3311 userpass
[256], /* Username and password info */
3312 hostname
[256], /* Hostname */
3313 resource
[1024]; /* Resource path */
3314 int port
; /* Port number */
3315 http_uri_status_t uri_status
; /* URI decode status */
3316 http_encryption_t encryption
; /* Encryption to use, if any */
3317 http_t
*http
; /* Connection for http/https URIs */
3318 http_status_t status
; /* Access status for http/https URIs */
3319 int infile
; /* Input file for local file URIs */
3320 char filename
[1024], /* Filename buffer */
3321 buffer
[4096]; /* Copy buffer */
3322 ssize_t bytes
; /* Bytes read */
3323 ipp_attribute_t
*attr
; /* Current attribute */
3324 cups_array_t
*ra
; /* Attributes to send in response */
3325 static const char * const uri_status_strings
[] =
3326 { /* URI decode errors */
3328 "Bad arguments to function.",
3329 "Bad resource in URI.",
3330 "Bad port number in URI.",
3331 "Bad hostname in URI.",
3332 "Bad username in URI.",
3333 "Bad scheme in URI.",
3342 if ((job
= find_job(client
)) == NULL
)
3344 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3345 httpFlush(client
->http
);
3350 * See if we already have a document for this job or the job has already
3351 * in a non-pending state...
3354 if (job
->state
> IPP_JSTATE_HELD
)
3356 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3357 "Job is not in a pending state.");
3358 httpFlush(client
->http
);
3361 else if (job
->filename
|| job
->fd
>= 0)
3363 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3364 "Multiple document jobs are not supported.");
3365 httpFlush(client
->http
);
3369 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3370 IPP_TAG_ZERO
)) == NULL
)
3372 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3373 "Missing required last-document attribute.");
3374 httpFlush(client
->http
);
3377 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3378 !ippGetBoolean(attr
, 0))
3380 respond_unsupported(client
, attr
);
3381 httpFlush(client
->http
);
3386 * Validate document attributes...
3389 if (!valid_doc_attributes(client
))
3391 httpFlush(client
->http
);
3396 * Do we have a file to print?
3399 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3401 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3402 "Unexpected document data following request.");
3407 * Do we have a document URI?
3410 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3411 IPP_TAG_URI
)) == NULL
)
3413 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3417 if (ippGetCount(uri
) != 1)
3419 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3420 "Too many document-uri values.");
3424 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3425 scheme
, sizeof(scheme
), userpass
,
3426 sizeof(userpass
), hostname
, sizeof(hostname
),
3427 &port
, resource
, sizeof(resource
));
3428 if (uri_status
< HTTP_URI_STATUS_OK
)
3430 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3431 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3435 if (strcmp(scheme
, "file") &&
3437 strcmp(scheme
, "https") &&
3438 #endif /* HAVE_SSL */
3439 strcmp(scheme
, "http"))
3441 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3442 "URI scheme \"%s\" not supported.", scheme
);
3446 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3448 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3449 "Unable to access URI: %s", strerror(errno
));
3454 * Get the document format for the job...
3457 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3459 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3460 IPP_TAG_MIMETYPE
)) != NULL
)
3461 job
->format
= ippGetString(attr
, 0, NULL
);
3463 job
->format
= "application/octet-stream";
3466 * Create a file for the request data...
3469 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3470 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3471 client
->printer
->directory
, job
->id
);
3472 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3473 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3474 client
->printer
->directory
, job
->id
);
3475 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3476 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3477 client
->printer
->directory
, job
->id
);
3478 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3479 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3480 client
->printer
->directory
, job
->id
);
3482 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3483 client
->printer
->directory
, job
->id
);
3485 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3487 _cupsRWUnlock(&(client
->printer
->rwlock
));
3491 job
->state
= IPP_JSTATE_ABORTED
;
3493 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3494 "Unable to create print file: %s", strerror(errno
));
3498 if (!strcmp(scheme
, "file"))
3500 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3502 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3503 "Unable to access URI: %s", strerror(errno
));
3509 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3510 (errno
== EAGAIN
|| errno
== EINTR
))
3512 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3514 int error
= errno
; /* Write error */
3516 job
->state
= IPP_JSTATE_ABORTED
;
3524 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3525 "Unable to write print file: %s", strerror(error
));
3536 if (port
== 443 || !strcmp(scheme
, "https"))
3537 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3539 #endif /* HAVE_SSL */
3540 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3542 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3543 1, 30000, NULL
)) == NULL
)
3545 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3546 "Unable to connect to %s: %s", hostname
,
3547 cupsLastErrorString());
3548 job
->state
= IPP_JSTATE_ABORTED
;
3557 httpClearFields(http
);
3558 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3559 if (httpGet(http
, resource
))
3561 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3562 "Unable to GET URI: %s", strerror(errno
));
3564 job
->state
= IPP_JSTATE_ABORTED
;
3574 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3576 if (status
!= HTTP_STATUS_OK
)
3578 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3579 "Unable to GET URI: %s", httpStatus(status
));
3581 job
->state
= IPP_JSTATE_ABORTED
;
3591 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3593 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3595 int error
= errno
; /* Write error */
3597 job
->state
= IPP_JSTATE_ABORTED
;
3605 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3606 "Unable to write print file: %s", strerror(error
));
3616 int error
= errno
; /* Write error */
3618 job
->state
= IPP_JSTATE_ABORTED
;
3623 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3624 "Unable to write print file: %s", strerror(error
));
3628 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3631 job
->filename
= strdup(filename
);
3632 job
->state
= IPP_JSTATE_PENDING
;
3634 _cupsRWUnlock(&(client
->printer
->rwlock
));
3637 * Process the job...
3641 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3643 job
->state
= IPP_JSTATE_ABORTED
;
3644 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3653 * Return the job info...
3656 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3658 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3659 cupsArrayAdd(ra
, "job-id");
3660 cupsArrayAdd(ra
, "job-state");
3661 cupsArrayAdd(ra
, "job-state-reasons");
3662 cupsArrayAdd(ra
, "job-uri");
3664 copy_job_attributes(client
, job
, ra
);
3665 cupsArrayDelete(ra
);
3670 * 'ipp_validate_job()' - Validate job creation attributes.
3674 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
3676 if (valid_job_attributes(client
))
3677 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3682 * 'process_client()' - Process client requests on a thread.
3685 static void * /* O - Exit status */
3686 process_client(_ipp_client_t
*client
) /* I - Client */
3689 * Loop until we are out of requests or timeout (30 seconds)...
3692 while (httpWait(client
->http
, 30000))
3693 if (!process_http(client
))
3697 * Close the conection to the client and return...
3700 delete_client(client
);
3707 * 'process_http()' - Process a HTTP request.
3710 int /* O - 1 on success, 0 on failure */
3711 process_http(_ipp_client_t
*client
) /* I - Client connection */
3713 char uri
[1024]; /* URI */
3714 http_state_t http_state
; /* HTTP state */
3715 http_status_t http_status
; /* HTTP status */
3716 ipp_state_t ipp_state
; /* State of IPP transfer */
3717 char scheme
[32], /* Method/scheme */
3718 userpass
[128], /* Username:password */
3719 hostname
[HTTP_MAX_HOST
];
3721 int port
; /* Port number */
3722 const char *encoding
; /* Content-Encoding value */
3723 static const char * const http_states
[] =
3724 { /* Strings for logging HTTP method */
3745 * Clear state variables...
3748 ippDelete(client
->request
);
3749 ippDelete(client
->response
);
3751 client
->request
= NULL
;
3752 client
->response
= NULL
;
3753 client
->operation
= HTTP_STATE_WAITING
;
3756 * Read a request from the connection...
3759 while ((http_state
= httpReadRequest(client
->http
, uri
,
3760 sizeof(uri
))) == HTTP_STATE_WAITING
)
3764 * Parse the request line...
3767 if (http_state
== HTTP_STATE_ERROR
)
3769 if (httpError(client
->http
) == EPIPE
)
3770 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
3772 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
3773 strerror(httpError(client
->http
)));
3777 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
3779 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
3780 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3783 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
3785 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
3786 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3790 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
3794 * Separate the URI into its components...
3797 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
3798 userpass
, sizeof(userpass
),
3799 hostname
, sizeof(hostname
), &port
,
3800 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
)
3802 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
3803 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3808 * Process the request...
3811 client
->start
= time(NULL
);
3812 client
->operation
= httpGetState(client
->http
);
3815 * Parse incoming parameters until the status changes...
3818 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
3820 if (http_status
!= HTTP_STATUS_OK
)
3822 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3826 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
3827 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
3830 * HTTP/1.1 and higher require the "Host:" field...
3833 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3838 * Handle HTTP Upgrade...
3841 if (!_cups_strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
3844 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
3849 * Handle HTTP Expect...
3852 if (httpGetExpect(client
->http
) &&
3853 (client
->operation
== HTTP_STATE_POST
||
3854 client
->operation
== HTTP_STATE_PUT
))
3856 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
3859 * Send 100-continue header...
3862 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
3868 * Send 417-expectation-failed header...
3871 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
3877 * Handle new transfers...
3880 encoding
= httpGetContentEncoding(client
->http
);
3882 switch (client
->operation
)
3884 case HTTP_STATE_OPTIONS
:
3886 * Do HEAD/OPTIONS command...
3889 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
3891 case HTTP_STATE_HEAD
:
3892 if (!strcmp(client
->uri
, "/icon.png"))
3893 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
3894 else if (!strcmp(client
->uri
, "/"))
3895 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
3897 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
3899 case HTTP_STATE_GET
:
3900 if (!strcmp(client
->uri
, "/icon.png"))
3903 * Send PNG icon file.
3906 int fd
; /* Icon file */
3907 struct stat fileinfo
; /* Icon file information */
3908 char buffer
[4096]; /* Copy buffer */
3909 ssize_t bytes
; /* Bytes */
3911 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
3913 if (!stat(client
->printer
->icon
, &fileinfo
) &&
3914 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
3916 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
3917 (size_t)fileinfo
.st_size
))
3923 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
3924 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
3926 httpFlushWrite(client
->http
);
3931 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
3933 else if (!strcmp(client
->uri
, "/"))
3936 * Show web status page...
3939 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
3943 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
3944 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
3947 "<title>%s</title>\n"
3948 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
3949 "type=\"image/png\">\n"
3953 "<h1><img align=\"right\" src=\"/icon.png\">%s</h1>\n"
3954 "<p>%s, %d job(s).</p>\n"
3957 client
->printer
->name
, client
->printer
->name
,
3958 client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" :
3959 client
->printer
->state
== IPP_PSTATE_PROCESSING
?
3960 "Printing" : "Stopped",
3961 cupsArrayCount(client
->printer
->jobs
));
3962 httpWrite2(client
->http
, "", 0);
3967 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
3970 case HTTP_STATE_POST
:
3971 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
3975 * Not an IPP request...
3978 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
3982 * Read the IPP request...
3985 client
->request
= ippNew();
3987 while ((ipp_state
= ippRead(client
->http
,
3988 client
->request
)) != IPP_STATE_DATA
)
3990 if (ipp_state
== IPP_STATE_ERROR
)
3992 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
3993 cupsLastErrorString());
3994 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4000 * Now that we have the IPP request, process the request...
4003 return (process_ipp(client
));
4006 break; /* Anti-compiler-warning-code */
4014 * 'process_ipp()' - Process an IPP request.
4017 static int /* O - 1 on success, 0 on error */
4018 process_ipp(_ipp_client_t
*client
) /* I - Client */
4020 ipp_tag_t group
; /* Current group tag */
4021 ipp_attribute_t
*attr
; /* Current attribute */
4022 ipp_attribute_t
*charset
; /* Character set attribute */
4023 ipp_attribute_t
*language
; /* Language attribute */
4024 ipp_attribute_t
*uri
; /* Printer URI attribute */
4025 int major
, minor
; /* Version number */
4026 const char *name
; /* Name of attribute */
4029 debug_attributes("Request", client
->request
, 1);
4032 * First build an empty response message for this request...
4035 client
->operation_id
= ippGetOperation(client
->request
);
4036 client
->response
= ippNewResponse(client
->request
);
4039 * Then validate the request header and required attributes...
4042 major
= ippGetVersion(client
->request
, &minor
);
4044 if (major
< 1 || major
> 2)
4047 * Return an error, since we only support IPP 1.x and 2.x.
4050 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
4051 "Bad request version number %d.%d.", major
, minor
);
4053 else if (ippGetRequestId(client
->request
) <= 0)
4054 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
4055 ippGetRequestId(client
->request
));
4056 else if (!ippFirstAttribute(client
->request
))
4057 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4058 "No attributes in request.");
4062 * Make sure that the attributes are provided in the correct order and
4063 * don't repeat groups...
4066 for (attr
= ippFirstAttribute(client
->request
),
4067 group
= ippGetGroupTag(attr
);
4069 attr
= ippNextAttribute(client
->request
))
4071 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
4074 * Out of order; return an error...
4077 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4078 "Attribute groups are out of order (%x < %x).",
4079 ippGetGroupTag(attr
), group
);
4083 group
= ippGetGroupTag(attr
);
4089 * Then make sure that the first three attributes are:
4091 * attributes-charset
4092 * attributes-natural-language
4093 * printer-uri/job-uri
4096 attr
= ippFirstAttribute(client
->request
);
4097 name
= ippGetName(attr
);
4098 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
4099 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
4104 attr
= ippNextAttribute(client
->request
);
4105 name
= ippGetName(attr
);
4107 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
4108 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
4113 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
4114 IPP_TAG_URI
)) != NULL
)
4116 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
4117 IPP_TAG_URI
)) != NULL
)
4123 _cups_strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
4124 _cups_strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
4127 * Bad character set...
4130 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4131 "Unsupported character set \"%s\".",
4132 ippGetString(charset
, 0, NULL
));
4134 else if (!charset
|| !language
|| !uri
)
4137 * Return an error, since attributes-charset,
4138 * attributes-natural-language, and printer-uri/job-uri are required
4139 * for all operations.
4142 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4143 "Missing required attributes.");
4147 char scheme
[32], /* URI scheme */
4148 userpass
[32], /* Username/password in URI */
4149 host
[256], /* Host name in URI */
4150 resource
[256]; /* Resource path in URI */
4151 int port
; /* Port number in URI */
4153 name
= ippGetName(uri
);
4155 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4156 scheme
, sizeof(scheme
),
4157 userpass
, sizeof(userpass
),
4158 host
, sizeof(host
), &port
,
4159 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
4160 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4161 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
4162 else if ((!strcmp(name
, "job-uri") &&
4163 strncmp(resource
, "/ipp/print/", 11)) ||
4164 (!strcmp(name
, "printer-uri") &&
4165 strcmp(resource
, "/ipp/print")))
4166 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
4167 name
, ippGetString(uri
, 0, NULL
));
4171 * Try processing the operation...
4174 switch (ippGetOperation(client
->request
))
4176 case IPP_OP_PRINT_JOB
:
4177 ipp_print_job(client
);
4180 case IPP_OP_PRINT_URI
:
4181 ipp_print_uri(client
);
4184 case IPP_OP_VALIDATE_JOB
:
4185 ipp_validate_job(client
);
4188 case IPP_OP_CREATE_JOB
:
4189 ipp_create_job(client
);
4192 case IPP_OP_SEND_DOCUMENT
:
4193 ipp_send_document(client
);
4196 case IPP_OP_SEND_URI
:
4197 ipp_send_uri(client
);
4200 case IPP_OP_CANCEL_JOB
:
4201 ipp_cancel_job(client
);
4204 case IPP_OP_GET_JOB_ATTRIBUTES
:
4205 ipp_get_job_attributes(client
);
4208 case IPP_OP_GET_JOBS
:
4209 ipp_get_jobs(client
);
4212 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
4213 ipp_get_printer_attributes(client
);
4217 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
4218 "Operation not supported.");
4227 * Send the HTTP header and return...
4230 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
4231 httpFlush(client
->http
); /* Flush trailing (junk) data */
4233 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
4234 ippLength(client
->response
)));
4239 * 'process_job()' - Process a print job.
4242 static void * /* O - Thread exit status */
4243 process_job(_ipp_job_t
*job
) /* I - Job */
4245 job
->state
= IPP_JSTATE_PROCESSING
;
4246 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
4248 if (job
->printer
->command
)
4251 * Execute a command with the job spool file and wait for it to complete...
4254 int pid
, /* Process ID */
4255 status
; /* Exit status */
4256 time_t start
, /* Start time */
4259 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
4263 if ((pid
= fork()) == 0)
4266 * Child comes here...
4269 execlp(job
->printer
->command
, job
->printer
->command
, job
->filename
,
4276 * Unable to fork process...
4279 perror("Unable to start job processing command");
4284 * Wait for child to complete...
4288 while (waitpid(pid
, &status
, 0) < 0);
4290 while (wait(&status
) < 0);
4291 #endif /* HAVE_WAITPID */
4295 if (WIFEXITED(status
))
4296 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
4297 job
->printer
->command
, WEXITSTATUS(status
));
4299 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
4300 job
->printer
->command
, WTERMSIG(status
));
4303 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
4304 job
->printer
->command
);
4308 * Make sure processing takes at least 5 seconds...
4312 if ((end
- start
) < 5)
4318 * Sleep for a random amount of time to simulate job processing.
4321 sleep(5 + (rand() % 11));
4325 job
->state
= IPP_JSTATE_CANCELED
;
4327 job
->state
= IPP_JSTATE_COMPLETED
;
4329 job
->completed
= time(NULL
);
4330 job
->printer
->state
= IPP_PSTATE_IDLE
;
4331 job
->printer
->active_job
= NULL
;
4339 * 'register_printer()' - Register a printer object via Bonjour.
4342 static int /* O - 1 on success, 0 on error */
4344 _ipp_printer_t
*printer
, /* I - Printer */
4345 const char *location
, /* I - Location */
4346 const char *make
, /* I - Manufacturer */
4347 const char *model
, /* I - Model name */
4348 const char *formats
, /* I - Supported formats */
4349 const char *adminurl
, /* I - Web interface URL */
4350 int color
, /* I - 1 = color, 0 = monochrome */
4351 int duplex
, /* I - 1 = duplex, 0 = simplex */
4352 const char *subtype
) /* I - Service subtype */
4354 DNSServiceErrorType error
; /* Error from Bonjour */
4355 char make_model
[256],/* Make and model together */
4356 product
[256], /* Product string */
4357 regtype
[256]; /* Bonjour service type */
4361 * Build the TXT record for IPP...
4364 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4365 snprintf(product
, sizeof(product
), "(%s)", model
);
4367 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
4368 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 9, "ipp/print");
4369 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
4371 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
4374 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
4376 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
4378 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
4380 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
4381 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
4382 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
4384 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
4388 * Create a shared service reference for Bonjour...
4391 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
4392 != kDNSServiceErr_NoError
)
4394 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
4399 * Register the _printer._tcp (LPD) service type with a port number of 0 to
4400 * defend our service name but not actually support LPD...
4403 printer
->printer_ref
= printer
->common_ref
;
4405 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
4406 kDNSServiceFlagsShareConnection
,
4407 0 /* interfaceIndex */, printer
->dnssd_name
,
4408 "_printer._tcp", NULL
/* domain */,
4409 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
4410 NULL
/* txtRecord */,
4411 (DNSServiceRegisterReply
)dnssd_callback
,
4412 printer
)) != kDNSServiceErr_NoError
)
4414 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
4415 printer
->dnssd_name
, error
);
4420 * Then register the _ipp._tcp (IPP) service type with the real port number to
4421 * advertise our IPP printer...
4424 printer
->ipp_ref
= printer
->common_ref
;
4426 if (subtype
&& *subtype
)
4427 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
4429 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
4431 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
4432 kDNSServiceFlagsShareConnection
,
4433 0 /* interfaceIndex */, printer
->dnssd_name
,
4434 regtype
, NULL
/* domain */,
4435 NULL
/* host */, htons(printer
->port
),
4436 TXTRecordGetLength(&(printer
->ipp_txt
)),
4437 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4438 (DNSServiceRegisterReply
)dnssd_callback
,
4439 printer
)) != kDNSServiceErr_NoError
)
4441 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4442 printer
->dnssd_name
, regtype
, error
);
4446 # if 0 /* ifdef HAVE_SSL */
4448 * Then register the _ipps._tcp (IPP) service type with the real port number to
4449 * advertise our IPP printer...
4452 printer
->ipps_ref
= printer
->common_ref
;
4454 if (subtype
&& *subtype
)
4455 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
4457 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
4459 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
4460 kDNSServiceFlagsShareConnection
,
4461 0 /* interfaceIndex */, printer
->dnssd_name
,
4462 regtype
, NULL
/* domain */,
4463 NULL
/* host */, htons(printer
->port
),
4464 TXTRecordGetLength(&(printer
->ipp_txt
)),
4465 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4466 (DNSServiceRegisterReply
)dnssd_callback
,
4467 printer
)) != kDNSServiceErr_NoError
)
4469 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4470 printer
->dnssd_name
, regtype
, error
);
4473 # endif /* HAVE_SSL */
4476 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4477 * real port number to advertise our IPP printer...
4480 printer
->http_ref
= printer
->common_ref
;
4482 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
4483 kDNSServiceFlagsShareConnection
,
4484 0 /* interfaceIndex */, printer
->dnssd_name
,
4485 "_http._tcp,_printer", NULL
/* domain */,
4486 NULL
/* host */, htons(printer
->port
),
4487 0 /* txtLen */, NULL
, /* txtRecord */
4488 (DNSServiceRegisterReply
)dnssd_callback
,
4489 printer
)) != kDNSServiceErr_NoError
)
4491 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4492 printer
->dnssd_name
, regtype
, error
);
4498 #endif /* HAVE_DNSSD */
4502 * 'respond_http()' - Send a HTTP response.
4505 int /* O - 1 on success, 0 on failure */
4507 _ipp_client_t
*client
, /* I - Client */
4508 http_status_t code
, /* I - HTTP status of response */
4509 const char *content_encoding
, /* I - Content-Encoding of response */
4510 const char *type
, /* I - MIME media type of response */
4511 size_t length
) /* I - Length of response */
4513 char message
[1024]; /* Text message */
4516 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
4518 if (code
== HTTP_STATUS_CONTINUE
)
4521 * 100-continue doesn't send any headers...
4524 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
4528 * Format an error message...
4531 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
)
4533 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
4535 type
= "text/plain";
4536 length
= strlen(message
);
4542 * Send the HTTP response header...
4545 httpClearFields(client
->http
);
4547 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
4548 client
->operation
== HTTP_STATE_OPTIONS
)
4549 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
4553 if (!strcmp(type
, "text/html"))
4554 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
4555 "text/html; charset=utf-8");
4557 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
4559 if (content_encoding
)
4560 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
4563 httpSetLength(client
->http
, length
);
4565 if (httpWriteResponse(client
->http
, code
) < 0)
4569 * Send the response data...
4575 * Send a plain text message.
4578 if (httpPrintf(client
->http
, "%s", message
) < 0)
4581 if (httpWrite2(client
->http
, "", 0) < 0)
4584 else if (client
->response
)
4587 * Send an IPP response...
4590 debug_attributes("Response", client
->response
, 2);
4592 ippSetState(client
->response
, IPP_STATE_IDLE
);
4594 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
4603 * 'respond_ipp()' - Send an IPP response.
4607 respond_ipp(_ipp_client_t
*client
, /* I - Client */
4608 ipp_status_t status
, /* I - status-code */
4609 const char *message
, /* I - printf-style status-message */
4610 ...) /* I - Additional args as needed */
4612 const char *formatted
= NULL
; /* Formatted message */
4615 ippSetStatusCode(client
->response
, status
);
4619 va_list ap
; /* Pointer to additional args */
4620 ipp_attribute_t
*attr
; /* New status-message attribute */
4622 va_start(ap
, message
);
4623 if ((attr
= ippFindAttribute(client
->response
, "status-message",
4624 IPP_TAG_TEXT
)) != NULL
)
4625 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
4627 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
4628 "status-message", NULL
, message
, ap
);
4631 formatted
= ippGetString(attr
, 0, NULL
);
4635 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
4636 ippOpString(client
->operation_id
), ippErrorString(status
),
4639 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
4640 ippOpString(client
->operation_id
), ippErrorString(status
));
4645 * 'respond_unsupported()' - Respond with an unsupported attribute.
4649 respond_unsupported(
4650 _ipp_client_t
*client
, /* I - Client */
4651 ipp_attribute_t
*attr
) /* I - Atribute */
4653 ipp_attribute_t
*temp
; /* Copy of attribute */
4656 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4657 "Unsupported %s %s%s value.", ippGetName(attr
),
4658 ippGetCount(attr
) > 1 ? "1setOf " : "",
4659 ippTagString(ippGetValueTag(attr
)));
4661 temp
= ippCopyAttribute(client
->response
, attr
, 0);
4662 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
4667 * 'run_printer()' - Run the printer service.
4671 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
4673 int num_fds
; /* Number of file descriptors */
4674 struct pollfd polldata
[3]; /* poll() data */
4675 int timeout
; /* Timeout for poll() */
4676 _ipp_client_t
*client
; /* New client */
4680 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
4683 polldata
[0].fd
= printer
->ipv4
;
4684 polldata
[0].events
= POLLIN
;
4686 polldata
[1].fd
= printer
->ipv6
;
4687 polldata
[1].events
= POLLIN
;
4692 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
4693 polldata
[num_fds
++].events
= POLLIN
;
4694 #endif /* HAVE_DNSSD */
4697 * Loop until we are killed or have a hard error...
4702 if (cupsArrayCount(printer
->jobs
))
4707 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
4709 perror("poll() failed");
4713 if (polldata
[0].revents
& POLLIN
)
4715 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
4717 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4719 perror("Unable to create client thread");
4720 delete_client(client
);
4725 if (polldata
[1].revents
& POLLIN
)
4727 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
4729 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4731 perror("Unable to create client thread");
4732 delete_client(client
);
4738 if (polldata
[2].revents
& POLLIN
)
4739 DNSServiceProcessResult(printer
->common_ref
);
4740 #endif /* HAVE_DNSSD */
4743 * Clean out old jobs...
4746 clean_jobs(printer
);
4752 * 'usage()' - Show program usage.
4756 usage(int status
) /* O - Exit status */
4760 puts(CUPS_SVERSION
" - Copyright 2010-2013 by Apple Inc. All rights "
4765 puts("Usage: ippserver [options] \"name\"");
4768 puts("-2 Supports 2-sided printing (default=1-sided)");
4769 puts("-M manufacturer Manufacturer name (default=Test)");
4770 puts("-P PIN printing mode");
4771 puts("-c command Run command for every print job");
4772 printf("-d spool-directory Spool directory "
4773 "(default=/tmp/ippserver.%d)\n", (int)getpid());
4774 puts("-f type/subtype[,...] List of supported types "
4775 "(default=application/pdf,image/jpeg)");
4776 puts("-h Show program help");
4777 puts("-i iconfile.png PNG icon file (default=printer.png)");
4778 puts("-k Keep job spool files");
4779 puts("-l location Location of printer (default=empty string)");
4780 puts("-m model Model name (default=Printer)");
4781 puts("-n hostname Hostname for printer");
4782 puts("-p port Port number (default=auto)");
4784 puts("-r subtype Bonjour service subtype (default=_print)");
4785 #endif /* HAVE_DNSSD */
4786 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
4787 puts("-v[vvv] Be (very) verbose");
4794 * 'valid_doc_attributes()' - Determine whether the document attributes are
4797 * When one or more document attributes are invalid, this function adds a
4798 * suitable response and attributes to the unsupported group.
4801 static int /* O - 1 if valid, 0 if not */
4802 valid_doc_attributes(
4803 _ipp_client_t
*client
) /* I - Client */
4805 int valid
= 1; /* Valid attributes? */
4806 ipp_op_t op
= ippGetOperation(client
->request
);
4808 const char *op_name
= ippOpString(op
);
4809 /* IPP operation name */
4810 ipp_attribute_t
*attr
, /* Current attribute */
4811 *supported
; /* xxx-supported attribute */
4812 const char *compression
= NULL
,
4813 /* compression value */
4814 *format
= NULL
; /* document-format value */
4818 * Check operation attributes...
4821 if ((attr
= ippFindAttribute(client
->request
, "compression",
4822 IPP_TAG_ZERO
)) != NULL
)
4825 * If compression is specified, only accept a supported value in a Print-Job
4826 * or Send-Document request...
4829 compression
= ippGetString(attr
, 0, NULL
);
4830 supported
= ippFindAttribute(client
->printer
->attrs
,
4831 "compression-supported", IPP_TAG_KEYWORD
);
4833 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
4834 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
4835 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
4836 op
!= IPP_OP_VALIDATE_JOB
) ||
4837 !ippContainsString(supported
, compression
))
4839 respond_unsupported(client
, attr
);
4844 fprintf(stderr
, "%s %s compression=\"%s\"\n",
4845 client
->hostname
, op_name
, compression
);
4847 if (strcmp(compression
, "none"))
4848 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
4853 * Is it a format we support?
4856 if ((attr
= ippFindAttribute(client
->request
, "document-format",
4857 IPP_TAG_ZERO
)) != NULL
)
4859 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
4860 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
4862 respond_unsupported(client
, attr
);
4867 format
= ippGetString(attr
, 0, NULL
);
4869 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
4870 client
->hostname
, op_name
, format
);
4875 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
,
4876 "document-format-default",
4877 IPP_TAG_MIMETYPE
), 0, NULL
);
4879 format
= "application/octet-stream"; /* Should never happen */
4881 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
4882 "document-format", NULL
, format
);
4885 if (!strcmp(format
, "application/octet-stream") &&
4886 (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
||
4887 ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
4890 * Auto-type the file using the first 4 bytes of the file...
4893 unsigned char header
[4]; /* First 4 bytes of file */
4895 memset(header
, 0, sizeof(header
));
4896 httpPeek(client
->http
, (char *)header
, sizeof(header
));
4898 if (!memcmp(header
, "%PDF", 4))
4899 format
= "application/pdf";
4900 else if (!memcmp(header
, "%!", 2))
4901 format
= "application/postscript";
4902 else if (!memcmp(header
, "\377\330\377", 3) &&
4903 header
[3] >= 0xe0 && header
[3] <= 0xef)
4904 format
= "image/jpeg";
4905 else if (!memcmp(header
, "\211PNG", 4))
4906 format
= "image/png";
4909 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
4910 client
->hostname
, op_name
, format
);
4913 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
4914 "document-format", NULL
, format
);
4916 ippSetString(client
->request
, &attr
, 0, format
);
4919 if (op
!= IPP_OP_CREATE_JOB
&&
4920 (supported
= ippFindAttribute(client
->printer
->attrs
,
4921 "document-format-supported",
4922 IPP_TAG_MIMETYPE
)) != NULL
&&
4923 !ippContainsString(supported
, format
))
4925 respond_unsupported(client
, attr
);
4934 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
4936 * When one or more job attributes are invalid, this function adds a suitable
4937 * response and attributes to the unsupported group.
4940 static int /* O - 1 if valid, 0 if not */
4941 valid_job_attributes(
4942 _ipp_client_t
*client
) /* I - Client */
4944 int i
, /* Looping var */
4945 valid
= 1; /* Valid attributes? */
4946 ipp_attribute_t
*attr
, /* Current attribute */
4947 *supported
; /* xxx-supported attribute */
4951 * Check operation attributes...
4954 valid
= valid_doc_attributes(client
);
4957 * Check the various job template attributes...
4960 if ((attr
= ippFindAttribute(client
->request
, "copies",
4961 IPP_TAG_ZERO
)) != NULL
)
4963 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
4964 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
4966 respond_unsupported(client
, attr
);
4971 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity",
4972 IPP_TAG_ZERO
)) != NULL
)
4974 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
4976 respond_unsupported(client
, attr
);
4981 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until",
4982 IPP_TAG_ZERO
)) != NULL
)
4984 if (ippGetCount(attr
) != 1 ||
4985 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
4986 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
4987 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
4988 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
4990 respond_unsupported(client
, attr
);
4995 if ((attr
= ippFindAttribute(client
->request
, "job-name",
4996 IPP_TAG_ZERO
)) != NULL
)
4998 if (ippGetCount(attr
) != 1 ||
4999 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5000 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
5002 respond_unsupported(client
, attr
);
5007 if ((attr
= ippFindAttribute(client
->request
, "job-priority",
5008 IPP_TAG_ZERO
)) != NULL
)
5010 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5011 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
5013 respond_unsupported(client
, attr
);
5018 if ((attr
= ippFindAttribute(client
->request
, "job-sheets",
5019 IPP_TAG_ZERO
)) != NULL
)
5021 if (ippGetCount(attr
) != 1 ||
5022 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5023 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5024 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5025 strcmp(ippGetString(attr
, 0, NULL
), "none"))
5027 respond_unsupported(client
, attr
);
5032 if ((attr
= ippFindAttribute(client
->request
, "media",
5033 IPP_TAG_ZERO
)) != NULL
)
5035 if (ippGetCount(attr
) != 1 ||
5036 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5037 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5038 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
5040 respond_unsupported(client
, attr
);
5046 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
5048 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
5051 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
5053 respond_unsupported(client
, attr
);
5059 if ((attr
= ippFindAttribute(client
->request
, "media-col",
5060 IPP_TAG_ZERO
)) != NULL
)
5062 if (ippGetCount(attr
) != 1 ||
5063 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
5065 respond_unsupported(client
, attr
);
5068 /* TODO: check for valid media-col */
5071 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling",
5072 IPP_TAG_ZERO
)) != NULL
)
5074 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5075 (strcmp(ippGetString(attr
, 0, NULL
),
5076 "separate-documents-uncollated-copies") &&
5077 strcmp(ippGetString(attr
, 0, NULL
),
5078 "separate-documents-collated-copies")))
5080 respond_unsupported(client
, attr
);
5085 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested",
5086 IPP_TAG_ZERO
)) != NULL
)
5088 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5089 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
5090 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
5092 respond_unsupported(client
, attr
);
5097 if ((attr
= ippFindAttribute(client
->request
, "page-ranges",
5098 IPP_TAG_ZERO
)) != NULL
)
5100 respond_unsupported(client
, attr
);
5104 if ((attr
= ippFindAttribute(client
->request
, "print-quality",
5105 IPP_TAG_ZERO
)) != NULL
)
5107 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5108 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
5109 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
5111 respond_unsupported(client
, attr
);
5116 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution",
5117 IPP_TAG_ZERO
)) != NULL
)
5119 respond_unsupported(client
, attr
);
5123 if ((attr
= ippFindAttribute(client
->request
, "sides",
5124 IPP_TAG_ZERO
)) != NULL
)
5126 const char *sides
= NULL
; /* "sides" value... */
5128 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
5130 respond_unsupported(client
, attr
);
5134 sides
= ippGetString(attr
, 0, NULL
);
5136 if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported",
5137 IPP_TAG_KEYWORD
)) != NULL
)
5139 if (!ippContainsString(supported
, sides
))
5141 respond_unsupported(client
, attr
);
5145 else if (strcmp(sides
, "one-sided"))
5147 respond_unsupported(client
, attr
);