2 * "$Id: ippserver.c 11986 2014-07-02 15:52:01Z msweet $"
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 *keypath
= NULL
; /* Keychain path */
326 #endif /* HAVE_SSL */
328 const char *subtype
= "_print"; /* Bonjour service subtype */
329 #endif /* HAVE_DNSSD */
330 int port
= 8631, /* Port number (0 = auto) */
331 duplex
= 0, /* Duplex mode */
332 ppm
= 10, /* Pages per minute for mono */
333 ppm_color
= 0, /* Pages per minute for color */
334 pin
= 0; /* PIN printing mode? */
335 char directory
[1024] = ""; /* Spool directory */
336 _ipp_printer_t
*printer
; /* Printer object */
340 * Parse command-line arguments...
343 for (i
= 1; i
< argc
; i
++)
344 if (argv
[i
][0] == '-')
346 for (opt
= argv
[i
] + 1; *opt
; opt
++)
350 case '2' : /* -2 (enable 2-sided printing) */
355 case 'K' : /* -K keypath */
361 #endif /* HAVE_SSL */
363 case 'M' : /* -M manufacturer */
370 case 'P' : /* -P (PIN printing mode) */
374 case 'c' : /* -c command */
382 case 'd' : /* -d spool-directory */
386 strlcpy(directory
, argv
[i
], sizeof(directory
));
389 case 'f' : /* -f type/subtype[,...] */
396 case 'h' : /* -h (show help) */
399 case 'i' : /* -i icon.png */
406 case 'k' : /* -k (keep files) */
410 case 'l' : /* -l location */
417 case 'm' : /* -m model */
424 case 'n' : /* -n hostname */
428 servername
= argv
[i
];
431 case 'p' : /* -p port */
433 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
435 port
= atoi(argv
[i
]);
439 case 'r' : /* -r subtype */
445 #endif /* HAVE_DNSSD */
447 case 's' : /* -s speed[,color-speed] */
451 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
455 case 'v' : /* -v (be verbose) */
459 default : /* Unknown */
460 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
471 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
479 * Apply defaults as needed...
484 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
486 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
488 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
489 directory
, strerror(errno
));
494 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
498 cupsSetServerCredentials(keypath
, servername
, 1);
499 #endif /* HAVE_SSL */
502 * Create the printer...
505 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
506 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
509 #endif /* HAVE_DNSSD */
510 directory
, command
)) == NULL
)
514 * Run the print service...
517 run_printer(printer
);
520 * Destroy the printer and exit...
523 delete_printer(printer
);
530 * 'clean_jobs()' - Clean out old (completed) jobs.
534 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
536 _ipp_job_t
*job
; /* Current job */
537 time_t cleantime
; /* Clean time */
540 if (cupsArrayCount(printer
->jobs
) == 0)
543 cleantime
= time(NULL
) - 60;
545 _cupsRWLockWrite(&(printer
->rwlock
));
546 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
548 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
549 if (job
->completed
&& job
->completed
< cleantime
)
551 cupsArrayRemove(printer
->jobs
, job
);
556 _cupsRWUnlock(&(printer
->rwlock
));
561 * 'compare_jobs()' - Compare two jobs.
564 static int /* O - Result of comparison */
565 compare_jobs(_ipp_job_t
*a
, /* I - First job */
566 _ipp_job_t
*b
) /* I - Second job */
568 return (b
->id
- a
->id
);
573 * 'copy_attributes()' - Copy attributes from one request to another.
577 copy_attributes(ipp_t
*to
, /* I - Destination request */
578 ipp_t
*from
, /* I - Source request */
579 cups_array_t
*ra
, /* I - Requested attributes */
580 ipp_tag_t group_tag
, /* I - Group to copy */
581 int quickcopy
) /* I - Do a quick copy? */
583 ipp_attribute_t
*fromattr
; /* Source attribute */
589 for (fromattr
= ippFirstAttribute(from
);
591 fromattr
= ippNextAttribute(from
))
594 * Filter attributes as needed...
597 ipp_tag_t fromgroup
= ippGetGroupTag(fromattr
);
598 const char *fromname
= ippGetName(fromattr
);
600 if ((group_tag
!= IPP_TAG_ZERO
&& fromgroup
!= group_tag
&&
601 fromgroup
!= IPP_TAG_ZERO
) || !fromname
)
604 if (!ra
|| cupsArrayFind(ra
, (void *)fromname
))
605 ippCopyAttribute(to
, fromattr
, quickcopy
);
611 * 'copy_job_attrs()' - Copy job attributes to the response.
616 _ipp_client_t
*client
, /* I - Client */
617 _ipp_job_t
*job
, /* I - Job */
618 cups_array_t
*ra
) /* I - requested-attributes */
620 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
622 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
623 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
624 "job-printer-up-time", (int)time(NULL
));
626 if (!ra
|| cupsArrayFind(ra
, "job-state"))
627 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
628 "job-state", job
->state
);
630 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
634 case IPP_JSTATE_PENDING
:
635 ippAddString(client
->response
, IPP_TAG_JOB
,
636 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
640 case IPP_JSTATE_HELD
:
642 ippAddString(client
->response
, IPP_TAG_JOB
,
643 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
644 "job-state-reasons", NULL
, "job-incoming");
645 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
646 ippAddString(client
->response
, IPP_TAG_JOB
,
647 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
648 "job-state-reasons", NULL
, "job-hold-until-specified");
650 ippAddString(client
->response
, IPP_TAG_JOB
,
651 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
652 "job-state-reasons", NULL
, "job-data-insufficient");
655 case IPP_JSTATE_PROCESSING
:
657 ippAddString(client
->response
, IPP_TAG_JOB
,
658 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
659 "job-state-reasons", NULL
, "processing-to-stop-point");
661 ippAddString(client
->response
, IPP_TAG_JOB
,
662 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
663 "job-state-reasons", NULL
, "job-printing");
666 case IPP_JSTATE_STOPPED
:
667 ippAddString(client
->response
, IPP_TAG_JOB
,
668 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
669 NULL
, "job-stopped");
672 case IPP_JSTATE_CANCELED
:
673 ippAddString(client
->response
, IPP_TAG_JOB
,
674 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
675 NULL
, "job-canceled-by-user");
678 case IPP_JSTATE_ABORTED
:
679 ippAddString(client
->response
, IPP_TAG_JOB
,
680 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
681 NULL
, "aborted-by-system");
684 case IPP_JSTATE_COMPLETED
:
685 ippAddString(client
->response
, IPP_TAG_JOB
,
686 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
687 NULL
, "job-completed-successfully");
692 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
693 ippAddInteger(client
->response
, IPP_TAG_JOB
,
694 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
695 "time-at-completed", (int)job
->completed
);
697 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
698 ippAddInteger(client
->response
, IPP_TAG_JOB
,
699 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
700 "time-at-processing", (int)job
->processing
);
705 * 'create_client()' - Accept a new network connection and create a client
709 static _ipp_client_t
* /* O - Client */
710 create_client(_ipp_printer_t
*printer
, /* I - Printer */
711 int sock
) /* I - Listen socket */
713 _ipp_client_t
*client
; /* Client */
716 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
718 perror("Unable to allocate memory for client");
722 client
->printer
= printer
;
725 * Accept the client and get the remote address...
728 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
730 perror("Unable to accept client connection");
737 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
740 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
747 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
751 static _ipp_job_t
* /* O - Job */
752 create_job(_ipp_client_t
*client
) /* I - Client */
754 _ipp_job_t
*job
; /* Job */
755 ipp_attribute_t
*attr
; /* Job attribute */
756 char uri
[1024]; /* job-uri value */
759 _cupsRWLockWrite(&(client
->printer
->rwlock
));
760 if (client
->printer
->active_job
&&
761 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
764 * Only accept a single job at a time...
767 _cupsRWLockWrite(&(client
->printer
->rwlock
));
772 * Allocate and initialize the job object...
775 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
777 perror("Unable to allocate memory for job");
781 job
->printer
= client
->printer
;
782 job
->attrs
= client
->request
;
783 job
->state
= IPP_JSTATE_HELD
;
785 client
->request
= NULL
;
788 * Set all but the first two attributes to the job attributes group...
791 for (ippFirstAttribute(job
->attrs
),
792 ippNextAttribute(job
->attrs
),
793 attr
= ippNextAttribute(job
->attrs
);
795 attr
= ippNextAttribute(job
->attrs
))
796 ippSetGroupTag(job
->attrs
, &attr
, IPP_TAG_JOB
);
799 * Get the requesting-user-name, document format, and priority...
802 if ((attr
= ippFindAttribute(job
->attrs
, "requesting-user-name",
803 IPP_TAG_NAME
)) != NULL
)
804 ippSetName(job
->attrs
, &attr
, "job-originating-user-name");
806 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
,
807 IPP_CONST_TAG(IPP_TAG_NAME
),
808 "job-originating-user-name", NULL
, "anonymous");
811 job
->username
= ippGetString(attr
, 0, NULL
);
813 job
->username
= "anonymous";
815 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
816 IPP_TAG_MIMETYPE
)) != NULL
)
817 job
->format
= ippGetString(attr
, 0, NULL
);
819 job
->format
= "application/octet-stream";
822 * Add job description attributes and add to the jobs array...
825 job
->id
= client
->printer
->next_job_id
++;
827 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
829 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
830 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
831 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
832 client
->printer
->uri
);
833 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
836 cupsArrayAdd(client
->printer
->jobs
, job
);
837 client
->printer
->active_job
= job
;
839 _cupsRWUnlock(&(client
->printer
->rwlock
));
846 * 'create_listener()' - Create a listener socket.
849 static int /* O - Listener socket or -1 on error */
850 create_listener(int family
, /* I - Address family */
851 int *port
) /* IO - Port number */
853 int sock
; /* Listener socket */
854 http_addrlist_t
*addrlist
; /* Listen address */
855 char service
[255]; /* Service port */
860 *port
= 8000 + (getuid() % 1000);
861 fprintf(stderr
, "Listening on port %d.\n", *port
);
864 snprintf(service
, sizeof(service
), "%d", *port
);
865 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
868 sock
= httpAddrListen(&(addrlist
->addr
), *port
);
870 httpAddrFreeList(addrlist
);
877 * 'create_media_col()' - Create a media-col value.
880 static ipp_t
* /* O - media-col collection */
881 create_media_col(const char *media
, /* I - Media name */
882 const char *type
, /* I - Nedua type */
883 int width
, /* I - x-dimension in 2540ths */
884 int length
, /* I - y-dimension in 2540ths */
885 int margins
) /* I - Value for margins */
887 ipp_t
*media_col
= ippNew(), /* media-col value */
888 *media_size
= create_media_size(width
, length
);
889 /* media-size value */
890 char media_key
[256]; /* media-key value */
893 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, type
,
894 margins
== 0 ? "_borderless" : "");
896 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
898 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
899 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
900 "media-bottom-margin", margins
);
901 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
902 "media-left-margin", margins
);
903 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
904 "media-right-margin", margins
);
905 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
906 "media-top-margin", margins
);
907 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type",
910 ippDelete(media_size
);
917 * 'create_media_size()' - Create a media-size value.
920 static ipp_t
* /* O - media-col collection */
921 create_media_size(int width
, /* I - x-dimension in 2540ths */
922 int length
) /* I - y-dimension in 2540ths */
924 ipp_t
*media_size
= ippNew(); /* media-size value */
927 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
929 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
937 * 'create_printer()' - Create, register, and listen for connections to a
941 static _ipp_printer_t
* /* O - Printer */
942 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
943 const char *name
, /* I - printer-name */
944 const char *location
, /* I - printer-location */
945 const char *make
, /* I - printer-make-and-model */
946 const char *model
, /* I - printer-make-and-model */
947 const char *icon
, /* I - printer-icons */
948 const char *docformats
, /* I - document-format-supported */
949 int ppm
, /* I - Pages per minute in grayscale */
950 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
951 int duplex
, /* I - 1 = duplex, 0 = simplex */
952 int port
, /* I - Port for listeners or 0 for auto */
953 int pin
, /* I - Require PIN printing */
955 const char *subtype
, /* I - Bonjour service subtype */
956 #endif /* HAVE_DNSSD */
957 const char *directory
, /* I - Spool directory */
958 const char *command
) /* I - Command to run on job files */
960 int i
, j
; /* Looping vars */
961 _ipp_printer_t
*printer
; /* Printer */
962 char hostname
[256], /* Hostname */
963 uri
[1024], /* Printer URI */
964 icons
[1024], /* printer-icons URI */
965 adminurl
[1024], /* printer-more-info URI */
966 device_id
[1024],/* printer-device-id */
967 make_model
[128];/* printer-make-and-model */
968 int num_formats
; /* Number of document-format-supported values */
969 char *defformat
, /* document-format-default value */
970 *formats
[100], /* document-format-supported values */
971 *ptr
; /* Pointer into string */
972 const char *prefix
; /* Prefix string */
973 int num_database
; /* Number of database values */
974 ipp_attribute_t
*media_col_database
,
975 /* media-col-database value */
976 *media_size_supported
;
977 /* media-size-supported value */
978 ipp_t
*media_col_default
;
979 /* media-col-default value */
980 int media_col_index
;/* Current media-col-database value */
981 int k_supported
; /* Maximum file size supported */
983 struct statvfs spoolinfo
; /* FS info for spool directory */
984 double spoolsize
; /* FS size */
985 #elif defined(HAVE_STATFS)
986 struct statfs spoolinfo
; /* FS info for spool directory */
987 double spoolsize
; /* FS size */
988 #endif /* HAVE_STATVFS */
989 static const int orients
[4] = /* orientation-requested-supported values */
992 IPP_ORIENT_LANDSCAPE
,
993 IPP_ORIENT_REVERSE_LANDSCAPE
,
994 IPP_ORIENT_REVERSE_PORTRAIT
996 static const char * const versions
[] =/* ipp-versions-supported values */
1002 static const int ops
[] = /* operations-supported values */
1006 IPP_OP_VALIDATE_JOB
,
1008 IPP_OP_SEND_DOCUMENT
,
1011 IPP_OP_GET_JOB_ATTRIBUTES
,
1013 IPP_OP_GET_PRINTER_ATTRIBUTES
1015 static const char * const charsets
[] =/* charset-supported values */
1020 static const char * const compressions
[] =/* compression-supported values */
1025 #endif /* HAVE_LIBZ */
1028 static const char * const job_creation
[] =
1029 { /* job-creation-attributes-supported values */
1031 "ipp-attribute-fidelity",
1033 "job-accounting-user-id",
1039 "multiple-document-handling",
1040 "orientation-requested",
1044 static const char * const media_col_supported
[] =
1045 { /* media-col-supported values */
1046 "media-bottom-margin",
1047 "media-left-margin",
1048 "media-right-margin",
1053 static const int media_xxx_margin_supported
[] =
1054 { /* media-xxx-margin-supported values */
1058 static const char * const multiple_document_handling
[] =
1059 { /* multiple-document-handling-supported values */
1060 "separate-documents-uncollated-copies",
1061 "separate-documents-collated-copies"
1063 static const int print_quality_supported
[] =
1064 { /* print-quality-supported values */
1069 static const int pwg_raster_document_resolution_supported
[] =
1075 static const char * const pwg_raster_document_type_supported
[] =
1083 static const char * const reference_uri_schemes_supported
[] =
1084 { /* reference-uri-schemes-supported */
1090 #endif /* HAVE_SSL */
1092 static const char * const sides_supported
[] =
1093 { /* sides-supported values */
1095 "two-sided-long-edge",
1096 "two-sided-short-edge"
1098 static const char * const which_jobs
[] =
1099 { /* which-jobs-supported values */
1108 "processing-stopped"
1113 * Allocate memory for the printer...
1116 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1118 perror("Unable to allocate memory for printer");
1124 printer
->name
= strdup(name
);
1126 printer
->dnssd_name
= strdup(printer
->name
);
1127 #endif /* HAVE_DNSSD */
1128 printer
->command
= command
? strdup(command
) : NULL
;
1129 printer
->directory
= strdup(directory
);
1130 printer
->hostname
= strdup(servername
? servername
:
1131 httpGetHostname(NULL
, hostname
,
1133 printer
->port
= port
;
1134 printer
->state
= IPP_PSTATE_IDLE
;
1135 printer
->state_reasons
= _IPP_PSTATE_NONE
;
1136 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1137 printer
->next_job_id
= 1;
1139 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1140 printer
->hostname
, printer
->port
, "/ipp/print");
1141 printer
->uri
= strdup(uri
);
1142 printer
->urilen
= strlen(uri
);
1145 printer
->icon
= strdup(icon
);
1147 _cupsRWInit(&(printer
->rwlock
));
1150 * Create the listener sockets...
1153 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1155 perror("Unable to create IPv4 listener");
1159 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1161 perror("Unable to create IPv6 listener");
1166 * Prepare values for the printer attributes...
1169 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1170 printer
->hostname
, printer
->port
, "/icon.png");
1171 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
,
1172 printer
->hostname
, printer
->port
, "/");
1176 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1177 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1180 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1183 formats
[0] = strdup(docformats
);
1184 defformat
= formats
[0];
1185 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1188 formats
[num_formats
++] = ptr
;
1190 if (!_cups_strcasecmp(ptr
, "application/octet-stream"))
1194 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1195 ptr
= device_id
+ strlen(device_id
);
1197 for (i
= 0; i
< num_formats
; i
++)
1199 if (!_cups_strcasecmp(formats
[i
], "application/pdf"))
1200 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1201 else if (!_cups_strcasecmp(formats
[i
], "application/postscript"))
1202 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1203 else if (!_cups_strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1204 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1205 else if (!_cups_strcasecmp(formats
[i
], "image/jpeg"))
1206 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1207 else if (!_cups_strcasecmp(formats
[i
], "image/png"))
1208 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1209 else if (_cups_strcasecmp(formats
[i
], "application/octet-stream"))
1210 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1215 strlcat(device_id
, ";", sizeof(device_id
));
1218 * Get the maximum spool size based on the size of the filesystem used for
1219 * the spool directory. If the host OS doesn't support the statfs call
1220 * or the filesystem is larger than 2TiB, always report INT_MAX.
1224 if (statvfs(printer
->directory
, &spoolinfo
))
1225 k_supported
= INT_MAX
;
1226 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1227 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1228 k_supported
= INT_MAX
;
1230 k_supported
= (int)spoolsize
;
1232 #elif defined(HAVE_STATFS)
1233 if (statfs(printer
->directory
, &spoolinfo
))
1234 k_supported
= INT_MAX
;
1235 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1236 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1237 k_supported
= INT_MAX
;
1239 k_supported
= (int)spoolsize
;
1242 k_supported
= INT_MAX
;
1243 #endif /* HAVE_STATVFS */
1246 * Create the printer attributes. This list of attributes is sorted to improve
1247 * performance when the client provides a requested-attributes attribute...
1250 printer
->attrs
= ippNew();
1252 /* charset-configured */
1253 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1254 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1255 "charset-configured", NULL
, "utf-8");
1257 /* charset-supported */
1258 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1259 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1260 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1263 /* color-supported */
1264 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1267 /* compression-supported */
1268 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1269 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1270 "compression-supported",
1271 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1274 /* copies-default */
1275 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1276 "copies-default", 1);
1278 /* copies-supported */
1279 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1281 /* document-format-default */
1282 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1283 "document-format-default", NULL
, defformat
);
1285 /* document-format-supported */
1286 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1287 "document-format-supported", num_formats
, NULL
,
1288 (const char * const *)formats
);
1290 /* finishings-default */
1291 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1292 "finishings-default", IPP_FINISHINGS_NONE
);
1294 /* finishings-supported */
1295 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1296 "finishings-supported", IPP_FINISHINGS_NONE
);
1298 /* generated-natural-language-supported */
1299 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1300 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1301 "generated-natural-language-supported", NULL
, "en");
1303 /* ipp-versions-supported */
1304 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1305 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1306 "ipp-versions-supported",
1307 sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1309 /* job-account-id-supported */
1310 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1312 /* job-accounting-user-id-supported */
1313 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1314 "job-accounting-user-id-supported", 1);
1316 /* job-creation-attributes-supported */
1317 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1318 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1319 "job-creation-attributes-supported",
1320 sizeof(job_creation
) / sizeof(job_creation
[0]),
1321 NULL
, job_creation
);
1323 /* job-k-octets-supported */
1324 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1327 /* job-password-supported */
1328 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1329 "job-password-supported", 4);
1331 /* job-priority-default */
1332 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1333 "job-priority-default", 50);
1335 /* job-priority-supported */
1336 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1337 "job-priority-supported", 100);
1339 /* job-sheets-default */
1340 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1341 IPP_CONST_TAG(IPP_TAG_NAME
),
1342 "job-sheets-default", NULL
, "none");
1344 /* job-sheets-supported */
1345 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1346 IPP_CONST_TAG(IPP_TAG_NAME
),
1347 "job-sheets-supported", NULL
, "none");
1349 /* media-bottom-margin-supported */
1350 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1351 "media-bottom-margin-supported",
1352 (int)(sizeof(media_xxx_margin_supported
) /
1353 sizeof(media_xxx_margin_supported
[0])),
1354 media_xxx_margin_supported
);
1356 /* media-col-database */
1357 for (num_database
= 0, i
= 0;
1358 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1361 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1362 num_database
+= 2; /* auto + envelope */
1363 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1364 num_database
+= 12; /* auto + photographic-* + borderless */
1366 num_database
+= (int)(sizeof(media_type_supported
) /
1367 sizeof(media_type_supported
[0])) + 6;
1368 /* All types + borderless */
1371 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1372 "media-col-database", num_database
,
1374 for (media_col_index
= 0, i
= 0;
1375 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1379 j
< (int)(sizeof(media_type_supported
) /
1380 sizeof(media_type_supported
[0]));
1383 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
&&
1384 strcmp(media_type_supported
[j
], "auto") &&
1385 strcmp(media_type_supported
[j
], "envelope"))
1387 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
&&
1388 strcmp(media_type_supported
[j
], "auto") &&
1389 strncmp(media_type_supported
[j
], "photographic-", 13))
1392 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1393 create_media_col(media_supported
[i
],
1394 media_type_supported
[j
],
1395 media_col_sizes
[i
][0],
1396 media_col_sizes
[i
][1],
1397 media_xxx_margin_supported
[1]));
1400 if (media_col_sizes
[i
][2] != _IPP_ENV_ONLY
&&
1401 (!strcmp(media_type_supported
[j
], "auto") ||
1402 !strncmp(media_type_supported
[j
], "photographic-", 13)))
1405 * Add borderless version for this combination...
1408 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1409 create_media_col(media_supported
[i
],
1410 media_type_supported
[j
],
1411 media_col_sizes
[i
][0],
1412 media_col_sizes
[i
][1],
1413 media_xxx_margin_supported
[0]));
1419 /* media-col-default */
1420 media_col_default
= create_media_col(media_supported
[0],
1421 media_type_supported
[0],
1422 media_col_sizes
[0][0],
1423 media_col_sizes
[0][1],
1424 media_xxx_margin_supported
[1]);
1426 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1428 ippDelete(media_col_default
);
1430 /* media-col-supported */
1431 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1432 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1433 "media-col-supported",
1434 (int)(sizeof(media_col_supported
) /
1435 sizeof(media_col_supported
[0])), NULL
,
1436 media_col_supported
);
1439 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1440 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1441 "media-default", NULL
, media_supported
[0]);
1443 /* media-left-margin-supported */
1444 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1445 "media-left-margin-supported",
1446 (int)(sizeof(media_xxx_margin_supported
) /
1447 sizeof(media_xxx_margin_supported
[0])),
1448 media_xxx_margin_supported
);
1450 /* media-right-margin-supported */
1451 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1452 "media-right-margin-supported",
1453 (int)(sizeof(media_xxx_margin_supported
) /
1454 sizeof(media_xxx_margin_supported
[0])),
1455 media_xxx_margin_supported
);
1457 /* media-supported */
1458 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1459 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1461 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1462 NULL
, media_supported
);
1464 /* media-size-supported */
1465 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1466 "media-size-supported",
1467 (int)(sizeof(media_col_sizes
) /
1468 sizeof(media_col_sizes
[0])),
1471 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1473 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1474 create_media_size(media_col_sizes
[i
][0],
1475 media_col_sizes
[i
][1]));
1477 /* media-top-margin-supported */
1478 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1479 "media-top-margin-supported",
1480 (int)(sizeof(media_xxx_margin_supported
) /
1481 sizeof(media_xxx_margin_supported
[0])),
1482 media_xxx_margin_supported
);
1484 /* media-type-supported */
1485 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1486 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1487 "media-type-supported",
1488 (int)(sizeof(media_type_supported
) /
1489 sizeof(media_type_supported
[0])),
1490 NULL
, media_type_supported
);
1492 /* multiple-document-handling-supported */
1493 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1494 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1495 "multiple-document-handling-supported",
1496 sizeof(multiple_document_handling
) /
1497 sizeof(multiple_document_handling
[0]), NULL
,
1498 multiple_document_handling
);
1500 /* multiple-document-jobs-supported */
1501 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1502 "multiple-document-jobs-supported", 0);
1504 /* natural-language-configured */
1505 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1506 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1507 "natural-language-configured", NULL
, "en");
1509 /* number-up-default */
1510 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1511 "number-up-default", 1);
1513 /* number-up-supported */
1514 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1515 "number-up-supported", 1);
1517 /* operations-supported */
1518 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1519 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1521 /* orientation-requested-default */
1522 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1523 "orientation-requested-default", 0);
1525 /* orientation-requested-supported */
1526 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1527 "orientation-requested-supported", 4, orients
);
1529 /* output-bin-default */
1530 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1531 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1532 "output-bin-default", NULL
, "face-down");
1534 /* output-bin-supported */
1535 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1536 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1537 "output-bin-supported", NULL
, "face-down");
1539 /* pages-per-minute */
1540 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1541 "pages-per-minute", ppm
);
1543 /* pages-per-minute-color */
1545 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1546 "pages-per-minute-color", ppm_color
);
1548 /* pdl-override-supported */
1549 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1550 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1551 "pdl-override-supported", NULL
, "attempted");
1553 /* print-quality-default */
1554 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1555 "print-quality-default", IPP_QUALITY_NORMAL
);
1557 /* print-quality-supported */
1558 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1559 "print-quality-supported",
1560 (int)(sizeof(print_quality_supported
) /
1561 sizeof(print_quality_supported
[0])),
1562 print_quality_supported
);
1564 /* printer-device-id */
1565 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1566 "printer-device-id", NULL
, device_id
);
1569 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1570 "printer-icons", NULL
, icons
);
1572 /* printer-is-accepting-jobs */
1573 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1577 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1580 /* printer-location */
1581 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1582 "printer-location", NULL
, location
);
1584 /* printer-make-and-model */
1585 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1586 "printer-make-and-model", NULL
, make_model
);
1588 /* printer-mandatory-job-attributes */
1591 static const char * const names
[] =
1593 "job-accounting-user-id",
1597 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1598 "printer-mandatory-job-attributes",
1599 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1602 /* printer-more-info */
1603 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1604 "printer-more-info", NULL
, adminurl
);
1607 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1610 /* printer-resolution-default */
1611 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1612 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1614 /* printer-resolution-supported */
1615 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1616 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1618 /* printer-uri-supported */
1619 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1620 "printer-uri-supported", NULL
, uri
);
1622 /* pwg-raster-document-xxx-supported */
1623 for (i
= 0; i
< num_formats
; i
++)
1624 if (!_cups_strcasecmp(formats
[i
], "image/pwg-raster"))
1627 if (i
< num_formats
)
1629 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
1630 "pwg-raster-document-resolution-supported",
1631 (int)(sizeof(pwg_raster_document_resolution_supported
) /
1632 sizeof(pwg_raster_document_resolution_supported
[0])),
1634 pwg_raster_document_resolution_supported
,
1635 pwg_raster_document_resolution_supported
);
1636 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1637 "pwg-raster-document-sheet-back", NULL
, "normal");
1638 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1639 "pwg-raster-document-type-supported",
1640 (int)(sizeof(pwg_raster_document_type_supported
) /
1641 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
1642 pwg_raster_document_type_supported
);
1645 /* reference-uri-scheme-supported */
1646 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1647 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
1648 "reference-uri-schemes-supported",
1649 (int)(sizeof(reference_uri_schemes_supported
) /
1650 sizeof(reference_uri_schemes_supported
[0])),
1651 NULL
, reference_uri_schemes_supported
);
1654 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1655 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1656 "sides-default", NULL
, "one-sided");
1658 /* sides-supported */
1659 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1660 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1661 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1663 /* uri-authentication-supported */
1664 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1665 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1666 "uri-authentication-supported", NULL
, "none");
1668 /* uri-security-supported */
1669 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1670 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1671 "uri-security-supported", NULL
, "none");
1673 /* which-jobs-supported */
1674 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1675 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1676 "which-jobs-supported",
1677 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1681 debug_attributes("Printer", printer
->attrs
, 0);
1685 * Register the printer with Bonjour...
1688 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
,
1689 ppm_color
> 0, duplex
, subtype
))
1691 #endif /* HAVE_DNSSD */
1701 * If we get here we were unable to create the printer...
1706 delete_printer(printer
);
1712 * 'debug_attributes()' - Print attributes in a request or response.
1716 debug_attributes(const char *title
, /* I - Title */
1717 ipp_t
*ipp
, /* I - Request/response */
1718 int type
) /* I - 0 = object, 1 = request, 2 = response */
1720 ipp_tag_t group_tag
; /* Current group */
1721 ipp_attribute_t
*attr
; /* Current attribute */
1722 char buffer
[2048]; /* String buffer for value */
1723 int major
, minor
; /* Version */
1729 fprintf(stderr
, "%s:\n", title
);
1730 major
= ippGetVersion(ipp
, &minor
);
1731 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1733 fprintf(stderr
, " operation-id=%s(%04x)\n",
1734 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1736 fprintf(stderr
, " status-code=%s(%04x)\n",
1737 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1738 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1740 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1742 attr
= ippNextAttribute(ipp
))
1744 if (ippGetGroupTag(attr
) != group_tag
)
1746 group_tag
= ippGetGroupTag(attr
);
1747 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1750 if (ippGetName(attr
))
1752 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1753 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1754 ippGetCount(attr
) > 1 ? "1setOf " : "",
1755 ippTagString(ippGetValueTag(attr
)), buffer
);
1762 * 'delete_client()' - Close the socket and free all memory used by a client
1767 delete_client(_ipp_client_t
*client
) /* I - Client */
1770 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1773 * Flush pending writes before closing...
1776 httpFlushWrite(client
->http
);
1782 httpClose(client
->http
);
1784 ippDelete(client
->request
);
1785 ippDelete(client
->response
);
1792 * 'delete_job()' - Remove from the printer and free all memory used by a job
1797 delete_job(_ipp_job_t
*job
) /* I - Job */
1800 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
1802 ippDelete(job
->attrs
);
1807 unlink(job
->filename
);
1809 free(job
->filename
);
1817 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1818 * used by a printer object.
1822 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
1824 if (printer
->ipv4
>= 0)
1825 close(printer
->ipv4
);
1827 if (printer
->ipv6
>= 0)
1828 close(printer
->ipv6
);
1831 if (printer
->printer_ref
)
1832 DNSServiceRefDeallocate(printer
->printer_ref
);
1834 if (printer
->ipp_ref
)
1835 DNSServiceRefDeallocate(printer
->ipp_ref
);
1838 if (printer
->ipps_ref
)
1839 DNSServiceRefDeallocate(printer
->ipps_ref
);
1840 # endif /* HAVE_SSL */
1841 if (printer
->http_ref
)
1842 DNSServiceRefDeallocate(printer
->http_ref
);
1844 if (printer
->common_ref
)
1845 DNSServiceRefDeallocate(printer
->common_ref
);
1847 TXTRecordDeallocate(&(printer
->ipp_txt
));
1849 if (printer
->dnssd_name
)
1850 free(printer
->dnssd_name
);
1851 #endif /* HAVE_DNSSD */
1854 free(printer
->name
);
1856 free(printer
->icon
);
1857 if (printer
->command
)
1858 free(printer
->command
);
1859 if (printer
->directory
)
1860 free(printer
->directory
);
1861 if (printer
->hostname
)
1862 free(printer
->hostname
);
1866 ippDelete(printer
->attrs
);
1867 cupsArrayDelete(printer
->jobs
);
1875 * 'dnssd_callback()' - Handle Bonjour registration events.
1880 DNSServiceRef sdRef
, /* I - Service reference */
1881 DNSServiceFlags flags
, /* I - Status flags */
1882 DNSServiceErrorType errorCode
, /* I - Error, if any */
1883 const char *name
, /* I - Service name */
1884 const char *regtype
, /* I - Service type */
1885 const char *domain
, /* I - Domain for service */
1886 _ipp_printer_t
*printer
) /* I - Printer */
1894 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
1895 regtype
, (int)errorCode
);
1898 else if (_cups_strcasecmp(name
, printer
->dnssd_name
))
1901 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
1903 /* No lock needed since only the main thread accesses/changes this */
1904 free(printer
->dnssd_name
);
1905 printer
->dnssd_name
= strdup(name
);
1908 #endif /* HAVE_DNSSD */
1912 * 'find_job()' - Find a job specified in a request.
1915 static _ipp_job_t
* /* O - Job or NULL */
1916 find_job(_ipp_client_t
*client
) /* I - Client */
1918 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
1919 _ipp_job_t key
, /* Job search key */
1920 *job
; /* Matching job, if any */
1925 if ((attr
= ippFindAttribute(client
->request
, "job-uri",
1926 IPP_TAG_URI
)) != NULL
)
1928 const char *uri
= ippGetString(attr
, 0, NULL
);
1930 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
1931 uri
[client
->printer
->urilen
] == '/')
1932 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
1934 else if ((attr
= ippFindAttribute(client
->request
, "job-id",
1935 IPP_TAG_INTEGER
)) != NULL
)
1936 key
.id
= ippGetInteger(attr
, 0);
1938 _cupsRWLockRead(&(client
->printer
->rwlock
));
1939 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
1940 _cupsRWUnlock(&(client
->printer
->rwlock
));
1947 * 'html_escape()' - Write a HTML-safe string.
1951 html_escape(_ipp_client_t
*client
, /* I - Client */
1952 const char *s
, /* I - String to write */
1953 size_t slen
) /* I - Number of characters to write */
1955 const char *start
, /* Start of segment */
1956 *end
; /* End of string */
1960 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
1962 while (*s
&& s
< end
)
1964 if (*s
== '&' || *s
== '<')
1967 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
1970 httpWrite2(client
->http
, "&", 5);
1972 httpWrite2(client
->http
, "<", 4);
1981 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
1986 * 'html_printf()' - Send formatted text to the client, quoting as needed.
1990 html_printf(_ipp_client_t
*client
, /* I - Client */
1991 const char *format
, /* I - Printf-style format string */
1992 ...) /* I - Additional arguments as needed */
1994 va_list ap
; /* Pointer to arguments */
1995 const char *start
; /* Start of string */
1996 char size
, /* Size character (h, l, L) */
1997 type
; /* Format type character */
1998 int width
, /* Width of field */
1999 prec
; /* Number of characters of precision */
2000 char tformat
[100], /* Temporary format string for sprintf() */
2001 *tptr
, /* Pointer into temporary format */
2002 temp
[1024]; /* Buffer for formatted numbers */
2003 char *s
; /* Pointer to string */
2007 * Loop through the format string, formatting as needed...
2010 va_start(ap
, format
);
2018 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2021 *tptr
++ = *format
++;
2025 httpWrite2(client
->http
, "%", 1);
2029 else if (strchr(" -+#\'", *format
))
2030 *tptr
++ = *format
++;
2035 * Get width from argument...
2039 width
= va_arg(ap
, int);
2041 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2042 tptr
+= strlen(tptr
);
2048 while (isdigit(*format
& 255))
2050 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2053 width
= width
* 10 + *format
++ - '0';
2059 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2067 * Get precision from argument...
2071 prec
= va_arg(ap
, int);
2073 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2074 tptr
+= strlen(tptr
);
2080 while (isdigit(*format
& 255))
2082 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2085 prec
= prec
* 10 + *format
++ - '0';
2090 if (*format
== 'l' && format
[1] == 'l')
2094 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2102 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2104 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2119 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2128 case 'E' : /* Floating point formats */
2133 if ((size_t)(width
+ 2) > sizeof(temp
))
2136 sprintf(temp
, tformat
, va_arg(ap
, double));
2138 httpWrite2(client
->http
, temp
, strlen(temp
));
2141 case 'B' : /* Integer formats */
2149 if ((size_t)(width
+ 2) > sizeof(temp
))
2152 # ifdef HAVE_LONG_LONG
2154 sprintf(temp
, tformat
, va_arg(ap
, long long));
2156 # endif /* HAVE_LONG_LONG */
2158 sprintf(temp
, tformat
, va_arg(ap
, long));
2160 sprintf(temp
, tformat
, va_arg(ap
, int));
2162 httpWrite2(client
->http
, temp
, strlen(temp
));
2165 case 'p' : /* Pointer value */
2166 if ((size_t)(width
+ 2) > sizeof(temp
))
2169 sprintf(temp
, tformat
, va_arg(ap
, void *));
2171 httpWrite2(client
->http
, temp
, strlen(temp
));
2174 case 'c' : /* Character or character array */
2177 temp
[0] = (char)va_arg(ap
, int);
2179 html_escape(client
, temp
, 1);
2182 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2185 case 's' : /* String */
2186 if ((s
= va_arg(ap
, char *)) == NULL
)
2189 html_escape(client
, s
, strlen(s
));
2198 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2205 * 'ipp_cancel_job()' - Cancel a job.
2209 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2211 _ipp_job_t
*job
; /* Job information */
2218 if ((job
= find_job(client
)) == NULL
)
2220 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2225 * See if the job is already completed, canceled, or aborted; if so,
2226 * we can't cancel...
2231 case IPP_JSTATE_CANCELED
:
2232 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2233 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2236 case IPP_JSTATE_ABORTED
:
2237 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2238 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2241 case IPP_JSTATE_COMPLETED
:
2242 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2243 "Job #%d is already completed - can\'t cancel.", job
->id
);
2251 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2253 if (job
->state
== IPP_JSTATE_PROCESSING
||
2254 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2258 job
->state
= IPP_JSTATE_CANCELED
;
2259 job
->completed
= time(NULL
);
2262 _cupsRWUnlock(&(client
->printer
->rwlock
));
2264 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2271 * 'ipp_create_job()' - Create a job object.
2275 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2277 _ipp_job_t
*job
; /* New job */
2278 cups_array_t
*ra
; /* Attributes to send in response */
2282 * Validate print job attributes...
2285 if (!valid_job_attributes(client
))
2287 httpFlush(client
->http
);
2292 * Do we have a file to print?
2295 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2297 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2298 "Unexpected document data following request.");
2306 if ((job
= create_job(client
)) == NULL
)
2308 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2309 "Currently printing another job.");
2314 * Return the job info...
2317 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2319 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2320 cupsArrayAdd(ra
, "job-id");
2321 cupsArrayAdd(ra
, "job-state");
2322 cupsArrayAdd(ra
, "job-state-reasons");
2323 cupsArrayAdd(ra
, "job-uri");
2325 copy_job_attributes(client
, job
, ra
);
2326 cupsArrayDelete(ra
);
2331 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2335 ipp_get_job_attributes(
2336 _ipp_client_t
*client
) /* I - Client */
2338 _ipp_job_t
*job
; /* Job */
2339 cups_array_t
*ra
; /* requested-attributes */
2342 if ((job
= find_job(client
)) == NULL
)
2344 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2348 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2350 ra
= ippCreateRequestedArray(client
->request
);
2351 copy_job_attributes(client
, job
, ra
);
2352 cupsArrayDelete(ra
);
2357 * 'ipp_get_jobs()' - Get a list of job objects.
2361 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2363 ipp_attribute_t
*attr
; /* Current attribute */
2364 const char *which_jobs
= NULL
;
2365 /* which-jobs values */
2366 int job_comparison
; /* Job comparison */
2367 ipp_jstate_t job_state
; /* job-state value */
2368 int first_job_id
, /* First job ID */
2369 limit
, /* Maximum number of jobs to return */
2370 count
; /* Number of jobs that match */
2371 const char *username
; /* Username */
2372 _ipp_job_t
*job
; /* Current job pointer */
2373 cups_array_t
*ra
; /* Requested attributes array */
2377 * See if the "which-jobs" attribute have been specified...
2380 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2381 IPP_TAG_KEYWORD
)) != NULL
)
2383 which_jobs
= ippGetString(attr
, 0, NULL
);
2384 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2387 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2389 job_comparison
= -1;
2390 job_state
= IPP_JSTATE_STOPPED
;
2392 else if (!strcmp(which_jobs
, "completed"))
2395 job_state
= IPP_JSTATE_CANCELED
;
2397 else if (!strcmp(which_jobs
, "aborted"))
2400 job_state
= IPP_JSTATE_ABORTED
;
2402 else if (!strcmp(which_jobs
, "all"))
2405 job_state
= IPP_JSTATE_PENDING
;
2407 else if (!strcmp(which_jobs
, "canceled"))
2410 job_state
= IPP_JSTATE_CANCELED
;
2412 else if (!strcmp(which_jobs
, "pending"))
2415 job_state
= IPP_JSTATE_PENDING
;
2417 else if (!strcmp(which_jobs
, "pending-held"))
2420 job_state
= IPP_JSTATE_HELD
;
2422 else if (!strcmp(which_jobs
, "processing"))
2425 job_state
= IPP_JSTATE_PROCESSING
;
2427 else if (!strcmp(which_jobs
, "processing-stopped"))
2430 job_state
= IPP_JSTATE_STOPPED
;
2434 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2435 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2436 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2437 "which-jobs", NULL
, which_jobs
);
2442 * See if they want to limit the number of jobs reported...
2445 if ((attr
= ippFindAttribute(client
->request
, "limit",
2446 IPP_TAG_INTEGER
)) != NULL
)
2448 limit
= ippGetInteger(attr
, 0);
2450 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2455 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2456 IPP_TAG_INTEGER
)) != NULL
)
2458 first_job_id
= ippGetInteger(attr
, 0);
2460 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2467 * See if we only want to see jobs for a specific user...
2472 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2473 IPP_TAG_BOOLEAN
)) != NULL
)
2475 int my_jobs
= ippGetBoolean(attr
, 0);
2477 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2478 my_jobs
? "true" : "false");
2482 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2483 IPP_TAG_NAME
)) == NULL
)
2485 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2486 "Need requesting-user-name with my-jobs.");
2490 username
= ippGetString(attr
, 0, NULL
);
2492 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2493 client
->hostname
, username
);
2498 * OK, build a list of jobs for this printer...
2501 ra
= ippCreateRequestedArray(client
->request
);
2503 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2505 _cupsRWLockRead(&(client
->printer
->rwlock
));
2507 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2508 (limit
<= 0 || count
< limit
) && job
;
2509 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2512 * Filter out jobs that don't match...
2515 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2516 (job_comparison
== 0 && job
->state
!= job_state
) ||
2517 (job_comparison
> 0 && job
->state
< job_state
) ||
2518 job
->id
< first_job_id
||
2519 (username
&& job
->username
&&
2520 _cups_strcasecmp(username
, job
->username
)))
2524 ippAddSeparator(client
->response
);
2527 copy_job_attributes(client
, job
, ra
);
2530 cupsArrayDelete(ra
);
2532 _cupsRWUnlock(&(client
->printer
->rwlock
));
2537 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2541 ipp_get_printer_attributes(
2542 _ipp_client_t
*client
) /* I - Client */
2544 cups_array_t
*ra
; /* Requested attributes array */
2545 _ipp_printer_t
*printer
; /* Printer */
2549 * Send the attributes...
2552 ra
= ippCreateRequestedArray(client
->request
);
2553 printer
= client
->printer
;
2555 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2557 _cupsRWLockRead(&(printer
->rwlock
));
2559 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2560 IPP_TAG_CUPS_CONST
);
2562 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
2563 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2564 "printer-state", printer
->state
);
2566 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
2568 if (printer
->state_reasons
== _IPP_PSTATE_NONE
)
2569 ippAddString(client
->response
, IPP_TAG_PRINTER
,
2570 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2571 "printer-state-reasons", NULL
, "none");
2574 int num_reasons
= 0;/* Number of reasons */
2575 const char *reasons
[32]; /* Reason strings */
2577 if (printer
->state_reasons
& _IPP_PSTATE_OTHER
)
2578 reasons
[num_reasons
++] = "other";
2579 if (printer
->state_reasons
& _IPP_PSTATE_COVER_OPEN
)
2580 reasons
[num_reasons
++] = "cover-open";
2581 if (printer
->state_reasons
& _IPP_PSTATE_INPUT_TRAY_MISSING
)
2582 reasons
[num_reasons
++] = "input-tray-missing";
2583 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_EMPTY
)
2584 reasons
[num_reasons
++] = "marker-supply-empty-warning";
2585 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_LOW
)
2586 reasons
[num_reasons
++] = "marker-supply-low-report";
2587 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL
)
2588 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
2589 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_FULL
)
2590 reasons
[num_reasons
++] = "marker-waste-full-warning";
2591 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_EMPTY
)
2592 reasons
[num_reasons
++] = "media-empty-warning";
2593 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_JAM
)
2594 reasons
[num_reasons
++] = "media-jam-warning";
2595 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_LOW
)
2596 reasons
[num_reasons
++] = "media-low-report";
2597 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_NEEDED
)
2598 reasons
[num_reasons
++] = "media-needed-report";
2599 if (printer
->state_reasons
& _IPP_PSTATE_MOVING_TO_PAUSED
)
2600 reasons
[num_reasons
++] = "moving-to-paused";
2601 if (printer
->state_reasons
& _IPP_PSTATE_PAUSED
)
2602 reasons
[num_reasons
++] = "paused";
2603 if (printer
->state_reasons
& _IPP_PSTATE_SPOOL_AREA_FULL
)
2604 reasons
[num_reasons
++] = "spool-area-full";
2605 if (printer
->state_reasons
& _IPP_PSTATE_TONER_EMPTY
)
2606 reasons
[num_reasons
++] = "toner-empty-warning";
2607 if (printer
->state_reasons
& _IPP_PSTATE_TONER_LOW
)
2608 reasons
[num_reasons
++] = "toner-low-report";
2610 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
2611 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2612 "printer-state-reasons", num_reasons
, NULL
, reasons
);
2616 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
2617 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2618 "printer-up-time", (int)time(NULL
));
2620 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
2621 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2623 printer
->active_job
&&
2624 printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
2626 _cupsRWUnlock(&(printer
->rwlock
));
2628 cupsArrayDelete(ra
);
2633 * 'ipp_print_job()' - Create a job object with an attached document.
2637 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
2639 _ipp_job_t
*job
; /* New job */
2640 char filename
[1024], /* Filename buffer */
2641 buffer
[4096]; /* Copy buffer */
2642 ssize_t bytes
; /* Bytes read */
2643 cups_array_t
*ra
; /* Attributes to send in response */
2647 * Validate print job attributes...
2650 if (!valid_job_attributes(client
))
2652 httpFlush(client
->http
);
2657 * Do we have a file to print?
2660 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
2662 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
2670 if ((job
= create_job(client
)) == NULL
)
2672 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2673 "Currently printing another job.");
2678 * Create a file for the request data...
2681 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
2682 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2683 client
->printer
->directory
, job
->id
);
2684 else if (!_cups_strcasecmp(job
->format
, "image/png"))
2685 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2686 client
->printer
->directory
, job
->id
);
2687 else if (!_cups_strcasecmp(job
->format
, "image/pwg-raster"))
2688 snprintf(filename
, sizeof(filename
), "%s/%d.ras",
2689 client
->printer
->directory
, job
->id
);
2690 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
2691 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2692 client
->printer
->directory
, job
->id
);
2693 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
2694 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2695 client
->printer
->directory
, job
->id
);
2697 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2698 client
->printer
->directory
, job
->id
);
2701 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
2703 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2705 job
->state
= IPP_JSTATE_ABORTED
;
2707 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2708 "Unable to create print file: %s", strerror(errno
));
2712 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2714 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2716 int error
= errno
; /* Write error */
2718 job
->state
= IPP_JSTATE_ABORTED
;
2725 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2726 "Unable to write print file: %s", strerror(error
));
2734 * Got an error while reading the print data, so abort this job.
2737 job
->state
= IPP_JSTATE_ABORTED
;
2744 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2745 "Unable to read print file.");
2751 int error
= errno
; /* Write error */
2753 job
->state
= IPP_JSTATE_ABORTED
;
2758 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2759 "Unable to write print file: %s", strerror(error
));
2764 job
->filename
= strdup(filename
);
2765 job
->state
= IPP_JSTATE_PENDING
;
2768 * Process the job...
2772 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
2774 job
->state
= IPP_JSTATE_ABORTED
;
2775 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
2784 * Return the job info...
2787 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2789 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2790 cupsArrayAdd(ra
, "job-id");
2791 cupsArrayAdd(ra
, "job-state");
2792 cupsArrayAdd(ra
, "job-state-reasons");
2793 cupsArrayAdd(ra
, "job-uri");
2795 copy_job_attributes(client
, job
, ra
);
2796 cupsArrayDelete(ra
);
2801 * 'ipp_print_uri()' - Create a job object with a referenced document.
2805 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
2807 _ipp_job_t
*job
; /* New job */
2808 ipp_attribute_t
*uri
; /* document-uri */
2809 char scheme
[256], /* URI scheme */
2810 userpass
[256], /* Username and password info */
2811 hostname
[256], /* Hostname */
2812 resource
[1024]; /* Resource path */
2813 int port
; /* Port number */
2814 http_uri_status_t uri_status
; /* URI decode status */
2815 http_encryption_t encryption
; /* Encryption to use, if any */
2816 http_t
*http
; /* Connection for http/https URIs */
2817 http_status_t status
; /* Access status for http/https URIs */
2818 int infile
; /* Input file for local file URIs */
2819 char filename
[1024], /* Filename buffer */
2820 buffer
[4096]; /* Copy buffer */
2821 ssize_t bytes
; /* Bytes read */
2822 cups_array_t
*ra
; /* Attributes to send in response */
2823 static const char * const uri_status_strings
[] =
2824 { /* URI decode errors */
2826 "Bad arguments to function.",
2827 "Bad resource in URI.",
2828 "Bad port number in URI.",
2829 "Bad hostname in URI.",
2830 "Bad username in URI.",
2831 "Bad scheme in URI.",
2837 * Validate print job attributes...
2840 if (!valid_job_attributes(client
))
2842 httpFlush(client
->http
);
2847 * Do we have a file to print?
2850 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2852 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2853 "Unexpected document data following request.");
2858 * Do we have a document URI?
2861 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
2862 IPP_TAG_URI
)) == NULL
)
2864 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
2868 if (ippGetCount(uri
) != 1)
2870 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2871 "Too many document-uri values.");
2875 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
2876 scheme
, sizeof(scheme
), userpass
,
2877 sizeof(userpass
), hostname
, sizeof(hostname
),
2878 &port
, resource
, sizeof(resource
));
2879 if (uri_status
< HTTP_URI_STATUS_OK
)
2881 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
2882 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
2886 if (strcmp(scheme
, "file") &&
2888 strcmp(scheme
, "https") &&
2889 #endif /* HAVE_SSL */
2890 strcmp(scheme
, "http"))
2892 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
2893 "URI scheme \"%s\" not supported.", scheme
);
2897 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
2899 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
2900 "Unable to access URI: %s", strerror(errno
));
2908 if ((job
= create_job(client
)) == NULL
)
2910 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2911 "Currently printing another job.");
2916 * Create a file for the request data...
2919 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
2920 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2921 client
->printer
->directory
, job
->id
);
2922 else if (!_cups_strcasecmp(job
->format
, "image/png"))
2923 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2924 client
->printer
->directory
, job
->id
);
2925 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
2926 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2927 client
->printer
->directory
, job
->id
);
2928 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
2929 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2930 client
->printer
->directory
, job
->id
);
2932 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2933 client
->printer
->directory
, job
->id
);
2935 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2937 job
->state
= IPP_JSTATE_ABORTED
;
2939 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2940 "Unable to create print file: %s", strerror(errno
));
2944 if (!strcmp(scheme
, "file"))
2946 if ((infile
= open(resource
, O_RDONLY
)) < 0)
2948 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
2949 "Unable to access URI: %s", strerror(errno
));
2955 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
2956 (errno
== EAGAIN
|| errno
== EINTR
))
2958 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2960 int error
= errno
; /* Write error */
2962 job
->state
= IPP_JSTATE_ABORTED
;
2970 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2971 "Unable to write print file: %s", strerror(error
));
2982 if (port
== 443 || !strcmp(scheme
, "https"))
2983 encryption
= HTTP_ENCRYPTION_ALWAYS
;
2985 #endif /* HAVE_SSL */
2986 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
2988 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
2989 1, 30000, NULL
)) == NULL
)
2991 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
2992 "Unable to connect to %s: %s", hostname
,
2993 cupsLastErrorString());
2994 job
->state
= IPP_JSTATE_ABORTED
;
3003 httpClearFields(http
);
3004 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3005 if (httpGet(http
, resource
))
3007 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3008 "Unable to GET URI: %s", strerror(errno
));
3010 job
->state
= IPP_JSTATE_ABORTED
;
3020 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3022 if (status
!= HTTP_STATUS_OK
)
3024 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3025 "Unable to GET URI: %s", httpStatus(status
));
3027 job
->state
= IPP_JSTATE_ABORTED
;
3037 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3039 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3041 int error
= errno
; /* Write error */
3043 job
->state
= IPP_JSTATE_ABORTED
;
3051 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3052 "Unable to write print file: %s", strerror(error
));
3062 int error
= errno
; /* Write error */
3064 job
->state
= IPP_JSTATE_ABORTED
;
3069 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3070 "Unable to write print file: %s", strerror(error
));
3075 job
->filename
= strdup(filename
);
3076 job
->state
= IPP_JSTATE_PENDING
;
3079 * Process the job...
3083 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3085 job
->state
= IPP_JSTATE_ABORTED
;
3086 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3095 * Return the job info...
3098 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3100 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3101 cupsArrayAdd(ra
, "job-id");
3102 cupsArrayAdd(ra
, "job-state");
3103 cupsArrayAdd(ra
, "job-state-reasons");
3104 cupsArrayAdd(ra
, "job-uri");
3106 copy_job_attributes(client
, job
, ra
);
3107 cupsArrayDelete(ra
);
3112 * 'ipp_send_document()' - Add an attached document to a job object created with
3117 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3119 _ipp_job_t
*job
; /* Job information */
3120 char filename
[1024], /* Filename buffer */
3121 buffer
[4096]; /* Copy buffer */
3122 ssize_t bytes
; /* Bytes read */
3123 ipp_attribute_t
*attr
; /* Current attribute */
3124 cups_array_t
*ra
; /* Attributes to send in response */
3131 if ((job
= find_job(client
)) == NULL
)
3133 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3134 httpFlush(client
->http
);
3139 * See if we already have a document for this job or the job has already
3140 * in a non-pending state...
3143 if (job
->state
> IPP_JSTATE_HELD
)
3145 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3146 "Job is not in a pending state.");
3147 httpFlush(client
->http
);
3150 else if (job
->filename
|| job
->fd
>= 0)
3152 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3153 "Multiple document jobs are not supported.");
3154 httpFlush(client
->http
);
3158 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3159 IPP_TAG_ZERO
)) == NULL
)
3161 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3162 "Missing required last-document attribute.");
3163 httpFlush(client
->http
);
3166 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3167 !ippGetBoolean(attr
, 0))
3169 respond_unsupported(client
, attr
);
3170 httpFlush(client
->http
);
3175 * Validate document attributes...
3178 if (!valid_doc_attributes(client
))
3180 httpFlush(client
->http
);
3185 * Get the document format for the job...
3188 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3190 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
3191 job
->format
= ippGetString(attr
, 0, NULL
);
3193 job
->format
= "application/octet-stream";
3196 * Create a file for the request data...
3199 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3200 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3201 client
->printer
->directory
, job
->id
);
3202 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3203 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3204 client
->printer
->directory
, job
->id
);
3205 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3206 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3207 client
->printer
->directory
, job
->id
);
3208 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3209 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3210 client
->printer
->directory
, job
->id
);
3212 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3213 client
->printer
->directory
, job
->id
);
3216 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3218 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3220 _cupsRWUnlock(&(client
->printer
->rwlock
));
3224 job
->state
= IPP_JSTATE_ABORTED
;
3226 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3227 "Unable to create print file: %s", strerror(errno
));
3231 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3233 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3235 int error
= errno
; /* Write error */
3237 job
->state
= IPP_JSTATE_ABORTED
;
3244 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3245 "Unable to write print file: %s", strerror(error
));
3253 * Got an error while reading the print data, so abort this job.
3256 job
->state
= IPP_JSTATE_ABORTED
;
3263 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3264 "Unable to read print file.");
3270 int error
= errno
; /* Write error */
3272 job
->state
= IPP_JSTATE_ABORTED
;
3277 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3278 "Unable to write print file: %s", strerror(error
));
3282 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3285 job
->filename
= strdup(filename
);
3286 job
->state
= IPP_JSTATE_PENDING
;
3288 _cupsRWUnlock(&(client
->printer
->rwlock
));
3291 * Process the job...
3295 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3297 job
->state
= IPP_JSTATE_ABORTED
;
3298 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3307 * Return the job info...
3310 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3312 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3313 cupsArrayAdd(ra
, "job-id");
3314 cupsArrayAdd(ra
, "job-state");
3315 cupsArrayAdd(ra
, "job-state-reasons");
3316 cupsArrayAdd(ra
, "job-uri");
3318 copy_job_attributes(client
, job
, ra
);
3319 cupsArrayDelete(ra
);
3324 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3329 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3331 _ipp_job_t
*job
; /* Job information */
3332 ipp_attribute_t
*uri
; /* document-uri */
3333 char scheme
[256], /* URI scheme */
3334 userpass
[256], /* Username and password info */
3335 hostname
[256], /* Hostname */
3336 resource
[1024]; /* Resource path */
3337 int port
; /* Port number */
3338 http_uri_status_t uri_status
; /* URI decode status */
3339 http_encryption_t encryption
; /* Encryption to use, if any */
3340 http_t
*http
; /* Connection for http/https URIs */
3341 http_status_t status
; /* Access status for http/https URIs */
3342 int infile
; /* Input file for local file URIs */
3343 char filename
[1024], /* Filename buffer */
3344 buffer
[4096]; /* Copy buffer */
3345 ssize_t bytes
; /* Bytes read */
3346 ipp_attribute_t
*attr
; /* Current attribute */
3347 cups_array_t
*ra
; /* Attributes to send in response */
3348 static const char * const uri_status_strings
[] =
3349 { /* URI decode errors */
3351 "Bad arguments to function.",
3352 "Bad resource in URI.",
3353 "Bad port number in URI.",
3354 "Bad hostname in URI.",
3355 "Bad username in URI.",
3356 "Bad scheme in URI.",
3365 if ((job
= find_job(client
)) == NULL
)
3367 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3368 httpFlush(client
->http
);
3373 * See if we already have a document for this job or the job has already
3374 * in a non-pending state...
3377 if (job
->state
> IPP_JSTATE_HELD
)
3379 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3380 "Job is not in a pending state.");
3381 httpFlush(client
->http
);
3384 else if (job
->filename
|| job
->fd
>= 0)
3386 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3387 "Multiple document jobs are not supported.");
3388 httpFlush(client
->http
);
3392 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3393 IPP_TAG_ZERO
)) == NULL
)
3395 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3396 "Missing required last-document attribute.");
3397 httpFlush(client
->http
);
3400 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3401 !ippGetBoolean(attr
, 0))
3403 respond_unsupported(client
, attr
);
3404 httpFlush(client
->http
);
3409 * Validate document attributes...
3412 if (!valid_doc_attributes(client
))
3414 httpFlush(client
->http
);
3419 * Do we have a file to print?
3422 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3424 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3425 "Unexpected document data following request.");
3430 * Do we have a document URI?
3433 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3434 IPP_TAG_URI
)) == NULL
)
3436 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3440 if (ippGetCount(uri
) != 1)
3442 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3443 "Too many document-uri values.");
3447 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3448 scheme
, sizeof(scheme
), userpass
,
3449 sizeof(userpass
), hostname
, sizeof(hostname
),
3450 &port
, resource
, sizeof(resource
));
3451 if (uri_status
< HTTP_URI_STATUS_OK
)
3453 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3454 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3458 if (strcmp(scheme
, "file") &&
3460 strcmp(scheme
, "https") &&
3461 #endif /* HAVE_SSL */
3462 strcmp(scheme
, "http"))
3464 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3465 "URI scheme \"%s\" not supported.", scheme
);
3469 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3471 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3472 "Unable to access URI: %s", strerror(errno
));
3477 * Get the document format for the job...
3480 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3482 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3483 IPP_TAG_MIMETYPE
)) != NULL
)
3484 job
->format
= ippGetString(attr
, 0, NULL
);
3486 job
->format
= "application/octet-stream";
3489 * Create a file for the request data...
3492 if (!_cups_strcasecmp(job
->format
, "image/jpeg"))
3493 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3494 client
->printer
->directory
, job
->id
);
3495 else if (!_cups_strcasecmp(job
->format
, "image/png"))
3496 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3497 client
->printer
->directory
, job
->id
);
3498 else if (!_cups_strcasecmp(job
->format
, "application/pdf"))
3499 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3500 client
->printer
->directory
, job
->id
);
3501 else if (!_cups_strcasecmp(job
->format
, "application/postscript"))
3502 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3503 client
->printer
->directory
, job
->id
);
3505 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3506 client
->printer
->directory
, job
->id
);
3508 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3510 _cupsRWUnlock(&(client
->printer
->rwlock
));
3514 job
->state
= IPP_JSTATE_ABORTED
;
3516 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3517 "Unable to create print file: %s", strerror(errno
));
3521 if (!strcmp(scheme
, "file"))
3523 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3525 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3526 "Unable to access URI: %s", strerror(errno
));
3532 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3533 (errno
== EAGAIN
|| errno
== EINTR
))
3535 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3537 int error
= errno
; /* Write error */
3539 job
->state
= IPP_JSTATE_ABORTED
;
3547 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3548 "Unable to write print file: %s", strerror(error
));
3559 if (port
== 443 || !strcmp(scheme
, "https"))
3560 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3562 #endif /* HAVE_SSL */
3563 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3565 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3566 1, 30000, NULL
)) == NULL
)
3568 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3569 "Unable to connect to %s: %s", hostname
,
3570 cupsLastErrorString());
3571 job
->state
= IPP_JSTATE_ABORTED
;
3580 httpClearFields(http
);
3581 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3582 if (httpGet(http
, resource
))
3584 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3585 "Unable to GET URI: %s", strerror(errno
));
3587 job
->state
= IPP_JSTATE_ABORTED
;
3597 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3599 if (status
!= HTTP_STATUS_OK
)
3601 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3602 "Unable to GET URI: %s", httpStatus(status
));
3604 job
->state
= IPP_JSTATE_ABORTED
;
3614 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3616 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3618 int error
= errno
; /* Write error */
3620 job
->state
= IPP_JSTATE_ABORTED
;
3628 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3629 "Unable to write print file: %s", strerror(error
));
3639 int error
= errno
; /* Write error */
3641 job
->state
= IPP_JSTATE_ABORTED
;
3646 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3647 "Unable to write print file: %s", strerror(error
));
3651 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3654 job
->filename
= strdup(filename
);
3655 job
->state
= IPP_JSTATE_PENDING
;
3657 _cupsRWUnlock(&(client
->printer
->rwlock
));
3660 * Process the job...
3664 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3666 job
->state
= IPP_JSTATE_ABORTED
;
3667 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3676 * Return the job info...
3679 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3681 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3682 cupsArrayAdd(ra
, "job-id");
3683 cupsArrayAdd(ra
, "job-state");
3684 cupsArrayAdd(ra
, "job-state-reasons");
3685 cupsArrayAdd(ra
, "job-uri");
3687 copy_job_attributes(client
, job
, ra
);
3688 cupsArrayDelete(ra
);
3693 * 'ipp_validate_job()' - Validate job creation attributes.
3697 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
3699 if (valid_job_attributes(client
))
3700 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3705 * 'process_client()' - Process client requests on a thread.
3708 static void * /* O - Exit status */
3709 process_client(_ipp_client_t
*client
) /* I - Client */
3712 * Loop until we are out of requests or timeout (30 seconds)...
3716 int first_time
= 1; /* First time request? */
3717 #endif /* HAVE_SSL */
3719 while (httpWait(client
->http
, 30000))
3725 * See if we need to negotiate a TLS connection...
3728 char buf
[1]; /* First byte from client */
3730 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
3732 fprintf(stderr
, "%s Negotiating TLS session.\n", client
->hostname
);
3734 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
3736 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
3743 #endif /* HAVE_SSL */
3745 if (!process_http(client
))
3750 * Close the conection to the client and return...
3753 delete_client(client
);
3760 * 'process_http()' - Process a HTTP request.
3763 int /* O - 1 on success, 0 on failure */
3764 process_http(_ipp_client_t
*client
) /* I - Client connection */
3766 char uri
[1024]; /* URI */
3767 http_state_t http_state
; /* HTTP state */
3768 http_status_t http_status
; /* HTTP status */
3769 ipp_state_t ipp_state
; /* State of IPP transfer */
3770 char scheme
[32], /* Method/scheme */
3771 userpass
[128], /* Username:password */
3772 hostname
[HTTP_MAX_HOST
];
3774 int port
; /* Port number */
3775 const char *encoding
; /* Content-Encoding value */
3776 static const char * const http_states
[] =
3777 { /* Strings for logging HTTP method */
3798 * Clear state variables...
3801 ippDelete(client
->request
);
3802 ippDelete(client
->response
);
3804 client
->request
= NULL
;
3805 client
->response
= NULL
;
3806 client
->operation
= HTTP_STATE_WAITING
;
3809 * Read a request from the connection...
3812 while ((http_state
= httpReadRequest(client
->http
, uri
,
3813 sizeof(uri
))) == HTTP_STATE_WAITING
)
3817 * Parse the request line...
3820 if (http_state
== HTTP_STATE_ERROR
)
3822 if (httpError(client
->http
) == EPIPE
)
3823 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
3825 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
3826 strerror(httpError(client
->http
)));
3830 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
3832 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
3833 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3836 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
3838 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
3839 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3843 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
3847 * Separate the URI into its components...
3850 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
3851 userpass
, sizeof(userpass
),
3852 hostname
, sizeof(hostname
), &port
,
3853 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
)
3855 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
3856 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3861 * Process the request...
3864 client
->start
= time(NULL
);
3865 client
->operation
= httpGetState(client
->http
);
3868 * Parse incoming parameters until the status changes...
3871 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
3873 if (http_status
!= HTTP_STATUS_OK
)
3875 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3879 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
3880 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
3883 * HTTP/1.1 and higher require the "Host:" field...
3886 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3891 * Handle HTTP Upgrade...
3894 if (!_cups_strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
3897 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
3902 * Handle HTTP Expect...
3905 if (httpGetExpect(client
->http
) &&
3906 (client
->operation
== HTTP_STATE_POST
||
3907 client
->operation
== HTTP_STATE_PUT
))
3909 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
3912 * Send 100-continue header...
3915 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
3921 * Send 417-expectation-failed header...
3924 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
3930 * Handle new transfers...
3933 encoding
= httpGetContentEncoding(client
->http
);
3935 switch (client
->operation
)
3937 case HTTP_STATE_OPTIONS
:
3939 * Do HEAD/OPTIONS command...
3942 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
3944 case HTTP_STATE_HEAD
:
3945 if (!strcmp(client
->uri
, "/icon.png"))
3946 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
3947 else if (!strcmp(client
->uri
, "/"))
3948 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
3950 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
3952 case HTTP_STATE_GET
:
3953 if (!strcmp(client
->uri
, "/icon.png"))
3956 * Send PNG icon file.
3959 int fd
; /* Icon file */
3960 struct stat fileinfo
; /* Icon file information */
3961 char buffer
[4096]; /* Copy buffer */
3962 ssize_t bytes
; /* Bytes */
3964 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
3966 if (!stat(client
->printer
->icon
, &fileinfo
) &&
3967 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
3969 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
3970 (size_t)fileinfo
.st_size
))
3976 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
3977 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
3979 httpFlushWrite(client
->http
);
3984 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
3986 else if (!strcmp(client
->uri
, "/"))
3989 * Show web status page...
3992 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
3996 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
3997 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
4000 "<title>%s</title>\n"
4001 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
4002 "type=\"image/png\">\n"
4006 "<h1><img align=\"right\" src=\"/icon.png\">%s</h1>\n"
4007 "<p>%s, %d job(s).</p>\n"
4010 client
->printer
->name
, client
->printer
->name
,
4011 client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" :
4012 client
->printer
->state
== IPP_PSTATE_PROCESSING
?
4013 "Printing" : "Stopped",
4014 cupsArrayCount(client
->printer
->jobs
));
4015 httpWrite2(client
->http
, "", 0);
4020 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4023 case HTTP_STATE_POST
:
4024 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4028 * Not an IPP request...
4031 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4035 * Read the IPP request...
4038 client
->request
= ippNew();
4040 while ((ipp_state
= ippRead(client
->http
,
4041 client
->request
)) != IPP_STATE_DATA
)
4043 if (ipp_state
== IPP_STATE_ERROR
)
4045 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
4046 cupsLastErrorString());
4047 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4053 * Now that we have the IPP request, process the request...
4056 return (process_ipp(client
));
4059 break; /* Anti-compiler-warning-code */
4067 * 'process_ipp()' - Process an IPP request.
4070 static int /* O - 1 on success, 0 on error */
4071 process_ipp(_ipp_client_t
*client
) /* I - Client */
4073 ipp_tag_t group
; /* Current group tag */
4074 ipp_attribute_t
*attr
; /* Current attribute */
4075 ipp_attribute_t
*charset
; /* Character set attribute */
4076 ipp_attribute_t
*language
; /* Language attribute */
4077 ipp_attribute_t
*uri
; /* Printer URI attribute */
4078 int major
, minor
; /* Version number */
4079 const char *name
; /* Name of attribute */
4082 debug_attributes("Request", client
->request
, 1);
4085 * First build an empty response message for this request...
4088 client
->operation_id
= ippGetOperation(client
->request
);
4089 client
->response
= ippNewResponse(client
->request
);
4092 * Then validate the request header and required attributes...
4095 major
= ippGetVersion(client
->request
, &minor
);
4097 if (major
< 1 || major
> 2)
4100 * Return an error, since we only support IPP 1.x and 2.x.
4103 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
4104 "Bad request version number %d.%d.", major
, minor
);
4106 else if (ippGetRequestId(client
->request
) <= 0)
4107 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
4108 ippGetRequestId(client
->request
));
4109 else if (!ippFirstAttribute(client
->request
))
4110 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4111 "No attributes in request.");
4115 * Make sure that the attributes are provided in the correct order and
4116 * don't repeat groups...
4119 for (attr
= ippFirstAttribute(client
->request
),
4120 group
= ippGetGroupTag(attr
);
4122 attr
= ippNextAttribute(client
->request
))
4124 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
4127 * Out of order; return an error...
4130 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4131 "Attribute groups are out of order (%x < %x).",
4132 ippGetGroupTag(attr
), group
);
4136 group
= ippGetGroupTag(attr
);
4142 * Then make sure that the first three attributes are:
4144 * attributes-charset
4145 * attributes-natural-language
4146 * printer-uri/job-uri
4149 attr
= ippFirstAttribute(client
->request
);
4150 name
= ippGetName(attr
);
4151 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
4152 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
4157 attr
= ippNextAttribute(client
->request
);
4158 name
= ippGetName(attr
);
4160 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
4161 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
4166 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
4167 IPP_TAG_URI
)) != NULL
)
4169 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
4170 IPP_TAG_URI
)) != NULL
)
4176 _cups_strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
4177 _cups_strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
4180 * Bad character set...
4183 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4184 "Unsupported character set \"%s\".",
4185 ippGetString(charset
, 0, NULL
));
4187 else if (!charset
|| !language
|| !uri
)
4190 * Return an error, since attributes-charset,
4191 * attributes-natural-language, and printer-uri/job-uri are required
4192 * for all operations.
4195 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4196 "Missing required attributes.");
4200 char scheme
[32], /* URI scheme */
4201 userpass
[32], /* Username/password in URI */
4202 host
[256], /* Host name in URI */
4203 resource
[256]; /* Resource path in URI */
4204 int port
; /* Port number in URI */
4206 name
= ippGetName(uri
);
4208 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4209 scheme
, sizeof(scheme
),
4210 userpass
, sizeof(userpass
),
4211 host
, sizeof(host
), &port
,
4212 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
4213 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4214 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
4215 else if ((!strcmp(name
, "job-uri") &&
4216 strncmp(resource
, "/ipp/print/", 11)) ||
4217 (!strcmp(name
, "printer-uri") &&
4218 strcmp(resource
, "/ipp/print")))
4219 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
4220 name
, ippGetString(uri
, 0, NULL
));
4224 * Try processing the operation...
4227 switch (ippGetOperation(client
->request
))
4229 case IPP_OP_PRINT_JOB
:
4230 ipp_print_job(client
);
4233 case IPP_OP_PRINT_URI
:
4234 ipp_print_uri(client
);
4237 case IPP_OP_VALIDATE_JOB
:
4238 ipp_validate_job(client
);
4241 case IPP_OP_CREATE_JOB
:
4242 ipp_create_job(client
);
4245 case IPP_OP_SEND_DOCUMENT
:
4246 ipp_send_document(client
);
4249 case IPP_OP_SEND_URI
:
4250 ipp_send_uri(client
);
4253 case IPP_OP_CANCEL_JOB
:
4254 ipp_cancel_job(client
);
4257 case IPP_OP_GET_JOB_ATTRIBUTES
:
4258 ipp_get_job_attributes(client
);
4261 case IPP_OP_GET_JOBS
:
4262 ipp_get_jobs(client
);
4265 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
4266 ipp_get_printer_attributes(client
);
4270 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
4271 "Operation not supported.");
4280 * Send the HTTP header and return...
4283 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
4284 httpFlush(client
->http
); /* Flush trailing (junk) data */
4286 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
4287 ippLength(client
->response
)));
4292 * 'process_job()' - Process a print job.
4295 static void * /* O - Thread exit status */
4296 process_job(_ipp_job_t
*job
) /* I - Job */
4298 job
->state
= IPP_JSTATE_PROCESSING
;
4299 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
4301 if (job
->printer
->command
)
4304 * Execute a command with the job spool file and wait for it to complete...
4307 int pid
, /* Process ID */
4308 status
; /* Exit status */
4309 time_t start
, /* Start time */
4312 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
4316 if ((pid
= fork()) == 0)
4319 * Child comes here...
4322 execlp(job
->printer
->command
, job
->printer
->command
, job
->filename
,
4329 * Unable to fork process...
4332 perror("Unable to start job processing command");
4337 * Wait for child to complete...
4341 while (waitpid(pid
, &status
, 0) < 0);
4343 while (wait(&status
) < 0);
4344 #endif /* HAVE_WAITPID */
4348 if (WIFEXITED(status
))
4349 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
4350 job
->printer
->command
, WEXITSTATUS(status
));
4352 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
4353 job
->printer
->command
, WTERMSIG(status
));
4356 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
4357 job
->printer
->command
);
4361 * Make sure processing takes at least 5 seconds...
4365 if ((end
- start
) < 5)
4371 * Sleep for a random amount of time to simulate job processing.
4374 sleep(5 + (rand() % 11));
4378 job
->state
= IPP_JSTATE_CANCELED
;
4380 job
->state
= IPP_JSTATE_COMPLETED
;
4382 job
->completed
= time(NULL
);
4383 job
->printer
->state
= IPP_PSTATE_IDLE
;
4384 job
->printer
->active_job
= NULL
;
4392 * 'register_printer()' - Register a printer object via Bonjour.
4395 static int /* O - 1 on success, 0 on error */
4397 _ipp_printer_t
*printer
, /* I - Printer */
4398 const char *location
, /* I - Location */
4399 const char *make
, /* I - Manufacturer */
4400 const char *model
, /* I - Model name */
4401 const char *formats
, /* I - Supported formats */
4402 const char *adminurl
, /* I - Web interface URL */
4403 int color
, /* I - 1 = color, 0 = monochrome */
4404 int duplex
, /* I - 1 = duplex, 0 = simplex */
4405 const char *subtype
) /* I - Service subtype */
4407 DNSServiceErrorType error
; /* Error from Bonjour */
4408 char make_model
[256],/* Make and model together */
4409 product
[256], /* Product string */
4410 regtype
[256]; /* Bonjour service type */
4414 * Build the TXT record for IPP...
4417 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4418 snprintf(product
, sizeof(product
), "(%s)", model
);
4420 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
4421 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 9, "ipp/print");
4422 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
4424 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
4427 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
4429 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
4431 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
4433 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
4434 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
4435 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
4437 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
4441 * Create a shared service reference for Bonjour...
4444 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
4445 != kDNSServiceErr_NoError
)
4447 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
4452 * Register the _printer._tcp (LPD) service type with a port number of 0 to
4453 * defend our service name but not actually support LPD...
4456 printer
->printer_ref
= printer
->common_ref
;
4458 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
4459 kDNSServiceFlagsShareConnection
,
4460 0 /* interfaceIndex */, printer
->dnssd_name
,
4461 "_printer._tcp", NULL
/* domain */,
4462 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
4463 NULL
/* txtRecord */,
4464 (DNSServiceRegisterReply
)dnssd_callback
,
4465 printer
)) != kDNSServiceErr_NoError
)
4467 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
4468 printer
->dnssd_name
, error
);
4473 * Then register the _ipp._tcp (IPP) service type with the real port number to
4474 * advertise our IPP printer...
4477 printer
->ipp_ref
= printer
->common_ref
;
4479 if (subtype
&& *subtype
)
4480 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
4482 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
4484 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
4485 kDNSServiceFlagsShareConnection
,
4486 0 /* interfaceIndex */, printer
->dnssd_name
,
4487 regtype
, NULL
/* domain */,
4488 NULL
/* host */, htons(printer
->port
),
4489 TXTRecordGetLength(&(printer
->ipp_txt
)),
4490 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4491 (DNSServiceRegisterReply
)dnssd_callback
,
4492 printer
)) != kDNSServiceErr_NoError
)
4494 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4495 printer
->dnssd_name
, regtype
, error
);
4501 * Then register the _ipps._tcp (IPP) service type with the real port number to
4502 * advertise our IPP printer...
4505 printer
->ipps_ref
= printer
->common_ref
;
4507 if (subtype
&& *subtype
)
4508 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
4510 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
4512 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
4513 kDNSServiceFlagsShareConnection
,
4514 0 /* interfaceIndex */, printer
->dnssd_name
,
4515 regtype
, NULL
/* domain */,
4516 NULL
/* host */, htons(printer
->port
),
4517 TXTRecordGetLength(&(printer
->ipp_txt
)),
4518 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4519 (DNSServiceRegisterReply
)dnssd_callback
,
4520 printer
)) != kDNSServiceErr_NoError
)
4522 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4523 printer
->dnssd_name
, regtype
, error
);
4526 # endif /* HAVE_SSL */
4529 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4530 * real port number to advertise our IPP printer...
4533 printer
->http_ref
= printer
->common_ref
;
4535 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
4536 kDNSServiceFlagsShareConnection
,
4537 0 /* interfaceIndex */, printer
->dnssd_name
,
4538 "_http._tcp,_printer", NULL
/* domain */,
4539 NULL
/* host */, htons(printer
->port
),
4540 0 /* txtLen */, NULL
, /* txtRecord */
4541 (DNSServiceRegisterReply
)dnssd_callback
,
4542 printer
)) != kDNSServiceErr_NoError
)
4544 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4545 printer
->dnssd_name
, regtype
, error
);
4551 #endif /* HAVE_DNSSD */
4555 * 'respond_http()' - Send a HTTP response.
4558 int /* O - 1 on success, 0 on failure */
4560 _ipp_client_t
*client
, /* I - Client */
4561 http_status_t code
, /* I - HTTP status of response */
4562 const char *content_encoding
, /* I - Content-Encoding of response */
4563 const char *type
, /* I - MIME media type of response */
4564 size_t length
) /* I - Length of response */
4566 char message
[1024]; /* Text message */
4569 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
4571 if (code
== HTTP_STATUS_CONTINUE
)
4574 * 100-continue doesn't send any headers...
4577 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
4581 * Format an error message...
4584 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
)
4586 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
4588 type
= "text/plain";
4589 length
= strlen(message
);
4595 * Send the HTTP response header...
4598 httpClearFields(client
->http
);
4600 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
4601 client
->operation
== HTTP_STATE_OPTIONS
)
4602 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
4606 if (!strcmp(type
, "text/html"))
4607 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
4608 "text/html; charset=utf-8");
4610 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
4612 if (content_encoding
)
4613 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
4616 httpSetLength(client
->http
, length
);
4618 if (httpWriteResponse(client
->http
, code
) < 0)
4622 * Send the response data...
4628 * Send a plain text message.
4631 if (httpPrintf(client
->http
, "%s", message
) < 0)
4634 if (httpWrite2(client
->http
, "", 0) < 0)
4637 else if (client
->response
)
4640 * Send an IPP response...
4643 debug_attributes("Response", client
->response
, 2);
4645 ippSetState(client
->response
, IPP_STATE_IDLE
);
4647 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
4656 * 'respond_ipp()' - Send an IPP response.
4660 respond_ipp(_ipp_client_t
*client
, /* I - Client */
4661 ipp_status_t status
, /* I - status-code */
4662 const char *message
, /* I - printf-style status-message */
4663 ...) /* I - Additional args as needed */
4665 const char *formatted
= NULL
; /* Formatted message */
4668 ippSetStatusCode(client
->response
, status
);
4672 va_list ap
; /* Pointer to additional args */
4673 ipp_attribute_t
*attr
; /* New status-message attribute */
4675 va_start(ap
, message
);
4676 if ((attr
= ippFindAttribute(client
->response
, "status-message",
4677 IPP_TAG_TEXT
)) != NULL
)
4678 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
4680 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
4681 "status-message", NULL
, message
, ap
);
4684 formatted
= ippGetString(attr
, 0, NULL
);
4688 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
4689 ippOpString(client
->operation_id
), ippErrorString(status
),
4692 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
4693 ippOpString(client
->operation_id
), ippErrorString(status
));
4698 * 'respond_unsupported()' - Respond with an unsupported attribute.
4702 respond_unsupported(
4703 _ipp_client_t
*client
, /* I - Client */
4704 ipp_attribute_t
*attr
) /* I - Atribute */
4706 ipp_attribute_t
*temp
; /* Copy of attribute */
4709 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4710 "Unsupported %s %s%s value.", ippGetName(attr
),
4711 ippGetCount(attr
) > 1 ? "1setOf " : "",
4712 ippTagString(ippGetValueTag(attr
)));
4714 temp
= ippCopyAttribute(client
->response
, attr
, 0);
4715 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
4720 * 'run_printer()' - Run the printer service.
4724 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
4726 int num_fds
; /* Number of file descriptors */
4727 struct pollfd polldata
[3]; /* poll() data */
4728 int timeout
; /* Timeout for poll() */
4729 _ipp_client_t
*client
; /* New client */
4733 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
4736 polldata
[0].fd
= printer
->ipv4
;
4737 polldata
[0].events
= POLLIN
;
4739 polldata
[1].fd
= printer
->ipv6
;
4740 polldata
[1].events
= POLLIN
;
4745 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
4746 polldata
[num_fds
++].events
= POLLIN
;
4747 #endif /* HAVE_DNSSD */
4750 * Loop until we are killed or have a hard error...
4755 if (cupsArrayCount(printer
->jobs
))
4760 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
4762 perror("poll() failed");
4766 if (polldata
[0].revents
& POLLIN
)
4768 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
4770 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4772 perror("Unable to create client thread");
4773 delete_client(client
);
4778 if (polldata
[1].revents
& POLLIN
)
4780 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
4782 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4784 perror("Unable to create client thread");
4785 delete_client(client
);
4791 if (polldata
[2].revents
& POLLIN
)
4792 DNSServiceProcessResult(printer
->common_ref
);
4793 #endif /* HAVE_DNSSD */
4796 * Clean out old jobs...
4799 clean_jobs(printer
);
4805 * 'usage()' - Show program usage.
4809 usage(int status
) /* O - Exit status */
4813 puts(CUPS_SVERSION
" - Copyright 2010-2013 by Apple Inc. All rights "
4818 puts("Usage: ippserver [options] \"name\"");
4821 puts("-2 Supports 2-sided printing (default=1-sided)");
4822 puts("-M manufacturer Manufacturer name (default=Test)");
4823 puts("-P PIN printing mode");
4824 puts("-c command Run command for every print job");
4825 printf("-d spool-directory Spool directory "
4826 "(default=/tmp/ippserver.%d)\n", (int)getpid());
4827 puts("-f type/subtype[,...] List of supported types "
4828 "(default=application/pdf,image/jpeg)");
4829 puts("-h Show program help");
4830 puts("-i iconfile.png PNG icon file (default=printer.png)");
4831 puts("-k Keep job spool files");
4832 puts("-l location Location of printer (default=empty string)");
4833 puts("-m model Model name (default=Printer)");
4834 puts("-n hostname Hostname for printer");
4835 puts("-p port Port number (default=auto)");
4837 puts("-r subtype Bonjour service subtype (default=_print)");
4838 #endif /* HAVE_DNSSD */
4839 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
4840 puts("-v[vvv] Be (very) verbose");
4847 * 'valid_doc_attributes()' - Determine whether the document attributes are
4850 * When one or more document attributes are invalid, this function adds a
4851 * suitable response and attributes to the unsupported group.
4854 static int /* O - 1 if valid, 0 if not */
4855 valid_doc_attributes(
4856 _ipp_client_t
*client
) /* I - Client */
4858 int valid
= 1; /* Valid attributes? */
4859 ipp_op_t op
= ippGetOperation(client
->request
);
4861 const char *op_name
= ippOpString(op
);
4862 /* IPP operation name */
4863 ipp_attribute_t
*attr
, /* Current attribute */
4864 *supported
; /* xxx-supported attribute */
4865 const char *compression
= NULL
,
4866 /* compression value */
4867 *format
= NULL
; /* document-format value */
4871 * Check operation attributes...
4874 if ((attr
= ippFindAttribute(client
->request
, "compression",
4875 IPP_TAG_ZERO
)) != NULL
)
4878 * If compression is specified, only accept a supported value in a Print-Job
4879 * or Send-Document request...
4882 compression
= ippGetString(attr
, 0, NULL
);
4883 supported
= ippFindAttribute(client
->printer
->attrs
,
4884 "compression-supported", IPP_TAG_KEYWORD
);
4886 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
4887 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
4888 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
4889 op
!= IPP_OP_VALIDATE_JOB
) ||
4890 !ippContainsString(supported
, compression
))
4892 respond_unsupported(client
, attr
);
4897 fprintf(stderr
, "%s %s compression=\"%s\"\n",
4898 client
->hostname
, op_name
, compression
);
4900 if (strcmp(compression
, "none"))
4901 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
4906 * Is it a format we support?
4909 if ((attr
= ippFindAttribute(client
->request
, "document-format",
4910 IPP_TAG_ZERO
)) != NULL
)
4912 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
4913 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
4915 respond_unsupported(client
, attr
);
4920 format
= ippGetString(attr
, 0, NULL
);
4922 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
4923 client
->hostname
, op_name
, format
);
4928 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
,
4929 "document-format-default",
4930 IPP_TAG_MIMETYPE
), 0, NULL
);
4932 format
= "application/octet-stream"; /* Should never happen */
4934 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
4935 "document-format", NULL
, format
);
4938 if (!strcmp(format
, "application/octet-stream") &&
4939 (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
||
4940 ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
4943 * Auto-type the file using the first 4 bytes of the file...
4946 unsigned char header
[4]; /* First 4 bytes of file */
4948 memset(header
, 0, sizeof(header
));
4949 httpPeek(client
->http
, (char *)header
, sizeof(header
));
4951 if (!memcmp(header
, "%PDF", 4))
4952 format
= "application/pdf";
4953 else if (!memcmp(header
, "%!", 2))
4954 format
= "application/postscript";
4955 else if (!memcmp(header
, "\377\330\377", 3) &&
4956 header
[3] >= 0xe0 && header
[3] <= 0xef)
4957 format
= "image/jpeg";
4958 else if (!memcmp(header
, "\211PNG", 4))
4959 format
= "image/png";
4962 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
4963 client
->hostname
, op_name
, format
);
4966 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
4967 "document-format", NULL
, format
);
4969 ippSetString(client
->request
, &attr
, 0, format
);
4972 if (op
!= IPP_OP_CREATE_JOB
&&
4973 (supported
= ippFindAttribute(client
->printer
->attrs
,
4974 "document-format-supported",
4975 IPP_TAG_MIMETYPE
)) != NULL
&&
4976 !ippContainsString(supported
, format
))
4978 respond_unsupported(client
, attr
);
4987 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
4989 * When one or more job attributes are invalid, this function adds a suitable
4990 * response and attributes to the unsupported group.
4993 static int /* O - 1 if valid, 0 if not */
4994 valid_job_attributes(
4995 _ipp_client_t
*client
) /* I - Client */
4997 int i
, /* Looping var */
4998 valid
= 1; /* Valid attributes? */
4999 ipp_attribute_t
*attr
, /* Current attribute */
5000 *supported
; /* xxx-supported attribute */
5004 * Check operation attributes...
5007 valid
= valid_doc_attributes(client
);
5010 * Check the various job template attributes...
5013 if ((attr
= ippFindAttribute(client
->request
, "copies",
5014 IPP_TAG_ZERO
)) != NULL
)
5016 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5017 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
5019 respond_unsupported(client
, attr
);
5024 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity",
5025 IPP_TAG_ZERO
)) != NULL
)
5027 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
5029 respond_unsupported(client
, attr
);
5034 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until",
5035 IPP_TAG_ZERO
)) != NULL
)
5037 if (ippGetCount(attr
) != 1 ||
5038 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5039 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5040 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5041 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
5043 respond_unsupported(client
, attr
);
5048 if ((attr
= ippFindAttribute(client
->request
, "job-name",
5049 IPP_TAG_ZERO
)) != NULL
)
5051 if (ippGetCount(attr
) != 1 ||
5052 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5053 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
5055 respond_unsupported(client
, attr
);
5060 if ((attr
= ippFindAttribute(client
->request
, "job-priority",
5061 IPP_TAG_ZERO
)) != NULL
)
5063 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5064 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
5066 respond_unsupported(client
, attr
);
5071 if ((attr
= ippFindAttribute(client
->request
, "job-sheets",
5072 IPP_TAG_ZERO
)) != NULL
)
5074 if (ippGetCount(attr
) != 1 ||
5075 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5076 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5077 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5078 strcmp(ippGetString(attr
, 0, NULL
), "none"))
5080 respond_unsupported(client
, attr
);
5085 if ((attr
= ippFindAttribute(client
->request
, "media",
5086 IPP_TAG_ZERO
)) != NULL
)
5088 if (ippGetCount(attr
) != 1 ||
5089 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5090 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5091 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
5093 respond_unsupported(client
, attr
);
5099 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
5101 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
5104 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
5106 respond_unsupported(client
, attr
);
5112 if ((attr
= ippFindAttribute(client
->request
, "media-col",
5113 IPP_TAG_ZERO
)) != NULL
)
5115 if (ippGetCount(attr
) != 1 ||
5116 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
5118 respond_unsupported(client
, attr
);
5121 /* TODO: check for valid media-col */
5124 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling",
5125 IPP_TAG_ZERO
)) != NULL
)
5127 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5128 (strcmp(ippGetString(attr
, 0, NULL
),
5129 "separate-documents-uncollated-copies") &&
5130 strcmp(ippGetString(attr
, 0, NULL
),
5131 "separate-documents-collated-copies")))
5133 respond_unsupported(client
, attr
);
5138 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested",
5139 IPP_TAG_ZERO
)) != NULL
)
5141 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5142 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
5143 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
5145 respond_unsupported(client
, attr
);
5150 if ((attr
= ippFindAttribute(client
->request
, "page-ranges",
5151 IPP_TAG_ZERO
)) != NULL
)
5153 respond_unsupported(client
, attr
);
5157 if ((attr
= ippFindAttribute(client
->request
, "print-quality",
5158 IPP_TAG_ZERO
)) != NULL
)
5160 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5161 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
5162 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
5164 respond_unsupported(client
, attr
);
5169 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution",
5170 IPP_TAG_ZERO
)) != NULL
)
5172 respond_unsupported(client
, attr
);
5176 if ((attr
= ippFindAttribute(client
->request
, "sides",
5177 IPP_TAG_ZERO
)) != NULL
)
5179 const char *sides
= NULL
; /* "sides" value... */
5181 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
5183 respond_unsupported(client
, attr
);
5187 sides
= ippGetString(attr
, 0, NULL
);
5189 if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported",
5190 IPP_TAG_KEYWORD
)) != NULL
)
5192 if (!ippContainsString(supported
, sides
))
5194 respond_unsupported(client
, attr
);
5198 else if (strcmp(sides
, "one-sided"))
5200 respond_unsupported(client
, attr
);
5210 * End of "$Id: ippserver.c 11986 2014-07-02 15:52:01Z msweet $".