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_filter_s
/**** Attribute filter ****/
154 cups_array_t
*ra
; /* Requested attributes */
155 ipp_tag_t group_tag
; /* Group to copy */
158 typedef struct _ipp_job_s _ipp_job_t
;
160 typedef struct _ipp_printer_s
/**** Printer data ****/
162 int ipv4
, /* IPv4 listener */
163 ipv6
; /* IPv6 listener */
165 DNSServiceRef common_ref
, /* Shared service connection */
166 ipp_ref
, /* Bonjour IPP service */
168 ipps_ref
, /* Bonjour IPPS service */
169 # endif /* HAVE_SSL */
170 http_ref
, /* Bonjour HTTP service */
171 printer_ref
; /* Bonjour LPD service */
172 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
173 char *dnssd_name
; /* printer-dnssd-name */
174 #endif /* HAVE_DNSSD */
175 char *name
, /* printer-name */
176 *icon
, /* Icon filename */
177 *directory
, /* Spool directory */
178 *hostname
, /* Hostname */
179 *uri
, /* printer-uri-supported */
180 *command
; /* Command to run with job file */
182 size_t urilen
; /* Length of printer URI */
183 ipp_t
*attrs
; /* Static attributes */
184 ipp_pstate_t state
; /* printer-state value */
185 _ipp_preasons_t state_reasons
; /* printer-state-reasons values */
186 cups_array_t
*jobs
; /* Jobs */
187 _ipp_job_t
*active_job
; /* Current active/pending job */
188 int next_job_id
; /* Next job-id value */
189 _cups_rwlock_t rwlock
; /* Printer lock */
192 struct _ipp_job_s
/**** Job data ****/
195 const char *name
, /* job-name */
196 *username
, /* job-originating-user-name */
197 *format
; /* document-format */
198 ipp_jstate_t state
; /* job-state value */
199 time_t processing
, /* time-at-processing value */
200 completed
; /* time-at-completed value */
201 ipp_t
*attrs
; /* Static attributes */
202 int cancel
; /* Non-zero when job canceled */
203 char *filename
; /* Print file name */
204 int fd
; /* Print file descriptor */
205 _ipp_printer_t
*printer
; /* Printer */
208 typedef struct _ipp_client_s
/**** Client data ****/
210 http_t
*http
; /* HTTP connection */
211 ipp_t
*request
, /* IPP request */
212 *response
; /* IPP response */
213 time_t start
; /* Request start time */
214 http_state_t operation
; /* Request operation */
215 ipp_op_t operation_id
; /* IPP operation-id */
216 char uri
[1024]; /* Request URI */
217 http_addr_t addr
; /* Client address */
218 char hostname
[256]; /* Client hostname */
219 _ipp_printer_t
*printer
; /* Printer */
220 _ipp_job_t
*job
; /* Current job, if any */
228 static void clean_jobs(_ipp_printer_t
*printer
);
229 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
230 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
231 ipp_tag_t group_tag
, int quickcopy
);
232 static void copy_job_attributes(_ipp_client_t
*client
,
233 _ipp_job_t
*job
, cups_array_t
*ra
);
234 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
235 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
236 static int create_listener(int family
, int *port
);
237 static ipp_t
*create_media_col(const char *media
, const char *type
,
238 int width
, int length
, int margins
);
239 static ipp_t
*create_media_size(int width
, int length
);
240 static _ipp_printer_t
*create_printer(const char *servername
,
241 const char *name
, const char *location
,
242 const char *make
, const char *model
,
244 const char *docformats
, int ppm
,
245 int ppm_color
, int duplex
, int port
,
249 #endif /* HAVE_DNSSD */
250 const char *directory
,
251 const char *command
);
252 static void debug_attributes(const char *title
, ipp_t
*ipp
,
254 static void delete_client(_ipp_client_t
*client
);
255 static void delete_job(_ipp_job_t
*job
);
256 static void delete_printer(_ipp_printer_t
*printer
);
258 static void dnssd_callback(DNSServiceRef sdRef
,
259 DNSServiceFlags flags
,
260 DNSServiceErrorType errorCode
,
264 _ipp_printer_t
*printer
);
265 #endif /* HAVE_DNSSD */
266 static int filter_cb(_ipp_filter_t
*filter
, ipp_t
*dst
, ipp_attribute_t
*attr
);
267 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
268 static void html_escape(_ipp_client_t
*client
, const char *s
,
270 static void html_printf(_ipp_client_t
*client
, const char *format
,
271 ...) __attribute__((__format__(__printf__
,
273 static void ipp_cancel_job(_ipp_client_t
*client
);
274 static void ipp_close_job(_ipp_client_t
*client
);
275 static void ipp_create_job(_ipp_client_t
*client
);
276 static void ipp_get_job_attributes(_ipp_client_t
*client
);
277 static void ipp_get_jobs(_ipp_client_t
*client
);
278 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
279 static void ipp_identify_printer(_ipp_client_t
*client
);
280 static void ipp_print_job(_ipp_client_t
*client
);
281 static void ipp_print_uri(_ipp_client_t
*client
);
282 static void ipp_send_document(_ipp_client_t
*client
);
283 static void ipp_send_uri(_ipp_client_t
*client
);
284 static void ipp_validate_job(_ipp_client_t
*client
);
285 static void *process_client(_ipp_client_t
*client
);
286 static int process_http(_ipp_client_t
*client
);
287 static int process_ipp(_ipp_client_t
*client
);
288 static void *process_job(_ipp_job_t
*job
);
290 static int register_printer(_ipp_printer_t
*printer
,
291 const char *location
, const char *make
,
292 const char *model
, const char *formats
,
293 const char *adminurl
, int color
,
294 int duplex
, const char *regtype
);
295 #endif /* HAVE_DNSSD */
296 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
297 const char *content_coding
,
298 const char *type
, size_t length
);
299 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
300 const char *message
, ...)
301 __attribute__ ((__format__ (__printf__
, 3, 4)));
302 static void respond_unsupported(_ipp_client_t
*client
,
303 ipp_attribute_t
*attr
);
304 static void run_printer(_ipp_printer_t
*printer
);
305 static void usage(int status
) __attribute__((noreturn
));
306 static int valid_doc_attributes(_ipp_client_t
*client
);
307 static int valid_job_attributes(_ipp_client_t
*client
);
314 static int KeepFiles
= 0,
319 * 'main()' - Main entry to the sample server.
322 int /* O - Exit status */
323 main(int argc
, /* I - Number of command-line args */
324 char *argv
[]) /* I - Command-line arguments */
326 int i
; /* Looping var */
327 const char *opt
, /* Current option character */
328 *command
= NULL
, /* Command to run with job files */
329 *servername
= NULL
, /* Server host name */
330 *name
= NULL
, /* Printer name */
331 *location
= "", /* Location of printer */
332 *make
= "Test", /* Manufacturer */
333 *model
= "Printer", /* Model */
334 *icon
= "printer.png", /* Icon file */
335 *formats
= "application/pdf,image/jpeg,image/pwg-raster";
336 /* Supported formats */
338 const char *keypath
= NULL
; /* Keychain path */
339 #endif /* HAVE_SSL */
341 const char *subtype
= "_print"; /* Bonjour service subtype */
342 #endif /* HAVE_DNSSD */
343 int port
= 8631, /* Port number (0 = auto) */
344 duplex
= 0, /* Duplex mode */
345 ppm
= 10, /* Pages per minute for mono */
346 ppm_color
= 0, /* Pages per minute for color */
347 pin
= 0; /* PIN printing mode? */
348 char directory
[1024] = ""; /* Spool directory */
349 _ipp_printer_t
*printer
; /* Printer object */
353 * Parse command-line arguments...
356 for (i
= 1; i
< argc
; i
++)
357 if (argv
[i
][0] == '-')
359 for (opt
= argv
[i
] + 1; *opt
; opt
++)
363 case '2' : /* -2 (enable 2-sided printing) */
368 case 'K' : /* -K keypath */
374 #endif /* HAVE_SSL */
376 case 'M' : /* -M manufacturer */
383 case 'P' : /* -P (PIN printing mode) */
387 case 'c' : /* -c command */
395 case 'd' : /* -d spool-directory */
399 strlcpy(directory
, argv
[i
], sizeof(directory
));
402 case 'f' : /* -f type/subtype[,...] */
409 case 'h' : /* -h (show help) */
412 case 'i' : /* -i icon.png */
419 case 'k' : /* -k (keep files) */
423 case 'l' : /* -l location */
430 case 'm' : /* -m model */
437 case 'n' : /* -n hostname */
441 servername
= argv
[i
];
444 case 'p' : /* -p port */
446 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
448 port
= atoi(argv
[i
]);
452 case 'r' : /* -r subtype */
458 #endif /* HAVE_DNSSD */
460 case 's' : /* -s speed[,color-speed] */
464 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
468 case 'v' : /* -v (be verbose) */
472 default : /* Unknown */
473 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
484 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
492 * Apply defaults as needed...
497 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
499 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
501 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
502 directory
, strerror(errno
));
507 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
511 cupsSetServerCredentials(keypath
, servername
, 1);
512 #endif /* HAVE_SSL */
515 * Create the printer...
518 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
519 formats
, ppm
, ppm_color
, duplex
, port
, pin
,
522 #endif /* HAVE_DNSSD */
523 directory
, command
)) == NULL
)
527 * Run the print service...
530 run_printer(printer
);
533 * Destroy the printer and exit...
536 delete_printer(printer
);
543 * 'clean_jobs()' - Clean out old (completed) jobs.
547 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
549 _ipp_job_t
*job
; /* Current job */
550 time_t cleantime
; /* Clean time */
553 if (cupsArrayCount(printer
->jobs
) == 0)
556 cleantime
= time(NULL
) - 60;
558 _cupsRWLockWrite(&(printer
->rwlock
));
559 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
561 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
562 if (job
->completed
&& job
->completed
< cleantime
)
564 cupsArrayRemove(printer
->jobs
, job
);
569 _cupsRWUnlock(&(printer
->rwlock
));
574 * 'compare_jobs()' - Compare two jobs.
577 static int /* O - Result of comparison */
578 compare_jobs(_ipp_job_t
*a
, /* I - First job */
579 _ipp_job_t
*b
) /* I - Second job */
581 return (b
->id
- a
->id
);
586 * 'copy_attributes()' - Copy attributes from one request to another.
590 copy_attributes(ipp_t
*to
, /* I - Destination request */
591 ipp_t
*from
, /* I - Source request */
592 cups_array_t
*ra
, /* I - Requested attributes */
593 ipp_tag_t group_tag
, /* I - Group to copy */
594 int quickcopy
) /* I - Do a quick copy? */
596 _ipp_filter_t filter
; /* Filter data */
600 filter
.group_tag
= group_tag
;
602 ippCopyAttributes(to
, from
, quickcopy
, (ipp_copycb_t
)filter_cb
, &filter
);
607 * 'copy_job_attrs()' - Copy job attributes to the response.
612 _ipp_client_t
*client
, /* I - Client */
613 _ipp_job_t
*job
, /* I - Job */
614 cups_array_t
*ra
) /* I - requested-attributes */
616 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_JOB
, 0);
618 if (job
->completed
&& (!ra
|| cupsArrayFind(ra
, "date-time-at-completed")))
619 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-completed", ippTimeToDate(job
->completed
));
621 if (job
->processing
&& (!ra
|| cupsArrayFind(ra
, "date-time-at-processing")))
622 ippAddDate(client
->response
, IPP_TAG_JOB
, "date-time-at-processing", ippTimeToDate(job
->processing
));
624 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
625 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
626 "job-printer-up-time", (int)time(NULL
));
628 if (!ra
|| cupsArrayFind(ra
, "job-state"))
629 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
630 "job-state", job
->state
);
632 if (!ra
|| cupsArrayFind(ra
, "job-state-message"))
636 case IPP_JSTATE_PENDING
:
637 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job pending.");
640 case IPP_JSTATE_HELD
:
642 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job incoming.");
643 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
644 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job held.");
646 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job created.");
649 case IPP_JSTATE_PROCESSING
:
651 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceling.");
653 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job printing.");
656 case IPP_JSTATE_STOPPED
:
657 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job stopped.");
660 case IPP_JSTATE_CANCELED
:
661 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job canceled.");
664 case IPP_JSTATE_ABORTED
:
665 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job aborted.");
668 case IPP_JSTATE_COMPLETED
:
669 ippAddString(client
->response
, IPP_TAG_JOB
, IPP_CONST_TAG(IPP_TAG_TEXT
), "job-state-message", NULL
, "Job completed.");
674 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
678 case IPP_JSTATE_PENDING
:
679 ippAddString(client
->response
, IPP_TAG_JOB
,
680 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
684 case IPP_JSTATE_HELD
:
686 ippAddString(client
->response
, IPP_TAG_JOB
,
687 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
688 "job-state-reasons", NULL
, "job-incoming");
689 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
690 ippAddString(client
->response
, IPP_TAG_JOB
,
691 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
692 "job-state-reasons", NULL
, "job-hold-until-specified");
694 ippAddString(client
->response
, IPP_TAG_JOB
,
695 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
696 "job-state-reasons", NULL
, "job-data-insufficient");
699 case IPP_JSTATE_PROCESSING
:
701 ippAddString(client
->response
, IPP_TAG_JOB
,
702 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
703 "job-state-reasons", NULL
, "processing-to-stop-point");
705 ippAddString(client
->response
, IPP_TAG_JOB
,
706 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
707 "job-state-reasons", NULL
, "job-printing");
710 case IPP_JSTATE_STOPPED
:
711 ippAddString(client
->response
, IPP_TAG_JOB
,
712 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
713 NULL
, "job-stopped");
716 case IPP_JSTATE_CANCELED
:
717 ippAddString(client
->response
, IPP_TAG_JOB
,
718 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
719 NULL
, "job-canceled-by-user");
722 case IPP_JSTATE_ABORTED
:
723 ippAddString(client
->response
, IPP_TAG_JOB
,
724 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
725 NULL
, "aborted-by-system");
728 case IPP_JSTATE_COMPLETED
:
729 ippAddString(client
->response
, IPP_TAG_JOB
,
730 IPP_CONST_TAG(IPP_TAG_KEYWORD
), "job-state-reasons",
731 NULL
, "job-completed-successfully");
736 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
737 ippAddInteger(client
->response
, IPP_TAG_JOB
,
738 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
739 "time-at-completed", (int)job
->completed
);
741 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
742 ippAddInteger(client
->response
, IPP_TAG_JOB
,
743 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
744 "time-at-processing", (int)job
->processing
);
749 * 'create_client()' - Accept a new network connection and create a client
753 static _ipp_client_t
* /* O - Client */
754 create_client(_ipp_printer_t
*printer
, /* I - Printer */
755 int sock
) /* I - Listen socket */
757 _ipp_client_t
*client
; /* Client */
760 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
762 perror("Unable to allocate memory for client");
766 client
->printer
= printer
;
769 * Accept the client and get the remote address...
772 if ((client
->http
= httpAcceptConnection(sock
, 1)) == NULL
)
774 perror("Unable to accept client connection");
781 httpGetHostname(client
->http
, client
->hostname
, sizeof(client
->hostname
));
784 fprintf(stderr
, "Accepted connection from %s\n", client
->hostname
);
791 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
795 static _ipp_job_t
* /* O - Job */
796 create_job(_ipp_client_t
*client
) /* I - Client */
798 _ipp_job_t
*job
; /* Job */
799 ipp_attribute_t
*attr
; /* Job attribute */
800 char uri
[1024]; /* job-uri value */
801 time_t curtime
; /* Current date/time */
804 _cupsRWLockWrite(&(client
->printer
->rwlock
));
805 if (client
->printer
->active_job
&&
806 client
->printer
->active_job
->state
< IPP_JSTATE_CANCELED
)
809 * Only accept a single job at a time...
812 _cupsRWLockWrite(&(client
->printer
->rwlock
));
817 * Allocate and initialize the job object...
820 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
822 perror("Unable to allocate memory for job");
826 job
->printer
= client
->printer
;
827 job
->attrs
= client
->request
;
828 job
->state
= IPP_JSTATE_HELD
;
830 client
->request
= NULL
;
833 * Set all but the first two attributes to the job attributes group...
836 for (ippFirstAttribute(job
->attrs
),
837 ippNextAttribute(job
->attrs
),
838 attr
= ippNextAttribute(job
->attrs
);
840 attr
= ippNextAttribute(job
->attrs
))
841 ippSetGroupTag(job
->attrs
, &attr
, IPP_TAG_JOB
);
844 * Get the requesting-user-name, document format, and priority...
847 if ((attr
= ippFindAttribute(job
->attrs
, "requesting-user-name",
848 IPP_TAG_NAME
)) != NULL
)
849 ippSetName(job
->attrs
, &attr
, "job-originating-user-name");
851 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
,
852 IPP_CONST_TAG(IPP_TAG_NAME
),
853 "job-originating-user-name", NULL
, "anonymous");
856 job
->username
= ippGetString(attr
, 0, NULL
);
858 job
->username
= "anonymous";
860 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
861 IPP_TAG_MIMETYPE
)) != NULL
)
862 job
->format
= ippGetString(attr
, 0, NULL
);
864 job
->format
= "application/octet-stream";
867 * Add job description attributes and add to the jobs array...
870 job
->id
= client
->printer
->next_job_id
++;
872 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
874 ippAddDate(job
->attrs
, IPP_TAG_JOB
, "date-time-at-creation", ippTimeToDate(time(&curtime
)));
875 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
876 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
877 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
, client
->printer
->uri
);
878 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation", (int)curtime
);
880 cupsArrayAdd(client
->printer
->jobs
, job
);
881 client
->printer
->active_job
= job
;
883 _cupsRWUnlock(&(client
->printer
->rwlock
));
890 * 'create_listener()' - Create a listener socket.
893 static int /* O - Listener socket or -1 on error */
894 create_listener(int family
, /* I - Address family */
895 int *port
) /* IO - Port number */
897 int sock
; /* Listener socket */
898 http_addrlist_t
*addrlist
; /* Listen address */
899 char service
[255]; /* Service port */
904 *port
= 8000 + (getuid() % 1000);
905 fprintf(stderr
, "Listening on port %d.\n", *port
);
908 snprintf(service
, sizeof(service
), "%d", *port
);
909 if ((addrlist
= httpAddrGetList(NULL
, family
, service
)) == NULL
)
912 sock
= httpAddrListen(&(addrlist
->addr
), *port
);
914 httpAddrFreeList(addrlist
);
921 * 'create_media_col()' - Create a media-col value.
924 static ipp_t
* /* O - media-col collection */
925 create_media_col(const char *media
, /* I - Media name */
926 const char *type
, /* I - Nedua type */
927 int width
, /* I - x-dimension in 2540ths */
928 int length
, /* I - y-dimension in 2540ths */
929 int margins
) /* I - Value for margins */
931 ipp_t
*media_col
= ippNew(), /* media-col value */
932 *media_size
= create_media_size(width
, length
);
933 /* media-size value */
934 char media_key
[256]; /* media-key value */
937 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, type
,
938 margins
== 0 ? "_borderless" : "");
940 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
942 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
943 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
944 "media-bottom-margin", margins
);
945 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
946 "media-left-margin", margins
);
947 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
948 "media-right-margin", margins
);
949 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
950 "media-top-margin", margins
);
951 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type",
954 ippDelete(media_size
);
961 * 'create_media_size()' - Create a media-size value.
964 static ipp_t
* /* O - media-col collection */
965 create_media_size(int width
, /* I - x-dimension in 2540ths */
966 int length
) /* I - y-dimension in 2540ths */
968 ipp_t
*media_size
= ippNew(); /* media-size value */
971 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
973 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
981 * 'create_printer()' - Create, register, and listen for connections to a
985 static _ipp_printer_t
* /* O - Printer */
986 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
987 const char *name
, /* I - printer-name */
988 const char *location
, /* I - printer-location */
989 const char *make
, /* I - printer-make-and-model */
990 const char *model
, /* I - printer-make-and-model */
991 const char *icon
, /* I - printer-icons */
992 const char *docformats
, /* I - document-format-supported */
993 int ppm
, /* I - Pages per minute in grayscale */
994 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
995 int duplex
, /* I - 1 = duplex, 0 = simplex */
996 int port
, /* I - Port for listeners or 0 for auto */
997 int pin
, /* I - Require PIN printing */
999 const char *subtype
, /* I - Bonjour service subtype */
1000 #endif /* HAVE_DNSSD */
1001 const char *directory
, /* I - Spool directory */
1002 const char *command
) /* I - Command to run on job files */
1004 int i
, j
; /* Looping vars */
1005 _ipp_printer_t
*printer
; /* Printer */
1006 char hostname
[256], /* Hostname */
1007 uri
[1024], /* Printer URI */
1008 icons
[1024], /* printer-icons URI */
1009 adminurl
[1024], /* printer-more-info URI */
1010 device_id
[1024],/* printer-device-id */
1011 make_model
[128],/* printer-make-and-model */
1012 uuid
[128]; /* printer-uuid */
1013 int num_formats
; /* Number of document-format-supported values */
1014 char *defformat
, /* document-format-default value */
1015 *formats
[100], /* document-format-supported values */
1016 *ptr
; /* Pointer into string */
1017 const char *prefix
; /* Prefix string */
1018 int num_database
; /* Number of database values */
1019 ipp_attribute_t
*media_col_database
,
1020 /* media-col-database value */
1021 *media_size_supported
;
1022 /* media-size-supported value */
1023 ipp_t
*media_col_default
;
1024 /* media-col-default value */
1025 int media_col_index
;/* Current media-col-database value */
1026 int k_supported
; /* Maximum file size supported */
1028 struct statvfs spoolinfo
; /* FS info for spool directory */
1029 double spoolsize
; /* FS size */
1030 #elif defined(HAVE_STATFS)
1031 struct statfs spoolinfo
; /* FS info for spool directory */
1032 double spoolsize
; /* FS size */
1033 #endif /* HAVE_STATVFS */
1034 static const int orients
[4] = /* orientation-requested-supported values */
1036 IPP_ORIENT_PORTRAIT
,
1037 IPP_ORIENT_LANDSCAPE
,
1038 IPP_ORIENT_REVERSE_LANDSCAPE
,
1039 IPP_ORIENT_REVERSE_PORTRAIT
1041 static const char * const versions
[] =/* ipp-versions-supported values */
1047 static const char * const features
[] =/* ipp-features-supported values */
1051 static const int ops
[] = /* operations-supported values */
1055 IPP_OP_VALIDATE_JOB
,
1057 IPP_OP_SEND_DOCUMENT
,
1060 IPP_OP_GET_JOB_ATTRIBUTES
,
1062 IPP_OP_GET_PRINTER_ATTRIBUTES
,
1063 IPP_OP_CANCEL_MY_JOBS
,
1065 IPP_OP_IDENTIFY_PRINTER
1067 static const char * const charsets
[] =/* charset-supported values */
1072 static const char * const compressions
[] =/* compression-supported values */
1077 #endif /* HAVE_LIBZ */
1080 static const char * const identify_actions
[] =
1085 static const char * const job_creation
[] =
1086 { /* job-creation-attributes-supported values */
1088 "ipp-attribute-fidelity",
1090 "job-accounting-user-id",
1096 "multiple-document-handling",
1097 "orientation-requested",
1101 static const char * const media_col_supported
[] =
1102 { /* media-col-supported values */
1103 "media-bottom-margin",
1104 "media-left-margin",
1105 "media-right-margin",
1110 static const int media_xxx_margin_supported
[] =
1111 { /* media-xxx-margin-supported values */
1115 static const char * const multiple_document_handling
[] =
1116 { /* multiple-document-handling-supported values */
1117 "separate-documents-uncollated-copies",
1118 "separate-documents-collated-copies"
1120 static const char * const overrides
[] =
1121 { /* overrides-supported */
1125 static const char * const print_color_mode_supported
[] =
1126 { /* print-color-mode-supported values */
1131 static const int print_quality_supported
[] =
1132 { /* print-quality-supported values */
1137 static const int pwg_raster_document_resolution_supported
[] =
1143 static const char * const pwg_raster_document_type_supported
[] =
1151 static const char * const reference_uri_schemes_supported
[] =
1152 { /* reference-uri-schemes-supported */
1158 #endif /* HAVE_SSL */
1160 static const char * const sides_supported
[] =
1161 { /* sides-supported values */
1163 "two-sided-long-edge",
1164 "two-sided-short-edge"
1166 static const char * const which_jobs
[] =
1167 { /* which-jobs-supported values */
1176 "processing-stopped"
1181 * Allocate memory for the printer...
1184 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1186 perror("Unable to allocate memory for printer");
1192 printer
->name
= strdup(name
);
1194 printer
->dnssd_name
= strdup(printer
->name
);
1195 #endif /* HAVE_DNSSD */
1196 printer
->command
= command
? strdup(command
) : NULL
;
1197 printer
->directory
= strdup(directory
);
1198 printer
->hostname
= strdup(servername
? servername
:
1199 httpGetHostname(NULL
, hostname
,
1201 printer
->port
= port
;
1202 printer
->state
= IPP_PSTATE_IDLE
;
1203 printer
->state_reasons
= _IPP_PSTATE_NONE
;
1204 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1205 printer
->next_job_id
= 1;
1207 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1208 printer
->hostname
, printer
->port
, "/ipp/print");
1209 printer
->uri
= strdup(uri
);
1210 printer
->urilen
= strlen(uri
);
1213 printer
->icon
= strdup(icon
);
1215 _cupsRWInit(&(printer
->rwlock
));
1218 * Create the listener sockets...
1221 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1223 perror("Unable to create IPv4 listener");
1227 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1229 perror("Unable to create IPv6 listener");
1234 * Prepare values for the printer attributes...
1237 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1238 printer
->hostname
, printer
->port
, "/icon.png");
1239 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
,
1240 printer
->hostname
, printer
->port
, "/");
1244 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1245 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1248 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1251 formats
[0] = strdup(docformats
);
1252 defformat
= formats
[0];
1253 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1256 formats
[num_formats
++] = ptr
;
1258 if (!strcasecmp(ptr
, "application/octet-stream"))
1262 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1263 ptr
= device_id
+ strlen(device_id
);
1265 for (i
= 0; i
< num_formats
; i
++)
1267 if (!strcasecmp(formats
[i
], "application/pdf"))
1268 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPDF", prefix
);
1269 else if (!strcasecmp(formats
[i
], "application/postscript"))
1270 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPS", prefix
);
1271 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1272 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPCL", prefix
);
1273 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1274 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sJPEG", prefix
);
1275 else if (!strcasecmp(formats
[i
], "image/png"))
1276 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%sPNG", prefix
);
1277 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1278 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s%s", prefix
, formats
[i
]);
1283 strlcat(device_id
, ";", sizeof(device_id
));
1286 * Get the maximum spool size based on the size of the filesystem used for
1287 * the spool directory. If the host OS doesn't support the statfs call
1288 * or the filesystem is larger than 2TiB, always report INT_MAX.
1292 if (statvfs(printer
->directory
, &spoolinfo
))
1293 k_supported
= INT_MAX
;
1294 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1295 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1296 k_supported
= INT_MAX
;
1298 k_supported
= (int)spoolsize
;
1300 #elif defined(HAVE_STATFS)
1301 if (statfs(printer
->directory
, &spoolinfo
))
1302 k_supported
= INT_MAX
;
1303 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1304 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1305 k_supported
= INT_MAX
;
1307 k_supported
= (int)spoolsize
;
1310 k_supported
= INT_MAX
;
1311 #endif /* HAVE_STATVFS */
1314 * Create the printer attributes. This list of attributes is sorted to improve
1315 * performance when the client provides a requested-attributes attribute...
1318 printer
->attrs
= ippNew();
1320 /* charset-configured */
1321 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1322 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1323 "charset-configured", NULL
, "utf-8");
1325 /* charset-supported */
1326 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1327 IPP_CONST_TAG(IPP_TAG_CHARSET
),
1328 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1331 /* color-supported */
1332 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1335 /* compression-supported */
1336 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1337 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1338 "compression-supported",
1339 (int)(sizeof(compressions
) / sizeof(compressions
[0])), NULL
,
1342 /* copies-default */
1343 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1344 "copies-default", 1);
1346 /* copies-supported */
1347 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1349 /* document-format-default */
1350 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1351 "document-format-default", NULL
, defformat
);
1353 /* document-format-supported */
1354 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1355 "document-format-supported", num_formats
, NULL
,
1356 (const char * const *)formats
);
1358 /* document-password-supported */
1359 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "document-password-supported", 127);
1361 /* finishings-default */
1362 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1363 "finishings-default", IPP_FINISHINGS_NONE
);
1365 /* finishings-supported */
1366 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1367 "finishings-supported", IPP_FINISHINGS_NONE
);
1369 /* generated-natural-language-supported */
1370 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1371 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1372 "generated-natural-language-supported", NULL
, "en");
1374 /* identify-actions-default */
1375 ippAddString (printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "identify-actions-default", NULL
, "sound");
1377 /* identify-actions-supported */
1378 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
);
1380 /* ipp-features-supported */
1381 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-features-supported", sizeof(features
) / sizeof(features
[0]), NULL
, features
);
1383 /* ipp-versions-supported */
1384 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "ipp-versions-supported", sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1386 /* job-account-id-default */
1387 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-account-id-default", NULL
, "");
1389 /* job-account-id-supported */
1390 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-account-id-supported", 1);
1392 /* job-accounting-user-id-default */
1393 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_NAME
), "job-accounting-user-id-default", NULL
, "");
1395 /* job-accounting-user-id-supported */
1396 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-accounting-user-id-supported", 1);
1398 /* job-creation-attributes-supported */
1399 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
);
1401 /* job-ids-supported */
1402 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-ids-supported", 1);
1404 /* job-k-octets-supported */
1405 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1408 /* job-password-supported */
1409 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1410 "job-password-supported", 4);
1412 /* job-preferred-attributes-supported */
1413 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "job-preferred-attributes-supported", 0);
1415 /* job-priority-default */
1416 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1417 "job-priority-default", 50);
1419 /* job-priority-supported */
1420 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1421 "job-priority-supported", 100);
1423 /* job-sheets-default */
1424 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1425 IPP_CONST_TAG(IPP_TAG_NAME
),
1426 "job-sheets-default", NULL
, "none");
1428 /* job-sheets-supported */
1429 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1430 IPP_CONST_TAG(IPP_TAG_NAME
),
1431 "job-sheets-supported", NULL
, "none");
1433 /* media-bottom-margin-supported */
1434 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1435 "media-bottom-margin-supported",
1436 (int)(sizeof(media_xxx_margin_supported
) /
1437 sizeof(media_xxx_margin_supported
[0])),
1438 media_xxx_margin_supported
);
1440 /* media-col-database */
1441 for (num_database
= 0, i
= 0;
1442 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1445 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1446 num_database
+= 2; /* auto + envelope */
1447 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1448 num_database
+= 12; /* auto + photographic-* + borderless */
1450 num_database
+= (int)(sizeof(media_type_supported
) /
1451 sizeof(media_type_supported
[0])) + 6;
1452 /* All types + borderless */
1455 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1456 "media-col-database", num_database
,
1458 for (media_col_index
= 0, i
= 0;
1459 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1463 j
< (int)(sizeof(media_type_supported
) /
1464 sizeof(media_type_supported
[0]));
1467 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
&&
1468 strcmp(media_type_supported
[j
], "auto") &&
1469 strcmp(media_type_supported
[j
], "envelope"))
1471 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
&&
1472 strcmp(media_type_supported
[j
], "auto") &&
1473 strncmp(media_type_supported
[j
], "photographic-", 13))
1476 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1477 create_media_col(media_supported
[i
],
1478 media_type_supported
[j
],
1479 media_col_sizes
[i
][0],
1480 media_col_sizes
[i
][1],
1481 media_xxx_margin_supported
[1]));
1484 if (media_col_sizes
[i
][2] != _IPP_ENV_ONLY
&&
1485 (!strcmp(media_type_supported
[j
], "auto") ||
1486 !strncmp(media_type_supported
[j
], "photographic-", 13)))
1489 * Add borderless version for this combination...
1492 ippSetCollection(printer
->attrs
, &media_col_database
, media_col_index
,
1493 create_media_col(media_supported
[i
],
1494 media_type_supported
[j
],
1495 media_col_sizes
[i
][0],
1496 media_col_sizes
[i
][1],
1497 media_xxx_margin_supported
[0]));
1503 /* media-col-default */
1504 media_col_default
= create_media_col(media_supported
[0],
1505 media_type_supported
[0],
1506 media_col_sizes
[0][0],
1507 media_col_sizes
[0][1],
1508 media_xxx_margin_supported
[1]);
1510 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1512 ippDelete(media_col_default
);
1514 /* media-col-supported */
1515 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1516 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1517 "media-col-supported",
1518 (int)(sizeof(media_col_supported
) /
1519 sizeof(media_col_supported
[0])), NULL
,
1520 media_col_supported
);
1523 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1524 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1525 "media-default", NULL
, media_supported
[0]);
1527 /* media-left-margin-supported */
1528 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1529 "media-left-margin-supported",
1530 (int)(sizeof(media_xxx_margin_supported
) /
1531 sizeof(media_xxx_margin_supported
[0])),
1532 media_xxx_margin_supported
);
1534 /* media-right-margin-supported */
1535 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1536 "media-right-margin-supported",
1537 (int)(sizeof(media_xxx_margin_supported
) /
1538 sizeof(media_xxx_margin_supported
[0])),
1539 media_xxx_margin_supported
);
1541 /* media-supported */
1542 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1543 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1545 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1546 NULL
, media_supported
);
1548 /* media-size-supported */
1549 media_size_supported
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1550 "media-size-supported",
1551 (int)(sizeof(media_col_sizes
) /
1552 sizeof(media_col_sizes
[0])),
1555 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1557 ippSetCollection(printer
->attrs
, &media_size_supported
, i
,
1558 create_media_size(media_col_sizes
[i
][0],
1559 media_col_sizes
[i
][1]));
1561 /* media-top-margin-supported */
1562 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1563 "media-top-margin-supported",
1564 (int)(sizeof(media_xxx_margin_supported
) /
1565 sizeof(media_xxx_margin_supported
[0])),
1566 media_xxx_margin_supported
);
1568 /* media-type-supported */
1569 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1570 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1571 "media-type-supported",
1572 (int)(sizeof(media_type_supported
) /
1573 sizeof(media_type_supported
[0])),
1574 NULL
, media_type_supported
);
1576 /* multiple-document-handling-supported */
1577 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
);
1579 /* multiple-document-jobs-supported */
1580 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "multiple-document-jobs-supported", 0);
1582 /* multiple-operation-time-out */
1583 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "multiple-operation-time-out", 60);
1585 /* multiple-operation-time-out-action */
1586 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "multiple-operation-time-out-action", NULL
, "abort-job");
1588 /* natural-language-configured */
1589 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1590 IPP_CONST_TAG(IPP_TAG_LANGUAGE
),
1591 "natural-language-configured", NULL
, "en");
1593 /* number-up-default */
1594 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1595 "number-up-default", 1);
1597 /* number-up-supported */
1598 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1599 "number-up-supported", 1);
1601 /* operations-supported */
1602 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1603 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1605 /* orientation-requested-default */
1606 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1607 "orientation-requested-default", 0);
1609 /* orientation-requested-supported */
1610 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1611 "orientation-requested-supported", 4, orients
);
1613 /* output-bin-default */
1614 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1615 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1616 "output-bin-default", NULL
, "face-down");
1618 /* output-bin-supported */
1619 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1620 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1621 "output-bin-supported", NULL
, "face-down");
1623 /* overrides-supported */
1624 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "overrides-supported", (int)(sizeof(overrides
) / sizeof(overrides
[0])), NULL
, overrides
);
1626 /* page-ranges-supported */
1627 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "page-ranges-supported", 1);
1629 /* pages-per-minute */
1630 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1631 "pages-per-minute", ppm
);
1633 /* pages-per-minute-color */
1635 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1636 "pages-per-minute-color", ppm_color
);
1638 /* pdl-override-supported */
1639 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1640 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1641 "pdl-override-supported", NULL
, "attempted");
1643 /* print-color-mode-default */
1644 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-color-mode-default", NULL
, "auto");
1646 /* print-color-mode-supported */
1647 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
);
1649 /* print-content-optimize-default */
1650 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-default", NULL
, "auto");
1652 /* print-content-optimize-supported */
1653 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-content-optimize-supported", NULL
, "auto");
1655 /* print-rendering-intent-default */
1656 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-default", NULL
, "auto");
1658 /* print-rendering-intent-supported */
1659 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "print-rendering-intent-supported", NULL
, "auto");
1661 /* print-quality-default */
1662 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "print-quality-default", IPP_QUALITY_NORMAL
);
1664 /* print-quality-supported */
1665 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
);
1667 /* printer-device-id */
1668 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1669 "printer-device-id", NULL
, device_id
);
1671 /* printer-get-attributes-supported */
1672 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_KEYWORD
), "printer-get-attributes-supported", NULL
, "document-format");
1674 /* printer-geo-location */
1675 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
, "printer-geo-location", 0);
1678 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1679 "printer-icons", NULL
, icons
);
1681 /* printer-is-accepting-jobs */
1682 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1686 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1689 /* printer-location */
1690 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1691 "printer-location", NULL
, location
);
1693 /* printer-make-and-model */
1694 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1695 "printer-make-and-model", NULL
, make_model
);
1697 /* printer-mandatory-job-attributes */
1700 static const char * const names
[] =
1702 "job-accounting-user-id",
1706 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1707 "printer-mandatory-job-attributes",
1708 (int)(sizeof(names
) / sizeof(names
[0])), NULL
, names
);
1711 /* printer-more-info */
1712 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1713 "printer-more-info", NULL
, adminurl
);
1716 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1719 /* printer-organization */
1720 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organization", NULL
, "Apple Inc.");
1722 /* printer-organizational-unit */
1723 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_CONST_TAG(IPP_TAG_TEXT
), "printer-organizational-unit", NULL
, "Printing Engineering");
1725 /* printer-resolution-default */
1726 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1727 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1729 /* printer-resolution-supported */
1730 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1731 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1733 /* printer-uri-supported */
1734 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uri-supported", NULL
, uri
);
1737 httpAssembleUUID(servername
, port
, name
, 0, uuid
, sizeof(uuid
));
1738 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "printer-uuid", NULL
, uuid
);
1740 /* pwg-raster-document-xxx-supported */
1741 for (i
= 0; i
< num_formats
; i
++)
1742 if (!strcasecmp(formats
[i
], "image/pwg-raster"))
1745 if (i
< num_formats
)
1747 ippAddResolutions(printer
->attrs
, IPP_TAG_PRINTER
,
1748 "pwg-raster-document-resolution-supported",
1749 (int)(sizeof(pwg_raster_document_resolution_supported
) /
1750 sizeof(pwg_raster_document_resolution_supported
[0])),
1752 pwg_raster_document_resolution_supported
,
1753 pwg_raster_document_resolution_supported
);
1754 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1755 "pwg-raster-document-sheet-back", NULL
, "normal");
1756 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
,
1757 "pwg-raster-document-type-supported",
1758 (int)(sizeof(pwg_raster_document_type_supported
) /
1759 sizeof(pwg_raster_document_type_supported
[0])), NULL
,
1760 pwg_raster_document_type_supported
);
1763 /* reference-uri-scheme-supported */
1764 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1765 IPP_CONST_TAG(IPP_TAG_URISCHEME
),
1766 "reference-uri-schemes-supported",
1767 (int)(sizeof(reference_uri_schemes_supported
) /
1768 sizeof(reference_uri_schemes_supported
[0])),
1769 NULL
, reference_uri_schemes_supported
);
1772 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1773 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1774 "sides-default", NULL
, "one-sided");
1776 /* sides-supported */
1777 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1778 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1779 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1781 /* uri-authentication-supported */
1782 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1783 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1784 "uri-authentication-supported", NULL
, "none");
1786 /* uri-security-supported */
1787 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
,
1788 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1789 "uri-security-supported", NULL
, "none");
1791 /* which-jobs-supported */
1792 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
,
1793 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
1794 "which-jobs-supported",
1795 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1799 debug_attributes("Printer", printer
->attrs
, 0);
1803 * Register the printer with Bonjour...
1806 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
,
1807 ppm_color
> 0, duplex
, subtype
))
1809 #endif /* HAVE_DNSSD */
1819 * If we get here we were unable to create the printer...
1824 delete_printer(printer
);
1830 * 'debug_attributes()' - Print attributes in a request or response.
1834 debug_attributes(const char *title
, /* I - Title */
1835 ipp_t
*ipp
, /* I - Request/response */
1836 int type
) /* I - 0 = object, 1 = request, 2 = response */
1838 ipp_tag_t group_tag
; /* Current group */
1839 ipp_attribute_t
*attr
; /* Current attribute */
1840 char buffer
[2048]; /* String buffer for value */
1841 int major
, minor
; /* Version */
1847 fprintf(stderr
, "%s:\n", title
);
1848 major
= ippGetVersion(ipp
, &minor
);
1849 fprintf(stderr
, " version=%d.%d\n", major
, minor
);
1851 fprintf(stderr
, " operation-id=%s(%04x)\n",
1852 ippOpString(ippGetOperation(ipp
)), ippGetOperation(ipp
));
1854 fprintf(stderr
, " status-code=%s(%04x)\n",
1855 ippErrorString(ippGetStatusCode(ipp
)), ippGetStatusCode(ipp
));
1856 fprintf(stderr
, " request-id=%d\n\n", ippGetRequestId(ipp
));
1858 for (attr
= ippFirstAttribute(ipp
), group_tag
= IPP_TAG_ZERO
;
1860 attr
= ippNextAttribute(ipp
))
1862 if (ippGetGroupTag(attr
) != group_tag
)
1864 group_tag
= ippGetGroupTag(attr
);
1865 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
1868 if (ippGetName(attr
))
1870 ippAttributeString(attr
, buffer
, sizeof(buffer
));
1871 fprintf(stderr
, " %s (%s%s) %s\n", ippGetName(attr
),
1872 ippGetCount(attr
) > 1 ? "1setOf " : "",
1873 ippTagString(ippGetValueTag(attr
)), buffer
);
1880 * 'delete_client()' - Close the socket and free all memory used by a client
1885 delete_client(_ipp_client_t
*client
) /* I - Client */
1888 fprintf(stderr
, "Closing connection from %s\n", client
->hostname
);
1891 * Flush pending writes before closing...
1894 httpFlushWrite(client
->http
);
1900 httpClose(client
->http
);
1902 ippDelete(client
->request
);
1903 ippDelete(client
->response
);
1910 * 'delete_job()' - Remove from the printer and free all memory used by a job
1915 delete_job(_ipp_job_t
*job
) /* I - Job */
1918 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
1920 ippDelete(job
->attrs
);
1925 unlink(job
->filename
);
1927 free(job
->filename
);
1935 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
1936 * used by a printer object.
1940 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
1942 if (printer
->ipv4
>= 0)
1943 close(printer
->ipv4
);
1945 if (printer
->ipv6
>= 0)
1946 close(printer
->ipv6
);
1949 if (printer
->printer_ref
)
1950 DNSServiceRefDeallocate(printer
->printer_ref
);
1952 if (printer
->ipp_ref
)
1953 DNSServiceRefDeallocate(printer
->ipp_ref
);
1956 if (printer
->ipps_ref
)
1957 DNSServiceRefDeallocate(printer
->ipps_ref
);
1958 # endif /* HAVE_SSL */
1959 if (printer
->http_ref
)
1960 DNSServiceRefDeallocate(printer
->http_ref
);
1962 if (printer
->common_ref
)
1963 DNSServiceRefDeallocate(printer
->common_ref
);
1965 TXTRecordDeallocate(&(printer
->ipp_txt
));
1967 if (printer
->dnssd_name
)
1968 free(printer
->dnssd_name
);
1969 #endif /* HAVE_DNSSD */
1972 free(printer
->name
);
1974 free(printer
->icon
);
1975 if (printer
->command
)
1976 free(printer
->command
);
1977 if (printer
->directory
)
1978 free(printer
->directory
);
1979 if (printer
->hostname
)
1980 free(printer
->hostname
);
1984 ippDelete(printer
->attrs
);
1985 cupsArrayDelete(printer
->jobs
);
1993 * 'dnssd_callback()' - Handle Bonjour registration events.
1998 DNSServiceRef sdRef
, /* I - Service reference */
1999 DNSServiceFlags flags
, /* I - Status flags */
2000 DNSServiceErrorType errorCode
, /* I - Error, if any */
2001 const char *name
, /* I - Service name */
2002 const char *regtype
, /* I - Service type */
2003 const char *domain
, /* I - Domain for service */
2004 _ipp_printer_t
*printer
) /* I - Printer */
2012 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2013 regtype
, (int)errorCode
);
2016 else if (strcasecmp(name
, printer
->dnssd_name
))
2019 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2021 /* No lock needed since only the main thread accesses/changes this */
2022 free(printer
->dnssd_name
);
2023 printer
->dnssd_name
= strdup(name
);
2026 #endif /* HAVE_DNSSD */
2030 * 'filter_cb()' - Filter printer attributes based on the requested array.
2033 static int /* O - 1 to copy, 0 to ignore */
2034 filter_cb(_ipp_filter_t
*filter
, /* I - Filter parameters */
2035 ipp_t
*dst
, /* I - Destination (unused) */
2036 ipp_attribute_t
*attr
) /* I - Source attribute */
2039 * Filter attributes as needed...
2042 ipp_tag_t group
= ippGetGroupTag(attr
);
2043 const char *name
= ippGetName(attr
);
2045 if ((filter
->group_tag
!= IPP_TAG_ZERO
&& group
!= filter
->group_tag
&& group
!= IPP_TAG_ZERO
) || !name
|| (!strcmp(name
, "media-col-database") && !cupsArrayFind(filter
->ra
, (void *)name
)))
2048 return (!filter
->ra
|| cupsArrayFind(filter
->ra
, (void *)name
) != NULL
);
2053 * 'find_job()' - Find a job specified in a request.
2056 static _ipp_job_t
* /* O - Job or NULL */
2057 find_job(_ipp_client_t
*client
) /* I - Client */
2059 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2060 _ipp_job_t key
, /* Job search key */
2061 *job
; /* Matching job, if any */
2066 if ((attr
= ippFindAttribute(client
->request
, "job-uri",
2067 IPP_TAG_URI
)) != NULL
)
2069 const char *uri
= ippGetString(attr
, 0, NULL
);
2071 if (!strncmp(uri
, client
->printer
->uri
, client
->printer
->urilen
) &&
2072 uri
[client
->printer
->urilen
] == '/')
2073 key
.id
= atoi(uri
+ client
->printer
->urilen
+ 1);
2075 else if ((attr
= ippFindAttribute(client
->request
, "job-id",
2076 IPP_TAG_INTEGER
)) != NULL
)
2077 key
.id
= ippGetInteger(attr
, 0);
2079 _cupsRWLockRead(&(client
->printer
->rwlock
));
2080 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2081 _cupsRWUnlock(&(client
->printer
->rwlock
));
2088 * 'html_escape()' - Write a HTML-safe string.
2092 html_escape(_ipp_client_t
*client
, /* I - Client */
2093 const char *s
, /* I - String to write */
2094 size_t slen
) /* I - Number of characters to write */
2096 const char *start
, /* Start of segment */
2097 *end
; /* End of string */
2101 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2103 while (*s
&& s
< end
)
2105 if (*s
== '&' || *s
== '<')
2108 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2111 httpWrite2(client
->http
, "&", 5);
2113 httpWrite2(client
->http
, "<", 4);
2122 httpWrite2(client
->http
, start
, (size_t)(s
- start
));
2127 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2131 html_printf(_ipp_client_t
*client
, /* I - Client */
2132 const char *format
, /* I - Printf-style format string */
2133 ...) /* I - Additional arguments as needed */
2135 va_list ap
; /* Pointer to arguments */
2136 const char *start
; /* Start of string */
2137 char size
, /* Size character (h, l, L) */
2138 type
; /* Format type character */
2139 int width
, /* Width of field */
2140 prec
; /* Number of characters of precision */
2141 char tformat
[100], /* Temporary format string for sprintf() */
2142 *tptr
, /* Pointer into temporary format */
2143 temp
[1024]; /* Buffer for formatted numbers */
2144 char *s
; /* Pointer to string */
2148 * Loop through the format string, formatting as needed...
2151 va_start(ap
, format
);
2159 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2162 *tptr
++ = *format
++;
2166 httpWrite2(client
->http
, "%", 1);
2170 else if (strchr(" -+#\'", *format
))
2171 *tptr
++ = *format
++;
2176 * Get width from argument...
2180 width
= va_arg(ap
, int);
2182 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", width
);
2183 tptr
+= strlen(tptr
);
2189 while (isdigit(*format
& 255))
2191 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2194 width
= width
* 10 + *format
++ - '0';
2200 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2208 * Get precision from argument...
2212 prec
= va_arg(ap
, int);
2214 snprintf(tptr
, sizeof(tformat
) - (size_t)(tptr
- tformat
), "%d", prec
);
2215 tptr
+= strlen(tptr
);
2221 while (isdigit(*format
& 255))
2223 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2226 prec
= prec
* 10 + *format
++ - '0';
2231 if (*format
== 'l' && format
[1] == 'l')
2235 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2243 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2245 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2260 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2269 case 'E' : /* Floating point formats */
2274 if ((size_t)(width
+ 2) > sizeof(temp
))
2277 sprintf(temp
, tformat
, va_arg(ap
, double));
2279 httpWrite2(client
->http
, temp
, strlen(temp
));
2282 case 'B' : /* Integer formats */
2290 if ((size_t)(width
+ 2) > sizeof(temp
))
2293 # ifdef HAVE_LONG_LONG
2295 sprintf(temp
, tformat
, va_arg(ap
, long long));
2297 # endif /* HAVE_LONG_LONG */
2299 sprintf(temp
, tformat
, va_arg(ap
, long));
2301 sprintf(temp
, tformat
, va_arg(ap
, int));
2303 httpWrite2(client
->http
, temp
, strlen(temp
));
2306 case 'p' : /* Pointer value */
2307 if ((size_t)(width
+ 2) > sizeof(temp
))
2310 sprintf(temp
, tformat
, va_arg(ap
, void *));
2312 httpWrite2(client
->http
, temp
, strlen(temp
));
2315 case 'c' : /* Character or character array */
2318 temp
[0] = (char)va_arg(ap
, int);
2320 html_escape(client
, temp
, 1);
2323 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2326 case 's' : /* String */
2327 if ((s
= va_arg(ap
, char *)) == NULL
)
2330 html_escape(client
, s
, strlen(s
));
2339 httpWrite2(client
->http
, start
, (size_t)(format
- start
));
2346 * 'ipp_cancel_job()' - Cancel a job.
2350 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2352 _ipp_job_t
*job
; /* Job information */
2359 if ((job
= find_job(client
)) == NULL
)
2361 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2366 * See if the job is already completed, canceled, or aborted; if so,
2367 * we can't cancel...
2372 case IPP_JSTATE_CANCELED
:
2373 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2374 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2377 case IPP_JSTATE_ABORTED
:
2378 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2379 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2382 case IPP_JSTATE_COMPLETED
:
2383 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2384 "Job #%d is already completed - can\'t cancel.", job
->id
);
2392 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2394 if (job
->state
== IPP_JSTATE_PROCESSING
||
2395 (job
->state
== IPP_JSTATE_HELD
&& job
->fd
>= 0))
2399 job
->state
= IPP_JSTATE_CANCELED
;
2400 job
->completed
= time(NULL
);
2403 _cupsRWUnlock(&(client
->printer
->rwlock
));
2405 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2412 * 'ipp_close_job()' - Close an open job.
2416 ipp_close_job(_ipp_client_t
*client
) /* I - Client */
2418 _ipp_job_t
*job
; /* Job information */
2425 if ((job
= find_job(client
)) == NULL
)
2427 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
2432 * See if the job is already completed, canceled, or aborted; if so,
2433 * we can't cancel...
2438 case IPP_JSTATE_CANCELED
:
2439 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2440 "Job #%d is canceled - can\'t close.", job
->id
);
2443 case IPP_JSTATE_ABORTED
:
2444 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2445 "Job #%d is aborted - can\'t close.", job
->id
);
2448 case IPP_JSTATE_COMPLETED
:
2449 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2450 "Job #%d is completed - can\'t close.", job
->id
);
2453 case IPP_JSTATE_PROCESSING
:
2454 case IPP_JSTATE_STOPPED
:
2455 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
2456 "Job #%d is already closed.", job
->id
);
2460 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2467 * 'ipp_create_job()' - Create a job object.
2471 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2473 _ipp_job_t
*job
; /* New job */
2474 cups_array_t
*ra
; /* Attributes to send in response */
2478 * Validate print job attributes...
2481 if (!valid_job_attributes(client
))
2483 httpFlush(client
->http
);
2488 * Do we have a file to print?
2491 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
2493 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2494 "Unexpected document data following request.");
2502 if ((job
= create_job(client
)) == NULL
)
2504 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2505 "Currently printing another job.");
2510 * Return the job info...
2513 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2515 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2516 cupsArrayAdd(ra
, "job-id");
2517 cupsArrayAdd(ra
, "job-state");
2518 cupsArrayAdd(ra
, "job-state-message");
2519 cupsArrayAdd(ra
, "job-state-reasons");
2520 cupsArrayAdd(ra
, "job-uri");
2522 copy_job_attributes(client
, job
, ra
);
2523 cupsArrayDelete(ra
);
2528 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2532 ipp_get_job_attributes(
2533 _ipp_client_t
*client
) /* I - Client */
2535 _ipp_job_t
*job
; /* Job */
2536 cups_array_t
*ra
; /* requested-attributes */
2539 if ((job
= find_job(client
)) == NULL
)
2541 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job not found.");
2545 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2547 ra
= ippCreateRequestedArray(client
->request
);
2548 copy_job_attributes(client
, job
, ra
);
2549 cupsArrayDelete(ra
);
2554 * 'ipp_get_jobs()' - Get a list of job objects.
2558 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2560 ipp_attribute_t
*attr
; /* Current attribute */
2561 const char *which_jobs
= NULL
;
2562 /* which-jobs values */
2563 int job_comparison
; /* Job comparison */
2564 ipp_jstate_t job_state
; /* job-state value */
2565 int first_job_id
, /* First job ID */
2566 limit
, /* Maximum number of jobs to return */
2567 count
; /* Number of jobs that match */
2568 const char *username
; /* Username */
2569 _ipp_job_t
*job
; /* Current job pointer */
2570 cups_array_t
*ra
; /* Requested attributes array */
2574 * See if the "which-jobs" attribute have been specified...
2577 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2578 IPP_TAG_KEYWORD
)) != NULL
)
2580 which_jobs
= ippGetString(attr
, 0, NULL
);
2581 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->hostname
, which_jobs
);
2584 if (!which_jobs
|| !strcmp(which_jobs
, "not-completed"))
2586 job_comparison
= -1;
2587 job_state
= IPP_JSTATE_STOPPED
;
2589 else if (!strcmp(which_jobs
, "completed"))
2592 job_state
= IPP_JSTATE_CANCELED
;
2594 else if (!strcmp(which_jobs
, "aborted"))
2597 job_state
= IPP_JSTATE_ABORTED
;
2599 else if (!strcmp(which_jobs
, "all"))
2602 job_state
= IPP_JSTATE_PENDING
;
2604 else if (!strcmp(which_jobs
, "canceled"))
2607 job_state
= IPP_JSTATE_CANCELED
;
2609 else if (!strcmp(which_jobs
, "pending"))
2612 job_state
= IPP_JSTATE_PENDING
;
2614 else if (!strcmp(which_jobs
, "pending-held"))
2617 job_state
= IPP_JSTATE_HELD
;
2619 else if (!strcmp(which_jobs
, "processing"))
2622 job_state
= IPP_JSTATE_PROCESSING
;
2624 else if (!strcmp(which_jobs
, "processing-stopped"))
2627 job_state
= IPP_JSTATE_STOPPED
;
2631 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
2632 "The which-jobs value \"%s\" is not supported.", which_jobs
);
2633 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2634 "which-jobs", NULL
, which_jobs
);
2639 * See if they want to limit the number of jobs reported...
2642 if ((attr
= ippFindAttribute(client
->request
, "limit",
2643 IPP_TAG_INTEGER
)) != NULL
)
2645 limit
= ippGetInteger(attr
, 0);
2647 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->hostname
, limit
);
2652 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2653 IPP_TAG_INTEGER
)) != NULL
)
2655 first_job_id
= ippGetInteger(attr
, 0);
2657 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->hostname
,
2664 * See if we only want to see jobs for a specific user...
2669 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2670 IPP_TAG_BOOLEAN
)) != NULL
)
2672 int my_jobs
= ippGetBoolean(attr
, 0);
2674 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->hostname
,
2675 my_jobs
? "true" : "false");
2679 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2680 IPP_TAG_NAME
)) == NULL
)
2682 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
2683 "Need requesting-user-name with my-jobs.");
2687 username
= ippGetString(attr
, 0, NULL
);
2689 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2690 client
->hostname
, username
);
2695 * OK, build a list of jobs for this printer...
2698 ra
= ippCreateRequestedArray(client
->request
);
2700 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2702 _cupsRWLockRead(&(client
->printer
->rwlock
));
2704 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2705 (limit
<= 0 || count
< limit
) && job
;
2706 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2709 * Filter out jobs that don't match...
2712 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2713 (job_comparison
== 0 && job
->state
!= job_state
) ||
2714 (job_comparison
> 0 && job
->state
< job_state
) ||
2715 job
->id
< first_job_id
||
2716 (username
&& job
->username
&&
2717 strcasecmp(username
, job
->username
)))
2721 ippAddSeparator(client
->response
);
2724 copy_job_attributes(client
, job
, ra
);
2727 cupsArrayDelete(ra
);
2729 _cupsRWUnlock(&(client
->printer
->rwlock
));
2734 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2738 ipp_get_printer_attributes(
2739 _ipp_client_t
*client
) /* I - Client */
2741 cups_array_t
*ra
; /* Requested attributes array */
2742 _ipp_printer_t
*printer
; /* Printer */
2746 * Send the attributes...
2749 ra
= ippCreateRequestedArray(client
->request
);
2750 printer
= client
->printer
;
2752 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2754 _cupsRWLockRead(&(printer
->rwlock
));
2756 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2757 IPP_TAG_CUPS_CONST
);
2759 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
2760 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2761 "printer-state", printer
->state
);
2763 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
2765 if (printer
->state_reasons
== _IPP_PSTATE_NONE
)
2766 ippAddString(client
->response
, IPP_TAG_PRINTER
,
2767 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2768 "printer-state-reasons", NULL
, "none");
2771 int num_reasons
= 0;/* Number of reasons */
2772 const char *reasons
[32]; /* Reason strings */
2774 if (printer
->state_reasons
& _IPP_PSTATE_OTHER
)
2775 reasons
[num_reasons
++] = "other";
2776 if (printer
->state_reasons
& _IPP_PSTATE_COVER_OPEN
)
2777 reasons
[num_reasons
++] = "cover-open";
2778 if (printer
->state_reasons
& _IPP_PSTATE_INPUT_TRAY_MISSING
)
2779 reasons
[num_reasons
++] = "input-tray-missing";
2780 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_EMPTY
)
2781 reasons
[num_reasons
++] = "marker-supply-empty-warning";
2782 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_SUPPLY_LOW
)
2783 reasons
[num_reasons
++] = "marker-supply-low-report";
2784 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL
)
2785 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
2786 if (printer
->state_reasons
& _IPP_PSTATE_MARKER_WASTE_FULL
)
2787 reasons
[num_reasons
++] = "marker-waste-full-warning";
2788 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_EMPTY
)
2789 reasons
[num_reasons
++] = "media-empty-warning";
2790 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_JAM
)
2791 reasons
[num_reasons
++] = "media-jam-warning";
2792 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_LOW
)
2793 reasons
[num_reasons
++] = "media-low-report";
2794 if (printer
->state_reasons
& _IPP_PSTATE_MEDIA_NEEDED
)
2795 reasons
[num_reasons
++] = "media-needed-report";
2796 if (printer
->state_reasons
& _IPP_PSTATE_MOVING_TO_PAUSED
)
2797 reasons
[num_reasons
++] = "moving-to-paused";
2798 if (printer
->state_reasons
& _IPP_PSTATE_PAUSED
)
2799 reasons
[num_reasons
++] = "paused";
2800 if (printer
->state_reasons
& _IPP_PSTATE_SPOOL_AREA_FULL
)
2801 reasons
[num_reasons
++] = "spool-area-full";
2802 if (printer
->state_reasons
& _IPP_PSTATE_TONER_EMPTY
)
2803 reasons
[num_reasons
++] = "toner-empty-warning";
2804 if (printer
->state_reasons
& _IPP_PSTATE_TONER_LOW
)
2805 reasons
[num_reasons
++] = "toner-low-report";
2807 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
2808 IPP_CONST_TAG(IPP_TAG_KEYWORD
),
2809 "printer-state-reasons", num_reasons
, NULL
, reasons
);
2813 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
2814 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2815 "printer-up-time", (int)time(NULL
));
2817 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
2818 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2820 printer
->active_job
&&
2821 printer
->active_job
->state
< IPP_JSTATE_CANCELED
);
2823 _cupsRWUnlock(&(printer
->rwlock
));
2825 cupsArrayDelete(ra
);
2830 * 'ipp_identify_printer()' - Beep or display a message.
2834 ipp_identify_printer(
2835 _ipp_client_t
*client
) /* I - Client */
2837 /* TODO: Do something */
2839 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
2844 * 'ipp_print_job()' - Create a job object with an attached document.
2848 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
2850 _ipp_job_t
*job
; /* New job */
2851 char filename
[1024], /* Filename buffer */
2852 buffer
[4096]; /* Copy buffer */
2853 ssize_t bytes
; /* Bytes read */
2854 cups_array_t
*ra
; /* Attributes to send in response */
2858 * Validate print job attributes...
2861 if (!valid_job_attributes(client
))
2863 httpFlush(client
->http
);
2868 * Do we have a file to print?
2871 if (httpGetState(client
->http
) == HTTP_STATE_POST_SEND
)
2873 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "No file in request.");
2881 if ((job
= create_job(client
)) == NULL
)
2883 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
2884 "Currently printing another job.");
2889 * Create a file for the request data...
2892 if (!strcasecmp(job
->format
, "image/jpeg"))
2893 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2894 client
->printer
->directory
, job
->id
);
2895 else if (!strcasecmp(job
->format
, "image/png"))
2896 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2897 client
->printer
->directory
, job
->id
);
2898 else if (!strcasecmp(job
->format
, "image/pwg-raster"))
2899 snprintf(filename
, sizeof(filename
), "%s/%d.ras",
2900 client
->printer
->directory
, job
->id
);
2901 else if (!strcasecmp(job
->format
, "application/pdf"))
2902 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2903 client
->printer
->directory
, job
->id
);
2904 else if (!strcasecmp(job
->format
, "application/postscript"))
2905 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2906 client
->printer
->directory
, job
->id
);
2908 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2909 client
->printer
->directory
, job
->id
);
2912 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
2914 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2916 job
->state
= IPP_JSTATE_ABORTED
;
2918 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2919 "Unable to create print file: %s", strerror(errno
));
2923 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
2925 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
2927 int error
= errno
; /* Write error */
2929 job
->state
= IPP_JSTATE_ABORTED
;
2936 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2937 "Unable to write print file: %s", strerror(error
));
2945 * Got an error while reading the print data, so abort this job.
2948 job
->state
= IPP_JSTATE_ABORTED
;
2955 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2956 "Unable to read print file.");
2962 int error
= errno
; /* Write error */
2964 job
->state
= IPP_JSTATE_ABORTED
;
2969 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
2970 "Unable to write print file: %s", strerror(error
));
2975 job
->filename
= strdup(filename
);
2976 job
->state
= IPP_JSTATE_PENDING
;
2979 * Process the job...
2983 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
2985 job
->state
= IPP_JSTATE_ABORTED
;
2986 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
2995 * Return the job info...
2998 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3000 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3001 cupsArrayAdd(ra
, "job-id");
3002 cupsArrayAdd(ra
, "job-state");
3003 cupsArrayAdd(ra
, "job-state-message");
3004 cupsArrayAdd(ra
, "job-state-reasons");
3005 cupsArrayAdd(ra
, "job-uri");
3007 copy_job_attributes(client
, job
, ra
);
3008 cupsArrayDelete(ra
);
3013 * 'ipp_print_uri()' - Create a job object with a referenced document.
3017 ipp_print_uri(_ipp_client_t
*client
) /* I - Client */
3019 _ipp_job_t
*job
; /* New job */
3020 ipp_attribute_t
*uri
; /* document-uri */
3021 char scheme
[256], /* URI scheme */
3022 userpass
[256], /* Username and password info */
3023 hostname
[256], /* Hostname */
3024 resource
[1024]; /* Resource path */
3025 int port
; /* Port number */
3026 http_uri_status_t uri_status
; /* URI decode status */
3027 http_encryption_t encryption
; /* Encryption to use, if any */
3028 http_t
*http
; /* Connection for http/https URIs */
3029 http_status_t status
; /* Access status for http/https URIs */
3030 int infile
; /* Input file for local file URIs */
3031 char filename
[1024], /* Filename buffer */
3032 buffer
[4096]; /* Copy buffer */
3033 ssize_t bytes
; /* Bytes read */
3034 cups_array_t
*ra
; /* Attributes to send in response */
3035 static const char * const uri_status_strings
[] =
3036 { /* URI decode errors */
3038 "Bad arguments to function.",
3039 "Bad resource in URI.",
3040 "Bad port number in URI.",
3041 "Bad hostname in URI.",
3042 "Bad username in URI.",
3043 "Bad scheme in URI.",
3049 * Validate print job attributes...
3052 if (!valid_job_attributes(client
))
3054 httpFlush(client
->http
);
3059 * Do we have a file to print?
3062 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3064 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3065 "Unexpected document data following request.");
3070 * Do we have a document URI?
3073 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3074 IPP_TAG_URI
)) == NULL
)
3076 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3080 if (ippGetCount(uri
) != 1)
3082 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3083 "Too many document-uri values.");
3087 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3088 scheme
, sizeof(scheme
), userpass
,
3089 sizeof(userpass
), hostname
, sizeof(hostname
),
3090 &port
, resource
, sizeof(resource
));
3091 if (uri_status
< HTTP_URI_STATUS_OK
)
3093 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3094 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3098 if (strcmp(scheme
, "file") &&
3100 strcmp(scheme
, "https") &&
3101 #endif /* HAVE_SSL */
3102 strcmp(scheme
, "http"))
3104 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3105 "URI scheme \"%s\" not supported.", scheme
);
3109 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3111 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3112 "Unable to access URI: %s", strerror(errno
));
3120 if ((job
= create_job(client
)) == NULL
)
3122 respond_ipp(client
, IPP_STATUS_ERROR_BUSY
,
3123 "Currently printing another job.");
3128 * Create a file for the request data...
3131 if (!strcasecmp(job
->format
, "image/jpeg"))
3132 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3133 client
->printer
->directory
, job
->id
);
3134 else if (!strcasecmp(job
->format
, "image/png"))
3135 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3136 client
->printer
->directory
, job
->id
);
3137 else if (!strcasecmp(job
->format
, "application/pdf"))
3138 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3139 client
->printer
->directory
, job
->id
);
3140 else if (!strcasecmp(job
->format
, "application/postscript"))
3141 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3142 client
->printer
->directory
, job
->id
);
3144 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3145 client
->printer
->directory
, job
->id
);
3147 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
3149 job
->state
= IPP_JSTATE_ABORTED
;
3151 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3152 "Unable to create print file: %s", strerror(errno
));
3156 if (!strcmp(scheme
, "file"))
3158 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3160 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3161 "Unable to access URI: %s", strerror(errno
));
3167 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3168 (errno
== EAGAIN
|| errno
== EINTR
))
3170 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3172 int error
= errno
; /* Write error */
3174 job
->state
= IPP_JSTATE_ABORTED
;
3182 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3183 "Unable to write print file: %s", strerror(error
));
3194 if (port
== 443 || !strcmp(scheme
, "https"))
3195 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3197 #endif /* HAVE_SSL */
3198 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3200 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3201 1, 30000, NULL
)) == NULL
)
3203 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3204 "Unable to connect to %s: %s", hostname
,
3205 cupsLastErrorString());
3206 job
->state
= IPP_JSTATE_ABORTED
;
3215 httpClearFields(http
);
3216 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3217 if (httpGet(http
, resource
))
3219 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3220 "Unable to GET URI: %s", strerror(errno
));
3222 job
->state
= IPP_JSTATE_ABORTED
;
3232 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3234 if (status
!= HTTP_STATUS_OK
)
3236 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3237 "Unable to GET URI: %s", httpStatus(status
));
3239 job
->state
= IPP_JSTATE_ABORTED
;
3249 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3251 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3253 int error
= errno
; /* Write error */
3255 job
->state
= IPP_JSTATE_ABORTED
;
3263 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3264 "Unable to write print file: %s", strerror(error
));
3274 int error
= errno
; /* Write error */
3276 job
->state
= IPP_JSTATE_ABORTED
;
3281 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3282 "Unable to write print file: %s", strerror(error
));
3287 job
->filename
= strdup(filename
);
3288 job
->state
= IPP_JSTATE_PENDING
;
3291 * Process the job...
3295 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3297 job
->state
= IPP_JSTATE_ABORTED
;
3298 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3307 * Return the job info...
3310 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3312 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3313 cupsArrayAdd(ra
, "job-id");
3314 cupsArrayAdd(ra
, "job-state");
3315 cupsArrayAdd(ra
, "job-state-reasons");
3316 cupsArrayAdd(ra
, "job-uri");
3318 copy_job_attributes(client
, job
, ra
);
3319 cupsArrayDelete(ra
);
3324 * 'ipp_send_document()' - Add an attached document to a job object created with
3329 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3331 _ipp_job_t
*job
; /* Job information */
3332 char filename
[1024], /* Filename buffer */
3333 buffer
[4096]; /* Copy buffer */
3334 ssize_t bytes
; /* Bytes read */
3335 ipp_attribute_t
*attr
; /* Current attribute */
3336 cups_array_t
*ra
; /* Attributes to send in response */
3343 if ((job
= find_job(client
)) == NULL
)
3345 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3346 httpFlush(client
->http
);
3351 * See if we already have a document for this job or the job has already
3352 * in a non-pending state...
3355 if (job
->state
> IPP_JSTATE_HELD
)
3357 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3358 "Job is not in a pending state.");
3359 httpFlush(client
->http
);
3362 else if (job
->filename
|| job
->fd
>= 0)
3364 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3365 "Multiple document jobs are not supported.");
3366 httpFlush(client
->http
);
3370 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3371 IPP_TAG_ZERO
)) == NULL
)
3373 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3374 "Missing required last-document attribute.");
3375 httpFlush(client
->http
);
3378 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3379 !ippGetBoolean(attr
, 0))
3381 respond_unsupported(client
, attr
);
3382 httpFlush(client
->http
);
3387 * Validate document attributes...
3390 if (!valid_doc_attributes(client
))
3392 httpFlush(client
->http
);
3397 * Get the document format for the job...
3400 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3402 if ((attr
= ippFindAttribute(client
->request
, "document-format", IPP_TAG_MIMETYPE
)) != NULL
)
3403 job
->format
= ippGetString(attr
, 0, NULL
);
3405 job
->format
= "application/octet-stream";
3408 * Create a file for the request data...
3411 if (!strcasecmp(job
->format
, "image/jpeg"))
3412 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3413 client
->printer
->directory
, job
->id
);
3414 else if (!strcasecmp(job
->format
, "image/png"))
3415 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3416 client
->printer
->directory
, job
->id
);
3417 else if (!strcasecmp(job
->format
, "application/pdf"))
3418 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3419 client
->printer
->directory
, job
->id
);
3420 else if (!strcasecmp(job
->format
, "application/postscript"))
3421 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3422 client
->printer
->directory
, job
->id
);
3424 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3425 client
->printer
->directory
, job
->id
);
3428 fprintf(stderr
, "Creating job file \"%s\", format \"%s\".\n", filename
, job
->format
);
3430 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3432 _cupsRWUnlock(&(client
->printer
->rwlock
));
3436 job
->state
= IPP_JSTATE_ABORTED
;
3438 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3439 "Unable to create print file: %s", strerror(errno
));
3443 while ((bytes
= httpRead2(client
->http
, buffer
, sizeof(buffer
))) > 0)
3445 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3447 int error
= errno
; /* Write error */
3449 job
->state
= IPP_JSTATE_ABORTED
;
3456 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3457 "Unable to write print file: %s", strerror(error
));
3465 * Got an error while reading the print data, so abort this job.
3468 job
->state
= IPP_JSTATE_ABORTED
;
3475 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3476 "Unable to read print file.");
3482 int error
= errno
; /* Write error */
3484 job
->state
= IPP_JSTATE_ABORTED
;
3489 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3490 "Unable to write print file: %s", strerror(error
));
3494 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3497 job
->filename
= strdup(filename
);
3498 job
->state
= IPP_JSTATE_PENDING
;
3500 _cupsRWUnlock(&(client
->printer
->rwlock
));
3503 * Process the job...
3507 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3509 job
->state
= IPP_JSTATE_ABORTED
;
3510 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3519 * Return the job info...
3522 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3524 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3525 cupsArrayAdd(ra
, "job-id");
3526 cupsArrayAdd(ra
, "job-state");
3527 cupsArrayAdd(ra
, "job-state-reasons");
3528 cupsArrayAdd(ra
, "job-uri");
3530 copy_job_attributes(client
, job
, ra
);
3531 cupsArrayDelete(ra
);
3536 * 'ipp_send_uri()' - Add a referenced document to a job object created with
3541 ipp_send_uri(_ipp_client_t
*client
) /* I - Client */
3543 _ipp_job_t
*job
; /* Job information */
3544 ipp_attribute_t
*uri
; /* document-uri */
3545 char scheme
[256], /* URI scheme */
3546 userpass
[256], /* Username and password info */
3547 hostname
[256], /* Hostname */
3548 resource
[1024]; /* Resource path */
3549 int port
; /* Port number */
3550 http_uri_status_t uri_status
; /* URI decode status */
3551 http_encryption_t encryption
; /* Encryption to use, if any */
3552 http_t
*http
; /* Connection for http/https URIs */
3553 http_status_t status
; /* Access status for http/https URIs */
3554 int infile
; /* Input file for local file URIs */
3555 char filename
[1024], /* Filename buffer */
3556 buffer
[4096]; /* Copy buffer */
3557 ssize_t bytes
; /* Bytes read */
3558 ipp_attribute_t
*attr
; /* Current attribute */
3559 cups_array_t
*ra
; /* Attributes to send in response */
3560 static const char * const uri_status_strings
[] =
3561 { /* URI decode errors */
3563 "Bad arguments to function.",
3564 "Bad resource in URI.",
3565 "Bad port number in URI.",
3566 "Bad hostname in URI.",
3567 "Bad username in URI.",
3568 "Bad scheme in URI.",
3577 if ((job
= find_job(client
)) == NULL
)
3579 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "Job does not exist.");
3580 httpFlush(client
->http
);
3585 * See if we already have a document for this job or the job has already
3586 * in a non-pending state...
3589 if (job
->state
> IPP_JSTATE_HELD
)
3591 respond_ipp(client
, IPP_STATUS_ERROR_NOT_POSSIBLE
,
3592 "Job is not in a pending state.");
3593 httpFlush(client
->http
);
3596 else if (job
->filename
|| job
->fd
>= 0)
3598 respond_ipp(client
, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED
,
3599 "Multiple document jobs are not supported.");
3600 httpFlush(client
->http
);
3604 if ((attr
= ippFindAttribute(client
->request
, "last-document",
3605 IPP_TAG_ZERO
)) == NULL
)
3607 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3608 "Missing required last-document attribute.");
3609 httpFlush(client
->http
);
3612 else if (ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
|| ippGetCount(attr
) != 1 ||
3613 !ippGetBoolean(attr
, 0))
3615 respond_unsupported(client
, attr
);
3616 httpFlush(client
->http
);
3621 * Validate document attributes...
3624 if (!valid_doc_attributes(client
))
3626 httpFlush(client
->http
);
3631 * Do we have a file to print?
3634 if (httpGetState(client
->http
) == HTTP_STATE_POST_RECV
)
3636 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3637 "Unexpected document data following request.");
3642 * Do we have a document URI?
3645 if ((uri
= ippFindAttribute(client
->request
, "document-uri",
3646 IPP_TAG_URI
)) == NULL
)
3648 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Missing document-uri.");
3652 if (ippGetCount(uri
) != 1)
3654 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
3655 "Too many document-uri values.");
3659 uri_status
= httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
3660 scheme
, sizeof(scheme
), userpass
,
3661 sizeof(userpass
), hostname
, sizeof(hostname
),
3662 &port
, resource
, sizeof(resource
));
3663 if (uri_status
< HTTP_URI_STATUS_OK
)
3665 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad document-uri: %s",
3666 uri_status_strings
[uri_status
- HTTP_URI_STATUS_OVERFLOW
]);
3670 if (strcmp(scheme
, "file") &&
3672 strcmp(scheme
, "https") &&
3673 #endif /* HAVE_SSL */
3674 strcmp(scheme
, "http"))
3676 respond_ipp(client
, IPP_STATUS_ERROR_URI_SCHEME
,
3677 "URI scheme \"%s\" not supported.", scheme
);
3681 if (!strcmp(scheme
, "file") && access(resource
, R_OK
))
3683 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3684 "Unable to access URI: %s", strerror(errno
));
3689 * Get the document format for the job...
3692 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3694 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
3695 IPP_TAG_MIMETYPE
)) != NULL
)
3696 job
->format
= ippGetString(attr
, 0, NULL
);
3698 job
->format
= "application/octet-stream";
3701 * Create a file for the request data...
3704 if (!strcasecmp(job
->format
, "image/jpeg"))
3705 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
3706 client
->printer
->directory
, job
->id
);
3707 else if (!strcasecmp(job
->format
, "image/png"))
3708 snprintf(filename
, sizeof(filename
), "%s/%d.png",
3709 client
->printer
->directory
, job
->id
);
3710 else if (!strcasecmp(job
->format
, "application/pdf"))
3711 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
3712 client
->printer
->directory
, job
->id
);
3713 else if (!strcasecmp(job
->format
, "application/postscript"))
3714 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
3715 client
->printer
->directory
, job
->id
);
3717 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
3718 client
->printer
->directory
, job
->id
);
3720 job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
3722 _cupsRWUnlock(&(client
->printer
->rwlock
));
3726 job
->state
= IPP_JSTATE_ABORTED
;
3728 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3729 "Unable to create print file: %s", strerror(errno
));
3733 if (!strcmp(scheme
, "file"))
3735 if ((infile
= open(resource
, O_RDONLY
)) < 0)
3737 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3738 "Unable to access URI: %s", strerror(errno
));
3744 if ((bytes
= read(infile
, buffer
, sizeof(buffer
))) < 0 &&
3745 (errno
== EAGAIN
|| errno
== EINTR
))
3747 else if (bytes
> 0 && write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3749 int error
= errno
; /* Write error */
3751 job
->state
= IPP_JSTATE_ABORTED
;
3759 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3760 "Unable to write print file: %s", strerror(error
));
3771 if (port
== 443 || !strcmp(scheme
, "https"))
3772 encryption
= HTTP_ENCRYPTION_ALWAYS
;
3774 #endif /* HAVE_SSL */
3775 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
3777 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
,
3778 1, 30000, NULL
)) == NULL
)
3780 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3781 "Unable to connect to %s: %s", hostname
,
3782 cupsLastErrorString());
3783 job
->state
= IPP_JSTATE_ABORTED
;
3792 httpClearFields(http
);
3793 httpSetField(http
, HTTP_FIELD_ACCEPT_LANGUAGE
, "en");
3794 if (httpGet(http
, resource
))
3796 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3797 "Unable to GET URI: %s", strerror(errno
));
3799 job
->state
= IPP_JSTATE_ABORTED
;
3809 while ((status
= httpUpdate(http
)) == HTTP_STATUS_CONTINUE
);
3811 if (status
!= HTTP_STATUS_OK
)
3813 respond_ipp(client
, IPP_STATUS_ERROR_DOCUMENT_ACCESS
,
3814 "Unable to GET URI: %s", httpStatus(status
));
3816 job
->state
= IPP_JSTATE_ABORTED
;
3826 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
3828 if (write(job
->fd
, buffer
, (size_t)bytes
) < bytes
)
3830 int error
= errno
; /* Write error */
3832 job
->state
= IPP_JSTATE_ABORTED
;
3840 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3841 "Unable to write print file: %s", strerror(error
));
3851 int error
= errno
; /* Write error */
3853 job
->state
= IPP_JSTATE_ABORTED
;
3858 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
,
3859 "Unable to write print file: %s", strerror(error
));
3863 _cupsRWLockWrite(&(client
->printer
->rwlock
));
3866 job
->filename
= strdup(filename
);
3867 job
->state
= IPP_JSTATE_PENDING
;
3869 _cupsRWUnlock(&(client
->printer
->rwlock
));
3872 * Process the job...
3876 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
3878 job
->state
= IPP_JSTATE_ABORTED
;
3879 respond_ipp(client
, IPP_STATUS_ERROR_INTERNAL
, "Unable to process job.");
3888 * Return the job info...
3891 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3893 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
3894 cupsArrayAdd(ra
, "job-id");
3895 cupsArrayAdd(ra
, "job-state");
3896 cupsArrayAdd(ra
, "job-state-reasons");
3897 cupsArrayAdd(ra
, "job-uri");
3899 copy_job_attributes(client
, job
, ra
);
3900 cupsArrayDelete(ra
);
3905 * 'ipp_validate_job()' - Validate job creation attributes.
3909 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
3911 if (valid_job_attributes(client
))
3912 respond_ipp(client
, IPP_STATUS_OK
, NULL
);
3917 * 'process_client()' - Process client requests on a thread.
3920 static void * /* O - Exit status */
3921 process_client(_ipp_client_t
*client
) /* I - Client */
3924 * Loop until we are out of requests or timeout (30 seconds)...
3928 int first_time
= 1; /* First time request? */
3929 #endif /* HAVE_SSL */
3931 while (httpWait(client
->http
, 30000))
3937 * See if we need to negotiate a TLS connection...
3940 char buf
[1]; /* First byte from client */
3942 if (recv(httpGetFd(client
->http
), buf
, 1, MSG_PEEK
) == 1 && (!buf
[0] || !strchr("DGHOPT", buf
[0])))
3944 fprintf(stderr
, "%s Negotiating TLS session.\n", client
->hostname
);
3946 if (httpEncryption(client
->http
, HTTP_ENCRYPTION_ALWAYS
))
3948 fprintf(stderr
, "%s Unable to encrypt connection: %s\n", client
->hostname
, cupsLastErrorString());
3955 #endif /* HAVE_SSL */
3957 if (!process_http(client
))
3962 * Close the conection to the client and return...
3965 delete_client(client
);
3972 * 'process_http()' - Process a HTTP request.
3975 int /* O - 1 on success, 0 on failure */
3976 process_http(_ipp_client_t
*client
) /* I - Client connection */
3978 char uri
[1024]; /* URI */
3979 http_state_t http_state
; /* HTTP state */
3980 http_status_t http_status
; /* HTTP status */
3981 ipp_state_t ipp_state
; /* State of IPP transfer */
3982 char scheme
[32], /* Method/scheme */
3983 userpass
[128], /* Username:password */
3984 hostname
[HTTP_MAX_HOST
];
3986 int port
; /* Port number */
3987 const char *encoding
; /* Content-Encoding value */
3988 static const char * const http_states
[] =
3989 { /* Strings for logging HTTP method */
4010 * Clear state variables...
4013 ippDelete(client
->request
);
4014 ippDelete(client
->response
);
4016 client
->request
= NULL
;
4017 client
->response
= NULL
;
4018 client
->operation
= HTTP_STATE_WAITING
;
4021 * Read a request from the connection...
4024 while ((http_state
= httpReadRequest(client
->http
, uri
,
4025 sizeof(uri
))) == HTTP_STATE_WAITING
)
4029 * Parse the request line...
4032 if (http_state
== HTTP_STATE_ERROR
)
4034 if (httpError(client
->http
) == EPIPE
)
4035 fprintf(stderr
, "%s Client closed connection.\n", client
->hostname
);
4037 fprintf(stderr
, "%s Bad request line (%s).\n", client
->hostname
,
4038 strerror(httpError(client
->http
)));
4042 else if (http_state
== HTTP_STATE_UNKNOWN_METHOD
)
4044 fprintf(stderr
, "%s Bad/unknown operation.\n", client
->hostname
);
4045 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4048 else if (http_state
== HTTP_STATE_UNKNOWN_VERSION
)
4050 fprintf(stderr
, "%s Bad HTTP version.\n", client
->hostname
);
4051 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4055 fprintf(stderr
, "%s %s %s\n", client
->hostname
, http_states
[http_state
],
4059 * Separate the URI into its components...
4062 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
4063 userpass
, sizeof(userpass
),
4064 hostname
, sizeof(hostname
), &port
,
4065 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_STATUS_OK
)
4067 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->hostname
, uri
);
4068 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4073 * Process the request...
4076 client
->start
= time(NULL
);
4077 client
->operation
= httpGetState(client
->http
);
4080 * Parse incoming parameters until the status changes...
4083 while ((http_status
= httpUpdate(client
->http
)) == HTTP_STATUS_CONTINUE
);
4085 if (http_status
!= HTTP_STATUS_OK
)
4087 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4091 if (!httpGetField(client
->http
, HTTP_FIELD_HOST
)[0] &&
4092 httpGetVersion(client
->http
) >= HTTP_VERSION_1_1
)
4095 * HTTP/1.1 and higher require the "Host:" field...
4098 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4103 * Handle HTTP Upgrade...
4106 if (!strcasecmp(httpGetField(client
->http
, HTTP_FIELD_CONNECTION
),
4109 if (!respond_http(client
, HTTP_STATUS_NOT_IMPLEMENTED
, NULL
, NULL
, 0))
4114 * Handle HTTP Expect...
4117 if (httpGetExpect(client
->http
) &&
4118 (client
->operation
== HTTP_STATE_POST
||
4119 client
->operation
== HTTP_STATE_PUT
))
4121 if (httpGetExpect(client
->http
) == HTTP_STATUS_CONTINUE
)
4124 * Send 100-continue header...
4127 if (!respond_http(client
, HTTP_STATUS_CONTINUE
, NULL
, NULL
, 0))
4133 * Send 417-expectation-failed header...
4136 if (!respond_http(client
, HTTP_STATUS_EXPECTATION_FAILED
, NULL
, NULL
, 0))
4142 * Handle new transfers...
4145 encoding
= httpGetContentEncoding(client
->http
);
4147 switch (client
->operation
)
4149 case HTTP_STATE_OPTIONS
:
4151 * Do HEAD/OPTIONS command...
4154 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, NULL
, 0));
4156 case HTTP_STATE_HEAD
:
4157 if (!strcmp(client
->uri
, "/icon.png"))
4158 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png", 0));
4159 else if (!strcmp(client
->uri
, "/"))
4160 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "text/html", 0));
4162 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4164 case HTTP_STATE_GET
:
4165 if (!strcmp(client
->uri
, "/icon.png"))
4168 * Send PNG icon file.
4171 int fd
; /* Icon file */
4172 struct stat fileinfo
; /* Icon file information */
4173 char buffer
[4096]; /* Copy buffer */
4174 ssize_t bytes
; /* Bytes */
4176 fprintf(stderr
, "Icon file is \"%s\".\n", client
->printer
->icon
);
4178 if (!stat(client
->printer
->icon
, &fileinfo
) &&
4179 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
4181 if (!respond_http(client
, HTTP_STATUS_OK
, NULL
, "image/png",
4182 (size_t)fileinfo
.st_size
))
4188 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
4189 httpWrite2(client
->http
, buffer
, (size_t)bytes
);
4191 httpFlushWrite(client
->http
);
4196 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4198 else if (!strcmp(client
->uri
, "/"))
4201 * Show web status page...
4204 if (!respond_http(client
, HTTP_STATUS_OK
, encoding
, "text/html", 0))
4208 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
4209 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
4212 "<title>%s</title>\n"
4213 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
4214 "type=\"image/png\">\n"
4218 "<h1><img align=\"right\" src=\"/icon.png\">%s</h1>\n"
4219 "<p>%s, %d job(s).</p>\n"
4222 client
->printer
->name
, client
->printer
->name
,
4223 client
->printer
->state
== IPP_PSTATE_IDLE
? "Idle" :
4224 client
->printer
->state
== IPP_PSTATE_PROCESSING
?
4225 "Printing" : "Stopped",
4226 cupsArrayCount(client
->printer
->jobs
));
4227 httpWrite2(client
->http
, "", 0);
4232 return (respond_http(client
, HTTP_STATUS_NOT_FOUND
, NULL
, NULL
, 0));
4235 case HTTP_STATE_POST
:
4236 if (strcmp(httpGetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
),
4240 * Not an IPP request...
4243 return (respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0));
4247 * Read the IPP request...
4250 client
->request
= ippNew();
4252 while ((ipp_state
= ippRead(client
->http
,
4253 client
->request
)) != IPP_STATE_DATA
)
4255 if (ipp_state
== IPP_STATE_ERROR
)
4257 fprintf(stderr
, "%s IPP read error (%s).\n", client
->hostname
,
4258 cupsLastErrorString());
4259 respond_http(client
, HTTP_STATUS_BAD_REQUEST
, NULL
, NULL
, 0);
4265 * Now that we have the IPP request, process the request...
4268 return (process_ipp(client
));
4271 break; /* Anti-compiler-warning-code */
4279 * 'process_ipp()' - Process an IPP request.
4282 static int /* O - 1 on success, 0 on error */
4283 process_ipp(_ipp_client_t
*client
) /* I - Client */
4285 ipp_tag_t group
; /* Current group tag */
4286 ipp_attribute_t
*attr
; /* Current attribute */
4287 ipp_attribute_t
*charset
; /* Character set attribute */
4288 ipp_attribute_t
*language
; /* Language attribute */
4289 ipp_attribute_t
*uri
; /* Printer URI attribute */
4290 int major
, minor
; /* Version number */
4291 const char *name
; /* Name of attribute */
4294 debug_attributes("Request", client
->request
, 1);
4297 * First build an empty response message for this request...
4300 client
->operation_id
= ippGetOperation(client
->request
);
4301 client
->response
= ippNewResponse(client
->request
);
4304 * Then validate the request header and required attributes...
4307 major
= ippGetVersion(client
->request
, &minor
);
4309 if (major
< 1 || major
> 2)
4312 * Return an error, since we only support IPP 1.x and 2.x.
4315 respond_ipp(client
, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED
,
4316 "Bad request version number %d.%d.", major
, minor
);
4318 else if (ippGetRequestId(client
->request
) <= 0)
4319 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
, "Bad request-id %d.",
4320 ippGetRequestId(client
->request
));
4321 else if (!ippFirstAttribute(client
->request
))
4322 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4323 "No attributes in request.");
4327 * Make sure that the attributes are provided in the correct order and
4328 * don't repeat groups...
4331 for (attr
= ippFirstAttribute(client
->request
),
4332 group
= ippGetGroupTag(attr
);
4334 attr
= ippNextAttribute(client
->request
))
4336 if (ippGetGroupTag(attr
) < group
&& ippGetGroupTag(attr
) != IPP_TAG_ZERO
)
4339 * Out of order; return an error...
4342 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4343 "Attribute groups are out of order (%x < %x).",
4344 ippGetGroupTag(attr
), group
);
4348 group
= ippGetGroupTag(attr
);
4354 * Then make sure that the first three attributes are:
4356 * attributes-charset
4357 * attributes-natural-language
4358 * printer-uri/job-uri
4361 attr
= ippFirstAttribute(client
->request
);
4362 name
= ippGetName(attr
);
4363 if (attr
&& name
&& !strcmp(name
, "attributes-charset") &&
4364 ippGetValueTag(attr
) == IPP_TAG_CHARSET
)
4369 attr
= ippNextAttribute(client
->request
);
4370 name
= ippGetName(attr
);
4372 if (attr
&& name
&& !strcmp(name
, "attributes-natural-language") &&
4373 ippGetValueTag(attr
) == IPP_TAG_LANGUAGE
)
4378 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
4379 IPP_TAG_URI
)) != NULL
)
4381 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
4382 IPP_TAG_URI
)) != NULL
)
4388 strcasecmp(ippGetString(charset
, 0, NULL
), "us-ascii") &&
4389 strcasecmp(ippGetString(charset
, 0, NULL
), "utf-8"))
4392 * Bad character set...
4395 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4396 "Unsupported character set \"%s\".",
4397 ippGetString(charset
, 0, NULL
));
4399 else if (!charset
|| !language
|| !uri
)
4402 * Return an error, since attributes-charset,
4403 * attributes-natural-language, and printer-uri/job-uri are required
4404 * for all operations.
4407 respond_ipp(client
, IPP_STATUS_ERROR_BAD_REQUEST
,
4408 "Missing required attributes.");
4412 char scheme
[32], /* URI scheme */
4413 userpass
[32], /* Username/password in URI */
4414 host
[256], /* Host name in URI */
4415 resource
[256]; /* Resource path in URI */
4416 int port
; /* Port number in URI */
4418 name
= ippGetName(uri
);
4420 if (httpSeparateURI(HTTP_URI_CODING_ALL
, ippGetString(uri
, 0, NULL
),
4421 scheme
, sizeof(scheme
),
4422 userpass
, sizeof(userpass
),
4423 host
, sizeof(host
), &port
,
4424 resource
, sizeof(resource
)) < HTTP_URI_STATUS_OK
)
4425 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4426 "Bad %s value '%s'.", name
, ippGetString(uri
, 0, NULL
));
4427 else if ((!strcmp(name
, "job-uri") &&
4428 strncmp(resource
, "/ipp/print/", 11)) ||
4429 (!strcmp(name
, "printer-uri") &&
4430 strcmp(resource
, "/ipp/print")))
4431 respond_ipp(client
, IPP_STATUS_ERROR_NOT_FOUND
, "%s %s not found.",
4432 name
, ippGetString(uri
, 0, NULL
));
4436 * Try processing the operation...
4439 switch (ippGetOperation(client
->request
))
4441 case IPP_OP_PRINT_JOB
:
4442 ipp_print_job(client
);
4445 case IPP_OP_PRINT_URI
:
4446 ipp_print_uri(client
);
4449 case IPP_OP_VALIDATE_JOB
:
4450 ipp_validate_job(client
);
4453 case IPP_OP_CREATE_JOB
:
4454 ipp_create_job(client
);
4457 case IPP_OP_SEND_DOCUMENT
:
4458 ipp_send_document(client
);
4461 case IPP_OP_SEND_URI
:
4462 ipp_send_uri(client
);
4465 case IPP_OP_CANCEL_JOB
:
4466 ipp_cancel_job(client
);
4469 case IPP_OP_GET_JOB_ATTRIBUTES
:
4470 ipp_get_job_attributes(client
);
4473 case IPP_OP_GET_JOBS
:
4474 ipp_get_jobs(client
);
4477 case IPP_OP_GET_PRINTER_ATTRIBUTES
:
4478 ipp_get_printer_attributes(client
);
4481 case IPP_OP_CLOSE_JOB
:
4482 ipp_close_job(client
);
4485 case IPP_OP_IDENTIFY_PRINTER
:
4486 ipp_identify_printer(client
);
4490 respond_ipp(client
, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED
,
4491 "Operation not supported.");
4500 * Send the HTTP header and return...
4503 if (httpGetState(client
->http
) != HTTP_STATE_POST_SEND
)
4504 httpFlush(client
->http
); /* Flush trailing (junk) data */
4506 return (respond_http(client
, HTTP_STATUS_OK
, NULL
, "application/ipp",
4507 ippLength(client
->response
)));
4512 * 'process_job()' - Process a print job.
4515 static void * /* O - Thread exit status */
4516 process_job(_ipp_job_t
*job
) /* I - Job */
4518 job
->state
= IPP_JSTATE_PROCESSING
;
4519 job
->printer
->state
= IPP_PSTATE_PROCESSING
;
4521 if (job
->printer
->command
)
4524 * Execute a command with the job spool file and wait for it to complete...
4527 int pid
, /* Process ID */
4528 status
; /* Exit status */
4529 time_t start
, /* Start time */
4532 fprintf(stderr
, "Running command \"%s %s\".\n", job
->printer
->command
,
4536 if ((pid
= fork()) == 0)
4539 * Child comes here...
4542 execlp(job
->printer
->command
, job
->printer
->command
, job
->filename
,
4549 * Unable to fork process...
4552 perror("Unable to start job processing command");
4557 * Wait for child to complete...
4561 while (waitpid(pid
, &status
, 0) < 0);
4563 while (wait(&status
) < 0);
4564 #endif /* HAVE_WAITPID */
4568 if (WIFEXITED(status
))
4569 fprintf(stderr
, "Command \"%s\" exited with status %d.\n",
4570 job
->printer
->command
, WEXITSTATUS(status
));
4572 fprintf(stderr
, "Command \"%s\" terminated with signal %d.\n",
4573 job
->printer
->command
, WTERMSIG(status
));
4576 fprintf(stderr
, "Command \"%s\" completed successfully.\n",
4577 job
->printer
->command
);
4581 * Make sure processing takes at least 5 seconds...
4585 if ((end
- start
) < 5)
4591 * Sleep for a random amount of time to simulate job processing.
4594 sleep(5 + (rand() % 11));
4598 job
->state
= IPP_JSTATE_CANCELED
;
4600 job
->state
= IPP_JSTATE_COMPLETED
;
4602 job
->completed
= time(NULL
);
4603 job
->printer
->state
= IPP_PSTATE_IDLE
;
4604 job
->printer
->active_job
= NULL
;
4612 * 'register_printer()' - Register a printer object via Bonjour.
4615 static int /* O - 1 on success, 0 on error */
4617 _ipp_printer_t
*printer
, /* I - Printer */
4618 const char *location
, /* I - Location */
4619 const char *make
, /* I - Manufacturer */
4620 const char *model
, /* I - Model name */
4621 const char *formats
, /* I - Supported formats */
4622 const char *adminurl
, /* I - Web interface URL */
4623 int color
, /* I - 1 = color, 0 = monochrome */
4624 int duplex
, /* I - 1 = duplex, 0 = simplex */
4625 const char *subtype
) /* I - Service subtype */
4627 DNSServiceErrorType error
; /* Error from Bonjour */
4628 char make_model
[256],/* Make and model together */
4629 product
[256], /* Product string */
4630 regtype
[256]; /* Bonjour service type */
4634 * Build the TXT record for IPP...
4637 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
4638 snprintf(product
, sizeof(product
), "(%s)", model
);
4640 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
4641 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 9, "ipp/print");
4642 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
4644 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
4647 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
4649 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
4651 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
4653 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
4654 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
4655 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
4657 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
4661 * Create a shared service reference for Bonjour...
4664 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
4665 != kDNSServiceErr_NoError
)
4667 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
4672 * Register the _printer._tcp (LPD) service type with a port number of 0 to
4673 * defend our service name but not actually support LPD...
4676 printer
->printer_ref
= printer
->common_ref
;
4678 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
4679 kDNSServiceFlagsShareConnection
,
4680 0 /* interfaceIndex */, printer
->dnssd_name
,
4681 "_printer._tcp", NULL
/* domain */,
4682 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
4683 NULL
/* txtRecord */,
4684 (DNSServiceRegisterReply
)dnssd_callback
,
4685 printer
)) != kDNSServiceErr_NoError
)
4687 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
4688 printer
->dnssd_name
, error
);
4693 * Then register the _ipp._tcp (IPP) service type with the real port number to
4694 * advertise our IPP printer...
4697 printer
->ipp_ref
= printer
->common_ref
;
4699 if (subtype
&& *subtype
)
4700 snprintf(regtype
, sizeof(regtype
), "_ipp._tcp,%s", subtype
);
4702 strlcpy(regtype
, "_ipp._tcp", sizeof(regtype
));
4704 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
4705 kDNSServiceFlagsShareConnection
,
4706 0 /* interfaceIndex */, printer
->dnssd_name
,
4707 regtype
, NULL
/* domain */,
4708 NULL
/* host */, htons(printer
->port
),
4709 TXTRecordGetLength(&(printer
->ipp_txt
)),
4710 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4711 (DNSServiceRegisterReply
)dnssd_callback
,
4712 printer
)) != kDNSServiceErr_NoError
)
4714 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4715 printer
->dnssd_name
, regtype
, error
);
4721 * Then register the _ipps._tcp (IPP) service type with the real port number to
4722 * advertise our IPP printer...
4725 printer
->ipps_ref
= printer
->common_ref
;
4727 if (subtype
&& *subtype
)
4728 snprintf(regtype
, sizeof(regtype
), "_ipps._tcp,%s", subtype
);
4730 strlcpy(regtype
, "_ipps._tcp", sizeof(regtype
));
4732 if ((error
= DNSServiceRegister(&(printer
->ipps_ref
),
4733 kDNSServiceFlagsShareConnection
,
4734 0 /* interfaceIndex */, printer
->dnssd_name
,
4735 regtype
, NULL
/* domain */,
4736 NULL
/* host */, htons(printer
->port
),
4737 TXTRecordGetLength(&(printer
->ipp_txt
)),
4738 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
4739 (DNSServiceRegisterReply
)dnssd_callback
,
4740 printer
)) != kDNSServiceErr_NoError
)
4742 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4743 printer
->dnssd_name
, regtype
, error
);
4746 # endif /* HAVE_SSL */
4749 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
4750 * real port number to advertise our IPP printer...
4753 printer
->http_ref
= printer
->common_ref
;
4755 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
4756 kDNSServiceFlagsShareConnection
,
4757 0 /* interfaceIndex */, printer
->dnssd_name
,
4758 "_http._tcp,_printer", NULL
/* domain */,
4759 NULL
/* host */, htons(printer
->port
),
4760 0 /* txtLen */, NULL
, /* txtRecord */
4761 (DNSServiceRegisterReply
)dnssd_callback
,
4762 printer
)) != kDNSServiceErr_NoError
)
4764 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
4765 printer
->dnssd_name
, regtype
, error
);
4771 #endif /* HAVE_DNSSD */
4775 * 'respond_http()' - Send a HTTP response.
4778 int /* O - 1 on success, 0 on failure */
4780 _ipp_client_t
*client
, /* I - Client */
4781 http_status_t code
, /* I - HTTP status of response */
4782 const char *content_encoding
, /* I - Content-Encoding of response */
4783 const char *type
, /* I - MIME media type of response */
4784 size_t length
) /* I - Length of response */
4786 char message
[1024]; /* Text message */
4789 fprintf(stderr
, "%s %s\n", client
->hostname
, httpStatus(code
));
4791 if (code
== HTTP_STATUS_CONTINUE
)
4794 * 100-continue doesn't send any headers...
4797 return (httpWriteResponse(client
->http
, HTTP_STATUS_CONTINUE
) == 0);
4801 * Format an error message...
4804 if (!type
&& !length
&& code
!= HTTP_STATUS_OK
)
4806 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
4808 type
= "text/plain";
4809 length
= strlen(message
);
4815 * Send the HTTP response header...
4818 httpClearFields(client
->http
);
4820 if (code
== HTTP_STATUS_METHOD_NOT_ALLOWED
||
4821 client
->operation
== HTTP_STATE_OPTIONS
)
4822 httpSetField(client
->http
, HTTP_FIELD_ALLOW
, "GET, HEAD, OPTIONS, POST");
4826 if (!strcmp(type
, "text/html"))
4827 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
,
4828 "text/html; charset=utf-8");
4830 httpSetField(client
->http
, HTTP_FIELD_CONTENT_TYPE
, type
);
4832 if (content_encoding
)
4833 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, content_encoding
);
4836 httpSetLength(client
->http
, length
);
4838 if (httpWriteResponse(client
->http
, code
) < 0)
4842 * Send the response data...
4848 * Send a plain text message.
4851 if (httpPrintf(client
->http
, "%s", message
) < 0)
4854 if (httpWrite2(client
->http
, "", 0) < 0)
4857 else if (client
->response
)
4860 * Send an IPP response...
4863 debug_attributes("Response", client
->response
, 2);
4865 ippSetState(client
->response
, IPP_STATE_IDLE
);
4867 if (ippWrite(client
->http
, client
->response
) != IPP_STATE_DATA
)
4876 * 'respond_ipp()' - Send an IPP response.
4880 respond_ipp(_ipp_client_t
*client
, /* I - Client */
4881 ipp_status_t status
, /* I - status-code */
4882 const char *message
, /* I - printf-style status-message */
4883 ...) /* I - Additional args as needed */
4885 const char *formatted
= NULL
; /* Formatted message */
4888 ippSetStatusCode(client
->response
, status
);
4892 va_list ap
; /* Pointer to additional args */
4893 ipp_attribute_t
*attr
; /* New status-message attribute */
4895 va_start(ap
, message
);
4896 if ((attr
= ippFindAttribute(client
->response
, "status-message",
4897 IPP_TAG_TEXT
)) != NULL
)
4898 ippSetStringfv(client
->response
, &attr
, 0, message
, ap
);
4900 attr
= ippAddStringfv(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
4901 "status-message", NULL
, message
, ap
);
4904 formatted
= ippGetString(attr
, 0, NULL
);
4908 fprintf(stderr
, "%s %s %s (%s)\n", client
->hostname
,
4909 ippOpString(client
->operation_id
), ippErrorString(status
),
4912 fprintf(stderr
, "%s %s %s\n", client
->hostname
,
4913 ippOpString(client
->operation_id
), ippErrorString(status
));
4918 * 'respond_unsupported()' - Respond with an unsupported attribute.
4922 respond_unsupported(
4923 _ipp_client_t
*client
, /* I - Client */
4924 ipp_attribute_t
*attr
) /* I - Atribute */
4926 ipp_attribute_t
*temp
; /* Copy of attribute */
4929 respond_ipp(client
, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES
,
4930 "Unsupported %s %s%s value.", ippGetName(attr
),
4931 ippGetCount(attr
) > 1 ? "1setOf " : "",
4932 ippTagString(ippGetValueTag(attr
)));
4934 temp
= ippCopyAttribute(client
->response
, attr
, 0);
4935 ippSetGroupTag(client
->response
, &temp
, IPP_TAG_UNSUPPORTED_GROUP
);
4940 * 'run_printer()' - Run the printer service.
4944 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
4946 int num_fds
; /* Number of file descriptors */
4947 struct pollfd polldata
[3]; /* poll() data */
4948 int timeout
; /* Timeout for poll() */
4949 _ipp_client_t
*client
; /* New client */
4953 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
4956 polldata
[0].fd
= printer
->ipv4
;
4957 polldata
[0].events
= POLLIN
;
4959 polldata
[1].fd
= printer
->ipv6
;
4960 polldata
[1].events
= POLLIN
;
4965 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
4966 polldata
[num_fds
++].events
= POLLIN
;
4967 #endif /* HAVE_DNSSD */
4970 * Loop until we are killed or have a hard error...
4975 if (cupsArrayCount(printer
->jobs
))
4980 if (poll(polldata
, (nfds_t
)num_fds
, timeout
) < 0 && errno
!= EINTR
)
4982 perror("poll() failed");
4986 if (polldata
[0].revents
& POLLIN
)
4988 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
4990 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4992 perror("Unable to create client thread");
4993 delete_client(client
);
4998 if (polldata
[1].revents
& POLLIN
)
5000 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
5002 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
5004 perror("Unable to create client thread");
5005 delete_client(client
);
5011 if (polldata
[2].revents
& POLLIN
)
5012 DNSServiceProcessResult(printer
->common_ref
);
5013 #endif /* HAVE_DNSSD */
5016 * Clean out old jobs...
5019 clean_jobs(printer
);
5025 * 'usage()' - Show program usage.
5029 usage(int status
) /* O - Exit status */
5033 puts(CUPS_SVERSION
" - Copyright 2010-2013 by Apple Inc. All rights "
5038 puts("Usage: ippserver [options] \"name\"");
5041 puts("-2 Supports 2-sided printing (default=1-sided)");
5042 puts("-M manufacturer Manufacturer name (default=Test)");
5043 puts("-P PIN printing mode");
5044 puts("-c command Run command for every print job");
5045 printf("-d spool-directory Spool directory "
5046 "(default=/tmp/ippserver.%d)\n", (int)getpid());
5047 puts("-f type/subtype[,...] List of supported types "
5048 "(default=application/pdf,image/jpeg)");
5049 puts("-h Show program help");
5050 puts("-i iconfile.png PNG icon file (default=printer.png)");
5051 puts("-k Keep job spool files");
5052 puts("-l location Location of printer (default=empty string)");
5053 puts("-m model Model name (default=Printer)");
5054 puts("-n hostname Hostname for printer");
5055 puts("-p port Port number (default=auto)");
5057 puts("-r subtype Bonjour service subtype (default=_print)");
5058 #endif /* HAVE_DNSSD */
5059 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
5060 puts("-v[vvv] Be (very) verbose");
5067 * 'valid_doc_attributes()' - Determine whether the document attributes are
5070 * When one or more document attributes are invalid, this function adds a
5071 * suitable response and attributes to the unsupported group.
5074 static int /* O - 1 if valid, 0 if not */
5075 valid_doc_attributes(
5076 _ipp_client_t
*client
) /* I - Client */
5078 int valid
= 1; /* Valid attributes? */
5079 ipp_op_t op
= ippGetOperation(client
->request
);
5081 const char *op_name
= ippOpString(op
);
5082 /* IPP operation name */
5083 ipp_attribute_t
*attr
, /* Current attribute */
5084 *supported
; /* xxx-supported attribute */
5085 const char *compression
= NULL
,
5086 /* compression value */
5087 *format
= NULL
; /* document-format value */
5091 * Check operation attributes...
5094 if ((attr
= ippFindAttribute(client
->request
, "compression",
5095 IPP_TAG_ZERO
)) != NULL
)
5098 * If compression is specified, only accept a supported value in a Print-Job
5099 * or Send-Document request...
5102 compression
= ippGetString(attr
, 0, NULL
);
5103 supported
= ippFindAttribute(client
->printer
->attrs
,
5104 "compression-supported", IPP_TAG_KEYWORD
);
5106 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5107 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
||
5108 (op
!= IPP_OP_PRINT_JOB
&& op
!= IPP_OP_SEND_DOCUMENT
&&
5109 op
!= IPP_OP_VALIDATE_JOB
) ||
5110 !ippContainsString(supported
, compression
))
5112 respond_unsupported(client
, attr
);
5117 fprintf(stderr
, "%s %s compression=\"%s\"\n",
5118 client
->hostname
, op_name
, compression
);
5120 if (strcmp(compression
, "none"))
5121 httpSetField(client
->http
, HTTP_FIELD_CONTENT_ENCODING
, compression
);
5126 * Is it a format we support?
5129 if ((attr
= ippFindAttribute(client
->request
, "document-format",
5130 IPP_TAG_ZERO
)) != NULL
)
5132 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_MIMETYPE
||
5133 ippGetGroupTag(attr
) != IPP_TAG_OPERATION
)
5135 respond_unsupported(client
, attr
);
5140 format
= ippGetString(attr
, 0, NULL
);
5142 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
5143 client
->hostname
, op_name
, format
);
5148 format
= ippGetString(ippFindAttribute(client
->printer
->attrs
,
5149 "document-format-default",
5150 IPP_TAG_MIMETYPE
), 0, NULL
);
5152 format
= "application/octet-stream"; /* Should never happen */
5154 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5155 "document-format", NULL
, format
);
5158 if (!strcmp(format
, "application/octet-stream") &&
5159 (ippGetOperation(client
->request
) == IPP_OP_PRINT_JOB
||
5160 ippGetOperation(client
->request
) == IPP_OP_SEND_DOCUMENT
))
5163 * Auto-type the file using the first 4 bytes of the file...
5166 unsigned char header
[4]; /* First 4 bytes of file */
5168 memset(header
, 0, sizeof(header
));
5169 httpPeek(client
->http
, (char *)header
, sizeof(header
));
5171 if (!memcmp(header
, "%PDF", 4))
5172 format
= "application/pdf";
5173 else if (!memcmp(header
, "%!", 2))
5174 format
= "application/postscript";
5175 else if (!memcmp(header
, "\377\330\377", 3) &&
5176 header
[3] >= 0xe0 && header
[3] <= 0xef)
5177 format
= "image/jpeg";
5178 else if (!memcmp(header
, "\211PNG", 4))
5179 format
= "image/png";
5182 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
5183 client
->hostname
, op_name
, format
);
5186 attr
= ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
5187 "document-format", NULL
, format
);
5189 ippSetString(client
->request
, &attr
, 0, format
);
5192 if (op
!= IPP_OP_CREATE_JOB
&&
5193 (supported
= ippFindAttribute(client
->printer
->attrs
,
5194 "document-format-supported",
5195 IPP_TAG_MIMETYPE
)) != NULL
&&
5196 !ippContainsString(supported
, format
))
5198 respond_unsupported(client
, attr
);
5207 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
5209 * When one or more job attributes are invalid, this function adds a suitable
5210 * response and attributes to the unsupported group.
5213 static int /* O - 1 if valid, 0 if not */
5214 valid_job_attributes(
5215 _ipp_client_t
*client
) /* I - Client */
5217 int i
, /* Looping var */
5218 valid
= 1; /* Valid attributes? */
5219 ipp_attribute_t
*attr
, /* Current attribute */
5220 *supported
; /* xxx-supported attribute */
5224 * Check operation attributes...
5227 valid
= valid_doc_attributes(client
);
5230 * Check the various job template attributes...
5233 if ((attr
= ippFindAttribute(client
->request
, "copies",
5234 IPP_TAG_ZERO
)) != NULL
)
5236 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5237 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 999)
5239 respond_unsupported(client
, attr
);
5244 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity",
5245 IPP_TAG_ZERO
)) != NULL
)
5247 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_BOOLEAN
)
5249 respond_unsupported(client
, attr
);
5254 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until",
5255 IPP_TAG_ZERO
)) != NULL
)
5257 if (ippGetCount(attr
) != 1 ||
5258 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5259 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5260 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5261 strcmp(ippGetString(attr
, 0, NULL
), "no-hold"))
5263 respond_unsupported(client
, attr
);
5268 if ((attr
= ippFindAttribute(client
->request
, "job-name",
5269 IPP_TAG_ZERO
)) != NULL
)
5271 if (ippGetCount(attr
) != 1 ||
5272 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5273 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
))
5275 respond_unsupported(client
, attr
);
5280 if ((attr
= ippFindAttribute(client
->request
, "job-priority",
5281 IPP_TAG_ZERO
)) != NULL
)
5283 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_INTEGER
||
5284 ippGetInteger(attr
, 0) < 1 || ippGetInteger(attr
, 0) > 100)
5286 respond_unsupported(client
, attr
);
5291 if ((attr
= ippFindAttribute(client
->request
, "job-sheets",
5292 IPP_TAG_ZERO
)) != NULL
)
5294 if (ippGetCount(attr
) != 1 ||
5295 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5296 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5297 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
) ||
5298 strcmp(ippGetString(attr
, 0, NULL
), "none"))
5300 respond_unsupported(client
, attr
);
5305 if ((attr
= ippFindAttribute(client
->request
, "media",
5306 IPP_TAG_ZERO
)) != NULL
)
5308 if (ippGetCount(attr
) != 1 ||
5309 (ippGetValueTag(attr
) != IPP_TAG_NAME
&&
5310 ippGetValueTag(attr
) != IPP_TAG_NAMELANG
&&
5311 ippGetValueTag(attr
) != IPP_TAG_KEYWORD
))
5313 respond_unsupported(client
, attr
);
5319 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
5321 if (!strcmp(ippGetString(attr
, 0, NULL
), media_supported
[i
]))
5324 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
5326 respond_unsupported(client
, attr
);
5332 if ((attr
= ippFindAttribute(client
->request
, "media-col",
5333 IPP_TAG_ZERO
)) != NULL
)
5335 if (ippGetCount(attr
) != 1 ||
5336 ippGetValueTag(attr
) != IPP_TAG_BEGIN_COLLECTION
)
5338 respond_unsupported(client
, attr
);
5341 /* TODO: check for valid media-col */
5344 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling",
5345 IPP_TAG_ZERO
)) != NULL
)
5347 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
||
5348 (strcmp(ippGetString(attr
, 0, NULL
),
5349 "separate-documents-uncollated-copies") &&
5350 strcmp(ippGetString(attr
, 0, NULL
),
5351 "separate-documents-collated-copies")))
5353 respond_unsupported(client
, attr
);
5358 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested",
5359 IPP_TAG_ZERO
)) != NULL
)
5361 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5362 ippGetInteger(attr
, 0) < IPP_ORIENT_PORTRAIT
||
5363 ippGetInteger(attr
, 0) > IPP_ORIENT_REVERSE_PORTRAIT
)
5365 respond_unsupported(client
, attr
);
5370 if ((attr
= ippFindAttribute(client
->request
, "page-ranges",
5371 IPP_TAG_ZERO
)) != NULL
)
5373 if (ippGetValueTag(attr
) != IPP_TAG_RANGE
)
5375 respond_unsupported(client
, attr
);
5380 if ((attr
= ippFindAttribute(client
->request
, "print-quality",
5381 IPP_TAG_ZERO
)) != NULL
)
5383 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_ENUM
||
5384 ippGetInteger(attr
, 0) < IPP_QUALITY_DRAFT
||
5385 ippGetInteger(attr
, 0) > IPP_QUALITY_HIGH
)
5387 respond_unsupported(client
, attr
);
5392 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution",
5393 IPP_TAG_ZERO
)) != NULL
)
5395 ipp_attribute_t
*supported
= ippFindAttribute(client
->printer
->attrs
, "printer-resolution-supported", IPP_TAG_RESOLUTION
);
5397 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_RESOLUTION
||
5400 respond_unsupported(client
, attr
);
5405 int i
, /* Looping var */
5406 count
, /* Number of supported values */
5407 xdpi
, /* Horizontal resolution for job template attribute */
5408 ydpi
, /* Vertical resolution for job template attribute */
5409 sydpi
; /* Vertical resolution for supported value */
5410 ipp_res_t units
, /* Units for job template attribute */
5411 sunits
; /* Units for supported value */
5413 xdpi
= ippGetResolution(attr
, 0, &ydpi
, &units
);
5414 count
= ippGetCount(supported
);
5416 for (i
= 0; i
< count
; i
++)
5418 if (xdpi
== ippGetResolution(supported
, i
, &sydpi
, &sunits
) && ydpi
== sydpi
&& units
== sunits
)
5424 respond_unsupported(client
, attr
);
5430 if ((attr
= ippFindAttribute(client
->request
, "sides",
5431 IPP_TAG_ZERO
)) != NULL
)
5433 const char *sides
= NULL
; /* "sides" value... */
5435 if (ippGetCount(attr
) != 1 || ippGetValueTag(attr
) != IPP_TAG_KEYWORD
)
5437 respond_unsupported(client
, attr
);
5441 sides
= ippGetString(attr
, 0, NULL
);
5443 if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides-supported",
5444 IPP_TAG_KEYWORD
)) != NULL
)
5446 if (!ippContainsString(supported
, sides
))
5448 respond_unsupported(client
, attr
);
5452 else if (strcmp(sides
, "one-sided"))
5454 respond_unsupported(client
, attr
);