4 * Sample IPP/2.0 server for CUPS.
6 * Copyright 2010-2011 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 * main() - Main entry to the sample server.
19 * clean_jobs() - Clean out old (completed) jobs.
20 * compare_jobs() - Compare two jobs.
21 * copy_attribute() - Copy a single attribute.
22 * copy_attributes() - Copy attributes from one request to another.
23 * copy_job_attrs() - Copy job attributes to the response.
24 * create_client() - Accept a new network connection and create a
26 * create_job() - Create a new job object from a Print-Job or
28 * create_listener() - Create a listener socket.
29 * create_media_col() - Create a media-col value.
30 * create_printer() - Create, register, and listen for connections
31 * to a printer object.
32 * create_requested_array() - Create an array for requested-attributes.
33 * debug_attributes() - Print attributes in a request or response.
34 * delete_client() - Close the socket and free all memory used by
36 * delete_job() - Remove from the printer and free all memory
37 * used by a job object.
38 * delete_printer() - Unregister, close listen sockets, and free
39 * all memory used by a printer object.
40 * dnssd_callback() - Handle Bonjour registration events.
41 * find_job() - Find a job specified in a request.
42 * html_escape() - Write a HTML-safe string.
43 * html_printf() - Send formatted text to the client, quoting
45 * ipp_cancel_job() - Cancel a job.
46 * ipp_create_job() - Create a job object.
47 * ipp_get_job_attributes() - Get the attributes for a job object.
48 * ipp_get_jobs() - Get a list of job objects.
49 * ipp_get_printer_attributes() - Get the attributes for a printer object.
50 * ipp_print_job() - Create a job object with an attached
52 * ipp_send_document() - Add an attached document to a job object
53 * created with Create-Job.
54 * ipp_validate_job() - Validate job creation attributes.
55 * process_client() - Process client requests on a thread.
56 * process_http() - Process a HTTP request.
57 * process_ipp() - Process an IPP request.
58 * process_job() - Process a print job.
59 * register_printer() - Register a printer object via Bonjour.
60 * respond_http() - Send a HTTP response.
61 * respond_ipp() - Send an IPP response.
62 * run_printer() - Run the printer service.
63 * usage() - Show program usage.
64 * valid_job_attributes() - Determine whether the job attributes are
69 * Include necessary headers...
72 #include <cups/cups-private.h>
75 #endif /* HAVE_DNSSD */
78 #ifdef HAVE_SYS_MOUNT_H
79 # include <sys/mount.h>
80 #endif /* HAVE_SYS_MOUNT_H */
81 #ifdef HAVE_SYS_STATFS_H
82 # include <sys/statfs.h>
83 #endif /* HAVE_SYS_STATFS_H */
84 #ifdef HAVE_SYS_STATVFS_H
85 # include <sys/statvfs.h>
86 #endif /* HAVE_SYS_STATVFS_H */
89 #endif /* HAVE_SYS_VFS_H */
96 enum _ipp_preasons_e
/* printer-state-reasons bit values */
98 _IPP_PRINTER_NONE
= 0x0000, /* none */
99 _IPP_PRINTER_OTHER
= 0x0001, /* other */
100 _IPP_PRINTER_COVER_OPEN
= 0x0002, /* cover-open */
101 _IPP_PRINTER_INPUT_TRAY_MISSING
= 0x0004,
102 /* input-tray-missing */
103 _IPP_PRINTER_MARKER_SUPPLY_EMPTY
= 0x0008,
104 /* marker-supply-empty */
105 _IPP_PRINTER_MARKER_SUPPLY_LOW
= 0x0010,
106 /* marker-suply-low */
107 _IPP_PRINTER_MARKER_WASTE_ALMOST_FULL
= 0x0020,
108 /* marker-waste-almost-full */
109 _IPP_PRINTER_MARKER_WASTE_FULL
= 0x0040,
110 /* marker-waste-full */
111 _IPP_PRINTER_MEDIA_EMPTY
= 0x0080, /* media-empty */
112 _IPP_PRINTER_MEDIA_JAM
= 0x0100, /* media-jam */
113 _IPP_PRINTER_MEDIA_LOW
= 0x0200, /* media-low */
114 _IPP_PRINTER_MEDIA_NEEDED
= 0x0400, /* media-needed */
115 _IPP_PRINTER_MOVING_TO_PAUSED
= 0x0800,
116 /* moving-to-paused */
117 _IPP_PRINTER_PAUSED
= 0x1000, /* paused */
118 _IPP_PRINTER_SPOOL_AREA_FULL
= 0x2000,/* spool-area-full */
119 _IPP_PRINTER_TONER_EMPTY
= 0x4000, /* toner-empty */
120 _IPP_PRINTER_TONER_LOW
= 0x8000 /* toner-low */
122 typedef unsigned int _ipp_preasons_t
; /* Bitfield for printer-state-reasons */
124 typedef enum _ipp_media_class_e
126 _IPP_GENERAL
, /* General-purpose size */
127 _IPP_PHOTO_ONLY
, /* Photo-only size */
128 _IPP_ENV_ONLY
/* Envelope-only size */
129 } _ipp_media_class_t
;
131 static const char * const media_supported
[] =
132 { /* media-supported values */
133 "iso_a4_210x297mm", /* A4 */
134 "iso_a5_148x210mm", /* A5 */
135 "iso_a6_105x148mm", /* A6 */
136 "iso_dl_110x220mm", /* DL */
137 "na_legal_8.5x14in", /* Legal */
138 "na_letter_8.5x11in", /* Letter */
139 "na_number-10_4.125x9.5in", /* #10 */
140 "na_index-3x5_3x5in", /* 3x5 */
141 "oe_photo-l_3.5x5in", /* L */
142 "na_index-4x6_4x6in", /* 4x6 */
143 "na_5x7_5x7in" /* 5x7 */
145 static const int media_col_sizes
[][3] =
146 { /* media-col-database sizes */
147 { 21000, 29700, _IPP_GENERAL
}, /* A4 */
148 { 14800, 21000, _IPP_PHOTO_ONLY
}, /* A5 */
149 { 10500, 14800, _IPP_PHOTO_ONLY
}, /* A6 */
150 { 11000, 22000, _IPP_ENV_ONLY
}, /* DL */
151 { 21590, 35560, _IPP_GENERAL
}, /* Legal */
152 { 21590, 27940, _IPP_GENERAL
}, /* Letter */
153 { 10477, 24130, _IPP_ENV_ONLY
}, /* #10 */
154 { 7630, 12700, _IPP_PHOTO_ONLY
}, /* 3x5 */
155 { 8890, 12700, _IPP_PHOTO_ONLY
}, /* L */
156 { 10160, 15240, _IPP_PHOTO_ONLY
}, /* 4x6 */
157 { 12700, 17780, _IPP_PHOTO_ONLY
} /* 5x7 */
159 static const char * const media_type_supported
[] =
160 /* media-type-supported values */
167 "photographic-glossy",
168 "photographic-high-gloss",
169 "photographic-matte",
170 "photographic-satin",
171 "photographic-semi-gloss",
173 "stationery-letterhead",
182 typedef struct _ipp_job_s _ipp_job_t
;
184 typedef struct _ipp_printer_s
/**** Printer data ****/
186 int ipv4
, /* IPv4 listener */
187 ipv6
; /* IPv6 listener */
189 DNSServiceRef common_ref
, /* Shared service connection */
190 ipp_ref
, /* Bonjour IPP service */
191 http_ref
, /* Bonjour HTTP service */
192 printer_ref
; /* Bonjour LPD service */
193 TXTRecordRef ipp_txt
; /* Bonjour IPP TXT record */
194 char *dnssd_name
; /* printer-dnssd-name */
195 #endif /* HAVE_DNSSD */
196 char *name
, /* printer-name */
197 *icon
, /* Icon filename */
198 *directory
, /* Spool directory */
199 *hostname
, /* Hostname */
200 *uri
; /* printer-uri-supported */
202 size_t urilen
; /* Length of printer URI */
203 ipp_t
*attrs
; /* Static attributes */
204 ipp_pstate_t state
; /* printer-state value */
205 _ipp_preasons_t state_reasons
; /* printer-state-reasons values */
206 cups_array_t
*jobs
; /* Jobs */
207 _ipp_job_t
*active_job
; /* Current active/pending job */
208 int next_job_id
; /* Next job-id value */
209 _cups_rwlock_t rwlock
; /* Printer lock */
212 struct _ipp_job_s
/**** Job data ****/
215 char *name
, /* job-name */
216 *username
, /* job-originating-user-name */
217 *format
; /* document-format */
218 ipp_jstate_t state
; /* job-state value */
219 time_t processing
, /* time-at-processing value */
220 completed
; /* time-at-completed value */
221 ipp_t
*attrs
; /* Static attributes */
222 int cancel
; /* Non-zero when job canceled */
223 char *filename
; /* Print file name */
224 int fd
; /* Print file descriptor */
225 _ipp_printer_t
*printer
; /* Printer */
228 typedef struct _ipp_client_s
/**** Client data ****/
230 http_t http
; /* HTTP connection */
231 ipp_t
*request
, /* IPP request */
232 *response
; /* IPP response */
233 time_t start
; /* Request start time */
234 http_state_t operation
; /* Request operation */
235 ipp_op_t operation_id
; /* IPP operation-id */
236 char uri
[1024]; /* Request URI */
237 http_addr_t addr
; /* Client address */
238 _ipp_printer_t
*printer
; /* Printer */
239 _ipp_job_t
*job
; /* Current job, if any */
247 static void clean_jobs(_ipp_printer_t
*printer
);
248 static int compare_jobs(_ipp_job_t
*a
, _ipp_job_t
*b
);
249 static ipp_attribute_t
*copy_attribute(ipp_t
*to
, ipp_attribute_t
*attr
,
250 ipp_tag_t group_tag
, int quickcopy
);
251 static void copy_attributes(ipp_t
*to
, ipp_t
*from
, cups_array_t
*ra
,
252 ipp_tag_t group_tag
, int quickcopy
);
253 static void copy_job_attributes(_ipp_client_t
*client
,
254 _ipp_job_t
*job
, cups_array_t
*ra
);
255 static _ipp_client_t
*create_client(_ipp_printer_t
*printer
, int sock
);
256 static _ipp_job_t
*create_job(_ipp_client_t
*client
);
257 static int create_listener(int family
, int *port
);
258 static ipp_t
*create_media_col(const char *media
, const char *type
,
259 int width
, int length
, int margins
);
260 static _ipp_printer_t
*create_printer(const char *servername
,
261 const char *name
, const char *location
,
262 const char *make
, const char *model
,
264 const char *docformats
, int ppm
,
265 int ppm_color
, int duplex
, int port
,
268 #endif /* HAVE_DNSSD */
269 const char *directory
);
270 static cups_array_t
*create_requested_array(_ipp_client_t
*client
);
271 static void debug_attributes(const char *title
, ipp_t
*ipp
);
272 static void delete_client(_ipp_client_t
*client
);
273 static void delete_job(_ipp_job_t
*job
);
274 static void delete_printer(_ipp_printer_t
*printer
);
276 static void dnssd_callback(DNSServiceRef sdRef
,
277 DNSServiceFlags flags
,
278 DNSServiceErrorType errorCode
,
282 _ipp_printer_t
*printer
);
283 #endif /* HAVE_DNSSD */
284 static _ipp_job_t
*find_job(_ipp_client_t
*client
);
285 static void html_escape(_ipp_client_t
*client
, const char *s
,
287 static void html_printf(_ipp_client_t
*client
, const char *format
,
290 __attribute__ ((__format__ (__printf__
, 2, 3)))
291 # endif /* __GNUC__ */
293 static void ipp_cancel_job(_ipp_client_t
*client
);
295 static void ipp_create_job(_ipp_client_t
*client
);
297 static void ipp_get_job_attributes(_ipp_client_t
*client
);
298 static void ipp_get_jobs(_ipp_client_t
*client
);
299 static void ipp_get_printer_attributes(_ipp_client_t
*client
);
300 static void ipp_print_job(_ipp_client_t
*client
);
302 static void ipp_send_document(_ipp_client_t
*client
);
304 static void ipp_validate_job(_ipp_client_t
*client
);
305 static void *process_client(_ipp_client_t
*client
);
306 static int process_http(_ipp_client_t
*client
);
307 static int process_ipp(_ipp_client_t
*client
);
308 static void *process_job(_ipp_job_t
*job
);
310 static int register_printer(_ipp_printer_t
*printer
,
311 const char *location
, const char *make
,
312 const char *model
, const char *formats
,
313 const char *adminurl
, int color
,
314 int duplex
, const char *regtype
);
315 #endif /* HAVE_DNSSD */
316 static int respond_http(_ipp_client_t
*client
, http_status_t code
,
317 const char *type
, size_t length
);
318 static void respond_ipp(_ipp_client_t
*client
, ipp_status_t status
,
319 const char *message
, ...)
321 __attribute__ ((__format__ (__printf__
, 3, 4)))
322 #endif /* __GNUC__ */
324 static void run_printer(_ipp_printer_t
*printer
);
325 static void usage(int status
);
326 static int valid_job_attributes(_ipp_client_t
*client
);
333 static int KeepFiles
= 0,
338 * 'main()' - Main entry to the sample server.
341 int /* O - Exit status */
342 main(int argc
, /* I - Number of command-line args */
343 char *argv
[]) /* I - Command-line arguments */
345 int i
; /* Looping var */
346 const char *opt
, /* Current option character */
347 *servername
= NULL
, /* Server host name */
348 *name
= NULL
, /* Printer name */
349 *location
= "", /* Location of printer */
350 *make
= "Test", /* Manufacturer */
351 *model
= "Printer", /* Model */
352 *icon
= "printer.png", /* Icon file */
353 *formats
= "application/pdf,image/jpeg";
354 /* Supported formats */
356 const char *regtype
= "_ipp._tcp"; /* Bonjour service type */
357 #endif /* HAVE_DNSSD */
358 int port
= 8631, /* Port number (0 = auto) TODO: FIX */
359 duplex
= 0, /* Duplex mode */
360 ppm
= 10, /* Pages per minute for mono */
361 ppm_color
= 0; /* Pages per minute for color */
362 char directory
[1024] = ""; /* Spool directory */
363 _ipp_printer_t
*printer
; /* Printer object */
367 * Parse command-line arguments...
370 for (i
= 1; i
< argc
; i
++)
371 if (argv
[i
][0] == '-')
373 for (opt
= argv
[i
] + 1; *opt
; opt
++)
376 case '2' : /* -2 (enable 2-sided printing) */
380 case 'M' : /* -M manufacturer */
387 case 'd' : /* -d spool-directory */
391 strlcpy(directory
, argv
[i
], sizeof(directory
));
394 case 'f' : /* -f type/subtype[,...] */
401 case 'h' : /* -h (show help) */
405 case 'i' : /* -i icon.png */
412 case 'k' : /* -k (keep files) */
416 case 'l' : /* -l location */
423 case 'm' : /* -m model */
430 case 'n' : /* -n hostname */
434 servername
= argv
[i
];
437 case 'p' : /* -p port */
439 if (i
>= argc
|| !isdigit(argv
[i
][0] & 255))
441 port
= atoi(argv
[i
]);
445 case 'r' : /* -r regtype */
451 #endif /* HAVE_DNSSD */
453 case 's' : /* -s speed[,color-speed] */
457 if (sscanf(argv
[i
], "%d,%d", &ppm
, &ppm_color
) < 1)
461 case 'v' : /* -v (be verbose) */
465 default : /* Unknown */
466 fprintf(stderr
, "Unknown option \"-%c\".\n", *opt
);
477 fprintf(stderr
, "Unexpected command-line argument \"%s\"\n", argv
[i
]);
485 * Apply defaults as needed...
490 snprintf(directory
, sizeof(directory
), "/tmp/ippserver.%d", (int)getpid());
492 if (mkdir(directory
, 0777) && errno
!= EEXIST
)
494 fprintf(stderr
, "Unable to create spool directory \"%s\": %s\n",
495 directory
, strerror(errno
));
500 fprintf(stderr
, "Using spool directory \"%s\".\n", directory
);
504 * Create the printer...
507 if ((printer
= create_printer(servername
, name
, location
, make
, model
, icon
,
508 formats
, ppm
, ppm_color
, duplex
, port
,
511 #endif /* HAVE_DNSSD */
516 * Run the print service...
519 run_printer(printer
);
522 * Destroy the printer and exit...
525 delete_printer(printer
);
532 * 'clean_jobs()' - Clean out old (completed) jobs.
536 clean_jobs(_ipp_printer_t
*printer
) /* I - Printer */
538 _ipp_job_t
*job
; /* Current job */
539 time_t cleantime
; /* Clean time */
542 if (cupsArrayCount(printer
->jobs
) == 0)
545 cleantime
= time(NULL
) - 60;
547 _cupsRWLockWrite(&(printer
->rwlock
));
548 for (job
= (_ipp_job_t
*)cupsArrayFirst(printer
->jobs
);
550 job
= (_ipp_job_t
*)cupsArrayNext(printer
->jobs
))
551 if (job
->completed
&& job
->completed
< cleantime
)
553 cupsArrayRemove(printer
->jobs
, job
);
558 _cupsRWUnlock(&(printer
->rwlock
));
563 * 'compare_jobs()' - Compare two jobs.
566 static int /* O - Result of comparison */
567 compare_jobs(_ipp_job_t
*a
, /* I - First job */
568 _ipp_job_t
*b
) /* I - Second job */
570 return (b
->id
- a
->id
);
575 * 'copy_attribute()' - Copy a single attribute.
578 static ipp_attribute_t
* /* O - New attribute */
580 ipp_t
*to
, /* O - Destination request/response */
581 ipp_attribute_t
*attr
, /* I - Attribute to copy */
582 ipp_tag_t group_tag
, /* I - Group to put the copy in */
583 int quickcopy
) /* I - Do a quick copy? */
585 int i
; /* Looping var */
586 ipp_attribute_t
*toattr
; /* Destination attribute */
589 if (Verbosity
&& attr
->name
)
591 char buffer
[2048]; /* Attribute value */
593 _ippAttrString(attr
, buffer
, sizeof(buffer
));
595 fprintf(stderr
, "Copying %s (%s%s) %s\n", attr
->name
,
596 attr
->num_values
> 1 ? "1setOf " : "",
597 ippTagString(attr
->value_tag
& ~IPP_TAG_COPY
), buffer
);
600 switch (attr
->value_tag
& ~IPP_TAG_COPY
)
603 toattr
= ippAddSeparator(to
);
606 case IPP_TAG_INTEGER
:
608 toattr
= ippAddIntegers(to
, group_tag
, attr
->value_tag
,
609 attr
->name
, attr
->num_values
, NULL
);
611 for (i
= 0; i
< attr
->num_values
; i
++)
612 toattr
->values
[i
].integer
= attr
->values
[i
].integer
;
615 case IPP_TAG_BOOLEAN
:
616 toattr
= ippAddBooleans(to
, group_tag
, attr
->name
,
617 attr
->num_values
, NULL
);
619 for (i
= 0; i
< attr
->num_values
; i
++)
620 toattr
->values
[i
].boolean
= attr
->values
[i
].boolean
;
625 case IPP_TAG_KEYWORD
:
627 case IPP_TAG_URISCHEME
:
628 case IPP_TAG_CHARSET
:
629 case IPP_TAG_LANGUAGE
:
630 case IPP_TAG_MIMETYPE
:
631 toattr
= ippAddStrings(to
, group_tag
,
632 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
633 attr
->name
, attr
->num_values
, NULL
, NULL
);
637 for (i
= 0; i
< attr
->num_values
; i
++)
638 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
642 for (i
= 0; i
< attr
->num_values
; i
++)
643 toattr
->values
[i
].string
.text
=
644 _cupsStrAlloc(attr
->values
[i
].string
.text
);
649 toattr
= ippAddDate(to
, group_tag
, attr
->name
,
650 attr
->values
[0].date
);
653 case IPP_TAG_RESOLUTION
:
654 toattr
= ippAddResolutions(to
, group_tag
, attr
->name
,
655 attr
->num_values
, IPP_RES_PER_INCH
,
658 for (i
= 0; i
< attr
->num_values
; i
++)
660 toattr
->values
[i
].resolution
.xres
= attr
->values
[i
].resolution
.xres
;
661 toattr
->values
[i
].resolution
.yres
= attr
->values
[i
].resolution
.yres
;
662 toattr
->values
[i
].resolution
.units
= attr
->values
[i
].resolution
.units
;
667 toattr
= ippAddRanges(to
, group_tag
, attr
->name
,
668 attr
->num_values
, NULL
, NULL
);
670 for (i
= 0; i
< attr
->num_values
; i
++)
672 toattr
->values
[i
].range
.lower
= attr
->values
[i
].range
.lower
;
673 toattr
->values
[i
].range
.upper
= attr
->values
[i
].range
.upper
;
677 case IPP_TAG_TEXTLANG
:
678 case IPP_TAG_NAMELANG
:
679 toattr
= ippAddStrings(to
, group_tag
,
680 (ipp_tag_t
)(attr
->value_tag
| quickcopy
),
681 attr
->name
, attr
->num_values
, NULL
, NULL
);
685 for (i
= 0; i
< attr
->num_values
; i
++)
687 toattr
->values
[i
].string
.charset
= attr
->values
[i
].string
.charset
;
688 toattr
->values
[i
].string
.text
= attr
->values
[i
].string
.text
;
693 for (i
= 0; i
< attr
->num_values
; i
++)
696 toattr
->values
[i
].string
.charset
=
697 _cupsStrAlloc(attr
->values
[i
].string
.charset
);
699 toattr
->values
[i
].string
.charset
=
700 toattr
->values
[0].string
.charset
;
702 toattr
->values
[i
].string
.text
=
703 _cupsStrAlloc(attr
->values
[i
].string
.text
);
708 case IPP_TAG_BEGIN_COLLECTION
:
709 toattr
= ippAddCollections(to
, group_tag
, attr
->name
,
710 attr
->num_values
, NULL
);
712 for (i
= 0; i
< attr
->num_values
; i
++)
714 toattr
->values
[i
].collection
= attr
->values
[i
].collection
;
715 attr
->values
[i
].collection
->use
++;
719 case IPP_TAG_STRING
:
722 toattr
= ippAddOctetString(to
, group_tag
, attr
->name
, NULL
, 0);
723 toattr
->value_tag
|= quickcopy
;
724 toattr
->values
[0].unknown
.data
= attr
->values
[0].unknown
.data
;
725 toattr
->values
[0].unknown
.length
= attr
->values
[0].unknown
.length
;
728 toattr
= ippAddOctetString(to
, attr
->group_tag
, attr
->name
,
729 attr
->values
[0].unknown
.data
,
730 attr
->values
[0].unknown
.length
);
734 toattr
= ippAddIntegers(to
, group_tag
, attr
->value_tag
,
735 attr
->name
, attr
->num_values
, NULL
);
737 for (i
= 0; i
< attr
->num_values
; i
++)
739 toattr
->values
[i
].unknown
.length
= attr
->values
[i
].unknown
.length
;
741 if (toattr
->values
[i
].unknown
.length
> 0)
743 if ((toattr
->values
[i
].unknown
.data
=
744 malloc(toattr
->values
[i
].unknown
.length
)) == NULL
)
745 toattr
->values
[i
].unknown
.length
= 0;
747 memcpy(toattr
->values
[i
].unknown
.data
,
748 attr
->values
[i
].unknown
.data
,
749 toattr
->values
[i
].unknown
.length
);
752 break; /* anti-compiler-warning-code */
760 * 'copy_attributes()' - Copy attributes from one request to another.
764 copy_attributes(ipp_t
*to
, /* I - Destination request */
765 ipp_t
*from
, /* I - Source request */
766 cups_array_t
*ra
, /* I - Requested attributes */
767 ipp_tag_t group_tag
, /* I - Group to copy */
768 int quickcopy
) /* I - Do a quick copy? */
770 ipp_attribute_t
*fromattr
; /* Source attribute */
776 for (fromattr
= from
->attrs
; fromattr
; fromattr
= fromattr
->next
)
779 * Filter attributes as needed...
782 if ((group_tag
!= IPP_TAG_ZERO
&& fromattr
->group_tag
!= group_tag
&&
783 fromattr
->group_tag
!= IPP_TAG_ZERO
) || !fromattr
->name
)
786 if (!ra
|| cupsArrayFind(ra
, fromattr
->name
))
787 copy_attribute(to
, fromattr
, fromattr
->group_tag
, quickcopy
);
793 * 'copy_job_attrs()' - Copy job attributes to the response.
798 _ipp_client_t
*client
, /* I - Client */
799 _ipp_job_t
*job
, /* I - Job */
800 cups_array_t
*ra
) /* I - requested-attributes */
802 copy_attributes(client
->response
, job
->attrs
, ra
, IPP_TAG_ZERO
, 0);
804 if (!ra
|| cupsArrayFind(ra
, "job-printer-up-time"))
805 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_INTEGER
,
806 "job-printer-up-time", (int)time(NULL
));
808 if (!ra
|| cupsArrayFind(ra
, "job-state"))
809 ippAddInteger(client
->response
, IPP_TAG_JOB
, IPP_TAG_ENUM
,
810 "job-state", job
->state
);
812 if (!ra
|| cupsArrayFind(ra
, "job-state-reasons"))
816 case IPP_JOB_PENDING
:
817 ippAddString(client
->response
, IPP_TAG_JOB
,
818 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
824 ippAddString(client
->response
, IPP_TAG_JOB
,
825 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
826 NULL
, "job-incoming");
827 else if (ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_ZERO
))
828 ippAddString(client
->response
, IPP_TAG_JOB
,
829 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
830 NULL
, "job-hold-until-specified");
833 case IPP_JOB_PROCESSING
:
835 ippAddString(client
->response
, IPP_TAG_JOB
,
836 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
837 NULL
, "processing-to-stop-point");
839 ippAddString(client
->response
, IPP_TAG_JOB
,
840 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
841 NULL
, "job-printing");
844 case IPP_JOB_STOPPED
:
845 ippAddString(client
->response
, IPP_TAG_JOB
,
846 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
847 NULL
, "job-stopped");
850 case IPP_JOB_CANCELED
:
851 ippAddString(client
->response
, IPP_TAG_JOB
,
852 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
853 NULL
, "job-canceled-by-user");
856 case IPP_JOB_ABORTED
:
857 ippAddString(client
->response
, IPP_TAG_JOB
,
858 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
859 NULL
, "aborted-by-system");
862 case IPP_JOB_COMPLETED
:
863 ippAddString(client
->response
, IPP_TAG_JOB
,
864 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "job-state-reasons",
865 NULL
, "job-completed-successfully");
870 if (!ra
|| cupsArrayFind(ra
, "time-at-completed"))
871 ippAddInteger(client
->response
, IPP_TAG_JOB
,
872 job
->completed
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
873 "time-at-completed", job
->completed
);
875 if (!ra
|| cupsArrayFind(ra
, "time-at-processing"))
876 ippAddInteger(client
->response
, IPP_TAG_JOB
,
877 job
->processing
? IPP_TAG_INTEGER
: IPP_TAG_NOVALUE
,
878 "time-at-processing", job
->processing
);
883 * 'create_client()' - Accept a new network connection and create a client
887 static _ipp_client_t
* /* O - Client */
888 create_client(_ipp_printer_t
*printer
, /* I - Printer */
889 int sock
) /* I - Listen socket */
891 _ipp_client_t
*client
; /* Client */
892 int val
; /* Parameter value */
893 socklen_t addrlen
; /* Length of address */
896 if ((client
= calloc(1, sizeof(_ipp_client_t
))) == NULL
)
898 perror("Unable to allocate memory for client");
902 client
->printer
= printer
;
903 client
->http
.activity
= time(NULL
);
904 client
->http
.hostaddr
= &(client
->addr
);
905 client
->http
.blocking
= 1;
908 * Accept the client and get the remote address...
911 addrlen
= sizeof(http_addr_t
);
913 if ((client
->http
.fd
= accept(sock
, (struct sockaddr
*)&(client
->addr
),
916 perror("Unable to accept client connection");
923 httpAddrString(&(client
->addr
), client
->http
.hostname
,
924 sizeof(client
->http
.hostname
));
927 fprintf(stderr
, "Accepted connection from %s (%s)\n", client
->http
.hostname
,
928 client
->http
.hostaddr
->addr
.sa_family
== AF_INET
? "IPv4" : "IPv6");
931 * Using TCP_NODELAY improves responsiveness, especially on systems
932 * with a slow loopback interface. Since we write large buffers
933 * when sending print files and requests, there shouldn't be any
934 * performance penalty for this...
938 setsockopt(client
->http
.fd
, IPPROTO_TCP
, TCP_NODELAY
, (char *)&val
,
946 * 'create_job()' - Create a new job object from a Print-Job or Create-Job
950 static _ipp_job_t
* /* O - Job */
951 create_job(_ipp_client_t
*client
) /* I - Client */
953 _ipp_job_t
*job
; /* Job */
954 ipp_attribute_t
*attr
; /* Job attribute */
955 char uri
[1024]; /* job-uri value */
958 _cupsRWLockWrite(&(client
->printer
->rwlock
));
959 if (client
->printer
->active_job
&&
960 client
->printer
->active_job
->state
< IPP_JOB_CANCELED
)
963 * Only accept a single job at a time...
966 _cupsRWLockWrite(&(client
->printer
->rwlock
));
971 * Allocate and initialize the job object...
974 if ((job
= calloc(1, sizeof(_ipp_job_t
))) == NULL
)
976 perror("Unable to allocate memory for job");
980 job
->printer
= client
->printer
;
981 job
->attrs
= client
->request
;
982 job
->state
= IPP_JOB_HELD
;
984 client
->request
= NULL
;
987 * Set all but the first two attributes to the job attributes group...
990 for (attr
= job
->attrs
->attrs
->next
->next
; attr
; attr
= attr
->next
)
991 attr
->group_tag
= IPP_TAG_JOB
;
994 * Get the requesting-user-name, document format, and priority...
997 if ((attr
= ippFindAttribute(job
->attrs
, "requesting-user-name",
998 IPP_TAG_NAME
)) != NULL
)
1000 _cupsStrFree(attr
->name
);
1001 attr
->name
= _cupsStrAlloc("job-originating-user-name");
1004 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_NAME
| IPP_TAG_COPY
,
1005 "job-originating-user-name", NULL
, "anonymous");
1008 job
->username
= attr
->values
[0].string
.text
;
1010 job
->username
= "anonymous";
1012 if ((attr
= ippFindAttribute(job
->attrs
, "document-format",
1013 IPP_TAG_MIMETYPE
)) != NULL
)
1014 job
->format
= attr
->values
[0].string
.text
;
1016 job
->format
= "application/octet-stream";
1019 * Add job description attributes and add to the jobs array...
1022 job
->id
= client
->printer
->next_job_id
++;
1024 snprintf(uri
, sizeof(uri
), "%s/%d", client
->printer
->uri
, job
->id
);
1026 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-id", job
->id
);
1027 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1028 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
, "job-printer-uri", NULL
,
1029 client
->printer
->uri
);
1030 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "time-at-creation",
1033 cupsArrayAdd(client
->printer
->jobs
, job
);
1034 client
->printer
->active_job
= job
;
1036 _cupsRWUnlock(&(client
->printer
->rwlock
));
1043 * 'create_listener()' - Create a listener socket.
1046 static int /* O - Listener socket or -1 on error */
1047 create_listener(int family
, /* I - Address family */
1048 int *port
) /* IO - Port number */
1050 int sock
, /* Listener socket */
1051 val
; /* Socket value */
1052 http_addr_t address
; /* Listen address */
1053 socklen_t addrlen
; /* Length of listen address */
1056 if ((sock
= socket(family
, SOCK_STREAM
, 0)) < 0)
1060 setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, &val
, sizeof(val
));
1063 if (family
== AF_INET6
)
1064 setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, &val
, sizeof(val
));
1065 #endif /* IPV6_V6ONLY */
1070 * Get the auto-assigned port number for the IPv4 socket...
1073 /* TODO: This code does not appear to work - port is always 0... */
1074 addrlen
= sizeof(address
);
1075 if (getsockname(sock
, (struct sockaddr
*)&address
, &addrlen
))
1077 perror("getsockname() failed");
1081 *port
= _httpAddrPort(&address
);
1083 fprintf(stderr
, "Listening on port %d.\n", *port
);
1086 memset(&address
, 0, sizeof(address
));
1087 if (family
== AF_INET
)
1089 address
.ipv4
.sin_family
= family
;
1090 address
.ipv4
.sin_port
= htons(*port
);
1094 address
.ipv6
.sin6_family
= family
;
1095 address
.ipv6
.sin6_port
= htons(*port
);
1098 if (bind(sock
, (struct sockaddr
*)&address
, httpAddrLength(&address
)))
1104 if (listen(sock
, 5))
1115 * 'create_media_col()' - Create a media-col value.
1118 static ipp_t
* /* O - media-col collection */
1119 create_media_col(const char *media
, /* I - Media name */
1120 const char *type
, /* I - Nedua type */
1121 int width
, /* I - x-dimension in 2540ths */
1122 int length
, /* I - y-dimension in 2540ths */
1123 int margins
) /* I - Value for margins */
1125 ipp_t
*media_col
= ippNew(), /* media-col value */
1126 *media_size
= ippNew(); /* media-size value */
1127 char media_key
[256]; /* media-key value */
1130 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "x-dimension",
1132 ippAddInteger(media_size
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
, "y-dimension",
1135 snprintf(media_key
, sizeof(media_key
), "%s_%s%s", media
, type
,
1136 margins
== 0 ? "_borderless" : "");
1138 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-key", NULL
,
1140 ippAddCollection(media_col
, IPP_TAG_PRINTER
, "media-size", media_size
);
1141 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1142 "media-bottom-margin", margins
);
1143 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1144 "media-left-margin", margins
);
1145 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1146 "media-right-margin", margins
);
1147 ippAddInteger(media_col
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1148 "media-top-margin", margins
);
1149 ippAddString(media_col
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
, "media-type",
1152 ippDelete(media_size
);
1159 * 'create_printer()' - Create, register, and listen for connections to a
1163 static _ipp_printer_t
* /* O - Printer */
1164 create_printer(const char *servername
, /* I - Server hostname (NULL for default) */
1165 const char *name
, /* I - printer-name */
1166 const char *location
, /* I - printer-location */
1167 const char *make
, /* I - printer-make-and-model */
1168 const char *model
, /* I - printer-make-and-model */
1169 const char *icon
, /* I - printer-icons */
1170 const char *docformats
, /* I - document-format-supported */
1171 int ppm
, /* I - Pages per minute in grayscale */
1172 int ppm_color
, /* I - Pages per minute in color (0 for gray) */
1173 int duplex
, /* I - 1 = duplex, 0 = simplex */
1174 int port
, /* I - Port for listeners or 0 for auto */
1176 const char *regtype
, /* I - Bonjour service type */
1177 #endif /* HAVE_DNSSD */
1178 const char *directory
) /* I - Spool directory */
1180 int i
, j
; /* Looping vars */
1181 _ipp_printer_t
*printer
; /* Printer */
1182 char hostname
[256], /* Hostname */
1183 uri
[1024], /* Printer URI */
1184 icons
[1024], /* printer-icons URI */
1185 adminurl
[1024], /* printer-more-info URI */
1186 device_id
[1024],/* printer-device-id */
1187 make_model
[128];/* printer-make-and-model */
1188 int num_formats
; /* Number of document-format-supported values */
1189 char *defformat
, /* document-format-default value */
1190 *formats
[100], /* document-format-supported values */
1191 *ptr
; /* Pointer into string */
1192 const char *prefix
; /* Prefix string */
1193 int num_database
; /* Number of database values */
1194 ipp_attribute_t
*media_col_database
;
1195 /* media-col-database value */
1196 ipp_t
*media_col_default
;
1197 /* media-col-default value */
1198 ipp_value_t
*media_col_value
;
1199 /* Current media-col-database value */
1200 int k_supported
; /* Maximum file size supported */
1202 struct statvfs spoolinfo
; /* FS info for spool directory */
1203 double spoolsize
; /* FS size */
1204 #elif defined(HAVE_STATFS)
1205 struct statfs spoolinfo
; /* FS info for spool directory */
1206 double spoolsize
; /* FS size */
1207 #endif /* HAVE_STATVFS */
1208 static const int orients
[4] = /* orientation-requested-supported values */
1212 IPP_REVERSE_LANDSCAPE
,
1213 IPP_REVERSE_PORTRAIT
1215 static const char * const versions
[] =/* ipp-versions-supported values */
1221 static const int ops
[] = /* operations-supported values */
1228 IPP_GET_JOB_ATTRIBUTES
,
1230 IPP_GET_PRINTER_ATTRIBUTES
1232 static const char * const charsets
[] =/* charset-supported values */
1237 static const char * const job_creation
[] =
1238 { /* job-creation-attributes-supported values */
1240 "ipp-attribute-fidelity",
1245 "multiple-document-handling",
1246 "orientation-requested",
1250 static const char * const media_col_supported
[] =
1251 { /* media-col-supported values */
1252 "media-bottom-margin",
1253 "media-left-margin",
1254 "media-right-margin",
1259 static const int media_xxx_margin_supported
[] =
1260 { /* media-xxx-margin-supported values */
1264 static const char * const multiple_document_handling
[] =
1265 { /* multiple-document-handling-supported values */
1266 "separate-documents-uncollated-copies",
1267 "separate-documents-collated-copies"
1269 static const int print_quality_supported
[] =
1270 { /* print-quality-supported values */
1275 static const char * const sides_supported
[] =
1276 { /* sides-supported values */
1278 "two-sided-long-edge",
1279 "two-sided-short-edge"
1281 static const char * const which_jobs
[] =
1282 { /* which-jobs-supported values */
1291 "processing-stopped"
1296 * Allocate memory for the printer...
1299 if ((printer
= calloc(1, sizeof(_ipp_printer_t
))) == NULL
)
1301 perror("Unable to allocate memory for printer");
1307 printer
->name
= _cupsStrAlloc(name
);
1308 #ifdef HAVE_DNSSD */
1309 printer
->dnssd_name
= _cupsStrRetain(printer
->name
);
1310 #endif /* HAVE_DNSSD */
1311 printer
->directory
= _cupsStrAlloc(directory
);
1312 printer
->hostname
= _cupsStrAlloc(servername
? servername
:
1313 httpGetHostname(NULL
, hostname
,
1315 printer
->port
= port
;
1316 printer
->state
= IPP_PRINTER_IDLE
;
1317 printer
->state_reasons
= _IPP_PRINTER_NONE
;
1318 printer
->jobs
= cupsArrayNew((cups_array_func_t
)compare_jobs
, NULL
);
1319 printer
->next_job_id
= 1;
1321 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1322 printer
->hostname
, printer
->port
, "/ipp");
1323 printer
->uri
= _cupsStrAlloc(uri
);
1324 printer
->urilen
= strlen(uri
);
1326 _cupsRWInit(&(printer
->rwlock
));
1329 * Create the listener sockets...
1332 if ((printer
->ipv4
= create_listener(AF_INET
, &(printer
->port
))) < 0)
1334 perror("Unable to create IPv4 listener");
1338 if ((printer
->ipv6
= create_listener(AF_INET6
, &(printer
->port
))) < 0)
1340 perror("Unable to create IPv6 listener");
1345 * Prepare values for the printer attributes...
1348 httpAssembleURI(HTTP_URI_CODING_ALL
, icons
, sizeof(icons
), "http", NULL
,
1349 printer
->hostname
, printer
->port
, "/icon.png");
1350 httpAssembleURI(HTTP_URI_CODING_ALL
, adminurl
, sizeof(adminurl
), "http", NULL
,
1351 printer
->hostname
, printer
->port
, "/");
1355 fprintf(stderr
, "printer-more-info=\"%s\"\n", adminurl
);
1356 fprintf(stderr
, "printer-uri=\"%s\"\n", uri
);
1359 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
1362 formats
[0] = strdup(docformats
);
1363 defformat
= formats
[0];
1364 for (ptr
= strchr(formats
[0], ','); ptr
; ptr
= strchr(ptr
, ','))
1367 formats
[num_formats
++] = ptr
;
1369 if (!strcasecmp(ptr
, "application/octet-stream"))
1373 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;", make
, model
);
1374 ptr
= device_id
+ strlen(device_id
);
1376 for (i
= 0; i
< num_formats
; i
++)
1378 if (!strcasecmp(formats
[i
], "application/pdf"))
1379 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPDF", prefix
);
1380 else if (!strcasecmp(formats
[i
], "application/postscript"))
1381 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPS", prefix
);
1382 else if (!strcasecmp(formats
[i
], "application/vnd.hp-PCL"))
1383 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPCL", prefix
);
1384 else if (!strcasecmp(formats
[i
], "image/jpeg"))
1385 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sJPEG", prefix
);
1386 else if (!strcasecmp(formats
[i
], "image/png"))
1387 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%sPNG", prefix
);
1388 else if (strcasecmp(formats
[i
], "application/octet-stream"))
1389 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%s%s", prefix
,
1395 strlcat(device_id
, ";", sizeof(device_id
));
1398 * Get the maximum spool size based on the size of the filesystem used for
1399 * the spool directory. If the host OS doesn't support the statfs call
1400 * or the filesystem is larger than 2TiB, always report INT_MAX.
1404 if (statvfs(printer
->directory
, &spoolinfo
))
1405 k_supported
= INT_MAX
;
1406 else if ((spoolsize
= (double)spoolinfo
.f_frsize
*
1407 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1408 k_supported
= INT_MAX
;
1410 k_supported
= (int)spoolsize
;
1412 #elif defined(HAVE_STATFS)
1413 if (statfs(printer
->directory
, &spoolinfo
))
1414 k_supported
= INT_MAX
;
1415 else if ((spoolsize
= (double)spoolinfo
.f_bsize
*
1416 spoolinfo
.f_blocks
/ 1024) > INT_MAX
)
1417 k_supported
= INT_MAX
;
1419 k_supported
= (int)spoolsize
;
1422 k_supported
= INT_MAX
;
1423 #endif /* HAVE_STATVFS */
1426 * Create the printer attributes. This list of attributes is sorted to improve
1427 * performance when the client provides a requested-attributes attribute...
1430 printer
->attrs
= ippNew();
1432 /* charset-configured */
1433 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_CHARSET
| IPP_TAG_COPY
,
1434 "charset-configured", NULL
, "utf-8");
1436 /* charset-supported */
1437 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_CHARSET
| IPP_TAG_COPY
,
1438 "charset-supported", sizeof(charsets
) / sizeof(charsets
[0]),
1441 /* color-supported */
1442 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "color-supported",
1445 /* compression-supported */
1446 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1447 "compression-supported", NULL
, "none");
1449 /* copies-default */
1450 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1451 "copies-default", 1);
1453 /* copies-supported */
1454 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "copies-supported", 1, 999);
1456 /* document-format-default */
1457 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1458 "document-format-default", NULL
, defformat
);
1460 /* document-format-supported */
1461 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_MIMETYPE
,
1462 "document-format-supported", num_formats
, NULL
,
1463 (const char * const *)formats
);
1465 /* finishings-default */
1466 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1467 "finishings-default", IPP_FINISHINGS_NONE
);
1469 /* finishings-supported */
1470 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1471 "finishings-supported", IPP_FINISHINGS_NONE
);
1473 /* generated-natural-language-supported */
1474 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_LANGUAGE
| IPP_TAG_COPY
,
1475 "generated-natural-language-supported", NULL
, "en");
1477 /* ipp-versions-supported */
1478 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1479 "ipp-versions-supported",
1480 sizeof(versions
) / sizeof(versions
[0]), NULL
, versions
);
1482 /* job-creation-attributes-supported */
1483 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1484 "job-creation-attributes-supported",
1485 sizeof(job_creation
) / sizeof(job_creation
[0]),
1486 NULL
, job_creation
);
1488 /* job-k-octets-supported */
1489 ippAddRange(printer
->attrs
, IPP_TAG_PRINTER
, "job-k-octets-supported", 0,
1492 /* job-priority-default */
1493 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1494 "job-priority-default", 50);
1496 /* job-priority-supported */
1497 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1498 "job-priority-supported", 100);
1500 /* job-sheets-default */
1501 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
| IPP_TAG_COPY
,
1502 "job-sheets-default", NULL
, "none");
1504 /* job-sheets-supported */
1505 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
| IPP_TAG_COPY
,
1506 "job-sheets-supported", NULL
, "none");
1508 /* media-bottom-margin-supported */
1509 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1510 "media-bottom-margin-supported",
1511 (int)(sizeof(media_xxx_margin_supported
) /
1512 sizeof(media_xxx_margin_supported
[0])),
1513 media_xxx_margin_supported
);
1515 /* media-col-database */
1516 for (num_database
= 0, i
= 0;
1517 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1520 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
)
1521 num_database
+= 2; /* auto + envelope */
1522 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
)
1523 num_database
+= 12; /* auto + photographic-* + borderless */
1525 num_database
+= (int)(sizeof(media_type_supported
) /
1526 sizeof(media_type_supported
[0])) + 6;
1527 /* All types + borderless */
1530 media_col_database
= ippAddCollections(printer
->attrs
, IPP_TAG_PRINTER
,
1531 "media-col-database", num_database
,
1533 for (media_col_value
= media_col_database
->values
, i
= 0;
1534 i
< (int)(sizeof(media_col_sizes
) / sizeof(media_col_sizes
[0]));
1538 j
< (int)(sizeof(media_type_supported
) /
1539 sizeof(media_type_supported
[0]));
1542 if (media_col_sizes
[i
][2] == _IPP_ENV_ONLY
&&
1543 strcmp(media_type_supported
[j
], "auto") &&
1544 strcmp(media_type_supported
[j
], "envelope"))
1546 else if (media_col_sizes
[i
][2] == _IPP_PHOTO_ONLY
&&
1547 strcmp(media_type_supported
[j
], "auto") &&
1548 strncmp(media_type_supported
[j
], "photographic-", 13))
1551 media_col_value
->collection
=
1552 create_media_col(media_supported
[i
], media_type_supported
[j
],
1553 media_col_sizes
[i
][0], media_col_sizes
[i
][1],
1554 media_xxx_margin_supported
[1]);
1557 if (media_col_sizes
[i
][2] != _IPP_ENV_ONLY
&&
1558 (!strcmp(media_type_supported
[j
], "auto") ||
1559 !strncmp(media_type_supported
[j
], "photographic-", 13)))
1562 * Add borderless version for this combination...
1565 media_col_value
->collection
=
1566 create_media_col(media_supported
[i
], media_type_supported
[j
],
1567 media_col_sizes
[i
][0], media_col_sizes
[i
][1],
1568 media_xxx_margin_supported
[0]);
1574 /* media-col-default */
1575 media_col_default
= create_media_col(media_supported
[0],
1576 media_type_supported
[0],
1577 media_col_sizes
[0][0],
1578 media_col_sizes
[0][1],
1579 media_xxx_margin_supported
[1]);
1581 ippAddCollection(printer
->attrs
, IPP_TAG_PRINTER
, "media-col-default",
1583 ippDelete(media_col_default
);
1585 /* media-col-supported */
1586 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1587 "media-col-supported",
1588 (int)(sizeof(media_col_supported
) /
1589 sizeof(media_col_supported
[0])), NULL
,
1590 media_col_supported
);
1593 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1594 "media-default", NULL
, media_supported
[0]);
1596 /* media-left-margin-supported */
1597 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1598 "media-left-margin-supported",
1599 (int)(sizeof(media_xxx_margin_supported
) /
1600 sizeof(media_xxx_margin_supported
[0])),
1601 media_xxx_margin_supported
);
1603 /* media-right-margin-supported */
1604 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1605 "media-right-margin-supported",
1606 (int)(sizeof(media_xxx_margin_supported
) /
1607 sizeof(media_xxx_margin_supported
[0])),
1608 media_xxx_margin_supported
);
1610 /* media-supported */
1611 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1613 (int)(sizeof(media_supported
) / sizeof(media_supported
[0])),
1614 NULL
, media_supported
);
1616 /* media-top-margin-supported */
1617 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1618 "media-top-margin-supported",
1619 (int)(sizeof(media_xxx_margin_supported
) /
1620 sizeof(media_xxx_margin_supported
[0])),
1621 media_xxx_margin_supported
);
1623 /* multiple-document-handling-supported */
1624 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1625 "multiple-document-handling-supported",
1626 sizeof(multiple_document_handling
) /
1627 sizeof(multiple_document_handling
[0]), NULL
,
1628 multiple_document_handling
);
1630 /* multiple-document-jobs-supported */
1631 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
,
1632 "multiple-document-jobs-supported", 0);
1634 /* natural-language-configured */
1635 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_LANGUAGE
| IPP_TAG_COPY
,
1636 "natural-language-configured", NULL
, "en");
1638 /* number-up-default */
1639 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1640 "number-up-default", 1);
1642 /* number-up-supported */
1643 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1644 "number-up-supported", 1);
1646 /* operations-supported */
1647 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1648 "operations-supported", sizeof(ops
) / sizeof(ops
[0]), ops
);
1650 /* orientation-requested-default */
1651 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NOVALUE
,
1652 "orientation-requested-default", 0);
1654 /* orientation-requested-supported */
1655 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1656 "orientation-requested-supported", 4, orients
);
1658 /* output-bin-default */
1659 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1660 "output-bin-default", NULL
, "face-down");
1662 /* output-bin-supported */
1663 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1664 "output-bin-supported", NULL
, "face-down");
1666 /* pages-per-minute */
1667 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1668 "pages-per-minute", ppm
);
1670 /* pages-per-minute-color */
1672 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
1673 "pages-per-minute-color", ppm_color
);
1675 /* pdl-override-supported */
1676 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1677 "pdl-override-supported", NULL
, "attempted");
1679 /* print-quality-default */
1680 ippAddInteger(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1681 "print-quality-default", IPP_QUALITY_NORMAL
);
1683 /* print-quality-supported */
1684 ippAddIntegers(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
1685 "print-quality-supported",
1686 (int)(sizeof(print_quality_supported
) /
1687 sizeof(print_quality_supported
[0])),
1688 print_quality_supported
);
1690 /* printer-device-id */
1691 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1692 "printer-device-id", NULL
, device_id
);
1695 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1696 "printer-icons", NULL
, icons
);
1698 /* printer-is-accepting-jobs */
1699 ippAddBoolean(printer
->attrs
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs",
1703 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1706 /* printer-location */
1707 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1708 "printer-location", NULL
, location
);
1710 /* printer-make-and-model */
1711 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
,
1712 "printer-make-and-model", NULL
, make_model
);
1714 /* printer-more-info */
1715 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1716 "printer-more-info", NULL
, adminurl
);
1719 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "printer-name",
1722 /* printer-resolution-default */
1723 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1724 "printer-resolution-default", IPP_RES_PER_INCH
, 600, 600);
1726 /* printer-resolution-supported */
1727 ippAddResolution(printer
->attrs
, IPP_TAG_PRINTER
,
1728 "printer-resolution-supported", IPP_RES_PER_INCH
, 600, 600);
1730 /* printer-uri-supported */
1731 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_URI
,
1732 "printer-uri-supported", NULL
, uri
);
1735 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1736 "sides-default", NULL
, "one-sided");
1738 /* sides-supported */
1739 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1740 "sides-supported", duplex
? 3 : 1, NULL
, sides_supported
);
1742 /* uri-authentication-supported */
1743 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1744 "uri-authentication-supported", NULL
, "none");
1746 /* uri-security-supported */
1747 ippAddString(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1748 "uri-security-supported", NULL
, "none");
1750 /* which-jobs-supported */
1751 ippAddStrings(printer
->attrs
, IPP_TAG_PRINTER
, IPP_TAG_KEYWORD
| IPP_TAG_COPY
,
1752 "which-jobs-supported",
1753 sizeof(which_jobs
) / sizeof(which_jobs
[0]), NULL
, which_jobs
);
1757 debug_attributes("Printer", printer
->attrs
);
1761 * Register the printer with Bonjour...
1764 if (!register_printer(printer
, location
, make
, model
, docformats
, adminurl
,
1765 ppm_color
> 0, duplex
, regtype
))
1767 #endif /* HAVE_DNSSD */
1777 * If we get here we were unable to create the printer...
1782 delete_printer(printer
);
1788 * 'create_requested_array()' - Create an array for requested-attributes.
1791 static cups_array_t
* /* O - requested-attributes array */
1792 create_requested_array(
1793 _ipp_client_t
*client
) /* I - Client */
1795 int i
; /* Looping var */
1796 ipp_attribute_t
*requested
; /* requested-attributes attribute */
1797 cups_array_t
*ra
; /* Requested attributes array */
1798 char *value
; /* Current value */
1802 * Get the requested-attributes attribute, and return NULL if we don't
1806 if ((requested
= ippFindAttribute(client
->request
, "requested-attributes",
1807 IPP_TAG_KEYWORD
)) == NULL
)
1811 * If the attribute contains a single "all" keyword, return NULL...
1814 if (requested
->num_values
== 1 &&
1815 !strcmp(requested
->values
[0].string
.text
, "all"))
1819 * Create an array using "strcmp" as the comparison function...
1822 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
1824 for (i
= 0; i
< requested
->num_values
; i
++)
1826 value
= requested
->values
[i
].string
.text
;
1828 if (!strcmp(value
, "job-template"))
1830 cupsArrayAdd(ra
, "copies");
1831 cupsArrayAdd(ra
, "copies-default");
1832 cupsArrayAdd(ra
, "copies-supported");
1833 cupsArrayAdd(ra
, "finishings");
1834 cupsArrayAdd(ra
, "finishings-default");
1835 cupsArrayAdd(ra
, "finishings-supported");
1836 cupsArrayAdd(ra
, "job-hold-until");
1837 cupsArrayAdd(ra
, "job-hold-until-default");
1838 cupsArrayAdd(ra
, "job-hold-until-supported");
1839 cupsArrayAdd(ra
, "job-priority");
1840 cupsArrayAdd(ra
, "job-priority-default");
1841 cupsArrayAdd(ra
, "job-priority-supported");
1842 cupsArrayAdd(ra
, "job-sheets");
1843 cupsArrayAdd(ra
, "job-sheets-default");
1844 cupsArrayAdd(ra
, "job-sheets-supported");
1845 cupsArrayAdd(ra
, "media");
1846 cupsArrayAdd(ra
, "media-col");
1847 cupsArrayAdd(ra
, "media-col-default");
1848 cupsArrayAdd(ra
, "media-col-supported");
1849 cupsArrayAdd(ra
, "media-default");
1850 cupsArrayAdd(ra
, "media-source-supported");
1851 cupsArrayAdd(ra
, "media-supported");
1852 cupsArrayAdd(ra
, "media-type-supported");
1853 cupsArrayAdd(ra
, "multiple-document-handling");
1854 cupsArrayAdd(ra
, "multiple-document-handling-default");
1855 cupsArrayAdd(ra
, "multiple-document-handling-supported");
1856 cupsArrayAdd(ra
, "number-up");
1857 cupsArrayAdd(ra
, "number-up-default");
1858 cupsArrayAdd(ra
, "number-up-supported");
1859 cupsArrayAdd(ra
, "orientation-requested");
1860 cupsArrayAdd(ra
, "orientation-requested-default");
1861 cupsArrayAdd(ra
, "orientation-requested-supported");
1862 cupsArrayAdd(ra
, "page-ranges");
1863 cupsArrayAdd(ra
, "page-ranges-supported");
1864 cupsArrayAdd(ra
, "printer-resolution");
1865 cupsArrayAdd(ra
, "printer-resolution-default");
1866 cupsArrayAdd(ra
, "printer-resolution-supported");
1867 cupsArrayAdd(ra
, "print-quality");
1868 cupsArrayAdd(ra
, "print-quality-default");
1869 cupsArrayAdd(ra
, "print-quality-supported");
1870 cupsArrayAdd(ra
, "sides");
1871 cupsArrayAdd(ra
, "sides-default");
1872 cupsArrayAdd(ra
, "sides-supported");
1874 else if (!strcmp(value
, "job-description"))
1876 cupsArrayAdd(ra
, "date-time-at-completed");
1877 cupsArrayAdd(ra
, "date-time-at-creation");
1878 cupsArrayAdd(ra
, "date-time-at-processing");
1879 cupsArrayAdd(ra
, "job-detailed-status-message");
1880 cupsArrayAdd(ra
, "job-document-access-errors");
1881 cupsArrayAdd(ra
, "job-id");
1882 cupsArrayAdd(ra
, "job-impressions");
1883 cupsArrayAdd(ra
, "job-impressions-completed");
1884 cupsArrayAdd(ra
, "job-k-octets");
1885 cupsArrayAdd(ra
, "job-k-octets-processed");
1886 cupsArrayAdd(ra
, "job-media-sheets");
1887 cupsArrayAdd(ra
, "job-media-sheets-completed");
1888 cupsArrayAdd(ra
, "job-message-from-operator");
1889 cupsArrayAdd(ra
, "job-more-info");
1890 cupsArrayAdd(ra
, "job-name");
1891 cupsArrayAdd(ra
, "job-originating-user-name");
1892 cupsArrayAdd(ra
, "job-printer-up-time");
1893 cupsArrayAdd(ra
, "job-printer-uri");
1894 cupsArrayAdd(ra
, "job-state");
1895 cupsArrayAdd(ra
, "job-state-message");
1896 cupsArrayAdd(ra
, "job-state-reasons");
1897 cupsArrayAdd(ra
, "job-uri");
1898 cupsArrayAdd(ra
, "number-of-documents");
1899 cupsArrayAdd(ra
, "number-of-intervening-jobs");
1900 cupsArrayAdd(ra
, "output-device-assigned");
1901 cupsArrayAdd(ra
, "time-at-completed");
1902 cupsArrayAdd(ra
, "time-at-creation");
1903 cupsArrayAdd(ra
, "time-at-processing");
1905 else if (!strcmp(value
, "printer-description"))
1907 cupsArrayAdd(ra
, "charset-configured");
1908 cupsArrayAdd(ra
, "charset-supported");
1909 cupsArrayAdd(ra
, "color-supported");
1910 cupsArrayAdd(ra
, "compression-supported");
1911 cupsArrayAdd(ra
, "document-format-default");
1912 cupsArrayAdd(ra
, "document-format-supported");
1913 cupsArrayAdd(ra
, "generated-natural-language-supported");
1914 cupsArrayAdd(ra
, "ipp-versions-supported");
1915 cupsArrayAdd(ra
, "job-impressions-supported");
1916 cupsArrayAdd(ra
, "job-k-octets-supported");
1917 cupsArrayAdd(ra
, "job-media-sheets-supported");
1918 cupsArrayAdd(ra
, "multiple-document-jobs-supported");
1919 cupsArrayAdd(ra
, "multiple-operation-time-out");
1920 cupsArrayAdd(ra
, "natural-language-configured");
1921 cupsArrayAdd(ra
, "notify-attributes-supported");
1922 cupsArrayAdd(ra
, "notify-lease-duration-default");
1923 cupsArrayAdd(ra
, "notify-lease-duration-supported");
1924 cupsArrayAdd(ra
, "notify-max-events-supported");
1925 cupsArrayAdd(ra
, "notify-events-default");
1926 cupsArrayAdd(ra
, "notify-events-supported");
1927 cupsArrayAdd(ra
, "notify-pull-method-supported");
1928 cupsArrayAdd(ra
, "notify-schemes-supported");
1929 cupsArrayAdd(ra
, "operations-supported");
1930 cupsArrayAdd(ra
, "pages-per-minute");
1931 cupsArrayAdd(ra
, "pages-per-minute-color");
1932 cupsArrayAdd(ra
, "pdl-override-supported");
1933 cupsArrayAdd(ra
, "printer-alert");
1934 cupsArrayAdd(ra
, "printer-alert-description");
1935 cupsArrayAdd(ra
, "printer-current-time");
1936 cupsArrayAdd(ra
, "printer-driver-installer");
1937 cupsArrayAdd(ra
, "printer-info");
1938 cupsArrayAdd(ra
, "printer-is-accepting-jobs");
1939 cupsArrayAdd(ra
, "printer-location");
1940 cupsArrayAdd(ra
, "printer-make-and-model");
1941 cupsArrayAdd(ra
, "printer-message-from-operator");
1942 cupsArrayAdd(ra
, "printer-more-info");
1943 cupsArrayAdd(ra
, "printer-more-info-manufacturer");
1944 cupsArrayAdd(ra
, "printer-name");
1945 cupsArrayAdd(ra
, "printer-state");
1946 cupsArrayAdd(ra
, "printer-state-message");
1947 cupsArrayAdd(ra
, "printer-state-reasons");
1948 cupsArrayAdd(ra
, "printer-up-time");
1949 cupsArrayAdd(ra
, "printer-uri-supported");
1950 cupsArrayAdd(ra
, "queued-job-count");
1951 cupsArrayAdd(ra
, "reference-uri-schemes-supported");
1952 cupsArrayAdd(ra
, "uri-authentication-supported");
1953 cupsArrayAdd(ra
, "uri-security-supported");
1955 else if (!strcmp(value
, "printer-defaults"))
1957 cupsArrayAdd(ra
, "copies-default");
1958 cupsArrayAdd(ra
, "document-format-default");
1959 cupsArrayAdd(ra
, "finishings-default");
1960 cupsArrayAdd(ra
, "job-hold-until-default");
1961 cupsArrayAdd(ra
, "job-priority-default");
1962 cupsArrayAdd(ra
, "job-sheets-default");
1963 cupsArrayAdd(ra
, "media-default");
1964 cupsArrayAdd(ra
, "media-col-default");
1965 cupsArrayAdd(ra
, "number-up-default");
1966 cupsArrayAdd(ra
, "orientation-requested-default");
1967 cupsArrayAdd(ra
, "sides-default");
1969 else if (!strcmp(value
, "subscription-template"))
1971 cupsArrayAdd(ra
, "notify-attributes");
1972 cupsArrayAdd(ra
, "notify-charset");
1973 cupsArrayAdd(ra
, "notify-events");
1974 cupsArrayAdd(ra
, "notify-lease-duration");
1975 cupsArrayAdd(ra
, "notify-natural-language");
1976 cupsArrayAdd(ra
, "notify-pull-method");
1977 cupsArrayAdd(ra
, "notify-recipient-uri");
1978 cupsArrayAdd(ra
, "notify-time-interval");
1979 cupsArrayAdd(ra
, "notify-user-data");
1982 cupsArrayAdd(ra
, value
);
1990 * 'debug_attributes()' - Print attributes in a request or response.
1994 debug_attributes(const char *title
, /* I - Title */
1995 ipp_t
*ipp
) /* I - Request/response */
1997 ipp_tag_t group_tag
; /* Current group */
1998 ipp_attribute_t
*attr
; /* Current attribute */
1999 char buffer
[2048]; /* String buffer for value */
2005 fprintf(stderr
, "%s:\n", title
);
2006 for (attr
= ipp
->attrs
, group_tag
= IPP_TAG_ZERO
; attr
; attr
= attr
->next
)
2008 if (attr
->group_tag
!= group_tag
)
2010 group_tag
= attr
->group_tag
;
2011 fprintf(stderr
, " %s\n", ippTagString(group_tag
));
2016 _ippAttrString(attr
, buffer
, sizeof(buffer
));
2017 fprintf(stderr
, " %s (%s%s) %s\n", attr
->name
,
2018 attr
->num_values
> 1 ? "1setOf " : "",
2019 ippTagString(attr
->value_tag
), buffer
);
2026 * 'delete_client()' - Close the socket and free all memory used by a client
2031 delete_client(_ipp_client_t
*client
) /* I - Client */
2034 fprintf(stderr
, "Closing connection from %s (%s)\n", client
->http
.hostname
,
2035 client
->http
.hostaddr
->addr
.sa_family
== AF_INET
? "IPv4" : "IPv6");
2038 * Flush pending writes before closing...
2041 httpFlushWrite(&(client
->http
));
2043 if (client
->http
.fd
>= 0)
2044 close(client
->http
.fd
);
2050 httpClearCookie(&(client
->http
));
2051 httpClearFields(&(client
->http
));
2053 ippDelete(client
->request
);
2055 ippDelete(client
->response
);
2062 * 'delete_job()' - Remove from the printer and free all memory used by a job
2067 delete_job(_ipp_job_t
*job
) /* I - Job */
2070 fprintf(stderr
, "Removing job #%d from history.\n", job
->id
);
2072 ippDelete(job
->attrs
);
2077 unlink(job
->filename
);
2079 free(job
->filename
);
2087 * 'delete_printer()' - Unregister, close listen sockets, and free all memory
2088 * used by a printer object.
2092 delete_printer(_ipp_printer_t
*printer
) /* I - Printer */
2094 if (printer
->ipv4
>= 0)
2095 close(printer
->ipv4
);
2097 if (printer
->ipv6
>= 0)
2098 close(printer
->ipv6
);
2101 if (printer
->printer_ref
)
2102 DNSServiceRefDeallocate(printer
->printer_ref
);
2104 if (printer
->ipp_ref
)
2105 DNSServiceRefDeallocate(printer
->ipp_ref
);
2107 if (printer
->http_ref
)
2108 DNSServiceRefDeallocate(printer
->http_ref
);
2110 if (printer
->common_ref
)
2111 DNSServiceRefDeallocate(printer
->common_ref
);
2113 TXTRecordDeallocate(&(printer
->ipp_txt
));
2115 if (printer
->dnssd_name
)
2116 _cupsStrFree(printer
->dnssd_name
);
2117 #endif /* HAVE_DNSSD */
2120 _cupsStrFree(printer
->name
);
2122 _cupsStrFree(printer
->icon
);
2123 if (printer
->directory
)
2124 _cupsStrFree(printer
->directory
);
2125 if (printer
->hostname
)
2126 _cupsStrFree(printer
->hostname
);
2128 _cupsStrFree(printer
->uri
);
2130 ippDelete(printer
->attrs
);
2131 cupsArrayDelete(printer
->jobs
);
2139 * 'dnssd_callback()' - Handle Bonjour registration events.
2144 DNSServiceRef sdRef
, /* I - Service reference */
2145 DNSServiceFlags flags
, /* I - Status flags */
2146 DNSServiceErrorType errorCode
, /* I - Error, if any */
2147 const char *name
, /* I - Service name */
2148 const char *regtype
, /* I - Service type */
2149 const char *domain
, /* I - Domain for service */
2150 _ipp_printer_t
*printer
) /* I - Printer */
2154 fprintf(stderr
, "DNSServiceRegister for %s failed with error %d.\n",
2155 regtype
, (int)errorCode
);
2158 else if (strcasecmp(name
, printer
->dnssd_name
))
2161 fprintf(stderr
, "Now using DNS-SD service name \"%s\".\n", name
);
2163 /* No lock needed since only the main thread accesses/changes this */
2164 _cupsStrFree(printer
->dnssd_name
);
2165 printer
->dnssd_name
= _cupsStrAlloc(name
);
2168 #endif /* HAVE_DNSSD */
2172 * 'find_job()' - Find a job specified in a request.
2175 static _ipp_job_t
* /* O - Job or NULL */
2176 find_job(_ipp_client_t
*client
) /* I - Client */
2178 ipp_attribute_t
*attr
; /* job-id or job-uri attribute */
2179 _ipp_job_t key
, /* Job search key */
2180 *job
; /* Matching job, if any */
2185 if ((attr
= ippFindAttribute(client
->request
, "job-uri",
2186 IPP_TAG_URI
)) != NULL
)
2188 if (!strncmp(attr
->values
[0].string
.text
, client
->printer
->uri
,
2189 client
->printer
->urilen
) &&
2190 attr
->values
[0].string
.text
[client
->printer
->urilen
] == '/')
2191 key
.id
= atoi(attr
->values
[0].string
.text
+ client
->printer
->urilen
+ 1);
2193 else if ((attr
= ippFindAttribute(client
->request
, "job-id",
2194 IPP_TAG_INTEGER
)) != NULL
)
2195 key
.id
= attr
->values
[0].integer
;
2197 _cupsRWLockRead(&(client
->printer
->rwlock
));
2198 job
= (_ipp_job_t
*)cupsArrayFind(client
->printer
->jobs
, &key
);
2199 _cupsRWUnlock(&(client
->printer
->rwlock
));
2206 * 'html_escape()' - Write a HTML-safe string.
2210 html_escape(_ipp_client_t
*client
, /* I - Client */
2211 const char *s
, /* I - String to write */
2212 size_t slen
) /* I - Number of characters to write */
2214 const char *start
, /* Start of segment */
2215 *end
; /* End of string */
2219 end
= s
+ (slen
> 0 ? slen
: strlen(s
));
2221 while (*s
&& s
< end
)
2223 if (*s
== '&' || *s
== '<')
2226 httpWrite2(&(client
->http
), start
, s
- start
);
2229 httpWrite2(&(client
->http
), "&", 5);
2231 httpWrite2(&(client
->http
), "<", 4);
2240 httpWrite2(&(client
->http
), start
, s
- start
);
2245 * 'html_printf()' - Send formatted text to the client, quoting as needed.
2249 html_printf(_ipp_client_t
*client
, /* I - Client */
2250 const char *format
, /* I - Printf-style format string */
2251 ...) /* I - Additional arguments as needed */
2253 va_list ap
; /* Pointer to arguments */
2254 const char *start
; /* Start of string */
2255 char size
, /* Size character (h, l, L) */
2256 type
; /* Format type character */
2257 int width
, /* Width of field */
2258 prec
; /* Number of characters of precision */
2259 char tformat
[100], /* Temporary format string for sprintf() */
2260 *tptr
, /* Pointer into temporary format */
2261 temp
[1024]; /* Buffer for formatted numbers */
2262 char *s
; /* Pointer to string */
2266 * Loop through the format string, formatting as needed...
2269 va_start(ap
, format
);
2277 httpWrite2(&(client
->http
), start
, format
- start
);
2280 *tptr
++ = *format
++;
2284 httpWrite2(&(client
->http
), "%", 1);
2288 else if (strchr(" -+#\'", *format
))
2289 *tptr
++ = *format
++;
2294 * Get width from argument...
2298 width
= va_arg(ap
, int);
2300 snprintf(tptr
, sizeof(tformat
) - (tptr
- tformat
), "%d", width
);
2301 tptr
+= strlen(tptr
);
2307 while (isdigit(*format
& 255))
2309 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2312 width
= width
* 10 + *format
++ - '0';
2318 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2326 * Get precision from argument...
2330 prec
= va_arg(ap
, int);
2332 snprintf(tptr
, sizeof(tformat
) - (tptr
- tformat
), "%d", prec
);
2333 tptr
+= strlen(tptr
);
2339 while (isdigit(*format
& 255))
2341 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2344 prec
= prec
* 10 + *format
++ - '0';
2349 if (*format
== 'l' && format
[1] == 'l')
2353 if (tptr
< (tformat
+ sizeof(tformat
) - 2))
2361 else if (*format
== 'h' || *format
== 'l' || *format
== 'L')
2363 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2378 if (tptr
< (tformat
+ sizeof(tformat
) - 1))
2387 case 'E' : /* Floating point formats */
2392 if ((width
+ 2) > sizeof(temp
))
2395 sprintf(temp
, tformat
, va_arg(ap
, double));
2397 httpWrite2(&(client
->http
), temp
, strlen(temp
));
2400 case 'B' : /* Integer formats */
2408 if ((width
+ 2) > sizeof(temp
))
2411 # ifdef HAVE_LONG_LONG
2413 sprintf(temp
, tformat
, va_arg(ap
, long long));
2415 # endif /* HAVE_LONG_LONG */
2417 sprintf(temp
, tformat
, va_arg(ap
, long));
2419 sprintf(temp
, tformat
, va_arg(ap
, int));
2421 httpWrite2(&(client
->http
), temp
, strlen(temp
));
2424 case 'p' : /* Pointer value */
2425 if ((width
+ 2) > sizeof(temp
))
2428 sprintf(temp
, tformat
, va_arg(ap
, void *));
2430 httpWrite2(&(client
->http
), temp
, strlen(temp
));
2433 case 'c' : /* Character or character array */
2436 temp
[0] = va_arg(ap
, int);
2438 html_escape(client
, temp
, 1);
2441 html_escape(client
, va_arg(ap
, char *), (size_t)width
);
2444 case 's' : /* String */
2445 if ((s
= va_arg(ap
, char *)) == NULL
)
2448 html_escape(client
, s
, strlen(s
));
2457 httpWrite2(&(client
->http
), start
, format
- start
);
2464 * 'ipp_cancel_job()' - Cancel a job.
2468 ipp_cancel_job(_ipp_client_t
*client
) /* I - Client */
2470 _ipp_job_t
*job
; /* Job information */
2477 if ((job
= find_job(client
)) == NULL
)
2481 * See if the job is already completed, canceled, or aborted; if so,
2482 * we can't cancel...
2487 case IPP_JOB_CANCELED
:
2488 respond_ipp(client
, IPP_NOT_POSSIBLE
,
2489 "Job #%d is already canceled - can\'t cancel.", job
->id
);
2492 case IPP_JOB_ABORTED
:
2493 respond_ipp(client
, IPP_NOT_POSSIBLE
,
2494 "Job #%d is already aborted - can\'t cancel.", job
->id
);
2497 case IPP_JOB_COMPLETED
:
2498 respond_ipp(client
, IPP_NOT_POSSIBLE
,
2499 "Job #%d is already completed - can\'t cancel.", job
->id
);
2507 _cupsRWLockWrite(&(client
->printer
->rwlock
));
2509 if (job
->state
== IPP_JOB_PROCESSING
||
2510 (job
->state
== IPP_JOB_HELD
&& job
->fd
>= 0))
2514 job
->state
= IPP_JOB_CANCELED
;
2515 job
->completed
= time(NULL
);
2518 _cupsRWUnlock(&(client
->printer
->rwlock
));
2520 respond_ipp(client
, IPP_OK
, NULL
);
2528 * 'ipp_create_job()' - Create a job object.
2532 ipp_create_job(_ipp_client_t
*client
) /* I - Client */
2539 * 'ipp_get_job_attributes()' - Get the attributes for a job object.
2543 ipp_get_job_attributes(
2544 _ipp_client_t
*client
) /* I - Client */
2546 _ipp_job_t
*job
; /* Job */
2547 cups_array_t
*ra
; /* requested-attributes */
2550 if ((job
= find_job(client
)) == NULL
)
2552 respond_ipp(client
, IPP_NOT_FOUND
, "Job not found.");
2556 respond_ipp(client
, IPP_OK
, NULL
);
2558 ra
= create_requested_array(client
);
2559 copy_job_attributes(client
, job
, ra
);
2560 cupsArrayDelete(ra
);
2565 * 'ipp_get_jobs()' - Get a list of job objects.
2569 ipp_get_jobs(_ipp_client_t
*client
) /* I - Client */
2571 ipp_attribute_t
*attr
; /* Current attribute */
2572 int job_comparison
; /* Job comparison */
2573 ipp_jstate_t job_state
; /* job-state value */
2574 int first_job_id
, /* First job ID */
2575 limit
, /* Maximum number of jobs to return */
2576 count
; /* Number of jobs that match */
2577 const char *username
; /* Username */
2578 _ipp_job_t
*job
; /* Current job pointer */
2579 cups_array_t
*ra
; /* Requested attributes array */
2583 * See if the "which-jobs" attribute have been specified...
2586 if ((attr
= ippFindAttribute(client
->request
, "which-jobs",
2587 IPP_TAG_KEYWORD
)) != NULL
)
2588 fprintf(stderr
, "%s Get-Jobs which-jobs=%s", client
->http
.hostname
,
2589 attr
->values
[0].string
.text
);
2591 if (!attr
|| !strcmp(attr
->values
[0].string
.text
, "not-completed"))
2593 job_comparison
= -1;
2594 job_state
= IPP_JOB_STOPPED
;
2596 else if (!strcmp(attr
->values
[0].string
.text
, "completed"))
2599 job_state
= IPP_JOB_CANCELED
;
2601 else if (!strcmp(attr
->values
[0].string
.text
, "aborted"))
2604 job_state
= IPP_JOB_ABORTED
;
2606 else if (!strcmp(attr
->values
[0].string
.text
, "all"))
2609 job_state
= IPP_JOB_PENDING
;
2611 else if (!strcmp(attr
->values
[0].string
.text
, "canceled"))
2614 job_state
= IPP_JOB_CANCELED
;
2616 else if (!strcmp(attr
->values
[0].string
.text
, "pending"))
2619 job_state
= IPP_JOB_PENDING
;
2621 else if (!strcmp(attr
->values
[0].string
.text
, "pending-held"))
2624 job_state
= IPP_JOB_HELD
;
2626 else if (!strcmp(attr
->values
[0].string
.text
, "processing"))
2629 job_state
= IPP_JOB_PROCESSING
;
2631 else if (!strcmp(attr
->values
[0].string
.text
, "processing-stopped"))
2634 job_state
= IPP_JOB_STOPPED
;
2638 respond_ipp(client
, IPP_ATTRIBUTES
,
2639 "The which-jobs value \"%s\" is not supported.",
2640 attr
->values
[0].string
.text
);
2641 ippAddString(client
->response
, IPP_TAG_UNSUPPORTED_GROUP
, IPP_TAG_KEYWORD
,
2642 "which-jobs", NULL
, attr
->values
[0].string
.text
);
2647 * See if they want to limit the number of jobs reported...
2650 if ((attr
= ippFindAttribute(client
->request
, "limit",
2651 IPP_TAG_INTEGER
)) != NULL
)
2653 limit
= attr
->values
[0].integer
;
2655 fprintf(stderr
, "%s Get-Jobs limit=%d", client
->http
.hostname
, limit
);
2660 if ((attr
= ippFindAttribute(client
->request
, "first-job-id",
2661 IPP_TAG_INTEGER
)) != NULL
)
2663 first_job_id
= attr
->values
[0].integer
;
2665 fprintf(stderr
, "%s Get-Jobs first-job-id=%d", client
->http
.hostname
,
2672 * See if we only want to see jobs for a specific user...
2677 if ((attr
= ippFindAttribute(client
->request
, "my-jobs",
2678 IPP_TAG_BOOLEAN
)) != NULL
)
2680 fprintf(stderr
, "%s Get-Jobs my-jobs=%s\n", client
->http
.hostname
,
2681 attr
->values
[0].boolean
? "true" : "false");
2683 if (attr
->values
[0].boolean
)
2685 if ((attr
= ippFindAttribute(client
->request
, "requesting-user-name",
2686 IPP_TAG_NAME
)) == NULL
)
2688 respond_ipp(client
, IPP_BAD_REQUEST
,
2689 "Need requesting-user-name with my-jobs.");
2693 username
= attr
->values
[0].string
.text
;
2695 fprintf(stderr
, "%s Get-Jobs requesting-user-name=\"%s\"\n",
2696 client
->http
.hostname
, username
);
2701 * OK, build a list of jobs for this printer...
2704 if ((ra
= create_requested_array(client
)) == NULL
&&
2705 !ippFindAttribute(client
->request
, "requested-attributes",
2709 * IPP conformance - Get-Jobs has a default requested-attributes value of
2710 * "job-id" and "job-uri".
2713 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2714 cupsArrayAdd(ra
, "job-id");
2715 cupsArrayAdd(ra
, "job-uri");
2718 respond_ipp(client
, IPP_OK
, NULL
);
2720 _cupsRWLockRead(&(client
->printer
->rwlock
));
2722 for (count
= 0, job
= (_ipp_job_t
*)cupsArrayFirst(client
->printer
->jobs
);
2723 (limit
<= 0 || count
< limit
) && job
;
2724 job
= (_ipp_job_t
*)cupsArrayNext(client
->printer
->jobs
))
2727 * Filter out jobs that don't match...
2730 if ((job_comparison
< 0 && job
->state
> job_state
) ||
2731 (job_comparison
== 0 && job
->state
!= job_state
) ||
2732 (job_comparison
> 0 && job
->state
< job_state
) ||
2733 job
->id
< first_job_id
||
2734 (username
&& job
->username
&& strcasecmp(username
, job
->username
)))
2738 ippAddSeparator(client
->response
);
2741 copy_job_attributes(client
, job
, ra
);
2744 cupsArrayDelete(ra
);
2746 _cupsRWUnlock(&(client
->printer
->rwlock
));
2751 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
2755 ipp_get_printer_attributes(
2756 _ipp_client_t
*client
) /* I - Client */
2758 cups_array_t
*ra
; /* Requested attributes array */
2759 _ipp_printer_t
*printer
; /* Printer */
2763 * Send the attributes...
2766 ra
= create_requested_array(client
);
2767 printer
= client
->printer
;
2769 respond_ipp(client
, IPP_OK
, NULL
);
2771 _cupsRWLockRead(&(printer
->rwlock
));
2773 copy_attributes(client
->response
, printer
->attrs
, ra
, IPP_TAG_ZERO
,
2776 if (!ra
|| cupsArrayFind(ra
, "printer-state"))
2777 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
,
2778 "printer-state", printer
->state
);
2780 if (!ra
|| cupsArrayFind(ra
, "printer-state-reasons"))
2782 if (printer
->state_reasons
== _IPP_PRINTER_NONE
)
2783 ippAddString(client
->response
, IPP_TAG_PRINTER
,
2784 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "printer-state-reasons",
2788 int num_reasons
= 0;/* Number of reasons */
2789 const char *reasons
[32]; /* Reason strings */
2791 if (printer
->state_reasons
& _IPP_PRINTER_OTHER
)
2792 reasons
[num_reasons
++] = "other";
2793 if (printer
->state_reasons
& _IPP_PRINTER_COVER_OPEN
)
2794 reasons
[num_reasons
++] = "cover-open";
2795 if (printer
->state_reasons
& _IPP_PRINTER_INPUT_TRAY_MISSING
)
2796 reasons
[num_reasons
++] = "input-tray-missing";
2797 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_SUPPLY_EMPTY
)
2798 reasons
[num_reasons
++] = "marker-supply-empty-warning";
2799 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_SUPPLY_LOW
)
2800 reasons
[num_reasons
++] = "marker-supply-low-report";
2801 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_WASTE_ALMOST_FULL
)
2802 reasons
[num_reasons
++] = "marker-waste-almost-full-report";
2803 if (printer
->state_reasons
& _IPP_PRINTER_MARKER_WASTE_FULL
)
2804 reasons
[num_reasons
++] = "marker-waste-full-warning";
2805 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_EMPTY
)
2806 reasons
[num_reasons
++] = "media-empty-warning";
2807 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_JAM
)
2808 reasons
[num_reasons
++] = "media-jam-warning";
2809 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_LOW
)
2810 reasons
[num_reasons
++] = "media-low-report";
2811 if (printer
->state_reasons
& _IPP_PRINTER_MEDIA_NEEDED
)
2812 reasons
[num_reasons
++] = "media-needed-report";
2813 if (printer
->state_reasons
& _IPP_PRINTER_MOVING_TO_PAUSED
)
2814 reasons
[num_reasons
++] = "moving-to-paused";
2815 if (printer
->state_reasons
& _IPP_PRINTER_PAUSED
)
2816 reasons
[num_reasons
++] = "paused";
2817 if (printer
->state_reasons
& _IPP_PRINTER_SPOOL_AREA_FULL
)
2818 reasons
[num_reasons
++] = "spool-area-full";
2819 if (printer
->state_reasons
& _IPP_PRINTER_TONER_EMPTY
)
2820 reasons
[num_reasons
++] = "toner-empty-warning";
2821 if (printer
->state_reasons
& _IPP_PRINTER_TONER_LOW
)
2822 reasons
[num_reasons
++] = "toner-low-report";
2824 ippAddStrings(client
->response
, IPP_TAG_PRINTER
,
2825 IPP_TAG_KEYWORD
| IPP_TAG_COPY
, "printer-state-reasons",
2826 num_reasons
, NULL
, reasons
);
2830 if (!ra
|| cupsArrayFind(ra
, "printer-up-time"))
2831 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2832 "printer-up-time", (int)time(NULL
));
2834 if (!ra
|| cupsArrayFind(ra
, "queued-job-count"))
2835 ippAddInteger(client
->response
, IPP_TAG_PRINTER
, IPP_TAG_INTEGER
,
2837 printer
->active_job
&&
2838 printer
->active_job
->state
< IPP_JOB_CANCELED
);
2840 _cupsRWUnlock(&(printer
->rwlock
));
2842 cupsArrayDelete(ra
);
2847 * 'ipp_print_job()' - Create a job object with an attached document.
2851 ipp_print_job(_ipp_client_t
*client
) /* I - Client */
2853 _ipp_job_t
*job
; /* New job */
2854 char filename
[1024], /* Filename buffer */
2855 buffer
[4096]; /* Copy buffer */
2856 ssize_t bytes
; /* Bytes read */
2857 cups_array_t
*ra
; /* Attributes to send in response */
2861 * Validate print job attributes...
2864 if (!valid_job_attributes(client
))
2866 httpFlush(&(client
->http
));
2871 * Do we have a file to print?
2874 if (client
->http
.state
== HTTP_POST_SEND
)
2876 respond_ipp(client
, IPP_BAD_REQUEST
, "No file in request.");
2884 if ((job
= create_job(client
)) == NULL
)
2886 respond_ipp(client
, IPP_PRINTER_BUSY
, "Currently printing another job.");
2891 * Create a file for the request data...
2894 if (!strcasecmp(job
->format
, "image/jpeg"))
2895 snprintf(filename
, sizeof(filename
), "%s/%d.jpg",
2896 client
->printer
->directory
, job
->id
);
2897 else if (!strcasecmp(job
->format
, "image/png"))
2898 snprintf(filename
, sizeof(filename
), "%s/%d.png",
2899 client
->printer
->directory
, job
->id
);
2900 else if (!strcasecmp(job
->format
, "application/pdf"))
2901 snprintf(filename
, sizeof(filename
), "%s/%d.pdf",
2902 client
->printer
->directory
, job
->id
);
2903 else if (!strcasecmp(job
->format
, "application/postscript"))
2904 snprintf(filename
, sizeof(filename
), "%s/%d.ps",
2905 client
->printer
->directory
, job
->id
);
2907 snprintf(filename
, sizeof(filename
), "%s/%d.prn",
2908 client
->printer
->directory
, job
->id
);
2910 if ((job
->fd
= open(filename
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600)) < 0)
2912 job
->state
= IPP_JOB_ABORTED
;
2914 respond_ipp(client
, IPP_INTERNAL_ERROR
,
2915 "Unable to create print file: %s", strerror(errno
));
2919 while ((bytes
= httpRead2(&(client
->http
), buffer
, sizeof(buffer
))) > 0)
2921 if (write(job
->fd
, buffer
, bytes
) < bytes
)
2923 int error
= errno
; /* Write error */
2925 job
->state
= IPP_JOB_ABORTED
;
2932 respond_ipp(client
, IPP_INTERNAL_ERROR
,
2933 "Unable to write print file: %s", strerror(error
));
2941 * Got an error while reading the print data, so abort this job.
2944 job
->state
= IPP_JOB_ABORTED
;
2951 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to read print file.");
2957 int error
= errno
; /* Write error */
2959 job
->state
= IPP_JOB_ABORTED
;
2964 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to write print file: %s",
2970 job
->filename
= strdup(filename
);
2971 job
->state
= IPP_JOB_PENDING
;
2974 * Process the job...
2977 if (!_cupsThreadCreate((_cups_thread_func_t
)process_job
, job
))
2979 job
->state
= IPP_JOB_ABORTED
;
2980 respond_ipp(client
, IPP_INTERNAL_ERROR
, "Unable to process job.");
2985 * Return the job info...
2988 respond_ipp(client
, IPP_OK
, NULL
);
2990 ra
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2991 cupsArrayAdd(ra
, "job-id");
2992 cupsArrayAdd(ra
, "job-state");
2993 cupsArrayAdd(ra
, "job-state-reasons");
2994 cupsArrayAdd(ra
, "job-uri");
2996 copy_job_attributes(client
, job
, ra
);
2997 cupsArrayDelete(ra
);
3003 * 'ipp_send_document()' - Add an attached document to a job object created with
3008 ipp_send_document(_ipp_client_t
*client
)/* I - Client */
3015 * 'ipp_validate_job()' - Validate job creation attributes.
3019 ipp_validate_job(_ipp_client_t
*client
) /* I - Client */
3025 * 'process_client()' - Process client requests on a thread.
3028 static void * /* O - Exit status */
3029 process_client(_ipp_client_t
*client
) /* I - Client */
3032 * Loop until we are out of requests or timeout (30 seconds)...
3035 while (httpWait(&(client
->http
), 30000))
3036 if (!process_http(client
))
3040 * Close the conection to the client and return...
3043 delete_client(client
);
3050 * 'process_http()' - Process a HTTP request.
3053 int /* O - 1 on success, 0 on failure */
3054 process_http(_ipp_client_t
*client
) /* I - Client connection */
3056 char line
[4096], /* Line from client... */
3057 operation
[64], /* Operation code from socket */
3058 uri
[1024], /* URI */
3059 version
[64], /* HTTP version number string */
3060 *ptr
; /* Pointer into strings */
3061 int major
, minor
; /* HTTP version numbers */
3062 http_status_t status
; /* Transfer status */
3063 ipp_state_t state
; /* State of IPP transfer */
3067 * Abort if we have an error on the connection...
3070 if (client
->http
.error
)
3074 * Clear state variables...
3077 httpClearFields(&(client
->http
));
3078 ippDelete(client
->request
);
3079 ippDelete(client
->response
);
3081 client
->http
.activity
= time(NULL
);
3082 client
->http
.version
= HTTP_1_1
;
3083 client
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
3084 client
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
3085 client
->http
.data_remaining
= 0;
3086 client
->request
= NULL
;
3087 client
->response
= NULL
;
3088 client
->operation
= HTTP_WAITING
;
3091 * Read a request from the connection...
3094 while ((ptr
= httpGets(line
, sizeof(line
) - 1, &(client
->http
))) != NULL
)
3102 * Parse the request line...
3105 fprintf(stderr
, "%s %s\n", client
->http
.hostname
, line
);
3107 switch (sscanf(line
, "%63s%1023s%63s", operation
, uri
, version
))
3110 fprintf(stderr
, "%s Bad request line.\n", client
->http
.hostname
);
3111 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3115 client
->http
.version
= HTTP_0_9
;
3119 if (sscanf(version
, "HTTP/%d.%d", &major
, &minor
) != 2)
3121 fprintf(stderr
, "%s Bad HTTP version.\n", client
->http
.hostname
);
3122 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3128 client
->http
.version
= (http_version_t
)(major
* 100 + minor
);
3129 if (client
->http
.version
== HTTP_1_1
)
3130 client
->http
.keep_alive
= HTTP_KEEPALIVE_ON
;
3132 client
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
3136 respond_http(client
, HTTP_NOT_SUPPORTED
, NULL
, 0);
3143 * Handle full URLs in the request line...
3146 if (!strncmp(client
->uri
, "http:", 5) || !strncmp(client
->uri
, "ipp:", 4))
3148 char scheme
[32], /* Method/scheme */
3149 userpass
[128], /* Username:password */
3150 hostname
[HTTP_MAX_HOST
];/* Hostname */
3151 int port
; /* Port number */
3154 * Separate the URI into its components...
3157 if (httpSeparateURI(HTTP_URI_CODING_MOST
, uri
, scheme
, sizeof(scheme
),
3158 userpass
, sizeof(userpass
),
3159 hostname
, sizeof(hostname
), &port
,
3160 client
->uri
, sizeof(client
->uri
)) < HTTP_URI_OK
)
3162 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->http
.hostname
, uri
);
3163 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3173 if (!_httpDecodeURI(client
->uri
, uri
, sizeof(client
->uri
)))
3175 fprintf(stderr
, "%s Bad URI \"%s\".\n", client
->http
.hostname
, uri
);
3176 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3182 * Process the request...
3185 if (!strcmp(operation
, "GET"))
3186 client
->http
.state
= HTTP_GET
;
3187 else if (!strcmp(operation
, "POST"))
3188 client
->http
.state
= HTTP_POST
;
3189 else if (!strcmp(operation
, "OPTIONS"))
3190 client
->http
.state
= HTTP_OPTIONS
;
3191 else if (!strcmp(operation
, "HEAD"))
3192 client
->http
.state
= HTTP_HEAD
;
3195 fprintf(stderr
, "%s Bad operation \"%s\".\n", client
->http
.hostname
,
3197 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3201 client
->start
= time(NULL
);
3202 client
->operation
= client
->http
.state
;
3203 client
->http
.status
= HTTP_OK
;
3206 * Parse incoming parameters until the status changes...
3209 while ((status
= httpUpdate(&(client
->http
))) == HTTP_CONTINUE
);
3211 if (status
!= HTTP_OK
)
3213 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3217 if (!client
->http
.fields
[HTTP_FIELD_HOST
][0] &&
3218 client
->http
.version
>= HTTP_1_1
)
3221 * HTTP/1.1 and higher require the "Host:" field...
3224 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3229 * Handle HTTP Upgrade...
3232 if (!strcasecmp(client
->http
.fields
[HTTP_FIELD_CONNECTION
], "Upgrade"))
3234 if (!respond_http(client
, HTTP_NOT_IMPLEMENTED
, NULL
, 0))
3239 * Handle HTTP Expect...
3242 if (client
->http
.expect
&&
3243 (client
->operation
== HTTP_POST
|| client
->operation
== HTTP_PUT
))
3245 if (client
->http
.expect
== HTTP_CONTINUE
)
3248 * Send 100-continue header...
3251 if (!respond_http(client
, HTTP_CONTINUE
, NULL
, 0))
3257 * Send 417-expectation-failed header...
3260 if (!respond_http(client
, HTTP_EXPECTATION_FAILED
, NULL
, 0))
3263 httpPrintf(&(client
->http
), "Content-Length: 0\r\n");
3264 httpPrintf(&(client
->http
), "\r\n");
3265 httpFlushWrite(&(client
->http
));
3266 client
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
3271 * Handle new transfers...
3274 switch (client
->operation
)
3278 * Do HEAD/OPTIONS command...
3281 return (respond_http(client
, HTTP_OK
, NULL
, 0));
3284 if (!strcmp(client
->uri
, "/icon.png"))
3285 return (respond_http(client
, HTTP_OK
, "image/png", 0));
3286 else if (!strcmp(client
->uri
, "/"))
3287 return (respond_http(client
, HTTP_OK
, "text/html", 0));
3289 return (respond_http(client
, HTTP_NOT_FOUND
, NULL
, 0));
3293 if (!strcmp(client
->uri
, "/icon.png"))
3296 * Send PNG icon file.
3299 int fd
; /* Icon file */
3300 struct stat fileinfo
; /* Icon file information */
3301 char buffer
[4096]; /* Copy buffer */
3302 ssize_t bytes
; /* Bytes */
3304 if (!stat(client
->printer
->icon
, &fileinfo
) &&
3305 (fd
= open(client
->printer
->icon
, O_RDONLY
)) >= 0)
3307 if (!respond_http(client
, HTTP_OK
, "image/png", fileinfo
.st_size
))
3313 while ((bytes
= read(fd
, buffer
, sizeof(buffer
))) > 0)
3314 httpWrite2(&(client
->http
), buffer
, bytes
);
3316 httpFlushWrite(&(client
->http
));
3321 return (respond_http(client
, HTTP_NOT_FOUND
, NULL
, 0));
3323 else if (!strcmp(client
->uri
, "/"))
3326 * Show web status page...
3329 if (!respond_http(client
, HTTP_OK
, "text/html", 0))
3333 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
3334 "\"http://www.w3.org/TR/html4/strict.dtd\">\n"
3337 "<title>%s</title>\n"
3338 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" "
3339 "type=\"image/png\">\n"
3344 "<p>%s, %d job(s).</p>\n"
3347 client
->printer
->name
, client
->printer
->name
,
3348 client
->printer
->state
== IPP_PRINTER_IDLE
? "Idle" :
3349 client
->printer
->state
== IPP_PRINTER_PROCESSING
?
3350 "Printing" : "Stopped",
3351 cupsArrayCount(client
->printer
->jobs
));
3352 httpWrite2(&(client
->http
), "", 0);
3357 return (respond_http(client
, HTTP_NOT_FOUND
, NULL
, 0));
3361 if (client
->http
.data_remaining
< 0 ||
3362 (!client
->http
.fields
[HTTP_FIELD_CONTENT_LENGTH
][0] &&
3363 client
->http
.data_encoding
== HTTP_ENCODE_LENGTH
))
3366 * Negative content lengths are invalid...
3369 return (respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0));
3372 if (strcmp(client
->http
.fields
[HTTP_FIELD_CONTENT_TYPE
],
3376 * Not an IPP request...
3379 return (respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0));
3383 * Read the IPP request...
3386 client
->request
= ippNew();
3388 while ((state
= ippRead(&(client
->http
), client
->request
)) != IPP_DATA
)
3389 if (state
== IPP_ERROR
)
3391 fprintf(stderr
, "%s IPP read error (%s).\n", client
->http
.hostname
,
3392 ippOpString(client
->request
->request
.op
.operation_id
));
3393 respond_http(client
, HTTP_BAD_REQUEST
, NULL
, 0);
3398 * Now that we have the IPP request, process the request...
3401 return (process_ipp(client
));
3404 break; /* Anti-compiler-warning-code */
3412 * 'process_ipp()' - Process an IPP request.
3415 static int /* O - 1 on success, 0 on error */
3416 process_ipp(_ipp_client_t
*client
) /* I - Client */
3418 ipp_tag_t group
; /* Current group tag */
3419 ipp_attribute_t
*attr
; /* Current attribute */
3420 ipp_attribute_t
*charset
; /* Character set attribute */
3421 ipp_attribute_t
*language
; /* Language attribute */
3422 ipp_attribute_t
*uri
; /* Printer URI attribute */
3425 debug_attributes("Request", client
->request
);
3428 * First build an empty response message for this request...
3431 client
->operation_id
= client
->request
->request
.op
.operation_id
;
3432 client
->response
= ippNew();
3434 client
->response
->request
.status
.version
[0] =
3435 client
->request
->request
.op
.version
[0];
3436 client
->response
->request
.status
.version
[1] =
3437 client
->request
->request
.op
.version
[1];
3438 client
->response
->request
.status
.request_id
=
3439 client
->request
->request
.op
.request_id
;
3442 * Then validate the request header and required attributes...
3445 if (client
->request
->request
.any
.version
[0] < 1 ||
3446 client
->request
->request
.any
.version
[0] > 2)
3449 * Return an error, since we only support IPP 1.x and 2.x.
3452 respond_ipp(client
, IPP_VERSION_NOT_SUPPORTED
,
3453 "Bad request version number %d.%d.",
3454 client
->request
->request
.any
.version
[0],
3455 client
->request
->request
.any
.version
[1]);
3457 else if (client
->request
->request
.any
.request_id
<= 0)
3458 respond_ipp(client
, IPP_BAD_REQUEST
, "Bad request-id %d.",
3459 client
->request
->request
.any
.request_id
);
3460 else if (!client
->request
->attrs
)
3461 respond_ipp(client
, IPP_BAD_REQUEST
, "No attributes in request.");
3465 * Make sure that the attributes are provided in the correct order and
3466 * don't repeat groups...
3469 for (attr
= client
->request
->attrs
, group
= attr
->group_tag
;
3472 if (attr
->group_tag
< group
&& attr
->group_tag
!= IPP_TAG_ZERO
)
3475 * Out of order; return an error...
3478 respond_ipp(client
, IPP_BAD_REQUEST
,
3479 "Attribute groups are out of order (%x < %x).",
3480 attr
->group_tag
, group
);
3484 group
= attr
->group_tag
;
3489 * Then make sure that the first three attributes are:
3491 * attributes-charset
3492 * attributes-natural-language
3493 * printer-uri/job-uri
3496 attr
= client
->request
->attrs
;
3497 if (attr
&& attr
->name
&&
3498 !strcmp(attr
->name
, "attributes-charset") &&
3499 (attr
->value_tag
& IPP_TAG_MASK
) == IPP_TAG_CHARSET
)
3507 if (attr
&& attr
->name
&&
3508 !strcmp(attr
->name
, "attributes-natural-language") &&
3509 (attr
->value_tag
& IPP_TAG_MASK
) == IPP_TAG_LANGUAGE
)
3514 if ((attr
= ippFindAttribute(client
->request
, "printer-uri",
3515 IPP_TAG_URI
)) != NULL
)
3517 else if ((attr
= ippFindAttribute(client
->request
, "job-uri",
3518 IPP_TAG_URI
)) != NULL
)
3523 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
3524 "attributes-charset", NULL
,
3525 charset
? charset
->values
[0].string
.text
: "utf-8");
3527 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
3528 "attributes-natural-language", NULL
,
3529 language
? language
->values
[0].string
.text
: "en");
3532 strcasecmp(charset
->values
[0].string
.text
, "us-ascii") &&
3533 strcasecmp(charset
->values
[0].string
.text
, "utf-8"))
3536 * Bad character set...
3539 respond_ipp(client
, IPP_BAD_REQUEST
,
3540 "Unsupported character set \"%s\".",
3541 charset
->values
[0].string
.text
);
3543 else if (!charset
|| !language
|| !uri
)
3546 * Return an error, since attributes-charset,
3547 * attributes-natural-language, and printer-uri/job-uri are required
3548 * for all operations.
3551 respond_ipp(client
, IPP_BAD_REQUEST
, "Missing required attributes.");
3553 else if (strcmp(uri
->values
[0].string
.text
, client
->printer
->uri
) &&
3554 strncmp(uri
->values
[0].string
.text
, client
->printer
->uri
,
3555 client
->printer
->urilen
))
3557 respond_ipp(client
, IPP_NOT_FOUND
, "%s %s not found.", uri
->name
,
3558 uri
->values
[0].string
.text
);
3563 * Try processing the operation...
3566 if (client
->http
.expect
== HTTP_CONTINUE
)
3569 * Send 100-continue header...
3572 if (!respond_http(client
, HTTP_CONTINUE
, NULL
, 0))
3576 switch (client
->request
->request
.op
.operation_id
)
3578 case IPP_PRINT_JOB
:
3579 ipp_print_job(client
);
3582 case IPP_VALIDATE_JOB
:
3583 ipp_validate_job(client
);
3586 case IPP_CANCEL_JOB
:
3587 ipp_cancel_job(client
);
3590 case IPP_GET_JOB_ATTRIBUTES
:
3591 ipp_get_job_attributes(client
);
3595 ipp_get_jobs(client
);
3598 case IPP_GET_PRINTER_ATTRIBUTES
:
3599 ipp_get_printer_attributes(client
);
3603 respond_ipp(client
, IPP_OPERATION_NOT_SUPPORTED
,
3604 "Operation not supported.");
3612 * Send the HTTP header and return...
3615 if (client
->http
.state
!= HTTP_POST_SEND
)
3616 httpFlush(&(client
->http
)); /* Flush trailing (junk) data */
3618 return (respond_http(client
, HTTP_OK
, "application/ipp",
3619 ippLength(client
->response
)));
3624 * 'process_job()' - Process a print job.
3627 static void * /* O - Thread exit status */
3628 process_job(_ipp_job_t
*job
) /* I - Job */
3630 job
->state
= IPP_JOB_PROCESSING
;
3631 job
->printer
->state
= IPP_PRINTER_PROCESSING
;
3636 job
->state
= IPP_JOB_CANCELED
;
3638 job
->state
= IPP_JOB_COMPLETED
;
3640 job
->completed
= time(NULL
);
3641 job
->printer
->state
= IPP_PRINTER_IDLE
;
3642 job
->printer
->active_job
= NULL
;
3650 * 'register_printer()' - Register a printer object via Bonjour.
3653 static int /* O - 1 on success, 0 on error */
3655 _ipp_printer_t
*printer
, /* I - Printer */
3656 const char *location
, /* I - Location */
3657 const char *make
, /* I - Manufacturer */
3658 const char *model
, /* I - Model name */
3659 const char *formats
, /* I - Supported formats */
3660 const char *adminurl
, /* I - Web interface URL */
3661 int color
, /* I - 1 = color, 0 = monochrome */
3662 int duplex
, /* I - 1 = duplex, 0 = simplex */
3663 const char *regtype
) /* I - Service type */
3665 DNSServiceErrorType error
; /* Error from Bonjour */
3666 char make_model
[256],/* Make and model together */
3667 product
[256]; /* Product string */
3671 * Build the TXT record for IPP...
3674 snprintf(make_model
, sizeof(make_model
), "%s %s", make
, model
);
3675 snprintf(product
, sizeof(product
), "(%s)", model
);
3677 TXTRecordCreate(&(printer
->ipp_txt
), 1024, NULL
);
3678 TXTRecordSetValue(&(printer
->ipp_txt
), "txtvers", 1, "1");
3679 TXTRecordSetValue(&(printer
->ipp_txt
), "qtotal", 1, "1");
3680 TXTRecordSetValue(&(printer
->ipp_txt
), "rp", 3, "ipp");
3681 TXTRecordSetValue(&(printer
->ipp_txt
), "ty", (uint8_t)strlen(make_model
),
3683 TXTRecordSetValue(&(printer
->ipp_txt
), "adminurl", (uint8_t)strlen(adminurl
),
3685 TXTRecordSetValue(&(printer
->ipp_txt
), "note", (uint8_t)strlen(location
),
3687 TXTRecordSetValue(&(printer
->ipp_txt
), "priority", 1, "0");
3688 TXTRecordSetValue(&(printer
->ipp_txt
), "product", (uint8_t)strlen(product
),
3690 TXTRecordSetValue(&(printer
->ipp_txt
), "pdl", (uint8_t)strlen(formats
),
3692 TXTRecordSetValue(&(printer
->ipp_txt
), "Color", 1, color
? "T" : "F");
3693 TXTRecordSetValue(&(printer
->ipp_txt
), "Duplex", 1, duplex
? "T" : "F");
3694 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MFG", (uint8_t)strlen(make
),
3696 TXTRecordSetValue(&(printer
->ipp_txt
), "usb_MDL", (uint8_t)strlen(model
),
3698 TXTRecordSetValue(&(printer
->ipp_txt
), "air", 4, "none");
3701 * Create a shared service reference for Bonjour...
3704 if ((error
= DNSServiceCreateConnection(&(printer
->common_ref
)))
3705 != kDNSServiceErr_NoError
)
3707 fprintf(stderr
, "Unable to create mDNSResponder connection: %d\n", error
);
3712 * Register the _printer._tcp (LPD) service type with a port number of 0 to
3713 * defend our service name but not actually support LPD...
3716 printer
->printer_ref
= printer
->common_ref
;
3718 if ((error
= DNSServiceRegister(&(printer
->printer_ref
),
3719 kDNSServiceFlagsShareConnection
,
3720 0 /* interfaceIndex */, printer
->dnssd_name
,
3721 "_printer._tcp", NULL
/* domain */,
3722 NULL
/* host */, 0 /* port */, 0 /* txtLen */,
3723 NULL
/* txtRecord */,
3724 (DNSServiceRegisterReply
)dnssd_callback
,
3725 printer
)) != kDNSServiceErr_NoError
)
3727 fprintf(stderr
, "Unable to register \"%s._printer._tcp\": %d\n",
3728 printer
->dnssd_name
, error
);
3733 * Then register the _ipp._tcp (IPP) service type with the real port number to
3734 * advertise our IPP printer...
3737 printer
->ipp_ref
= printer
->common_ref
;
3739 if ((error
= DNSServiceRegister(&(printer
->ipp_ref
),
3740 kDNSServiceFlagsShareConnection
,
3741 0 /* interfaceIndex */, printer
->dnssd_name
,
3742 regtype
, NULL
/* domain */,
3743 NULL
/* host */, htons(printer
->port
),
3744 TXTRecordGetLength(&(printer
->ipp_txt
)),
3745 TXTRecordGetBytesPtr(&(printer
->ipp_txt
)),
3746 (DNSServiceRegisterReply
)dnssd_callback
,
3747 printer
)) != kDNSServiceErr_NoError
)
3749 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
3750 printer
->dnssd_name
, regtype
, error
);
3755 * Similarly, register the _http._tcp,_printer (HTTP) service type with the
3756 * real port number to advertise our IPP printer...
3759 printer
->http_ref
= printer
->common_ref
;
3761 if ((error
= DNSServiceRegister(&(printer
->http_ref
),
3762 kDNSServiceFlagsShareConnection
,
3763 0 /* interfaceIndex */, printer
->dnssd_name
,
3764 "_http._tcp,_printer", NULL
/* domain */,
3765 NULL
/* host */, htons(printer
->port
),
3766 0 /* txtLen */, NULL
, /* txtRecord */
3767 (DNSServiceRegisterReply
)dnssd_callback
,
3768 printer
)) != kDNSServiceErr_NoError
)
3770 fprintf(stderr
, "Unable to register \"%s.%s\": %d\n",
3771 printer
->dnssd_name
, regtype
, error
);
3777 #endif /* HAVE_DNSSD */
3781 * 'respond_http()' - Send a HTTP response.
3784 int /* O - 1 on success, 0 on failure */
3785 respond_http(_ipp_client_t
*client
, /* I - Client */
3786 http_status_t code
, /* I - HTTP status of response */
3787 const char *type
, /* I - MIME type of response */
3788 size_t length
) /* I - Length of response */
3790 char message
[1024]; /* Text message */
3793 fprintf(stderr
, "%s %s\n", client
->http
.hostname
, httpStatus(code
));
3795 if (code
== HTTP_CONTINUE
)
3798 * 100-continue doesn't send any headers...
3801 return (httpPrintf(&(client
->http
), "HTTP/%d.%d 100 Continue\r\n\r\n",
3802 client
->http
.version
/ 100,
3803 client
->http
.version
% 100) > 0);
3807 * Format an error message...
3810 if (!type
&& !length
&& code
!= HTTP_OK
)
3812 snprintf(message
, sizeof(message
), "%d - %s\n", code
, httpStatus(code
));
3814 type
= "text/plain";
3815 length
= strlen(message
);
3821 * Send the HTTP status header...
3824 httpFlushWrite(&(client
->http
));
3826 client
->http
.data_encoding
= HTTP_ENCODE_FIELDS
;
3828 if (httpPrintf(&(client
->http
), "HTTP/%d.%d %d %s\r\n", client
->http
.version
/ 100,
3829 client
->http
.version
% 100, code
, httpStatus(code
)) < 0)
3833 * Follow the header with the response fields...
3836 if (httpPrintf(&(client
->http
), "Date: %s\r\n", httpGetDateString(time(NULL
))) < 0)
3839 if (client
->http
.keep_alive
&& client
->http
.version
>= HTTP_1_0
)
3841 if (httpPrintf(&(client
->http
),
3842 "Connection: Keep-Alive\r\n"
3843 "Keep-Alive: timeout=10\r\n") < 0)
3847 if (code
== HTTP_METHOD_NOT_ALLOWED
|| client
->operation
== HTTP_OPTIONS
)
3849 if (httpPrintf(&(client
->http
), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
3855 if (!strcmp(type
, "text/html"))
3857 if (httpPrintf(&(client
->http
),
3858 "Content-Type: text/html; charset=utf-8\r\n") < 0)
3861 else if (httpPrintf(&(client
->http
), "Content-Type: %s\r\n", type
) < 0)
3865 if (length
== 0 && !message
[0])
3867 if (httpPrintf(&(client
->http
), "Transfer-Encoding: chunked\r\n\r\n") < 0)
3870 else if (httpPrintf(&(client
->http
), "Content-Length: " CUPS_LLFMT
"\r\n\r\n",
3871 CUPS_LLCAST length
) < 0)
3874 if (httpFlushWrite(&(client
->http
)) < 0)
3878 * Send the response data...
3884 * Send a plain text message.
3887 if (httpPrintf(&(client
->http
), "%s", message
) < 0)
3890 else if (client
->response
)
3893 * Send an IPP response...
3896 debug_attributes("Response", client
->response
);
3898 client
->http
.data_encoding
= HTTP_ENCODE_LENGTH
;
3899 client
->http
.data_remaining
= (off_t
)ippLength(client
->response
);
3900 client
->response
->state
= IPP_IDLE
;
3902 if (ippWrite(&(client
->http
), client
->response
) != IPP_DATA
)
3906 client
->http
.data_encoding
= HTTP_ENCODE_CHUNKED
;
3909 * Flush the data and return...
3912 return (httpFlushWrite(&(client
->http
)) >= 0);
3917 * 'respond_ipp()' - Send an IPP response.
3921 respond_ipp(_ipp_client_t
*client
, /* I - Client */
3922 ipp_status_t status
, /* I - status-code */
3923 const char *message
, /* I - printf-style status-message */
3924 ...) /* I - Additional args as needed */
3926 va_list ap
; /* Pointer to additional args */
3927 char formatted
[1024]; /* Formatted errror message */
3930 client
->response
->request
.status
.status_code
= status
;
3932 if (!client
->response
->attrs
)
3934 ippAddString(client
->response
, IPP_TAG_OPERATION
,
3935 IPP_TAG_CHARSET
| IPP_TAG_COPY
, "attributes-charset", NULL
,
3937 ippAddString(client
->response
, IPP_TAG_OPERATION
,
3938 IPP_TAG_LANGUAGE
| IPP_TAG_COPY
, "attributes-natural-language",
3944 va_start(ap
, message
);
3945 vsnprintf(formatted
, sizeof(formatted
), message
, ap
);
3948 ippAddString(client
->response
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
3949 "status-message", NULL
, formatted
);
3952 formatted
[0] = '\0';
3954 fprintf(stderr
, "%s %s %s (%s)\n", client
->http
.hostname
,
3955 ippOpString(client
->operation_id
), ippErrorString(status
), formatted
);
3960 * 'run_printer()' - Run the printer service.
3964 run_printer(_ipp_printer_t
*printer
) /* I - Printer */
3966 int num_fds
; /* Number of file descriptors */
3967 struct pollfd polldata
[3]; /* poll() data */
3968 int timeout
; /* Timeout for poll() */
3969 _ipp_client_t
*client
; /* New client */
3973 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners...
3976 polldata
[0].fd
= printer
->ipv4
;
3977 polldata
[0].events
= POLLIN
;
3979 polldata
[1].fd
= printer
->ipv6
;
3980 polldata
[1].events
= POLLIN
;
3985 polldata
[num_fds
].fd
= DNSServiceRefSockFD(printer
->common_ref
);
3986 polldata
[num_fds
++].events
= POLLIN
;
3987 #endif /* HAVE_DNSSD */
3990 * Loop until we are killed or have a hard error...
3995 if (cupsArrayCount(printer
->jobs
))
4000 if (poll(polldata
, num_fds
, timeout
) < 0 && errno
!= EINTR
)
4002 perror("poll() failed");
4006 if (polldata
[0].revents
& POLLIN
)
4008 if ((client
= create_client(printer
, printer
->ipv4
)) != NULL
)
4010 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4012 perror("Unable to create client thread");
4013 delete_client(client
);
4018 if (polldata
[1].revents
& POLLIN
)
4020 if ((client
= create_client(printer
, printer
->ipv6
)) != NULL
)
4022 if (!_cupsThreadCreate((_cups_thread_func_t
)process_client
, client
))
4024 perror("Unable to create client thread");
4025 delete_client(client
);
4031 if (polldata
[2].revents
& POLLIN
)
4032 DNSServiceProcessResult(printer
->common_ref
);
4033 #endif /* HAVE_DNSSD */
4036 * Clean out old jobs...
4039 clean_jobs(printer
);
4045 * 'usage()' - Show program usage.
4049 usage(int status
) /* O - Exit status */
4053 puts(CUPS_SVERSION
" - Copyright 2010 by Apple Inc. All rights reserved.");
4057 puts("Usage: ippserver [options] \"name\"");
4060 puts("-2 Supports 2-sided printing (default=1-sided)");
4061 puts("-M manufacturer Manufacturer name (default=Test)");
4062 printf("-d spool-directory Spool directory "
4063 "(default=/tmp/ippserver.%d)\n", (int)getpid());
4064 puts("-f type/subtype[,...] List of supported types "
4065 "(default=application/pdf,image/jpeg)");
4066 puts("-h Show program help");
4067 puts("-i iconfile.png PNG icon file (default=printer.png)");
4068 puts("-l location Location of printer (default=empty string)");
4069 puts("-m model Model name (default=Printer)");
4070 puts("-n hostname Hostname for printer");
4071 puts("-p port Port number (default=auto)");
4072 puts("-r regtype Bonjour service type (default=_ipp._tcp)");
4073 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)");
4074 puts("-v[vvv] Be (very) verbose");
4081 * 'valid_job_attributes()' - Determine whether the job attributes are valid.
4083 * When one or more job attributes are invalid, this function adds a suitable
4084 * response and attributes to the unsupported group.
4087 static int /* O - 1 if valid, 0 if not */
4088 valid_job_attributes(
4089 _ipp_client_t
*client
) /* I - Client */
4091 int i
; /* Looping var */
4092 ipp_attribute_t
*attr
, /* Current attribute */
4093 *supported
; /* document-format-supported */
4094 const char *format
= NULL
; /* document-format value */
4095 int valid
= 1; /* Valid attributes? */
4099 * Check operation attributes...
4102 #define respond_unsupported(client, attr) \
4105 respond_ipp(client, IPP_ATTRIBUTES, "Unsupported %s %s%s value.", \
4106 attr->name, attr->num_values > 1 ? "1setOf " : "", \
4107 ippTagString(attr->value_tag)); \
4110 copy_attribute(client->response, attr, IPP_TAG_UNSUPPORTED_GROUP, 0)
4112 if ((attr
= ippFindAttribute(client
->request
, "compression",
4113 IPP_TAG_ZERO
)) != NULL
)
4116 * If compression is specified, only accept "none"...
4119 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_KEYWORD
||
4120 strcmp(attr
->values
[0].string
.text
, "none"))
4122 respond_unsupported(client
, attr
);
4125 fprintf(stderr
, "%s Print-Job compression=\"%s\"\n", client
->http
.hostname
,
4126 attr
->values
[0].string
.text
);
4130 * Is it a format we support?
4133 if ((attr
= ippFindAttribute(client
->request
, "document-format",
4134 IPP_TAG_ZERO
)) != NULL
)
4136 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_MIMETYPE
)
4138 respond_unsupported(client
, attr
);
4142 format
= attr
->values
[0].string
.text
;
4144 fprintf(stderr
, "%s %s document-format=\"%s\"\n",
4145 client
->http
.hostname
,
4146 ippOpString(client
->request
->request
.op
.operation_id
), format
);
4150 format
= "application/octet-stream";
4152 if (!strcmp(format
, "application/octet-stream") &&
4153 client
->request
->request
.op
.operation_id
!= IPP_VALIDATE_JOB
)
4156 * Auto-type the file using the first 4 bytes of the file...
4159 unsigned char header
[4]; /* First 4 bytes of file */
4161 memset(header
, 0, sizeof(header
));
4162 _httpPeek(&(client
->http
), (char *)header
, sizeof(header
));
4164 if (!memcmp(header
, "%PDF", 4))
4165 format
= "application/pdf";
4166 else if (!memcmp(header
, "%!", 2))
4167 format
= "application/postscript";
4168 else if (!memcmp(header
, "\377\330\377", 3) &&
4169 header
[3] >= 0xe0 && header
[3] <= 0xef)
4170 format
= "image/jpeg";
4171 else if (!memcmp(header
, "\211PNG", 4))
4172 format
= "image/png";
4175 fprintf(stderr
, "%s %s Auto-typed document-format=\"%s\"\n",
4176 client
->http
.hostname
,
4177 ippOpString(client
->request
->request
.op
.operation_id
), format
);
4180 ippAddString(client
->request
, IPP_TAG_JOB
, IPP_TAG_MIMETYPE
,
4181 "document-format", NULL
, format
);
4184 _cupsStrFree(attr
->values
[0].string
.text
);
4185 attr
->values
[0].string
.text
= _cupsStrAlloc(format
);
4189 if ((supported
= ippFindAttribute(client
->printer
->attrs
,
4190 "document-format-supported",
4191 IPP_TAG_MIMETYPE
)) != NULL
)
4193 for (i
= 0; i
< supported
->num_values
; i
++)
4194 if (!strcasecmp(format
, supported
->values
[i
].string
.text
))
4197 if (i
>= supported
->num_values
)
4198 respond_unsupported(client
, attr
);
4202 * Check the various job template attributes...
4205 if ((attr
= ippFindAttribute(client
->request
, "copies",
4206 IPP_TAG_ZERO
)) != NULL
)
4208 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_INTEGER
||
4209 attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> 999)
4211 respond_unsupported(client
, attr
);
4215 if ((attr
= ippFindAttribute(client
->request
, "ipp-attribute-fidelity",
4216 IPP_TAG_ZERO
)) != NULL
)
4218 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_BOOLEAN
)
4220 respond_unsupported(client
, attr
);
4224 if ((attr
= ippFindAttribute(client
->request
, "job-hold-until",
4225 IPP_TAG_ZERO
)) != NULL
)
4227 if (attr
->num_values
!= 1 ||
4228 (attr
->value_tag
!= IPP_TAG_NAME
&&
4229 attr
->value_tag
!= IPP_TAG_NAMELANG
&&
4230 attr
->value_tag
!= IPP_TAG_KEYWORD
) ||
4231 strcmp(attr
->values
[0].string
.text
, "no-hold"))
4233 respond_unsupported(client
, attr
);
4237 if ((attr
= ippFindAttribute(client
->request
, "job-name",
4238 IPP_TAG_ZERO
)) != NULL
)
4240 if (attr
->num_values
!= 1 ||
4241 (attr
->value_tag
!= IPP_TAG_NAME
&&
4242 attr
->value_tag
!= IPP_TAG_NAMELANG
))
4244 respond_unsupported(client
, attr
);
4248 if ((attr
= ippFindAttribute(client
->request
, "job-priority",
4249 IPP_TAG_ZERO
)) != NULL
)
4251 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_INTEGER
||
4252 attr
->values
[0].integer
< 1 || attr
->values
[0].integer
> 100)
4254 respond_unsupported(client
, attr
);
4258 if ((attr
= ippFindAttribute(client
->request
, "job-sheets",
4259 IPP_TAG_ZERO
)) != NULL
)
4261 if (attr
->num_values
!= 1 ||
4262 (attr
->value_tag
!= IPP_TAG_NAME
&&
4263 attr
->value_tag
!= IPP_TAG_NAMELANG
&&
4264 attr
->value_tag
!= IPP_TAG_KEYWORD
) ||
4265 strcmp(attr
->values
[0].string
.text
, "none"))
4267 respond_unsupported(client
, attr
);
4271 if ((attr
= ippFindAttribute(client
->request
, "media",
4272 IPP_TAG_ZERO
)) != NULL
)
4274 if (attr
->num_values
!= 1 ||
4275 (attr
->value_tag
!= IPP_TAG_NAME
&&
4276 attr
->value_tag
!= IPP_TAG_NAMELANG
&&
4277 attr
->value_tag
!= IPP_TAG_KEYWORD
))
4279 respond_unsupported(client
, attr
);
4284 i
< (int)(sizeof(media_supported
) / sizeof(media_supported
[0]));
4286 if (!strcmp(attr
->values
[0].string
.text
, media_supported
[i
]))
4289 if (i
>= (int)(sizeof(media_supported
) / sizeof(media_supported
[0])))
4291 respond_unsupported(client
, attr
);
4296 if ((attr
= ippFindAttribute(client
->request
, "media-col",
4297 IPP_TAG_ZERO
)) != NULL
)
4299 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_BEGIN_COLLECTION
)
4301 respond_unsupported(client
, attr
);
4303 /* TODO: check for valid media-col */
4306 if ((attr
= ippFindAttribute(client
->request
, "multiple-document-handling",
4307 IPP_TAG_ZERO
)) != NULL
)
4309 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_KEYWORD
||
4310 (strcmp(attr
->values
[0].string
.text
,
4311 "separate-documents-uncollated-copies") &&
4312 strcmp(attr
->values
[0].string
.text
,
4313 "separate-documents-collated-copies")))
4315 respond_unsupported(client
, attr
);
4319 if ((attr
= ippFindAttribute(client
->request
, "orientation-requested",
4320 IPP_TAG_ZERO
)) != NULL
)
4322 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_ENUM
||
4323 attr
->values
[0].integer
< IPP_PORTRAIT
||
4324 attr
->values
[0].integer
> IPP_REVERSE_PORTRAIT
)
4326 respond_unsupported(client
, attr
);
4330 if ((attr
= ippFindAttribute(client
->request
, "page-ranges",
4331 IPP_TAG_ZERO
)) != NULL
)
4333 respond_unsupported(client
, attr
);
4336 if ((attr
= ippFindAttribute(client
->request
, "print-quality",
4337 IPP_TAG_ZERO
)) != NULL
)
4339 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_ENUM
||
4340 attr
->values
[0].integer
< IPP_QUALITY_DRAFT
||
4341 attr
->values
[0].integer
> IPP_QUALITY_HIGH
)
4343 respond_unsupported(client
, attr
);
4347 if ((attr
= ippFindAttribute(client
->request
, "printer-resolution",
4348 IPP_TAG_ZERO
)) != NULL
)
4350 respond_unsupported(client
, attr
);
4353 if ((attr
= ippFindAttribute(client
->request
, "sides",
4354 IPP_TAG_ZERO
)) != NULL
)
4356 if (attr
->num_values
!= 1 || attr
->value_tag
!= IPP_TAG_KEYWORD
)
4358 respond_unsupported(client
, attr
);
4361 if ((supported
= ippFindAttribute(client
->printer
->attrs
, "sides",
4362 IPP_TAG_KEYWORD
)) != NULL
)
4364 for (i
= 0; i
< supported
->num_values
; i
++)
4365 if (!strcmp(attr
->values
[0].string
.text
,
4366 supported
->values
[i
].string
.text
))
4369 if (i
>= supported
->num_values
)
4371 respond_unsupported(client
, attr
);
4376 respond_unsupported(client
, attr
);