4 * Sample IPP Everywhere 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...
35 #include <cups/cups.h> /* Public API */
36 #include <config.h> /* CUPS configuration header */
37 #include <cups/thread-private.h> /* For multithreading functions */
43 #endif /* HAVE_DNSSD */
46 #include <sys/fcntl.h>
48 #ifdef HAVE_SYS_MOUNT_H
49 # include <sys/mount.h>
50 #endif /* HAVE_SYS_MOUNT_H */
51 #ifdef HAVE_SYS_STATFS_H
52 # include <sys/statfs.h>
53 #endif /* HAVE_SYS_STATFS_H */
54 #ifdef HAVE_SYS_STATVFS_H
55 # include <sys/statvfs.h>
56 #endif /* HAVE_SYS_STATVFS_H */
59 #endif /* HAVE_SYS_VFS_H */
66 enum _ipp_preasons_e
/* printer-state-reasons bit values */
68 _IPP_PSTATE_NONE
= 0x0000, /* none */
69 _IPP_PSTATE_OTHER
= 0x0001, /* other */
70 _IPP_PSTATE_COVER_OPEN
= 0x0002, /* cover-open */
71 _IPP_PSTATE_INPUT_TRAY_MISSING
= 0x0004,
72 /* input-tray-missing */
73 _IPP_PSTATE_MARKER_SUPPLY_EMPTY
= 0x0008,
74 /* marker-supply-empty */
75 _IPP_PSTATE_MARKER_SUPPLY_LOW
= 0x0010,
76 /* marker-suply-low */
77 _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL
= 0x0020,
78 /* marker-waste-almost-full */
79 _IPP_PSTATE_MARKER_WASTE_FULL
= 0x0040,
80 /* marker-waste-full */
81 _IPP_PSTATE_MEDIA_EMPTY
= 0x0080, /* media-empty */
82 _IPP_PSTATE_MEDIA_JAM
= 0x0100, /* media-jam */
83 _IPP_PSTATE_MEDIA_LOW
= 0x0200, /* media-low */
84 _IPP_PSTATE_MEDIA_NEEDED
= 0x0400, /* media-needed */
85 _IPP_PSTATE_MOVING_TO_PAUSED
= 0x0800,
86 /* moving-to-paused */
87 _IPP_PSTATE_PAUSED
= 0x1000, /* paused */
88 _IPP_PSTATE_SPOOL_AREA_FULL
= 0x2000, /* spool-area-full */
89 _IPP_PSTATE_TONER_EMPTY
= 0x4000, /* toner-empty */
90 _IPP_PSTATE_TONER_LOW
= 0x8000 /* toner-low */
92 typedef unsigned int _ipp_preasons_t
; /* Bitfield for printer-state-reasons */
94 typedef enum _ipp_media_class_e
96 _IPP_GENERAL
, /* General-purpose size */
97 _IPP_PHOTO_ONLY
, /* Photo-only size */
98 _IPP_ENV_ONLY
/* Envelope-only size */
101 static const char * const media_supported
[] =
102 { /* media-supported values */
103 "iso_a4_210x297mm", /* A4 */
104 "iso_a5_148x210mm", /* A5 */
105 "iso_a6_105x148mm", /* A6 */
106 "iso_dl_110x220mm", /* DL */
107 "na_legal_8.5x14in", /* Legal */
108 "na_letter_8.5x11in", /* Letter */
109 "na_number-10_4.125x9.5in", /* #10 */
110 "na_index-3x5_3x5in", /* 3x5 */
111 "oe_photo-l_3.5x5in", /* L */
112 "na_index-4x6_4x6in", /* 4x6 */
113 "na_5x7_5x7in" /* 5x7 aka 2L */
115 static const int media_col_sizes
[][3] =
116 { /* media-col-database sizes */
117 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
118 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
119 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
120 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
121 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
122 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
123 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
124 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
125 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
126 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
127 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 aka 2L */
129 static const char * const media_type_supported
[] =
130 /* media-type-supported values */
137 "photographic-glossy",
138 "photographic-high-gloss",
139 "photographic-matte",
140 "photographic-satin",
141 "photographic-semi-gloss",
143 "stationery-letterhead",
152 typedef struct _ipp_job_s _ipp_job_t
;
154 typedef struct _ipp_printer_s
/**** Printer data ****/
156 int ipv4
, /* IPv4 listener */
157 ipv6
; /* IPv6 listener */
159 DNSServiceRef common_ref
, /* Shared service connection */
160 ipp_ref
, /* Bonjour IPP service */
162 ipps_ref
, /* Bonjour IPPS service */
163 # endif /* HAVE_SSL */
164 http_ref
, /* Bonjour HTTP service */
165 printer_ref
; /* Bonjour LPD service */
166 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
167 char *dnssd_name
; /* printer-dnssd-name */
168 #endif /* HAVE_DNSSD */
169 char *name
, /* printer-name */
170 *icon
, /* Icon filename */
171 *directory
, /* Spool directory */
172 *hostname
, /* Hostname */
173 *uri
, /* printer-uri-supported */
174 *command
; /* Command to run with job file */
176 size_t urilen
; /* Length of printer URI */
177 ipp_t
*attrs
; /* Static attributes */
178 ipp_pstate_t state
; /* printer-state value */
179 _ipp_preasons_t state_reasons
; /* printer-state-reasons values */
180 cups_array_t
*jobs
; /* Jobs */
181 _ipp_job_t
*active_job
; /* Current active/pending job */
182 int next_job_id
; /* Next job-id value */
183 _cups_rwlock_t rwlock
; /* Printer lock */
186 struct _ipp_job_s
/**** Job data ****/
189 const char *name
, /* job-name */
190 *username
, /* job-originating-user-name */
191 *format
; /* document-format */
192 ipp_jstate_t state
; /* job-state value */
193 time_t processing
, /* time-at-processing value */
194 completed
; /* time-at-completed value */
195 ipp_t
*attrs
; /* Static attributes */
196 int cancel
; /* Non-zero when job canceled */
197 char *filename
; /* Print file name */
198 int fd
; /* Print file descriptor */
199 _ipp_printer_t
*printer
; /* Printer */
202 typedef struct _ipp_client_s
/**** Client data ****/
204 http_t
*http
; /* HTTP connection */
205 ipp_t
*request
, /* IPP request */
206 *response
; /* IPP response */
207 time_t start
; /* Request start time */
208 http_state_t operation
; /* Request operation */
209 ipp_op_t operation_id
; /* IPP operation-id */
210 char uri
[1024]; /* Request URI */
211 http_addr_t addr
; /* Client address */
212 char hostname
[256]; /* Client hostname */
213 _ipp_printer_t
*printer
; /* Printer */
214 _ipp_job_t
*job
; /* Current job, if any */
222 static void clean_jobs(_ipp_printer_t
*printer
);
223 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
224 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
225 ipp_tag_t group_tag
, int quickcopy
);
226 static void copy_job_attributes(_ipp_client_t
*client
,
227 _ipp_job_t
*job
, cups_array_t
*ra
);
228 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
229 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
230 static int create_listener(int family
, int *port
);
231 static ipp_t
*create_media_col(const char *media
, const char *type
,
232 int width
, int length
, int margins
);
233 static ipp_t
*create_media_size(int width
, int length
);
234 static _ipp_printer_t
*create_printer(const char *servername
,
235 const char *name
, const char *location
,
236 const char *make
, const char *model
,
238 const char *docformats
, int ppm
,
239 int ppm_color
, int duplex
, int port
,
243 #endif /* HAVE_DNSSD */
244 const char *directory
,
245 const char *command
);
246 static void debug_attributes(const char *title
, ipp_t
*ipp
,
248 static void delete_client(_ipp_client_t
*client
);
249 static void delete_job(_ipp_job_t
*job
);
250 static void delete_printer(_ipp_printer_t
*printer
);
252 static void dnssd_callback(DNSServiceRef sdRef
,
253 DNSServiceFlags flags
,
254 DNSServiceErrorType errorCode
,
258 _ipp_printer_t
*printer
);
259 #endif /* HAVE_DNSSD */
260 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
261 static void html_escape(_ipp_client_t
*client
, const char *s
,
263 static void html_printf(_ipp_client_t
*client
, const char *format
,
264 ...) __attribute__((__format__(__printf__
,
266 static void ipp_cancel_job(_ipp_client_t
*client
);
267 static void ipp_close_job(_ipp_client_t
*client
);
268 static void ipp_create_job(_ipp_client_t
*client
);
269 static void ipp_get_job_attributes(_ipp_client_t
*client
);
270 static void ipp_get_jobs(_ipp_client_t
*client
);
271 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
272 static void ipp_identify_printer(_ipp_client_t
*client
);
273 static void ipp_print_job(_ipp_client_t
*client
);
274 static void ipp_print_uri(_ipp_client_t
*client
);
275 static void ipp_send_document(_ipp_client_t
*client
);
276 static void ipp_send_uri(_ipp_client_t
*client
);
277 static void ipp_validate_job(_ipp_client_t
*client
);
278 static void *process_client(_ipp_client_t
*client
);
279 static int process_http(_ipp_client_t
*client
);
280 static int process_ipp(_ipp_client_t
*client
);
281 static void *process_job(_ipp_job_t
*job
);
283 static int register_printer(_ipp_printer_t
*printer
,
284 const char *location
, const char *make
,
285 const char *model
, const char *formats
,
286 const char *adminurl
, int color
,
287 int duplex
, const char *regtype
);
288 #endif /* HAVE_DNSSD */
289 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
290 const char *content_coding
,
291 const char *type
, size_t length
);
292 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
293 const char *message
, ...)
294 __attribute__ ((__format__ (__printf__
, 3, 4)));
295 static void respond_unsupported(_ipp_client_t
*client
,
296 ipp_attribute_t
*attr
);
297 static void run_printer(_ipp_printer_t
*printer
);
298 static void usage(int status
) __attribute__((noreturn
));
299 static int valid_doc_attributes(_ipp_client_t
*client
);
300 static int valid_job_attributes(_ipp_client_t
*client
);
307 static int KeepFiles
= 0,
312 * 'main()' - Main entry to the sample server.
315 int /* O - Exit status */
316 main(int argc
, /* I - Number of command-line args */
317 char *argv
[]) /* I - Command-line arguments */
319 int i
; /* Looping var */
320 const char *opt
, /* Current option character */
321 *command
= NULL
, /* Command to run with job files */
322 *servername
= NULL
, /* Server host name */
323 *name
= NULL
, /* Printer name */
324 *location
= "", /* Location of printer */
325 *make
= "Test", /* Manufacturer */
326 *model
= "Printer", /* Model */
327 *icon
= "printer.png", /* Icon file */
328 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
329 /* Supported formats */
331 const char *keypath
= NULL
; /* Keychain path */
332 #endif /* HAVE_SSL */
334 const char *subtype
= "_print"; /* Bonjour service subtype */
335 #endif /* HAVE_DNSSD */
336 int port
= 8631, /* Port number (0 = auto) */
337 duplex
= 0, /* Duplex mode */
338 ppm
= 10, /* Pages per minute for mono */
339 ppm_color
= 0, /* Pages per minute for color */
340 pin
= 0; /* PIN printing mode? */
341 char directory
[1024] = ""; /* Spool directory */
342 _ipp_printer_t
*printer
; /* Printer object */
346 * Parse command-line arguments...
349 for (i
= 1; i
< argc
; i
++)
350 if (argv
[i
][0] == '-')
352 for (opt
= argv
[i
] + 1; *opt
; opt
++)
356 case '2' : /* -2 (enable 2-sided printing) */
361 case 'K' : /* -K keypath */
367 #endif /* HAVE_SSL */
369 case 'M' : /* -M manufacturer */
376 case 'P' : /* -P (PIN printing mode) */
380 case 'c' : /* -c command */
388 case 'd' : /* -d spool-directory */
392 strlcpy(directory
, argv
[i
], sizeof(directory
));
395 case 'f' : /* -f type/subtype[,...] */
402 case 'h' : /* -h (show help) */
405 case 'i' : /* -i icon.png */
412 case 'k' : /* -k (keep files) */
416 case 'l' : /* -l location */
423 case 'm' : /* -m model */
430 case 'n' : /* -n hostname */
434 servername
= argv
[i
];
437 case 'p' : /* -p port */
439 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
441 port
= atoi(argv
[i
]);
445 case 'r' : /* -r subtype */
451 #endif /* HAVE_DNSSD */
453 case 's' : /* -s speed[,color-speed] */
457 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
461 case 'v' : /* -v (be verbose) */
465 default : /* Unknown */
466 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
477 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
485 * Apply defaults as needed...
490 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
492 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
494 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
495 directory
, strerror(errno
));
500 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
504 cupsSetServerCredentials(keypath
, servername
, 1);
505 #endif /* HAVE_SSL */
508 * Create the printer...
511 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
512 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
515 #endif /* HAVE_DNSSD */
516 directory
, command
)) == NULL
)
520 * Run the print service...
523 run_printer(printer
);
526 * Destroy the printer and exit...
529 delete_printer(printer
);
536 * 'clean_jobs()' - Clean out old (completed) jobs.
540 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
542 _ipp_job_t
*job
; /* Current job */
543 time_t cleantime
; /* Clean time */
546 if (cupsArrayCount(printer
->jobs
) == 0)
549 cleantime
= time(NULL
) - 60;
551 _cupsRWLockWrite(&(printer
->rwlock
));
552 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
554 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
555 if (job
->completed
&& job
->completed
< cleantime
)
557 cupsArrayRemove(printer
->jobs
, job
);
562 _cupsRWUnlock(&(printer
->rwlock
));
567 * 'compare_jobs()' - Compare two jobs.
570 static int /* O - Result of comparison */
571 compare_jobs(_ipp_job_t
*a
, /* I - First job */
572 _ipp_job_t
*b
) /* I - Second job */
574 return (b
->id
- a
->id
);
579 * 'copy_attributes()' - Copy attributes from one request to another.
583 copy_attributes(ipp_t
*to
, /* I - Destination request */
584 ipp_t
*from
, /* I - Source request */
585 cups_array_t
*ra
, /* I - Requested attributes */
586 ipp_tag_t group_tag
, /* I - Group to copy */
587 int quickcopy
) /* I - Do a quick copy? */
589 ipp_attribute_t
*fromattr
; /* Source attribute */
595 for (fromattr
= ippFirstAttribute(from
);
597 fromattr
= ippNextAttribute(from
))
600 * Filter attributes as needed...
603 ipp_tag_t fromgroup
= ippGetGroupTag(fromattr
);
604 const char *fromname
= ippGetName(fromattr
);
606 if ((group_tag
!= IPP_TAG_ZERO
&& fromgroup
!= group_tag
&&
607 fromgroup
!= IPP_TAG_ZERO
) || !fromname
)
610 if (!ra
|| cupsArrayFind(ra
, (void *)fromname
))
611 ippCopyAttribute(to
, fromattr
, quickcopy
);
617 * 'copy_job_attrs()' - Copy job attributes to the response.
622 _ipp_client_t
*client
, /* I - Client */
623 _ipp_job_t
*job
, /* I - Job */
624 cups_array_t
*ra
) /* I - requested-attributes */
626 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
628 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
629 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
630 "job-printer-up-time", (int)time(NULL
));
632 if (!ra
|| cupsArrayFind(ra
, "job-state"))
633 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
634 "job-state", job
->state
);
636 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
640 case IPP_JSTATE_PENDING
:
641 ippAddString(client
->response
, IPP_TAG_JOB
,
642 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
646 case IPP_JSTATE_HELD
:
648 ippAddString(client
->response
, IPP_TAG_JOB
,
649 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
650 "job-state-reasons", NULL
, "job-incoming");
651 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
652 ippAddString(client
->response
, IPP_TAG_JOB
,
653 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
654 "job-state-reasons", NULL
, "job-hold-until-specified");
656 ippAddString(client
->response
, IPP_TAG_JOB
,
657 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
658 "job-state-reasons", NULL
, "job-data-insufficient");
661 case IPP_JSTATE_PROCESSING
:
663 ippAddString(client
->response
, IPP_TAG_JOB
,
664 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
665 "job-state-reasons", NULL
, "processing-to-stop-point");
667 ippAddString(client
->response
, IPP_TAG_JOB
,
668 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
669 "job-state-reasons", NULL
, "job-printing");
672 case IPP_JSTATE_STOPPED
:
673 ippAddString(client
->response
, IPP_TAG_JOB
,
674 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
675 NULL
, "job-stopped");
678 case IPP_JSTATE_CANCELED
:
679 ippAddString(client
->response
, IPP_TAG_JOB
,
680 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
681 NULL
, "job-canceled-by-user");
684 case IPP_JSTATE_ABORTED
:
685 ippAddString(client
->response
, IPP_TAG_JOB
,
686 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
687 NULL
, "aborted-by-system");
690 case IPP_JSTATE_COMPLETED
:
691 ippAddString(client
->response
, IPP_TAG_JOB
,
692 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
693 NULL
, "job-completed-successfully");
698 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
699 ippAddInteger(client
->response
, IPP_TAG_JOB
,
700 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
701 "time-at-completed", (int)job
->completed
);
703 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
704 ippAddInteger(client
->response
, IPP_TAG_JOB
,
705 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
706 "time-at-processing", (int)job
->processing
);
711 * 'create_client()' - Accept a new network connection and create a client
715 static _ipp_client_t
* /* O - Client */
716 create_client(_ipp_printer_t
*printer
, /* I - Printer */
717 int sock
) /* I - Listen socket */
719 _ipp_client_t
*client
; /* Client */
722 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
724 perror("Unable to allocate memory for client");
728 client
->printer
= printer
;
731 * Accept the client and get the remote address...
734 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
736 perror("Unable to accept client connection");
743 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
746 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
753 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
757 static _ipp_job_t
* /* O - Job */
758 create_job(_ipp_client_t
*client
) /* I - Client */
760 _ipp_job_t
*job
; /* Job */
761 ipp_attribute_t
*attr
; /* Job attribute */
762 char uri
[1024]; /* job-uri value */
765 _cupsRWLockWrite(&(client
->printer
->rwlock
));
766 if (client
->printer
->active_job
&&
767 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
770 * Only accept a single job at a time...
773 _cupsRWLockWrite(&(client
->printer
->rwlock
));
778 * Allocate and initialize the job object...
781 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
783 perror("Unable to allocate memory for job");
787 job
->printer
= client
->printer
;
788 job
->attrs
= client
->request
;
789 job
->state
= IPP_JSTATE_HELD
;
791 client
->request
= NULL
;
794 * Set all but the first two attributes to the job attributes group...
797 for (ippFirstAttribute(job
->attrs
),
798 ippNextAttribute(job
->attrs
),
799 attr
= ippNextAttribute(job
->attrs
);
801 attr
= ippNextAttribute(job
->attrs
))
802 ippSetGroupTag(job
->attrs
, &attr
, IPP_TAG_JOB
);
805 * Get the requesting-user-name, document format, and priority...
808 if ((attr
= ippFindAttribute(job
->attrs
, "requesting-user-name",
809 IPP_TAG_NAME
)) != NULL
)
810 ippSetName(job
->attrs
, &attr
, "job-originating-user-name");
812 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
,
813 IPP_CONST_TAG(IPP_TAG_NAME
),
814 "job-originating-user-name", NULL
, "anonymous");
817 job
->username
= ippGetString(attr
, 0, NULL
);
819 job
->username
= "anonymous";
821 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
822 IPP_TAG_MIMETYPE
)) != NULL
)
823 job
->format
= ippGetString(attr
, 0, NULL
);
825 job
->format
= "application/octet-stream";
828 * Add job description attributes and add to the jobs array...
831 job
->id
= client
->printer
->next_job_id
++;
833 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
835 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
836 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
837 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
838 client
->printer
->uri
);
839 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
842 cupsArrayAdd(client
->printer
->jobs
, job
);
843 client
->printer
->active_job
= job
;
845 _cupsRWUnlock(&(client
->printer
->rwlock
));
852 * 'create_listener()' - Create a listener socket.
855 static int /* O - Listener socket or -1 on error */
856 create_listener(int family
, /* I - Address family */
857 int *port
) /* IO - Port number */
859 int sock
; /* Listener socket */
860 http_addrlist_t
*addrlist
; /* Listen address */
861 char service
[255]; /* Service port */
866 *port
= 8000 + (getuid() % 1000);
867 fprintf(stderr
, "Listening on port %d.\n", *port
);
870 snprintf(service
, sizeof(service
), "%d", *port
);
871 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
874 sock
= httpAddrListen(&(addrlist
->addr
), *port
);
876 httpAddrFreeList(addrlist
);
883 * 'create_media_col()' - Create a media-col value.
886 static ipp_t
* /* O - media-col collection */
887 create_media_col(const char *media
, /* I - Media name */
888 const char *type
, /* I - Nedua type */
889 int width
, /* I - x-dimension in 2540ths */
890 int length
, /* I - y-dimension in 2540ths */
891 int margins
) /* I - Value for margins */
893 ipp_t
*media_col
= ippNew(), /* media-col value */
894 *media_size
= create_media_size(width
, length
);
895 /* media-size value */
896 char media_key
[256]; /* media-key value */
899 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, type
,
900 margins
== 0 ? "_borderless" : "");
902 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
904 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
905 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
906 "media-bottom-margin", margins
);
907 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
908 "media-left-margin", margins
);
909 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
910 "media-right-margin", margins
);
911 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
912 "media-top-margin", margins
);
913 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type",
916 ippDelete(media_size
);
923 * 'create_media_size()' - Create a media-size value.
926 static ipp_t
* /* O - media-col collection */
927 create_media_size(int width
, /* I - x-dimension in 2540ths */
928 int length
) /* I - y-dimension in 2540ths */
930 ipp_t
*media_size
= ippNew(); /* media-size value */
933 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
935 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
943 * 'create_printer()' - Create, register, and listen for connections to a
947 static _ipp_printer_t
* /* O - Printer */
948 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
949 const char *name
, /* I - printer-name */
950 const char *location
, /* I - printer-location */
951 const char *make
, /* I - printer-make-and-model */
952 const char *model
, /* I - printer-make-and-model */
953 const char *icon
, /* I - printer-icons */
954 const char *docformats
, /* I - document-format-supported */
955 int ppm
, /* I - Pages per minute in grayscale */
956 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
957 int duplex
, /* I - 1 = duplex, 0 = simplex */
958 int port
, /* I - Port for listeners or 0 for auto */
959 int pin
, /* I - Require PIN printing */
961 const char *subtype
, /* I - Bonjour service subtype */
962 #endif /* HAVE_DNSSD */
963 const char *directory
, /* I - Spool directory */
964 const char *command
) /* I - Command to run on job files */
966 int i
, j
; /* Looping vars */
967 _ipp_printer_t
*printer
; /* Printer */
968 char hostname
[256], /* Hostname */
969 uri
[1024], /* Printer URI */
970 icons
[1024], /* printer-icons URI */
971 adminurl
[1024], /* printer-more-info URI */
972 device_id
[1024],/* printer-device-id */
973 make_model
[128];/* printer-make-and-model */
974 int num_formats
; /* Number of document-format-supported values */
975 char *defformat
, /* document-format-default value */
976 *formats
[100], /* document-format-supported values */
977 *ptr
; /* Pointer into string */
978 const char *prefix
; /* Prefix string */
979 int num_database
; /* Number of database values */
980 ipp_attribute_t
*media_col_database
,
981 /* media-col-database value */
982 *media_size_supported
;
983 /* media-size-supported value */
984 ipp_t
*media_col_default
;
985 /* media-col-default value */
986 int media_col_index
;/* Current media-col-database value */
987 int k_supported
; /* Maximum file size supported */
989 struct statvfs spoolinfo
; /* FS info for spool directory */
990 double spoolsize
; /* FS size */
991 #elif defined(HAVE_STATFS)
992 struct statfs spoolinfo
; /* FS info for spool directory */
993 double spoolsize
; /* FS size */
994 #endif /* HAVE_STATVFS */
995 static const int orients
[4] = /* orientation-requested-supported values */
998 IPP_ORIENT_LANDSCAPE
,
999 IPP_ORIENT_REVERSE_LANDSCAPE
,
1000 IPP_ORIENT_REVERSE_PORTRAIT
1002 static const char * const versions
[] =/* ipp-versions-supported values */
1008 static const char * const features
[] =/* ipp-features-supported values */
1012 static const int ops
[] = /* operations-supported values */
1016 IPP_OP_VALIDATE_JOB
,
1018 IPP_OP_SEND_DOCUMENT
,
1021 IPP_OP_GET_JOB_ATTRIBUTES
,
1023 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1024 IPP_OP_CANCEL_MY_JOBS
,
1026 IPP_OP_IDENTIFY_PRINTER
1028 static const char * const charsets
[] =/* charset-supported values */
1033 static const char * const compressions
[] =/* compression-supported values */
1038 #endif /* HAVE_LIBZ */
1041 static const char * const identify_actions
[] =
1046 static const char * const job_creation
[] =
1047 { /* job-creation-attributes-supported values */
1049 "ipp-attribute-fidelity",
1051 "job-accounting-user-id",
1057 "multiple-document-handling",
1058 "orientation-requested",
1062 static const char * const media_col_supported
[] =
1063 { /* media-col-supported values */
1064 "media-bottom-margin",
1065 "media-left-margin",
1066 "media-right-margin",
1071 static const int media_xxx_margin_supported
[] =
1072 { /* media-xxx-margin-supported values */
1076 static const char * const multiple_document_handling
[] =
1077 { /* multiple-document-handling-supported values */
1078 "separate-documents-uncollated-copies",
1079 "separate-documents-collated-copies"
1081 static const char * const print_color_mode_supported
[] =
1082 { /* print-color-mode-supported values */
1087 static const int print_quality_supported
[] =
1088 { /* print-quality-supported values */
1093 static const int pwg_raster_document_resolution_supported
[] =
1099 static const char * const pwg_raster_document_type_supported
[] =
1107 static const char * const reference_uri_schemes_supported
[] =
1108 { /* reference-uri-schemes-supported */
1114 #endif /* HAVE_SSL */
1116 static const char * const sides_supported
[] =
1117 { /* sides-supported values */
1119 "two-sided-long-edge",
1120 "two-sided-short-edge"
1122 static const char * const which_jobs
[] =
1123 { /* which-jobs-supported values */
1132 "processing-stopped"
1137 * Allocate memory for the printer...
1140 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1142 perror("Unable to allocate memory for printer");
1148 printer
->name
= strdup(name
);
1150 printer
->dnssd_name
= strdup(printer
->name
);
1151 #endif /* HAVE_DNSSD */
1152 printer
->command
= command
? strdup(command
) : NULL
;
1153 printer
->directory
= strdup(directory
);
1154 printer
->hostname
= strdup(servername
? servername
:
1155 httpGetHostname(NULL
, hostname
,
1157 printer
->port
= port
;
1158 printer
->state
= IPP_PSTATE_IDLE
;
1159 printer
->state_reasons
= _IPP_PSTATE_NONE
;
1160 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1161 printer
->next_job_id
= 1;
1163 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1164 printer
->hostname
, printer
->port
, "/ipp/print");
1165 printer
->uri
= strdup(uri
);
1166 printer
->urilen
= strlen(uri
);
1169 printer
->icon
= strdup(icon
);
1171 _cupsRWInit(&(printer
->rwlock
));
1174 * Create the listener sockets...
1177 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1179 perror("Unable to create IPv4 listener");
1183 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1185 perror("Unable to create IPv6 listener");
1190 * Prepare values for the printer attributes...
1193 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1194 printer
->hostname
, printer
->port
, "/icon.png");
1195 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
,
1196 printer
->hostname
, printer
->port
, "/");
1200 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1201 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1204 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1207 formats
[0] = strdup(docformats
);
1208 defformat
= formats
[0];
1209 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1212 formats
[num_formats
++] = ptr
;
1214 if (!strcasecmp(ptr
, "application/octet-stream"))
1218 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1219 ptr
= device_id
+ strlen(device_id
);
1221 for (i
= 0; i
< num_formats
; i
++)
1223 if (!strcasecmp(formats
[i
], "application/pdf"))
1224 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1225 else if (!strcasecmp(formats
[i
], "application/postscript"))
1226 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1227 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1228 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1229 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1230 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1231 else if (!strcasecmp(formats
[i
], "image/png"))
1232 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1233 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1234 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1239 strlcat(device_id
, ";", sizeof(device_id
));
1242 * Get the maximum spool size based on the size of the filesystem used for
1243 * the spool directory. If the host OS doesn't support the statfs call
1244 * or the filesystem is larger than 2TiB, always report INT_MAX.
1248 if (statvfs(printer
->directory
, &spoolinfo
))
1249 k_supported
= INT_MAX
;
1250 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1251 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1252 k_supported
= INT_MAX
;
1254 k_supported
= (int)spoolsize
;
1256 #elif defined(HAVE_STATFS)
1257 if (statfs(printer
->directory
, &spoolinfo
))
1258 k_supported
= INT_MAX
;
1259 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1260 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1261 k_supported
= INT_MAX
;
1263 k_supported
= (int)spoolsize
;
1266 k_supported
= INT_MAX
;
1267 #endif /* HAVE_STATVFS */
1270 * Create the printer attributes. This list of attributes is sorted to improve
1271 * performance when the client provides a requested-attributes attribute...
1274 printer
->attrs
= ippNew();
1276 /* charset-configured */
1277 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1278 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1279 "charset-configured", NULL
, "utf-8");
1281 /* charset-supported */
1282 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1283 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1284 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1287 /* color-supported */
1288 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1291 /* compression-supported */
1292 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1293 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1294 "compression-supported",
1295 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1298 /* copies-default */
1299 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1300 "copies-default", 1);
1302 /* copies-supported */
1303 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1305 /* document-format-default */
1306 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1307 "document-format-default", NULL
, defformat
);
1309 /* document-format-supported */
1310 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1311 "document-format-supported", num_formats
, NULL
,
1312 (const char * const *)formats
);
1314 /* finishings-default */
1315 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1316 "finishings-default", IPP_FINISHINGS_NONE
);
1318 /* finishings-supported */
1319 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1320 "finishings-supported", IPP_FINISHINGS_NONE
);
1322 /* generated-natural-language-supported */
1323 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1324 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1325 "generated-natural-language-supported", NULL
, "en");
1327 /* identify-actions-default */
1328 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1330 /* identify-actions-supported */
1331 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-supported", sizeof(identify_actions
) / sizeof(identify_actions
[0]), NULL
, identify_actions
);
1333 /* ipp-features-supported */
1334 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1336 /* ipp-versions-supported */
1337 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1339 /* job-account-id-default */
1340 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1342 /* job-account-id-supported */
1343 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1345 /* job-accounting-user-id-default */
1346 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1348 /* job-accounting-user-id-supported */
1349 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1351 /* job-creation-attributes-supported */
1352 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-creation-attributes-supported", sizeof(job_creation
) / sizeof(job_creation
[0]), NULL
, job_creation
);
1354 /* job-ids-supported */
1355 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1357 /* job-k-octets-supported */
1358 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1361 /* job-password-supported */
1362 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1363 "job-password-supported", 4);
1365 /* job-priority-default */
1366 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1367 "job-priority-default", 50);
1369 /* job-priority-supported */
1370 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1371 "job-priority-supported", 100);
1373 /* job-sheets-default */
1374 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1375 IPP_CONST_TAG(IPP_TAG_NAME
),
1376 "job-sheets-default", NULL
, "none");
1378 /* job-sheets-supported */
1379 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1380 IPP_CONST_TAG(IPP_TAG_NAME
),
1381 "job-sheets-supported", NULL
, "none");
1383 /* media-bottom-margin-supported */
1384 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1385 "media-bottom-margin-supported",
1386 (int)(sizeof(media_xxx_margin_supported
) /
1387 sizeof(media_xxx_margin_supported
[0])),
1388 media_xxx_margin_supported
);
1390 /* media-col-database */
1391 for (num_database
= 0, i
= 0;
1392 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1395 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1396 num_database
+= 2; /* auto + envelope */
1397 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1398 num_database
+= 12; /* auto + photographic-* + borderless */
1400 num_database
+= (int)(sizeof(media_type_supported
) /
1401 sizeof(media_type_supported
[0])) + 6;
1402 /* All types + borderless */
1405 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1406 "media-col-database", num_database
,
1408 for (media_col_index
= 0, i
= 0;
1409 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1413 j
< (int)(sizeof(media_type_supported
) /
1414 sizeof(media_type_supported
[0]));
1417 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
&&
1418 strcmp(media_type_supported
[j
], "auto") &&
1419 strcmp(media_type_supported
[j
], "envelope"))
1421 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
&&
1422 strcmp(media_type_supported
[j
], "auto") &&
1423 strncmp(media_type_supported
[j
], "photographic-", 13))
1426 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1427 create_media_col(media_supported
[i
],
1428 media_type_supported
[j
],
1429 media_col_sizes
[i
][0],
1430 media_col_sizes
[i
][1],
1431 media_xxx_margin_supported
[1]));
1434 if (media_col_sizes
[i
][2] != _IPP_ENV_ONLY
&&
1435 (!strcmp(media_type_supported
[j
], "auto") ||
1436 !strncmp(media_type_supported
[j
], "photographic-", 13)))
1439 * Add borderless version for this combination...
1442 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1443 create_media_col(media_supported
[i
],
1444 media_type_supported
[j
],
1445 media_col_sizes
[i
][0],
1446 media_col_sizes
[i
][1],
1447 media_xxx_margin_supported
[0]));
1453 /* media-col-default */
1454 media_col_default
= create_media_col(media_supported
[0],
1455 media_type_supported
[0],
1456 media_col_sizes
[0][0],
1457 media_col_sizes
[0][1],
1458 media_xxx_margin_supported
[1]);
1460 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1462 ippDelete(media_col_default
);
1464 /* media-col-supported */
1465 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1466 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1467 "media-col-supported",
1468 (int)(sizeof(media_col_supported
) /
1469 sizeof(media_col_supported
[0])), NULL
,
1470 media_col_supported
);
1473 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1474 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1475 "media-default", NULL
, media_supported
[0]);
1477 /* media-left-margin-supported */
1478 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1479 "media-left-margin-supported",
1480 (int)(sizeof(media_xxx_margin_supported
) /
1481 sizeof(media_xxx_margin_supported
[0])),
1482 media_xxx_margin_supported
);
1484 /* media-right-margin-supported */
1485 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1486 "media-right-margin-supported",
1487 (int)(sizeof(media_xxx_margin_supported
) /
1488 sizeof(media_xxx_margin_supported
[0])),
1489 media_xxx_margin_supported
);
1491 /* media-supported */
1492 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1493 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1495 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1496 NULL
, media_supported
);
1498 /* media-size-supported */
1499 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1500 "media-size-supported",
1501 (int)(sizeof(media_col_sizes
) /
1502 sizeof(media_col_sizes
[0])),
1505 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1507 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1508 create_media_size(media_col_sizes
[i
][0],
1509 media_col_sizes
[i
][1]));
1511 /* media-top-margin-supported */
1512 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1513 "media-top-margin-supported",
1514 (int)(sizeof(media_xxx_margin_supported
) /
1515 sizeof(media_xxx_margin_supported
[0])),
1516 media_xxx_margin_supported
);
1518 /* media-type-supported */
1519 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1520 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1521 "media-type-supported",
1522 (int)(sizeof(media_type_supported
) /
1523 sizeof(media_type_supported
[0])),
1524 NULL
, media_type_supported
);
1526 /* multiple-document-handling-supported */
1527 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-document-handling-supported", sizeof(multiple_document_handling
) / sizeof(multiple_document_handling
[0]), NULL
, multiple_document_handling
);
1529 /* multiple-document-jobs-supported */
1530 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1532 /* multiple-operation-time-out */
1533 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1535 /* multiple-operation-time-out-action */
1536 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "cancel-job");
1538 /* natural-language-configured */
1539 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1540 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1541 "natural-language-configured", NULL
, "en");
1543 /* number-up-default */
1544 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1545 "number-up-default", 1);
1547 /* number-up-supported */
1548 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1549 "number-up-supported", 1);
1551 /* operations-supported */
1552 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1553 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1555 /* orientation-requested-default */
1556 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1557 "orientation-requested-default", 0);
1559 /* orientation-requested-supported */
1560 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1561 "orientation-requested-supported", 4, orients
);
1563 /* output-bin-default */
1564 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1565 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1566 "output-bin-default", NULL
, "face-down");
1568 /* output-bin-supported */
1569 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1570 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1571 "output-bin-supported", NULL
, "face-down");
1573 /* overrides-supported */
1574 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "overrides-supported", 0);
1576 /* page-ranges-supported */
1577 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 0);
1579 /* pages-per-minute */
1580 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1581 "pages-per-minute", ppm
);
1583 /* pages-per-minute-color */
1585 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1586 "pages-per-minute-color", ppm_color
);
1588 /* pdl-override-supported */
1589 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1590 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1591 "pdl-override-supported", NULL
, "attempted");
1593 /* preferred-attributes-supported */
1594 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "preferred-attributes-supported", 0);
1596 /* print-color-mode-default */
1597 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1599 /* print-color-mode-supported */
1600 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported
) / sizeof(print_color_mode_supported
[0])), NULL
, print_color_mode_supported
);
1602 /* print-content-optimize-default */
1603 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1605 /* print-content-optimize-supported */
1606 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1608 /* print-rendering-intent-default */
1609 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1611 /* print-rendering-intent-supported */
1612 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1614 /* print-quality-default */
1615 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1617 /* print-quality-supported */
1618 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-supported", (int)(sizeof(print_quality_supported
) / sizeof(print_quality_supported
[0])), print_quality_supported
);
1620 /* printer-device-id */
1621 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1622 "printer-device-id", NULL
, device_id
);
1624 /* printer-get-attributes-supported */
1625 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1627 /* printer-geo-location */
1628 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "printer-geo-location", 0);
1631 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1632 "printer-icons", NULL
, icons
);
1634 /* printer-is-accepting-jobs */
1635 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1639 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1642 /* printer-location */
1643 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1644 "printer-location", NULL
, location
);
1646 /* printer-make-and-model */
1647 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1648 "printer-make-and-model", NULL
, make_model
);
1650 /* printer-mandatory-job-attributes */
1653 static const char * const names
[] =
1655 "job-accounting-user-id",
1659 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1660 "printer-mandatory-job-attributes",
1661 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1664 /* printer-more-info */
1665 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1666 "printer-more-info", NULL
, adminurl
);
1669 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1672 /* printer-organization */
1673 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "printer-organization", NULL
, "Apple Inc.");
1675 /* printer-organizational-unit */
1676 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "printer-organizational-unit", NULL
, "Printing Engineering");
1678 /* printer-resolution-default */
1679 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1680 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1682 /* printer-resolution-supported */
1683 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1684 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1686 /* printer-uri-supported */
1687 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1688 "printer-uri-supported", NULL
, uri
);
1690 /* pwg-raster-document-xxx-supported */
1691 for (i
= 0; i
< num_formats
; i
++)
1692 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
1695 if (i
< num_formats
)
1697 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
1698 "pwg-raster-document-resolution-supported",
1699 (int)(sizeof(pwg_raster_document_resolution_supported
) /
1700 sizeof(pwg_raster_document_resolution_supported
[0])),
1702 pwg_raster_document_resolution_supported
,
1703 pwg_raster_document_resolution_supported
);
1704 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1705 "pwg-raster-document-sheet-back", NULL
, "normal");
1706 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1707 "pwg-raster-document-type-supported",
1708 (int)(sizeof(pwg_raster_document_type_supported
) /
1709 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
1710 pwg_raster_document_type_supported
);
1713 /* reference-uri-scheme-supported */
1714 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1715 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
1716 "reference-uri-schemes-supported",
1717 (int)(sizeof(reference_uri_schemes_supported
) /
1718 sizeof(reference_uri_schemes_supported
[0])),
1719 NULL
, reference_uri_schemes_supported
);
1722 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1723 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1724 "sides-default", NULL
, "one-sided");
1726 /* sides-supported */
1727 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1728 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1729 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1731 /* uri-authentication-supported */
1732 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1733 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1734 "uri-authentication-supported", NULL
, "none");
1736 /* uri-security-supported */
1737 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1738 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1739 "uri-security-supported", NULL
, "none");
1741 /* which-jobs-supported */
1742 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1743 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1744 "which-jobs-supported",
1745 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1749 debug_attributes("Printer", printer
->attrs
, 0);
1753 * Register the printer with Bonjour...
1756 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
,
1757 ppm_color
> 0, duplex
, subtype
))
1759 #endif /* HAVE_DNSSD */
1769 * If we get here we were unable to create the printer...
1774 delete_printer(printer
);
1780 * 'debug_attributes()' - Print attributes in a request or response.
1784 debug_attributes(const char *title
, /* I - Title */
1785 ipp_t
*ipp
, /* I - Request/response */
1786 int type
) /* I - 0 = object, 1 = request, 2 = response */
1788 ipp_tag_t group_tag
; /* Current group */
1789 ipp_attribute_t
*attr
; /* Current attribute */
1790 char buffer
[2048]; /* String buffer for value */
1791 int major
, minor
; /* Version */
1797 fprintf(stderr
, "%s:\n", title
);
1798 major
= ippGetVersion(ipp
, &minor
);
1799 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1801 fprintf(stderr
, " operation-id=%s(%04x)\n",
1802 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1804 fprintf(stderr
, " status-code=%s(%04x)\n",
1805 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1806 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1808 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1810 attr
= ippNextAttribute(ipp
))
1812 if (ippGetGroupTag(attr
) != group_tag
)
1814 group_tag
= ippGetGroupTag(attr
);
1815 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1818 if (ippGetName(attr
))
1820 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1821 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1822 ippGetCount(attr
) > 1 ? "1setOf " : "",
1823 ippTagString(ippGetValueTag(attr
)), buffer
);
1830 * 'delete_client()' - Close the socket and free all memory used by a client
1835 delete_client(_ipp_client_t
*client
) /* I - Client */
1838 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1841 * Flush pending writes before closing...
1844 httpFlushWrite(client
->http
);
1850 httpClose(client
->http
);
1852 ippDelete(client
->request
);
1853 ippDelete(client
->response
);
1860 * 'delete_job()' - Remove from the printer and free all memory used by a job
1865 delete_job(_ipp_job_t
*job
) /* I - Job */
1868 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
1870 ippDelete(job
->attrs
);
1875 unlink(job
->filename
);
1877 free(job
->filename
);
1885 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1886 * used by a printer object.
1890 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
1892 if (printer
->ipv4
>= 0)
1893 close(printer
->ipv4
);
1895 if (printer
->ipv6
>= 0)
1896 close(printer
->ipv6
);
1899 if (printer
->printer_ref
)
1900 DNSServiceRefDeallocate(printer
->printer_ref
);
1902 if (printer
->ipp_ref
)
1903 DNSServiceRefDeallocate(printer
->ipp_ref
);
1906 if (printer
->ipps_ref
)
1907 DNSServiceRefDeallocate(printer
->ipps_ref
);
1908 # endif /* HAVE_SSL */
1909 if (printer
->http_ref
)
1910 DNSServiceRefDeallocate(printer
->http_ref
);
1912 if (printer
->common_ref
)
1913 DNSServiceRefDeallocate(printer
->common_ref
);
1915 TXTRecordDeallocate(&(printer
->ipp_txt
));
1917 if (printer
->dnssd_name
)
1918 free(printer
->dnssd_name
);
1919 #endif /* HAVE_DNSSD */
1922 free(printer
->name
);
1924 free(printer
->icon
);
1925 if (printer
->command
)
1926 free(printer
->command
);
1927 if (printer
->directory
)
1928 free(printer
->directory
);
1929 if (printer
->hostname
)
1930 free(printer
->hostname
);
1934 ippDelete(printer
->attrs
);
1935 cupsArrayDelete(printer
->jobs
);
1943 * 'dnssd_callback()' - Handle Bonjour registration events.
1948 DNSServiceRef sdRef
, /* I - Service reference */
1949 DNSServiceFlags flags
, /* I - Status flags */
1950 DNSServiceErrorType errorCode
, /* I - Error, if any */
1951 const char *name
, /* I - Service name */
1952 const char *regtype
, /* I - Service type */
1953 const char *domain
, /* I - Domain for service */
1954 _ipp_printer_t
*printer
) /* I - Printer */
1962 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
1963 regtype
, (int)errorCode
);
1966 else if (strcasecmp(name
, printer
->dnssd_name
))
1969 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
1971 /* No lock needed since only the main thread accesses/changes this */
1972 free(printer
->dnssd_name
);
1973 printer
->dnssd_name
= strdup(name
);
1976 #endif /* HAVE_DNSSD */
1980 * 'find_job()' - Find a job specified in a request.
1983 static _ipp_job_t
* /* O - Job or NULL */
1984 find_job(_ipp_client_t
*client
) /* I - Client */
1986 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
1987 _ipp_job_t key
, /* Job search key */
1988 *job
; /* Matching job, if any */
1993 if ((attr
= ippFindAttribute(client
->request
, "job-uri",
1994 IPP_TAG_URI
)) != NULL
)
1996 const char *uri
= ippGetString(attr
, 0, NULL
);
1998 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
1999 uri
[client
->printer
->urilen
] == '/')
2000 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2002 else if ((attr
= ippFindAttribute(client
->request
, "job-id",
2003 IPP_TAG_INTEGER
)) != NULL
)
2004 key
.id
= ippGetInteger(attr
, 0);
2006 _cupsRWLockRead(&(client
->printer
->rwlock
));
2007 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2008 _cupsRWUnlock(&(client
->printer
->rwlock
));
2015 * 'html_escape()' - Write a HTML-safe string.
2019 html_escape(_ipp_client_t
*client
, /* I - Client */
2020 const char *s
, /* I - String to write */
2021 size_t slen
) /* I - Number of characters to write */
2023 const char *start
, /* Start of segment */
2024 *end
; /* End of string */
2028 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2030 while (*s
&& s
< end
)
2032 if (*s
== '&' || *s
== '<')
2035 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2038 httpWrite2(client
->http
, "&", 5);
2040 httpWrite2(client
->http
, "<", 4);
2049 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2054 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2058 html_printf(_ipp_client_t
*client
, /* I - Client */
2059 const char *format
, /* I - Printf-style format string */
2060 ...) /* I - Additional arguments as needed */
2062 va_list ap
; /* Pointer to arguments */
2063 const char *start
; /* Start of string */
2064 char size
, /* Size character (h, l, L) */
2065 type
; /* Format type character */
2066 int width
, /* Width of field */
2067 prec
; /* Number of characters of precision */
2068 char tformat
[100], /* Temporary format string for sprintf() */
2069 *tptr
, /* Pointer into temporary format */
2070 temp
[1024]; /* Buffer for formatted numbers */
2071 char *s
; /* Pointer to string */
2075 * Loop through the format string, formatting as needed...
2078 va_start(ap
, format
);
2086 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2089 *tptr
++ = *format
++;
2093 httpWrite2(client
->http
, "%", 1);
2097 else if (strchr(" -+#\'", *format
))
2098 *tptr
++ = *format
++;
2103 * Get width from argument...
2107 width
= va_arg(ap
, int);
2109 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2110 tptr
+= strlen(tptr
);
2116 while (isdigit(*format
& 255))
2118 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2121 width
= width
* 10 + *format
++ - '0';
2127 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2135 * Get precision from argument...
2139 prec
= va_arg(ap
, int);
2141 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2142 tptr
+= strlen(tptr
);
2148 while (isdigit(*format
& 255))
2150 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2153 prec
= prec
* 10 + *format
++ - '0';
2158 if (*format
== 'l' && format
[1] == 'l')
2162 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2170 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2172 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2187 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2196 case 'E' : /* Floating point formats */
2201 if ((size_t)(width
+ 2) > sizeof(temp
))
2204 sprintf(temp
, tformat
, va_arg(ap
, double));
2206 httpWrite2(client
->http
, temp
, strlen(temp
));
2209 case 'B' : /* Integer formats */
2217 if ((size_t)(width
+ 2) > sizeof(temp
))
2220 # ifdef HAVE_LONG_LONG
2222 sprintf(temp
, tformat
, va_arg(ap
, long long));
2224 # endif /* HAVE_LONG_LONG */
2226 sprintf(temp
, tformat
, va_arg(ap
, long));
2228 sprintf(temp
, tformat
, va_arg(ap
, int));
2230 httpWrite2(client
->http
, temp
, strlen(temp
));
2233 case 'p' : /* Pointer value */
2234 if ((size_t)(width
+ 2) > sizeof(temp
))
2237 sprintf(temp
, tformat
, va_arg(ap
, void *));
2239 httpWrite2(client
->http
, temp
, strlen(temp
));
2242 case 'c' : /* Character or character array */
2245 temp
[0] = (char)va_arg(ap
, int);
2247 html_escape(client
, temp
, 1);
2250 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2253 case 's' : /* String */
2254 if ((s
= va_arg(ap
, char *)) == NULL
)
2257 html_escape(client
, s
, strlen(s
));
2266 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2273 * 'ipp_cancel_job()' - Cancel a job.
2277 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2279 _ipp_job_t
*job
; /* Job information */
2286 if ((job
= find_job(client
)) == NULL
)
2288 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2293 * See if the job is already completed, canceled, or aborted; if so,
2294 * we can't cancel...
2299 case IPP_JSTATE_CANCELED
:
2300 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2301 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2304 case IPP_JSTATE_ABORTED
:
2305 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2306 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2309 case IPP_JSTATE_COMPLETED
:
2310 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2311 "Job #%d is already completed - can\'t cancel.", job
->id
);
2319 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2321 if (job
->state
== IPP_JSTATE_PROCESSING
||
2322 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2326 job
->state
= IPP_JSTATE_CANCELED
;
2327 job
->completed
= time(NULL
);
2330 _cupsRWUnlock(&(client
->printer
->rwlock
));
2332 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2339 * 'ipp_close_job()' - Close an open job.
2343 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2345 _ipp_job_t
*job
; /* Job information */
2352 if ((job
= find_job(client
)) == NULL
)
2354 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2359 * See if the job is already completed, canceled, or aborted; if so,
2360 * we can't cancel...
2365 case IPP_JSTATE_CANCELED
:
2366 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2367 "Job #%d is canceled - can\'t close.", job
->id
);
2370 case IPP_JSTATE_ABORTED
:
2371 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2372 "Job #%d is aborted - can\'t close.", job
->id
);
2375 case IPP_JSTATE_COMPLETED
:
2376 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2377 "Job #%d is completed - can\'t close.", job
->id
);
2380 case IPP_JSTATE_PROCESSING
:
2381 case IPP_JSTATE_STOPPED
:
2382 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2383 "Job #%d is already closed.", job
->id
);
2387 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2394 * 'ipp_create_job()' - Create a job object.
2398 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2400 _ipp_job_t
*job
; /* New job */
2401 cups_array_t
*ra
; /* Attributes to send in response */
2405 * Validate print job attributes...
2408 if (!valid_job_attributes(client
))
2410 httpFlush(client
->http
);
2415 * Do we have a file to print?
2418 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2420 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2421 "Unexpected document data following request.");
2429 if ((job
= create_job(client
)) == NULL
)
2431 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2432 "Currently printing another job.");
2437 * Return the job info...
2440 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2442 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2443 cupsArrayAdd(ra
, "job-id");
2444 cupsArrayAdd(ra
, "job-state");
2445 cupsArrayAdd(ra
, "job-state-reasons");
2446 cupsArrayAdd(ra
, "job-uri");
2448 copy_job_attributes(client
, job
, ra
);
2449 cupsArrayDelete(ra
);
2454 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2458 ipp_get_job_attributes(
2459 _ipp_client_t
*client
) /* I - Client */
2461 _ipp_job_t
*job
; /* Job */
2462 cups_array_t
*ra
; /* requested-attributes */
2465 if ((job
= find_job(client
)) == NULL
)
2467 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2471 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2473 ra
= ippCreateRequestedArray(client
->request
);
2474 copy_job_attributes(client
, job
, ra
);
2475 cupsArrayDelete(ra
);
2480 * 'ipp_get_jobs()' - Get a list of job objects.
2484 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2486 ipp_attribute_t
*attr
; /* Current attribute */
2487 const char *which_jobs
= NULL
;
2488 /* which-jobs values */
2489 int job_comparison
; /* Job comparison */
2490 ipp_jstate_t job_state
; /* job-state value */
2491 int first_job_id
, /* First job ID */
2492 limit
, /* Maximum number of jobs to return */
2493 count
; /* Number of jobs that match */
2494 const char *username
; /* Username */
2495 _ipp_job_t
*job
; /* Current job pointer */
2496 cups_array_t
*ra
; /* Requested attributes array */
2500 * See if the "which-jobs" attribute have been specified...
2503 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2504 IPP_TAG_KEYWORD
)) != NULL
)
2506 which_jobs
= ippGetString(attr
, 0, NULL
);
2507 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2510 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2512 job_comparison
= -1;
2513 job_state
= IPP_JSTATE_STOPPED
;
2515 else if (!strcmp(which_jobs
, "completed"))
2518 job_state
= IPP_JSTATE_CANCELED
;
2520 else if (!strcmp(which_jobs
, "aborted"))
2523 job_state
= IPP_JSTATE_ABORTED
;
2525 else if (!strcmp(which_jobs
, "all"))
2528 job_state
= IPP_JSTATE_PENDING
;
2530 else if (!strcmp(which_jobs
, "canceled"))
2533 job_state
= IPP_JSTATE_CANCELED
;
2535 else if (!strcmp(which_jobs
, "pending"))
2538 job_state
= IPP_JSTATE_PENDING
;
2540 else if (!strcmp(which_jobs
, "pending-held"))
2543 job_state
= IPP_JSTATE_HELD
;
2545 else if (!strcmp(which_jobs
, "processing"))
2548 job_state
= IPP_JSTATE_PROCESSING
;
2550 else if (!strcmp(which_jobs
, "processing-stopped"))
2553 job_state
= IPP_JSTATE_STOPPED
;
2557 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2558 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2559 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2560 "which-jobs", NULL
, which_jobs
);
2565 * See if they want to limit the number of jobs reported...
2568 if ((attr
= ippFindAttribute(client
->request
, "limit",
2569 IPP_TAG_INTEGER
)) != NULL
)
2571 limit
= ippGetInteger(attr
, 0);
2573 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2578 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2579 IPP_TAG_INTEGER
)) != NULL
)
2581 first_job_id
= ippGetInteger(attr
, 0);
2583 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2590 * See if we only want to see jobs for a specific user...
2595 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2596 IPP_TAG_BOOLEAN
)) != NULL
)
2598 int my_jobs
= ippGetBoolean(attr
, 0);
2600 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2601 my_jobs
? "true" : "false");
2605 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2606 IPP_TAG_NAME
)) == NULL
)
2608 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2609 "Need requesting-user-name with my-jobs.");
2613 username
= ippGetString(attr
, 0, NULL
);
2615 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2616 client
->hostname
, username
);
2621 * OK, build a list of jobs for this printer...
2624 ra
= ippCreateRequestedArray(client
->request
);
2626 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2628 _cupsRWLockRead(&(client
->printer
->rwlock
));
2630 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2631 (limit
<= 0 || count
< limit
) && job
;
2632 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2635 * Filter out jobs that don't match...
2638 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2639 (job_comparison
== 0 && job
->state
!= job_state
) ||
2640 (job_comparison
> 0 && job
->state
< job_state
) ||
2641 job
->id
< first_job_id
||
2642 (username
&& job
->username
&&
2643 strcasecmp(username
, job
->username
)))
2647 ippAddSeparator(client
->response
);
2650 copy_job_attributes(client
, job
, ra
);
2653 cupsArrayDelete(ra
);
2655 _cupsRWUnlock(&(client
->printer
->rwlock
));
2660 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2664 ipp_get_printer_attributes(
2665 _ipp_client_t
*client
) /* I - Client */
2667 cups_array_t
*ra
; /* Requested attributes array */
2668 _ipp_printer_t
*printer
; /* Printer */
2672 * Send the attributes...
2675 ra
= ippCreateRequestedArray(client
->request
);
2676 printer
= client
->printer
;
2678 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2680 _cupsRWLockRead(&(printer
->rwlock
));
2682 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2683 IPP_TAG_CUPS_CONST
);
2685 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
2686 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2687 "printer-state", printer
->state
);
2689 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
2691 if (printer
->state_reasons
== _IPP_PSTATE_NONE
)
2692 ippAddString(client
->response
, IPP_TAG_PRINTER
,
2693 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2694 "printer-state-reasons", NULL
, "none");
2697 int num_reasons
= 0;/* Number of reasons */
2698 const char *reasons
[32]; /* Reason strings */
2700 if (printer
->state_reasons
& _IPP_PSTATE_OTHER
)
2701 reasons
[num_reasons
++] = "other";
2702 if (printer
->state_reasons
& _IPP_PSTATE_COVER_OPEN
)
2703 reasons
[num_reasons
++] = "cover-open";
2704 if (printer
->state_reasons
& _IPP_PSTATE_INPUT_TRAY_MISSING
)
2705 reasons
[num_reasons
++] = "input-tray-missing";
2706 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_EMPTY
)
2707 reasons
[num_reasons
++] = "marker-supply-empty-warning";
2708 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_LOW
)
2709 reasons
[num_reasons
++] = "marker-supply-low-report";
2710 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL
)
2711 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
2712 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_FULL
)
2713 reasons
[num_reasons
++] = "marker-waste-full-warning";
2714 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_EMPTY
)
2715 reasons
[num_reasons
++] = "media-empty-warning";
2716 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_JAM
)
2717 reasons
[num_reasons
++] = "media-jam-warning";
2718 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_LOW
)
2719 reasons
[num_reasons
++] = "media-low-report";
2720 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_NEEDED
)
2721 reasons
[num_reasons
++] = "media-needed-report";
2722 if (printer
->state_reasons
& _IPP_PSTATE_MOVING_TO_PAUSED
)
2723 reasons
[num_reasons
++] = "moving-to-paused";
2724 if (printer
->state_reasons
& _IPP_PSTATE_PAUSED
)
2725 reasons
[num_reasons
++] = "paused";
2726 if (printer
->state_reasons
& _IPP_PSTATE_SPOOL_AREA_FULL
)
2727 reasons
[num_reasons
++] = "spool-area-full";
2728 if (printer
->state_reasons
& _IPP_PSTATE_TONER_EMPTY
)
2729 reasons
[num_reasons
++] = "toner-empty-warning";
2730 if (printer
->state_reasons
& _IPP_PSTATE_TONER_LOW
)
2731 reasons
[num_reasons
++] = "toner-low-report";
2733 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
2734 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2735 "printer-state-reasons", num_reasons
, NULL
, reasons
);
2739 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
2740 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2741 "printer-up-time", (int)time(NULL
));
2743 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
2744 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2746 printer
->active_job
&&
2747 printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
2749 _cupsRWUnlock(&(printer
->rwlock
));
2751 cupsArrayDelete(ra
);
2756 * 'ipp_identify_printer()' - Beep or display a message.
2760 ipp_identify_printer(
2761 _ipp_client_t
*client
) /* I - Client */
2763 /* TODO: Do something */
2765 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2770 * 'ipp_print_job()' - Create a job object with an attached document.
2774 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
2776 _ipp_job_t
*job
; /* New job */
2777 char filename
[1024], /* Filename buffer */
2778 buffer
[4096]; /* Copy buffer */
2779 ssize_t bytes
; /* Bytes read */
2780 cups_array_t
*ra
; /* Attributes to send in response */
2784 * Validate print job attributes...
2787 if (!valid_job_attributes(client
))
2789 httpFlush(client
->http
);
2794 * Do we have a file to print?
2797 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
2799 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
2807 if ((job
= create_job(client
)) == NULL
)
2809 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2810 "Currently printing another job.");
2815 * Create a file for the request data...
2818 if (!strcasecmp(job
->format
, "image/jpeg"))
2819 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2820 client
->printer
->directory
, job
->id
);
2821 else if (!strcasecmp(job
->format
, "image/png"))
2822 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2823 client
->printer
->directory
, job
->id
);
2824 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
2825 snprintf(filename
, sizeof(filename
), "%s/%d.ras",
2826 client
->printer
->directory
, job
->id
);
2827 else if (!strcasecmp(job
->format
, "application/pdf"))
2828 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2829 client
->printer
->directory
, job
->id
);
2830 else if (!strcasecmp(job
->format
, "application/postscript"))
2831 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2832 client
->printer
->directory
, job
->id
);
2834 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2835 client
->printer
->directory
, job
->id
);
2838 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
2840 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2842 job
->state
= IPP_JSTATE_ABORTED
;
2844 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2845 "Unable to create print file: %s", strerror(errno
));
2849 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2851 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2853 int error
= errno
; /* Write error */
2855 job
->state
= IPP_JSTATE_ABORTED
;
2862 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2863 "Unable to write print file: %s", strerror(error
));
2871 * Got an error while reading the print data, so abort this job.
2874 job
->state
= IPP_JSTATE_ABORTED
;
2881 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2882 "Unable to read print file.");
2888 int error
= errno
; /* Write error */
2890 job
->state
= IPP_JSTATE_ABORTED
;
2895 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2896 "Unable to write print file: %s", strerror(error
));
2901 job
->filename
= strdup(filename
);
2902 job
->state
= IPP_JSTATE_PENDING
;
2905 * Process the job...
2909 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
2911 job
->state
= IPP_JSTATE_ABORTED
;
2912 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
2921 * Return the job info...
2924 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2926 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2927 cupsArrayAdd(ra
, "job-id");
2928 cupsArrayAdd(ra
, "job-state");
2929 cupsArrayAdd(ra
, "job-state-reasons");
2930 cupsArrayAdd(ra
, "job-uri");
2932 copy_job_attributes(client
, job
, ra
);
2933 cupsArrayDelete(ra
);
2938 * 'ipp_print_uri()' - Create a job object with a referenced document.
2942 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
2944 _ipp_job_t
*job
; /* New job */
2945 ipp_attribute_t
*uri
; /* document-uri */
2946 char scheme
[256], /* URI scheme */
2947 userpass
[256], /* Username and password info */
2948 hostname
[256], /* Hostname */
2949 resource
[1024]; /* Resource path */
2950 int port
; /* Port number */
2951 http_uri_status_t uri_status
; /* URI decode status */
2952 http_encryption_t encryption
; /* Encryption to use, if any */
2953 http_t
*http
; /* Connection for http/https URIs */
2954 http_status_t status
; /* Access status for http/https URIs */
2955 int infile
; /* Input file for local file URIs */
2956 char filename
[1024], /* Filename buffer */
2957 buffer
[4096]; /* Copy buffer */
2958 ssize_t bytes
; /* Bytes read */
2959 cups_array_t
*ra
; /* Attributes to send in response */
2960 static const char * const uri_status_strings
[] =
2961 { /* URI decode errors */
2963 "Bad arguments to function.",
2964 "Bad resource in URI.",
2965 "Bad port number in URI.",
2966 "Bad hostname in URI.",
2967 "Bad username in URI.",
2968 "Bad scheme in URI.",
2974 * Validate print job attributes...
2977 if (!valid_job_attributes(client
))
2979 httpFlush(client
->http
);
2984 * Do we have a file to print?
2987 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2989 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2990 "Unexpected document data following request.");
2995 * Do we have a document URI?
2998 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
2999 IPP_TAG_URI
)) == NULL
)
3001 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3005 if (ippGetCount(uri
) != 1)
3007 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3008 "Too many document-uri values.");
3012 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3013 scheme
, sizeof(scheme
), userpass
,
3014 sizeof(userpass
), hostname
, sizeof(hostname
),
3015 &port
, resource
, sizeof(resource
));
3016 if (uri_status
< HTTP_URI_STATUS_OK
)
3018 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3019 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3023 if (strcmp(scheme
, "file") &&
3025 strcmp(scheme
, "https") &&
3026 #endif /* HAVE_SSL */
3027 strcmp(scheme
, "http"))
3029 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3030 "URI scheme \"%s\" not supported.", scheme
);
3034 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3036 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3037 "Unable to access URI: %s", strerror(errno
));
3045 if ((job
= create_job(client
)) == NULL
)
3047 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3048 "Currently printing another job.");
3053 * Create a file for the request data...
3056 if (!strcasecmp(job
->format
, "image/jpeg"))
3057 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3058 client
->printer
->directory
, job
->id
);
3059 else if (!strcasecmp(job
->format
, "image/png"))
3060 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3061 client
->printer
->directory
, job
->id
);
3062 else if (!strcasecmp(job
->format
, "application/pdf"))
3063 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3064 client
->printer
->directory
, job
->id
);
3065 else if (!strcasecmp(job
->format
, "application/postscript"))
3066 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3067 client
->printer
->directory
, job
->id
);
3069 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3070 client
->printer
->directory
, job
->id
);
3072 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3074 job
->state
= IPP_JSTATE_ABORTED
;
3076 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3077 "Unable to create print file: %s", strerror(errno
));
3081 if (!strcmp(scheme
, "file"))
3083 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3085 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3086 "Unable to access URI: %s", strerror(errno
));
3092 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3093 (errno
== EAGAIN
|| errno
== EINTR
))
3095 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3097 int error
= errno
; /* Write error */
3099 job
->state
= IPP_JSTATE_ABORTED
;
3107 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3108 "Unable to write print file: %s", strerror(error
));
3119 if (port
== 443 || !strcmp(scheme
, "https"))
3120 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3122 #endif /* HAVE_SSL */
3123 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3125 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3126 1, 30000, NULL
)) == NULL
)
3128 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3129 "Unable to connect to %s: %s", hostname
,
3130 cupsLastErrorString());
3131 job
->state
= IPP_JSTATE_ABORTED
;
3140 httpClearFields(http
);
3141 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3142 if (httpGet(http
, resource
))
3144 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3145 "Unable to GET URI: %s", strerror(errno
));
3147 job
->state
= IPP_JSTATE_ABORTED
;
3157 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3159 if (status
!= HTTP_STATUS_OK
)
3161 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3162 "Unable to GET URI: %s", httpStatus(status
));
3164 job
->state
= IPP_JSTATE_ABORTED
;
3174 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3176 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3178 int error
= errno
; /* Write error */
3180 job
->state
= IPP_JSTATE_ABORTED
;
3188 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3189 "Unable to write print file: %s", strerror(error
));
3199 int error
= errno
; /* Write error */
3201 job
->state
= IPP_JSTATE_ABORTED
;
3206 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3207 "Unable to write print file: %s", strerror(error
));
3212 job
->filename
= strdup(filename
);
3213 job
->state
= IPP_JSTATE_PENDING
;
3216 * Process the job...
3220 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3222 job
->state
= IPP_JSTATE_ABORTED
;
3223 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3232 * Return the job info...
3235 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3237 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3238 cupsArrayAdd(ra
, "job-id");
3239 cupsArrayAdd(ra
, "job-state");
3240 cupsArrayAdd(ra
, "job-state-reasons");
3241 cupsArrayAdd(ra
, "job-uri");
3243 copy_job_attributes(client
, job
, ra
);
3244 cupsArrayDelete(ra
);
3249 * 'ipp_send_document()' - Add an attached document to a job object created with
3254 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3256 _ipp_job_t
*job
; /* Job information */
3257 char filename
[1024], /* Filename buffer */
3258 buffer
[4096]; /* Copy buffer */
3259 ssize_t bytes
; /* Bytes read */
3260 ipp_attribute_t
*attr
; /* Current attribute */
3261 cups_array_t
*ra
; /* Attributes to send in response */
3268 if ((job
= find_job(client
)) == NULL
)
3270 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3271 httpFlush(client
->http
);
3276 * See if we already have a document for this job or the job has already
3277 * in a non-pending state...
3280 if (job
->state
> IPP_JSTATE_HELD
)
3282 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3283 "Job is not in a pending state.");
3284 httpFlush(client
->http
);
3287 else if (job
->filename
|| job
->fd
>= 0)
3289 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3290 "Multiple document jobs are not supported.");
3291 httpFlush(client
->http
);
3295 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3296 IPP_TAG_ZERO
)) == NULL
)
3298 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3299 "Missing required last-document attribute.");
3300 httpFlush(client
->http
);
3303 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3304 !ippGetBoolean(attr
, 0))
3306 respond_unsupported(client
, attr
);
3307 httpFlush(client
->http
);
3312 * Validate document attributes...
3315 if (!valid_doc_attributes(client
))
3317 httpFlush(client
->http
);
3322 * Get the document format for the job...
3325 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3327 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
3328 job
->format
= ippGetString(attr
, 0, NULL
);
3330 job
->format
= "application/octet-stream";
3333 * Create a file for the request data...
3336 if (!strcasecmp(job
->format
, "image/jpeg"))
3337 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3338 client
->printer
->directory
, job
->id
);
3339 else if (!strcasecmp(job
->format
, "image/png"))
3340 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3341 client
->printer
->directory
, job
->id
);
3342 else if (!strcasecmp(job
->format
, "application/pdf"))
3343 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3344 client
->printer
->directory
, job
->id
);
3345 else if (!strcasecmp(job
->format
, "application/postscript"))
3346 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3347 client
->printer
->directory
, job
->id
);
3349 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3350 client
->printer
->directory
, job
->id
);
3353 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3355 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3357 _cupsRWUnlock(&(client
->printer
->rwlock
));
3361 job
->state
= IPP_JSTATE_ABORTED
;
3363 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3364 "Unable to create print file: %s", strerror(errno
));
3368 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3370 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3372 int error
= errno
; /* Write error */
3374 job
->state
= IPP_JSTATE_ABORTED
;
3381 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3382 "Unable to write print file: %s", strerror(error
));
3390 * Got an error while reading the print data, so abort this job.
3393 job
->state
= IPP_JSTATE_ABORTED
;
3400 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3401 "Unable to read print file.");
3407 int error
= errno
; /* Write error */
3409 job
->state
= IPP_JSTATE_ABORTED
;
3414 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3415 "Unable to write print file: %s", strerror(error
));
3419 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3422 job
->filename
= strdup(filename
);
3423 job
->state
= IPP_JSTATE_PENDING
;
3425 _cupsRWUnlock(&(client
->printer
->rwlock
));
3428 * Process the job...
3432 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3434 job
->state
= IPP_JSTATE_ABORTED
;
3435 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3444 * Return the job info...
3447 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3449 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3450 cupsArrayAdd(ra
, "job-id");
3451 cupsArrayAdd(ra
, "job-state");
3452 cupsArrayAdd(ra
, "job-state-reasons");
3453 cupsArrayAdd(ra
, "job-uri");
3455 copy_job_attributes(client
, job
, ra
);
3456 cupsArrayDelete(ra
);
3461 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3466 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3468 _ipp_job_t
*job
; /* Job information */
3469 ipp_attribute_t
*uri
; /* document-uri */
3470 char scheme
[256], /* URI scheme */
3471 userpass
[256], /* Username and password info */
3472 hostname
[256], /* Hostname */
3473 resource
[1024]; /* Resource path */
3474 int port
; /* Port number */
3475 http_uri_status_t uri_status
; /* URI decode status */
3476 http_encryption_t encryption
; /* Encryption to use, if any */
3477 http_t
*http
; /* Connection for http/https URIs */
3478 http_status_t status
; /* Access status for http/https URIs */
3479 int infile
; /* Input file for local file URIs */
3480 char filename
[1024], /* Filename buffer */
3481 buffer
[4096]; /* Copy buffer */
3482 ssize_t bytes
; /* Bytes read */
3483 ipp_attribute_t
*attr
; /* Current attribute */
3484 cups_array_t
*ra
; /* Attributes to send in response */
3485 static const char * const uri_status_strings
[] =
3486 { /* URI decode errors */
3488 "Bad arguments to function.",
3489 "Bad resource in URI.",
3490 "Bad port number in URI.",
3491 "Bad hostname in URI.",
3492 "Bad username in URI.",
3493 "Bad scheme in URI.",
3502 if ((job
= find_job(client
)) == NULL
)
3504 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3505 httpFlush(client
->http
);
3510 * See if we already have a document for this job or the job has already
3511 * in a non-pending state...
3514 if (job
->state
> IPP_JSTATE_HELD
)
3516 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3517 "Job is not in a pending state.");
3518 httpFlush(client
->http
);
3521 else if (job
->filename
|| job
->fd
>= 0)
3523 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3524 "Multiple document jobs are not supported.");
3525 httpFlush(client
->http
);
3529 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3530 IPP_TAG_ZERO
)) == NULL
)
3532 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3533 "Missing required last-document attribute.");
3534 httpFlush(client
->http
);
3537 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3538 !ippGetBoolean(attr
, 0))
3540 respond_unsupported(client
, attr
);
3541 httpFlush(client
->http
);
3546 * Validate document attributes...
3549 if (!valid_doc_attributes(client
))
3551 httpFlush(client
->http
);
3556 * Do we have a file to print?
3559 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3561 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3562 "Unexpected document data following request.");
3567 * Do we have a document URI?
3570 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3571 IPP_TAG_URI
)) == NULL
)
3573 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3577 if (ippGetCount(uri
) != 1)
3579 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3580 "Too many document-uri values.");
3584 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3585 scheme
, sizeof(scheme
), userpass
,
3586 sizeof(userpass
), hostname
, sizeof(hostname
),
3587 &port
, resource
, sizeof(resource
));
3588 if (uri_status
< HTTP_URI_STATUS_OK
)
3590 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3591 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3595 if (strcmp(scheme
, "file") &&
3597 strcmp(scheme
, "https") &&
3598 #endif /* HAVE_SSL */
3599 strcmp(scheme
, "http"))
3601 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3602 "URI scheme \"%s\" not supported.", scheme
);
3606 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3608 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3609 "Unable to access URI: %s", strerror(errno
));
3614 * Get the document format for the job...
3617 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3619 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3620 IPP_TAG_MIMETYPE
)) != NULL
)
3621 job
->format
= ippGetString(attr
, 0, NULL
);
3623 job
->format
= "application/octet-stream";
3626 * Create a file for the request data...
3629 if (!strcasecmp(job
->format
, "image/jpeg"))
3630 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3631 client
->printer
->directory
, job
->id
);
3632 else if (!strcasecmp(job
->format
, "image/png"))
3633 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3634 client
->printer
->directory
, job
->id
);
3635 else if (!strcasecmp(job
->format
, "application/pdf"))
3636 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3637 client
->printer
->directory
, job
->id
);
3638 else if (!strcasecmp(job
->format
, "application/postscript"))
3639 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3640 client
->printer
->directory
, job
->id
);
3642 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3643 client
->printer
->directory
, job
->id
);
3645 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3647 _cupsRWUnlock(&(client
->printer
->rwlock
));
3651 job
->state
= IPP_JSTATE_ABORTED
;
3653 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3654 "Unable to create print file: %s", strerror(errno
));
3658 if (!strcmp(scheme
, "file"))
3660 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3662 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3663 "Unable to access URI: %s", strerror(errno
));
3669 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3670 (errno
== EAGAIN
|| errno
== EINTR
))
3672 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3674 int error
= errno
; /* Write error */
3676 job
->state
= IPP_JSTATE_ABORTED
;
3684 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3685 "Unable to write print file: %s", strerror(error
));
3696 if (port
== 443 || !strcmp(scheme
, "https"))
3697 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3699 #endif /* HAVE_SSL */
3700 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3702 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3703 1, 30000, NULL
)) == NULL
)
3705 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3706 "Unable to connect to %s: %s", hostname
,
3707 cupsLastErrorString());
3708 job
->state
= IPP_JSTATE_ABORTED
;
3717 httpClearFields(http
);
3718 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3719 if (httpGet(http
, resource
))
3721 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3722 "Unable to GET URI: %s", strerror(errno
));
3724 job
->state
= IPP_JSTATE_ABORTED
;
3734 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3736 if (status
!= HTTP_STATUS_OK
)
3738 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3739 "Unable to GET URI: %s", httpStatus(status
));
3741 job
->state
= IPP_JSTATE_ABORTED
;
3751 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3753 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3755 int error
= errno
; /* Write error */
3757 job
->state
= IPP_JSTATE_ABORTED
;
3765 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3766 "Unable to write print file: %s", strerror(error
));
3776 int error
= errno
; /* Write error */
3778 job
->state
= IPP_JSTATE_ABORTED
;
3783 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3784 "Unable to write print file: %s", strerror(error
));
3788 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3791 job
->filename
= strdup(filename
);
3792 job
->state
= IPP_JSTATE_PENDING
;
3794 _cupsRWUnlock(&(client
->printer
->rwlock
));
3797 * Process the job...
3801 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3803 job
->state
= IPP_JSTATE_ABORTED
;
3804 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3813 * Return the job info...
3816 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3818 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3819 cupsArrayAdd(ra
, "job-id");
3820 cupsArrayAdd(ra
, "job-state");
3821 cupsArrayAdd(ra
, "job-state-reasons");
3822 cupsArrayAdd(ra
, "job-uri");
3824 copy_job_attributes(client
, job
, ra
);
3825 cupsArrayDelete(ra
);
3830 * 'ipp_validate_job()' - Validate job creation attributes.
3834 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
3836 if (valid_job_attributes(client
))
3837 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3842 * 'process_client()' - Process client requests on a thread.
3845 static void * /* O - Exit status */
3846 process_client(_ipp_client_t
*client
) /* I - Client */
3849 * Loop until we are out of requests or timeout (30 seconds)...
3853 int first_time
= 1; /* First time request? */
3854 #endif /* HAVE_SSL */
3856 while (httpWait(client
->http
, 30000))
3862 * See if we need to negotiate a TLS connection...
3865 char buf
[1]; /* First byte from client */
3867 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
3869 fprintf(stderr
, "%s Negotiating TLS session.\n", client
->hostname
);
3871 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
3873 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
3880 #endif /* HAVE_SSL */
3882 if (!process_http(client
))
3887 * Close the conection to the client and return...
3890 delete_client(client
);
3897 * 'process_http()' - Process a HTTP request.
3900 int /* O - 1 on success, 0 on failure */
3901 process_http(_ipp_client_t
*client
) /* I - Client connection */
3903 char uri
[1024]; /* URI */
3904 http_state_t http_state
; /* HTTP state */
3905 http_status_t http_status
; /* HTTP status */
3906 ipp_state_t ipp_state
; /* State of IPP transfer */
3907 char scheme
[32], /* Method/scheme */
3908 userpass
[128], /* Username:password */
3909 hostname
[HTTP_MAX_HOST
];
3911 int port
; /* Port number */
3912 const char *encoding
; /* Content-Encoding value */
3913 static const char * const http_states
[] =
3914 { /* Strings for logging HTTP method */
3935 * Clear state variables...
3938 ippDelete(client
->request
);
3939 ippDelete(client
->response
);
3941 client
->request
= NULL
;
3942 client
->response
= NULL
;
3943 client
->operation
= HTTP_STATE_WAITING
;
3946 * Read a request from the connection...
3949 while ((http_state
= httpReadRequest(client
->http
, uri
,
3950 sizeof(uri
))) == HTTP_STATE_WAITING
)
3954 * Parse the request line...
3957 if (http_state
== HTTP_STATE_ERROR
)
3959 if (httpError(client
->http
) == EPIPE
)
3960 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
3962 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
3963 strerror(httpError(client
->http
)));
3967 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
3969 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
3970 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3973 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
3975 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
3976 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3980 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
3984 * Separate the URI into its components...
3987 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
3988 userpass
, sizeof(userpass
),
3989 hostname
, sizeof(hostname
), &port
,
3990 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
)
3992 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
3993 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
3998 * Process the request...
4001 client
->start
= time(NULL
);
4002 client
->operation
= httpGetState(client
->http
);
4005 * Parse incoming parameters until the status changes...
4008 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4010 if (http_status
!= HTTP_STATUS_OK
)
4012 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4016 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4017 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4020 * HTTP/1.1 and higher require the "Host:" field...
4023 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4028 * Handle HTTP Upgrade...
4031 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4034 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4039 * Handle HTTP Expect...
4042 if (httpGetExpect(client
->http
) &&
4043 (client
->operation
== HTTP_STATE_POST
||
4044 client
->operation
== HTTP_STATE_PUT
))
4046 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4049 * Send 100-continue header...
4052 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4058 * Send 417-expectation-failed header...
4061 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4067 * Handle new transfers...
4070 encoding
= httpGetContentEncoding(client
->http
);
4072 switch (client
->operation
)
4074 case HTTP_STATE_OPTIONS
:
4076 * Do HEAD/OPTIONS command...
4079 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4081 case HTTP_STATE_HEAD
:
4082 if (!strcmp(client
->uri
, "/icon.png"))
4083 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4084 else if (!strcmp(client
->uri
, "/"))
4085 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4087 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4089 case HTTP_STATE_GET
:
4090 if (!strcmp(client
->uri
, "/icon.png"))
4093 * Send PNG icon file.
4096 int fd
; /* Icon file */
4097 struct stat fileinfo
; /* Icon file information */
4098 char buffer
[4096]; /* Copy buffer */
4099 ssize_t bytes
; /* Bytes */
4101 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4103 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4104 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4106 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4107 (size_t)fileinfo
.st_size
))
4113 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4114 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4116 httpFlushWrite(client
->http
);
4121 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4123 else if (!strcmp(client
->uri
, "/"))
4126 * Show web status page...
4129 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4133 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
4134 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
4137 "<title>%s</title>\n"
4138 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
4139 "type=\"image/png\">\n"
4143 "<h1><img align=\"right\" src=\"/icon.png\">%s</h1>\n"
4144 "<p>%s, %d job(s).</p>\n"
4147 client
->printer
->name
, client
->printer
->name
,
4148 client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" :
4149 client
->printer
->state
== IPP_PSTATE_PROCESSING
?
4150 "Printing" : "Stopped",
4151 cupsArrayCount(client
->printer
->jobs
));
4152 httpWrite2(client
->http
, "", 0);
4157 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4160 case HTTP_STATE_POST
:
4161 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4165 * Not an IPP request...
4168 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4172 * Read the IPP request...
4175 client
->request
= ippNew();
4177 while ((ipp_state
= ippRead(client
->http
,
4178 client
->request
)) != IPP_STATE_DATA
)
4180 if (ipp_state
== IPP_STATE_ERROR
)
4182 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
4183 cupsLastErrorString());
4184 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4190 * Now that we have the IPP request, process the request...
4193 return (process_ipp(client
));
4196 break; /* Anti-compiler-warning-code */
4204 * 'process_ipp()' - Process an IPP request.
4207 static int /* O - 1 on success, 0 on error */
4208 process_ipp(_ipp_client_t
*client
) /* I - Client */
4210 ipp_tag_t group
; /* Current group tag */
4211 ipp_attribute_t
*attr
; /* Current attribute */
4212 ipp_attribute_t
*charset
; /* Character set attribute */
4213 ipp_attribute_t
*language
; /* Language attribute */
4214 ipp_attribute_t
*uri
; /* Printer URI attribute */
4215 int major
, minor
; /* Version number */
4216 const char *name
; /* Name of attribute */
4219 debug_attributes("Request", client
->request
, 1);
4222 * First build an empty response message for this request...
4225 client
->operation_id
= ippGetOperation(client
->request
);
4226 client
->response
= ippNewResponse(client
->request
);
4229 * Then validate the request header and required attributes...
4232 major
= ippGetVersion(client
->request
, &minor
);
4234 if (major
< 1 || major
> 2)
4237 * Return an error, since we only support IPP 1.x and 2.x.
4240 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
4241 "Bad request version number %d.%d.", major
, minor
);
4243 else if (ippGetRequestId(client
->request
) <= 0)
4244 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
4245 ippGetRequestId(client
->request
));
4246 else if (!ippFirstAttribute(client
->request
))
4247 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4248 "No attributes in request.");
4252 * Make sure that the attributes are provided in the correct order and
4253 * don't repeat groups...
4256 for (attr
= ippFirstAttribute(client
->request
),
4257 group
= ippGetGroupTag(attr
);
4259 attr
= ippNextAttribute(client
->request
))
4261 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
4264 * Out of order; return an error...
4267 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4268 "Attribute groups are out of order (%x < %x).",
4269 ippGetGroupTag(attr
), group
);
4273 group
= ippGetGroupTag(attr
);
4279 * Then make sure that the first three attributes are:
4281 * attributes-charset
4282 * attributes-natural-language
4283 * printer-uri/job-uri
4286 attr
= ippFirstAttribute(client
->request
);
4287 name
= ippGetName(attr
);
4288 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
4289 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
4294 attr
= ippNextAttribute(client
->request
);
4295 name
= ippGetName(attr
);
4297 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
4298 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
4303 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
4304 IPP_TAG_URI
)) != NULL
)
4306 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
4307 IPP_TAG_URI
)) != NULL
)
4313 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
4314 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
4317 * Bad character set...
4320 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4321 "Unsupported character set \"%s\".",
4322 ippGetString(charset
, 0, NULL
));
4324 else if (!charset
|| !language
|| !uri
)
4327 * Return an error, since attributes-charset,
4328 * attributes-natural-language, and printer-uri/job-uri are required
4329 * for all operations.
4332 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4333 "Missing required attributes.");
4337 char scheme
[32], /* URI scheme */
4338 userpass
[32], /* Username/password in URI */
4339 host
[256], /* Host name in URI */
4340 resource
[256]; /* Resource path in URI */
4341 int port
; /* Port number in URI */
4343 name
= ippGetName(uri
);
4345 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4346 scheme
, sizeof(scheme
),
4347 userpass
, sizeof(userpass
),
4348 host
, sizeof(host
), &port
,
4349 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
4350 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4351 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
4352 else if ((!strcmp(name
, "job-uri") &&
4353 strncmp(resource
, "/ipp/print/", 11)) ||
4354 (!strcmp(name
, "printer-uri") &&
4355 strcmp(resource
, "/ipp/print")))
4356 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
4357 name
, ippGetString(uri
, 0, NULL
));
4361 * Try processing the operation...
4364 switch (ippGetOperation(client
->request
))
4366 case IPP_OP_PRINT_JOB
:
4367 ipp_print_job(client
);
4370 case IPP_OP_PRINT_URI
:
4371 ipp_print_uri(client
);
4374 case IPP_OP_VALIDATE_JOB
:
4375 ipp_validate_job(client
);
4378 case IPP_OP_CREATE_JOB
:
4379 ipp_create_job(client
);
4382 case IPP_OP_SEND_DOCUMENT
:
4383 ipp_send_document(client
);
4386 case IPP_OP_SEND_URI
:
4387 ipp_send_uri(client
);
4390 case IPP_OP_CANCEL_JOB
:
4391 ipp_cancel_job(client
);
4394 case IPP_OP_GET_JOB_ATTRIBUTES
:
4395 ipp_get_job_attributes(client
);
4398 case IPP_OP_GET_JOBS
:
4399 ipp_get_jobs(client
);
4402 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
4403 ipp_get_printer_attributes(client
);
4406 case IPP_OP_CLOSE_JOB
:
4407 ipp_close_job(client
);
4410 case IPP_OP_IDENTIFY_PRINTER
:
4411 ipp_identify_printer(client
);
4415 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
4416 "Operation not supported.");
4425 * Send the HTTP header and return...
4428 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
4429 httpFlush(client
->http
); /* Flush trailing (junk) data */
4431 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
4432 ippLength(client
->response
)));
4437 * 'process_job()' - Process a print job.
4440 static void * /* O - Thread exit status */
4441 process_job(_ipp_job_t
*job
) /* I - Job */
4443 job
->state
= IPP_JSTATE_PROCESSING
;
4444 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
4446 if (job
->printer
->command
)
4449 * Execute a command with the job spool file and wait for it to complete...
4452 int pid
, /* Process ID */
4453 status
; /* Exit status */
4454 time_t start
, /* Start time */
4457 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
4461 if ((pid
= fork()) == 0)
4464 * Child comes here...
4467 execlp(job
->printer
->command
, job
->printer
->command
, job
->filename
,
4474 * Unable to fork process...
4477 perror("Unable to start job processing command");
4482 * Wait for child to complete...
4486 while (waitpid(pid
, &status
, 0) < 0);
4488 while (wait(&status
) < 0);
4489 #endif /* HAVE_WAITPID */
4493 if (WIFEXITED(status
))
4494 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
4495 job
->printer
->command
, WEXITSTATUS(status
));
4497 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
4498 job
->printer
->command
, WTERMSIG(status
));
4501 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
4502 job
->printer
->command
);
4506 * Make sure processing takes at least 5 seconds...
4510 if ((end
- start
) < 5)
4516 * Sleep for a random amount of time to simulate job processing.
4519 sleep(5 + (rand() % 11));
4523 job
->state
= IPP_JSTATE_CANCELED
;
4525 job
->state
= IPP_JSTATE_COMPLETED
;
4527 job
->completed
= time(NULL
);
4528 job
->printer
->state
= IPP_PSTATE_IDLE
;
4529 job
->printer
->active_job
= NULL
;
4537 * 'register_printer()' - Register a printer object via Bonjour.
4540 static int /* O - 1 on success, 0 on error */
4542 _ipp_printer_t
*printer
, /* I - Printer */
4543 const char *location
, /* I - Location */
4544 const char *make
, /* I - Manufacturer */
4545 const char *model
, /* I - Model name */
4546 const char *formats
, /* I - Supported formats */
4547 const char *adminurl
, /* I - Web interface URL */
4548 int color
, /* I - 1 = color, 0 = monochrome */
4549 int duplex
, /* I - 1 = duplex, 0 = simplex */
4550 const char *subtype
) /* I - Service subtype */
4552 DNSServiceErrorType error
; /* Error from Bonjour */
4553 char make_model
[256],/* Make and model together */
4554 product
[256], /* Product string */
4555 regtype
[256]; /* Bonjour service type */
4559 * Build the TXT record for IPP...
4562 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4563 snprintf(product
, sizeof(product
), "(%s)", model
);
4565 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
4566 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 9, "ipp/print");
4567 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
4569 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
4572 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
4574 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
4576 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
4578 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
4579 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
4580 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
4582 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
4586 * Create a shared service reference for Bonjour...
4589 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
4590 != kDNSServiceErr_NoError
)
4592 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
4597 * Register the _printer._tcp (LPD) service type with a port number of 0 to
4598 * defend our service name but not actually support LPD...
4601 printer
->printer_ref
= printer
->common_ref
;
4603 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
4604 kDNSServiceFlagsShareConnection
,
4605 0 /* interfaceIndex */, printer
->dnssd_name
,
4606 "_printer._tcp", NULL
/* domain */,
4607 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
4608 NULL
/* txtRecord */,
4609 (DNSServiceRegisterReply
)dnssd_callback
,
4610 printer
)) != kDNSServiceErr_NoError
)
4612 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
4613 printer
->dnssd_name
, error
);
4618 * Then register the _ipp._tcp (IPP) service type with the real port number to
4619 * advertise our IPP printer...
4622 printer
->ipp_ref
= printer
->common_ref
;
4624 if (subtype
&& *subtype
)
4625 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
4627 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
4629 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
4630 kDNSServiceFlagsShareConnection
,
4631 0 /* interfaceIndex */, printer
->dnssd_name
,
4632 regtype
, NULL
/* domain */,
4633 NULL
/* host */, htons(printer
->port
),
4634 TXTRecordGetLength(&(printer
->ipp_txt
)),
4635 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4636 (DNSServiceRegisterReply
)dnssd_callback
,
4637 printer
)) != kDNSServiceErr_NoError
)
4639 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4640 printer
->dnssd_name
, regtype
, error
);
4646 * Then register the _ipps._tcp (IPP) service type with the real port number to
4647 * advertise our IPP printer...
4650 printer
->ipps_ref
= printer
->common_ref
;
4652 if (subtype
&& *subtype
)
4653 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
4655 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
4657 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
4658 kDNSServiceFlagsShareConnection
,
4659 0 /* interfaceIndex */, printer
->dnssd_name
,
4660 regtype
, NULL
/* domain */,
4661 NULL
/* host */, htons(printer
->port
),
4662 TXTRecordGetLength(&(printer
->ipp_txt
)),
4663 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4664 (DNSServiceRegisterReply
)dnssd_callback
,
4665 printer
)) != kDNSServiceErr_NoError
)
4667 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4668 printer
->dnssd_name
, regtype
, error
);
4671 # endif /* HAVE_SSL */
4674 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4675 * real port number to advertise our IPP printer...
4678 printer
->http_ref
= printer
->common_ref
;
4680 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
4681 kDNSServiceFlagsShareConnection
,
4682 0 /* interfaceIndex */, printer
->dnssd_name
,
4683 "_http._tcp,_printer", NULL
/* domain */,
4684 NULL
/* host */, htons(printer
->port
),
4685 0 /* txtLen */, NULL
, /* txtRecord */
4686 (DNSServiceRegisterReply
)dnssd_callback
,
4687 printer
)) != kDNSServiceErr_NoError
)
4689 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4690 printer
->dnssd_name
, regtype
, error
);
4696 #endif /* HAVE_DNSSD */
4700 * 'respond_http()' - Send a HTTP response.
4703 int /* O - 1 on success, 0 on failure */
4705 _ipp_client_t
*client
, /* I - Client */
4706 http_status_t code
, /* I - HTTP status of response */
4707 const char *content_encoding
, /* I - Content-Encoding of response */
4708 const char *type
, /* I - MIME media type of response */
4709 size_t length
) /* I - Length of response */
4711 char message
[1024]; /* Text message */
4714 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
4716 if (code
== HTTP_STATUS_CONTINUE
)
4719 * 100-continue doesn't send any headers...
4722 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
4726 * Format an error message...
4729 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
)
4731 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
4733 type
= "text/plain";
4734 length
= strlen(message
);
4740 * Send the HTTP response header...
4743 httpClearFields(client
->http
);
4745 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
4746 client
->operation
== HTTP_STATE_OPTIONS
)
4747 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
4751 if (!strcmp(type
, "text/html"))
4752 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
4753 "text/html; charset=utf-8");
4755 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
4757 if (content_encoding
)
4758 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
4761 httpSetLength(client
->http
, length
);
4763 if (httpWriteResponse(client
->http
, code
) < 0)
4767 * Send the response data...
4773 * Send a plain text message.
4776 if (httpPrintf(client
->http
, "%s", message
) < 0)
4779 if (httpWrite2(client
->http
, "", 0) < 0)
4782 else if (client
->response
)
4785 * Send an IPP response...
4788 debug_attributes("Response", client
->response
, 2);
4790 ippSetState(client
->response
, IPP_STATE_IDLE
);
4792 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
4801 * 'respond_ipp()' - Send an IPP response.
4805 respond_ipp(_ipp_client_t
*client
, /* I - Client */
4806 ipp_status_t status
, /* I - status-code */
4807 const char *message
, /* I - printf-style status-message */
4808 ...) /* I - Additional args as needed */
4810 const char *formatted
= NULL
; /* Formatted message */
4813 ippSetStatusCode(client
->response
, status
);
4817 va_list ap
; /* Pointer to additional args */
4818 ipp_attribute_t
*attr
; /* New status-message attribute */
4820 va_start(ap
, message
);
4821 if ((attr
= ippFindAttribute(client
->response
, "status-message",
4822 IPP_TAG_TEXT
)) != NULL
)
4823 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
4825 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
4826 "status-message", NULL
, message
, ap
);
4829 formatted
= ippGetString(attr
, 0, NULL
);
4833 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
4834 ippOpString(client
->operation_id
), ippErrorString(status
),
4837 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
4838 ippOpString(client
->operation_id
), ippErrorString(status
));
4843 * 'respond_unsupported()' - Respond with an unsupported attribute.
4847 respond_unsupported(
4848 _ipp_client_t
*client
, /* I - Client */
4849 ipp_attribute_t
*attr
) /* I - Atribute */
4851 ipp_attribute_t
*temp
; /* Copy of attribute */
4854 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4855 "Unsupported %s %s%s value.", ippGetName(attr
),
4856 ippGetCount(attr
) > 1 ? "1setOf " : "",
4857 ippTagString(ippGetValueTag(attr
)));
4859 temp
= ippCopyAttribute(client
->response
, attr
, 0);
4860 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
4865 * 'run_printer()' - Run the printer service.
4869 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
4871 int num_fds
; /* Number of file descriptors */
4872 struct pollfd polldata
[3]; /* poll() data */
4873 int timeout
; /* Timeout for poll() */
4874 _ipp_client_t
*client
; /* New client */
4878 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
4881 polldata
[0].fd
= printer
->ipv4
;
4882 polldata
[0].events
= POLLIN
;
4884 polldata
[1].fd
= printer
->ipv6
;
4885 polldata
[1].events
= POLLIN
;
4890 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
4891 polldata
[num_fds
++].events
= POLLIN
;
4892 #endif /* HAVE_DNSSD */
4895 * Loop until we are killed or have a hard error...
4900 if (cupsArrayCount(printer
->jobs
))
4905 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
4907 perror("poll() failed");
4911 if (polldata
[0].revents
& POLLIN
)
4913 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
4915 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4917 perror("Unable to create client thread");
4918 delete_client(client
);
4923 if (polldata
[1].revents
& POLLIN
)
4925 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
4927 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4929 perror("Unable to create client thread");
4930 delete_client(client
);
4936 if (polldata
[2].revents
& POLLIN
)
4937 DNSServiceProcessResult(printer
->common_ref
);
4938 #endif /* HAVE_DNSSD */
4941 * Clean out old jobs...
4944 clean_jobs(printer
);
4950 * 'usage()' - Show program usage.
4954 usage(int status
) /* O - Exit status */
4958 puts(CUPS_SVERSION
" - Copyright 2010-2013 by Apple Inc. All rights "
4963 puts("Usage: ippserver [options] \"name\"");
4966 puts("-2 Supports 2-sided printing (default=1-sided)");
4967 puts("-M manufacturer Manufacturer name (default=Test)");
4968 puts("-P PIN printing mode");
4969 puts("-c command Run command for every print job");
4970 printf("-d spool-directory Spool directory "
4971 "(default=/tmp/ippserver.%d)\n", (int)getpid());
4972 puts("-f type/subtype[,...] List of supported types "
4973 "(default=application/pdf,image/jpeg)");
4974 puts("-h Show program help");
4975 puts("-i iconfile.png PNG icon file (default=printer.png)");
4976 puts("-k Keep job spool files");
4977 puts("-l location Location of printer (default=empty string)");
4978 puts("-m model Model name (default=Printer)");
4979 puts("-n hostname Hostname for printer");
4980 puts("-p port Port number (default=auto)");
4982 puts("-r subtype Bonjour service subtype (default=_print)");
4983 #endif /* HAVE_DNSSD */
4984 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
4985 puts("-v[vvv] Be (very) verbose");
4992 * 'valid_doc_attributes()' - Determine whether the document attributes are
4995 * When one or more document attributes are invalid, this function adds a
4996 * suitable response and attributes to the unsupported group.
4999 static int /* O - 1 if valid, 0 if not */
5000 valid_doc_attributes(
5001 _ipp_client_t
*client
) /* I - Client */
5003 int valid
= 1; /* Valid attributes? */
5004 ipp_op_t op
= ippGetOperation(client
->request
);
5006 const char *op_name
= ippOpString(op
);
5007 /* IPP operation name */
5008 ipp_attribute_t
*attr
, /* Current attribute */
5009 *supported
; /* xxx-supported attribute */
5010 const char *compression
= NULL
,
5011 /* compression value */
5012 *format
= NULL
; /* document-format value */
5016 * Check operation attributes...
5019 if ((attr
= ippFindAttribute(client
->request
, "compression",
5020 IPP_TAG_ZERO
)) != NULL
)
5023 * If compression is specified, only accept a supported value in a Print-Job
5024 * or Send-Document request...
5027 compression
= ippGetString(attr
, 0, NULL
);
5028 supported
= ippFindAttribute(client
->printer
->attrs
,
5029 "compression-supported", IPP_TAG_KEYWORD
);
5031 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5032 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
5033 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
5034 op
!= IPP_OP_VALIDATE_JOB
) ||
5035 !ippContainsString(supported
, compression
))
5037 respond_unsupported(client
, attr
);
5042 fprintf(stderr
, "%s %s compression=\"%s\"\n",
5043 client
->hostname
, op_name
, compression
);
5045 if (strcmp(compression
, "none"))
5046 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
5051 * Is it a format we support?
5054 if ((attr
= ippFindAttribute(client
->request
, "document-format",
5055 IPP_TAG_ZERO
)) != NULL
)
5057 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
5058 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
5060 respond_unsupported(client
, attr
);
5065 format
= ippGetString(attr
, 0, NULL
);
5067 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
5068 client
->hostname
, op_name
, format
);
5073 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
,
5074 "document-format-default",
5075 IPP_TAG_MIMETYPE
), 0, NULL
);
5077 format
= "application/octet-stream"; /* Should never happen */
5079 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5080 "document-format", NULL
, format
);
5083 if (!strcmp(format
, "application/octet-stream") &&
5084 (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
||
5085 ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
5088 * Auto-type the file using the first 4 bytes of the file...
5091 unsigned char header
[4]; /* First 4 bytes of file */
5093 memset(header
, 0, sizeof(header
));
5094 httpPeek(client
->http
, (char *)header
, sizeof(header
));
5096 if (!memcmp(header
, "%PDF", 4))
5097 format
= "application/pdf";
5098 else if (!memcmp(header
, "%!", 2))
5099 format
= "application/postscript";
5100 else if (!memcmp(header
, "\377\330\377", 3) &&
5101 header
[3] >= 0xe0 && header
[3] <= 0xef)
5102 format
= "image/jpeg";
5103 else if (!memcmp(header
, "\211PNG", 4))
5104 format
= "image/png";
5107 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
5108 client
->hostname
, op_name
, format
);
5111 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5112 "document-format", NULL
, format
);
5114 ippSetString(client
->request
, &attr
, 0, format
);
5117 if (op
!= IPP_OP_CREATE_JOB
&&
5118 (supported
= ippFindAttribute(client
->printer
->attrs
,
5119 "document-format-supported",
5120 IPP_TAG_MIMETYPE
)) != NULL
&&
5121 !ippContainsString(supported
, format
))
5123 respond_unsupported(client
, attr
);
5132 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5134 * When one or more job attributes are invalid, this function adds a suitable
5135 * response and attributes to the unsupported group.
5138 static int /* O - 1 if valid, 0 if not */
5139 valid_job_attributes(
5140 _ipp_client_t
*client
) /* I - Client */
5142 int i
, /* Looping var */
5143 valid
= 1; /* Valid attributes? */
5144 ipp_attribute_t
*attr
, /* Current attribute */
5145 *supported
; /* xxx-supported attribute */
5149 * Check operation attributes...
5152 valid
= valid_doc_attributes(client
);
5155 * Check the various job template attributes...
5158 if ((attr
= ippFindAttribute(client
->request
, "copies",
5159 IPP_TAG_ZERO
)) != NULL
)
5161 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5162 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
5164 respond_unsupported(client
, attr
);
5169 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity",
5170 IPP_TAG_ZERO
)) != NULL
)
5172 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
5174 respond_unsupported(client
, attr
);
5179 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until",
5180 IPP_TAG_ZERO
)) != NULL
)
5182 if (ippGetCount(attr
) != 1 ||
5183 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5184 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5185 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5186 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
5188 respond_unsupported(client
, attr
);
5193 if ((attr
= ippFindAttribute(client
->request
, "job-name",
5194 IPP_TAG_ZERO
)) != NULL
)
5196 if (ippGetCount(attr
) != 1 ||
5197 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5198 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
5200 respond_unsupported(client
, attr
);
5205 if ((attr
= ippFindAttribute(client
->request
, "job-priority",
5206 IPP_TAG_ZERO
)) != NULL
)
5208 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5209 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
5211 respond_unsupported(client
, attr
);
5216 if ((attr
= ippFindAttribute(client
->request
, "job-sheets",
5217 IPP_TAG_ZERO
)) != NULL
)
5219 if (ippGetCount(attr
) != 1 ||
5220 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5221 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5222 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5223 strcmp(ippGetString(attr
, 0, NULL
), "none"))
5225 respond_unsupported(client
, attr
);
5230 if ((attr
= ippFindAttribute(client
->request
, "media",
5231 IPP_TAG_ZERO
)) != NULL
)
5233 if (ippGetCount(attr
) != 1 ||
5234 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5235 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5236 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
5238 respond_unsupported(client
, attr
);
5244 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
5246 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
5249 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
5251 respond_unsupported(client
, attr
);
5257 if ((attr
= ippFindAttribute(client
->request
, "media-col",
5258 IPP_TAG_ZERO
)) != NULL
)
5260 if (ippGetCount(attr
) != 1 ||
5261 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
5263 respond_unsupported(client
, attr
);
5266 /* TODO: check for valid media-col */
5269 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling",
5270 IPP_TAG_ZERO
)) != NULL
)
5272 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5273 (strcmp(ippGetString(attr
, 0, NULL
),
5274 "separate-documents-uncollated-copies") &&
5275 strcmp(ippGetString(attr
, 0, NULL
),
5276 "separate-documents-collated-copies")))
5278 respond_unsupported(client
, attr
);
5283 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested",
5284 IPP_TAG_ZERO
)) != NULL
)
5286 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5287 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
5288 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
5290 respond_unsupported(client
, attr
);
5295 if ((attr
= ippFindAttribute(client
->request
, "page-ranges",
5296 IPP_TAG_ZERO
)) != NULL
)
5298 respond_unsupported(client
, attr
);
5302 if ((attr
= ippFindAttribute(client
->request
, "print-quality",
5303 IPP_TAG_ZERO
)) != NULL
)
5305 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5306 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
5307 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
5309 respond_unsupported(client
, attr
);
5314 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution",
5315 IPP_TAG_ZERO
)) != NULL
)
5317 respond_unsupported(client
, attr
);
5321 if ((attr
= ippFindAttribute(client
->request
, "sides",
5322 IPP_TAG_ZERO
)) != NULL
)
5324 const char *sides
= NULL
; /* "sides" value... */
5326 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
5328 respond_unsupported(client
, attr
);
5332 sides
= ippGetString(attr
, 0, NULL
);
5334 if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported",
5335 IPP_TAG_KEYWORD
)) != NULL
)
5337 if (!ippContainsString(supported
, sides
))
5339 respond_unsupported(client
, attr
);
5343 else if (strcmp(sides
, "one-sided"))
5345 respond_unsupported(client
, attr
);