Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/tools/ippeveprinter.c
1090 views
1
/*
2
* IPP Everywhere printer application for CUPS.
3
*
4
* Copyright © 2021-2022 by OpenPrinting.
5
* Copyright © 2020 by the IEEE-ISTO Printer Working Group.
6
* Copyright © 2010-2021 by Apple Inc.
7
*
8
* Licensed under Apache License v2.0. See the file "LICENSE" for more
9
* information.
10
*
11
* Note: This program began life as the "ippserver" sample code that first
12
* appeared in CUPS 1.4. The name has been changed in order to distinguish it
13
* from the PWG's much more ambitious "ippserver" program, which supports
14
* different kinds of IPP services and multiple services per instance - the
15
* "ippeveprinter" program exposes a single print service conforming to the
16
* current IPP Everywhere specification, thus the new name.
17
*/
18
19
/*
20
* Include necessary headers...
21
*/
22
23
#include <cups/cups-private.h>
24
#include <cups/debug-private.h>
25
#if !CUPS_LITE
26
# include <cups/ppd-private.h>
27
#endif /* !CUPS_LITE */
28
29
#include <limits.h>
30
#include <fcntl.h>
31
#include <sys/stat.h>
32
33
#ifdef _WIN32
34
# include <io.h>
35
# include <process.h>
36
# define WEXITSTATUS(s) (s)
37
# include <winsock2.h>
38
typedef ULONG nfds_t;
39
# define poll WSAPoll
40
#else
41
extern char **environ;
42
43
# include <spawn.h>
44
# include <sys/wait.h>
45
# include <poll.h>
46
#endif /* _WIN32 */
47
48
#ifndef O_BINARY
49
# define O_BINARY 0 /* Windows "binary file" nonsense */
50
#endif /* !O_BINARY */
51
52
#ifdef HAVE_MDNSRESPONDER
53
# include <dns_sd.h>
54
#elif defined(HAVE_AVAHI)
55
# include <avahi-client/client.h>
56
# include <avahi-client/publish.h>
57
# include <avahi-common/alternative.h>
58
# include <avahi-common/error.h>
59
# include <avahi-common/malloc.h>
60
# include <avahi-common/thread-watch.h>
61
#endif /* HAVE_MDNSRESPONDER */
62
63
#ifdef HAVE_SYS_MOUNT_H
64
# include <sys/mount.h>
65
#endif /* HAVE_SYS_MOUNT_H */
66
#ifdef HAVE_SYS_STATFS_H
67
# include <sys/statfs.h>
68
#endif /* HAVE_SYS_STATFS_H */
69
#ifdef HAVE_SYS_STATVFS_H
70
# include <sys/statvfs.h>
71
#endif /* HAVE_SYS_STATVFS_H */
72
#ifdef HAVE_SYS_VFS_H
73
# include <sys/vfs.h>
74
#endif /* HAVE_SYS_VFS_H */
75
76
#if HAVE_LIBPAM
77
# ifdef HAVE_PAM_PAM_APPL_H
78
# include <pam/pam_appl.h>
79
# else
80
# include <security/pam_appl.h>
81
# endif /* HAVE_PAM_PAM_APPL_H */
82
#endif /* HAVE_LIBPAM */
83
84
#include "printer-png.h"
85
#include "printer-lg-png.h"
86
#include "printer-sm-png.h"
87
88
89
/*
90
* Constants...
91
*/
92
93
enum ippeve_preason_e /* printer-state-reasons bit values */
94
{
95
IPPEVE_PREASON_NONE = 0x0000, /* none */
96
IPPEVE_PREASON_OTHER = 0x0001, /* other */
97
IPPEVE_PREASON_COVER_OPEN = 0x0002, /* cover-open */
98
IPPEVE_PREASON_INPUT_TRAY_MISSING = 0x0004,
99
/* input-tray-missing */
100
IPPEVE_PREASON_MARKER_SUPPLY_EMPTY = 0x0008,
101
/* marker-supply-empty */
102
IPPEVE_PREASON_MARKER_SUPPLY_LOW = 0x0010,
103
/* marker-supply-low */
104
IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL = 0x0020,
105
/* marker-waste-almost-full */
106
IPPEVE_PREASON_MARKER_WASTE_FULL = 0x0040,
107
/* marker-waste-full */
108
IPPEVE_PREASON_MEDIA_EMPTY = 0x0080, /* media-empty */
109
IPPEVE_PREASON_MEDIA_JAM = 0x0100, /* media-jam */
110
IPPEVE_PREASON_MEDIA_LOW = 0x0200, /* media-low */
111
IPPEVE_PREASON_MEDIA_NEEDED = 0x0400, /* media-needed */
112
IPPEVE_PREASON_MOVING_TO_PAUSED = 0x0800,
113
/* moving-to-paused */
114
IPPEVE_PREASON_PAUSED = 0x1000, /* paused */
115
IPPEVE_PREASON_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */
116
IPPEVE_PREASON_TONER_EMPTY = 0x4000, /* toner-empty */
117
IPPEVE_PREASON_TONER_LOW = 0x8000 /* toner-low */
118
};
119
typedef unsigned int ippeve_preason_t; /* Bitfield for printer-state-reasons */
120
static const char * const ippeve_preason_strings[] =
121
{ /* Strings for each bit */
122
/* "none" is implied for no bits set */
123
"other",
124
"cover-open",
125
"input-tray-missing",
126
"marker-supply-empty",
127
"marker-supply-low",
128
"marker-waste-almost-full",
129
"marker-waste-full",
130
"media-empty",
131
"media-jam",
132
"media-low",
133
"media-needed",
134
"moving-to-paused",
135
"paused",
136
"spool-area-full",
137
"toner-empty",
138
"toner-low"
139
};
140
141
142
/*
143
* URL scheme for web resources...
144
*/
145
146
#ifdef HAVE_TLS
147
# define WEB_SCHEME "https"
148
#else
149
# define WEB_SCHEME "http"
150
#endif /* HAVE_TLS */
151
152
153
/*
154
* Structures...
155
*/
156
157
#ifdef HAVE_MDNSRESPONDER
158
typedef DNSServiceRef ippeve_srv_t; /* Service reference */
159
typedef TXTRecordRef ippeve_txt_t; /* TXT record */
160
161
#elif defined(HAVE_AVAHI)
162
typedef AvahiEntryGroup *ippeve_srv_t; /* Service reference */
163
typedef AvahiStringList *ippeve_txt_t; /* TXT record */
164
165
#else
166
typedef void *ippeve_srv_t; /* Service reference */
167
typedef void *ippeve_txt_t; /* TXT record */
168
#endif /* HAVE_MDNSRESPONDER */
169
170
#if HAVE_LIBPAM
171
typedef struct ippeve_authdata_s /* Authentication data */
172
{
173
char username[HTTP_MAX_VALUE], /* Username string */
174
*password; /* Password string */
175
} ippeve_authdata_t;
176
#endif /* HAVE_LIBPAM */
177
178
typedef struct ippeve_filter_s /**** Attribute filter ****/
179
{
180
cups_array_t *ra; /* Requested attributes */
181
ipp_tag_t group_tag; /* Group to copy */
182
} ippeve_filter_t;
183
184
typedef struct ippeve_job_s ippeve_job_t;
185
186
typedef struct ippeve_printer_s /**** Printer data ****/
187
{
188
/* TODO: One IPv4 and one IPv6 listener are really not sufficient */
189
int ipv4, /* IPv4 listener */
190
ipv6; /* IPv6 listener */
191
#ifdef HAVE_MDNSRESPONDER
192
ippeve_srv_t ipp_ref, /* DNS-SD IPP service */
193
ipps_ref, /* DNS-SD IPPS service */
194
http_ref, /* DNS-SD HTTP service */
195
printer_ref; /* DNS-SD LPD service */
196
#elif defined(HAVE_AVAHI)
197
ippeve_srv_t dnssd_ref; /* DNS-SD services */
198
#endif /* HAVE_MDNSRESPONDER */
199
char *dnssd_subtypes;/* DNS-SD subtypes */
200
int dnssd_collision;/* Name collision? */
201
char *dnssd_name, /* printer-dns-sd-name */
202
*name, /* printer-name */
203
*icons[3], /* Icon filenames */
204
*strings, /* Strings filename */
205
*directory, /* Spool directory */
206
*hostname, /* Hostname */
207
*device_uri, /* Device URI (if any) */
208
*output_format, /* Output format */
209
#if !CUPS_LITE
210
*ppdfile, /* PPD file (if any) */
211
#endif /* !CUPS_LITE */
212
*command; /* Command to run with job file */
213
int port; /* Port */
214
int web_forms; /* Enable web interface forms? */
215
size_t urilen; /* Length of printer URI */
216
ipp_t *attrs; /* Static attributes */
217
time_t start_time; /* Startup time */
218
time_t config_time; /* printer-config-change-time */
219
ipp_pstate_t state; /* printer-state value */
220
ippeve_preason_t state_reasons; /* printer-state-reasons values */
221
time_t state_time; /* printer-state-change-time */
222
cups_array_t *jobs; /* Jobs */
223
ippeve_job_t *active_job; /* Current active/pending job */
224
int next_job_id; /* Next job-id value */
225
_cups_rwlock_t rwlock; /* Printer lock */
226
} ippeve_printer_t;
227
228
struct ippeve_job_s /**** Job data ****/
229
{
230
int id; /* Job ID */
231
const char *name, /* job-name */
232
*username, /* job-originating-user-name */
233
*format; /* document-format */
234
ipp_jstate_t state; /* job-state value */
235
char *message; /* job-state-message value */
236
int msglevel; /* job-state-message log level (0=error, 1=info) */
237
time_t created, /* time-at-creation value */
238
processing, /* time-at-processing value */
239
completed; /* time-at-completed value */
240
int impressions, /* job-impressions value */
241
impcompleted; /* job-impressions-completed value */
242
ipp_t *attrs; /* Static attributes */
243
int cancel; /* Non-zero when job canceled */
244
char *filename; /* Print file name */
245
int fd; /* Print file descriptor */
246
ippeve_printer_t *printer; /* Printer */
247
};
248
249
typedef struct ippeve_client_s /**** Client data ****/
250
{
251
http_t *http; /* HTTP connection */
252
ipp_t *request, /* IPP request */
253
*response; /* IPP response */
254
time_t start; /* Request start time */
255
http_state_t operation; /* Request operation */
256
ipp_op_t operation_id; /* IPP operation-id */
257
char uri[1024], /* Request URI */
258
*options, /* URI options */
259
host_field[HTTP_MAX_VALUE];
260
/* Host: header */
261
int host_port; /* Port number from Host: header */
262
http_addr_t addr; /* Client address */
263
char hostname[256], /* Client hostname */
264
username[HTTP_MAX_VALUE];
265
/* Authenticated username, if any */
266
ippeve_printer_t *printer; /* Printer */
267
ippeve_job_t *job; /* Current job, if any */
268
} ippeve_client_t;
269
270
271
/*
272
* Local functions...
273
*/
274
275
static http_status_t authenticate_request(ippeve_client_t *client);
276
static void clean_jobs(ippeve_printer_t *printer);
277
static int compare_jobs(ippeve_job_t *a, ippeve_job_t *b);
278
static void copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra, ipp_tag_t group_tag, int quickcopy);
279
static void copy_job_attributes(ippeve_client_t *client, ippeve_job_t *job, cups_array_t *ra);
280
static ippeve_client_t *create_client(ippeve_printer_t *printer, int sock);
281
static ippeve_job_t *create_job(ippeve_client_t *client);
282
static int create_job_file(ippeve_job_t *job, char *fname, size_t fnamesize, const char *dir, const char *ext);
283
static int create_listener(const char *name, int port, int family);
284
static ipp_t *create_media_col(const char *media, const char *source, const char *type, int width, int length, int bottom, int left, int right, int top);
285
static ipp_t *create_media_size(int width, int length);
286
static ippeve_printer_t *create_printer(const char *servername, int serverport, const char *name, const char *location, const char *icons, const char *strings, cups_array_t *docformats, const char *subtypes, const char *directory, const char *command, const char *device_uri, const char *output_format, ipp_t *attrs);
287
static void debug_attributes(const char *title, ipp_t *ipp, int response);
288
static void delete_client(ippeve_client_t *client);
289
static void delete_job(ippeve_job_t *job);
290
static void delete_printer(ippeve_printer_t *printer);
291
#ifdef HAVE_MDNSRESPONDER
292
static void DNSSD_API dnssd_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, ippeve_printer_t *printer);
293
#elif defined(HAVE_AVAHI)
294
static void dnssd_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, void *context);
295
static void dnssd_client_cb(AvahiClient *c, AvahiClientState state, void *userdata);
296
#endif /* HAVE_MDNSRESPONDER */
297
static void dnssd_init(void);
298
static int filter_cb(ippeve_filter_t *filter, ipp_t *dst, ipp_attribute_t *attr);
299
static ippeve_job_t *find_job(ippeve_client_t *client);
300
static void finish_document_data(ippeve_client_t *client, ippeve_job_t *job);
301
static void finish_document_uri(ippeve_client_t *client, ippeve_job_t *job);
302
static void flush_document_data(ippeve_client_t *client);
303
static int have_document_data(ippeve_client_t *client);
304
static void html_escape(ippeve_client_t *client, const char *s, size_t slen);
305
static void html_footer(ippeve_client_t *client);
306
static void html_header(ippeve_client_t *client, const char *title, int refresh);
307
static void html_printf(ippeve_client_t *client, const char *format, ...) _CUPS_FORMAT(2, 3);
308
static void ipp_cancel_job(ippeve_client_t *client);
309
static void ipp_cancel_my_jobs(ippeve_client_t *client);
310
static void ipp_close_job(ippeve_client_t *client);
311
static void ipp_create_job(ippeve_client_t *client);
312
static void ipp_get_job_attributes(ippeve_client_t *client);
313
static void ipp_get_jobs(ippeve_client_t *client);
314
static void ipp_get_printer_attributes(ippeve_client_t *client);
315
static void ipp_identify_printer(ippeve_client_t *client);
316
static void ipp_print_job(ippeve_client_t *client);
317
static void ipp_print_uri(ippeve_client_t *client);
318
static void ipp_send_document(ippeve_client_t *client);
319
static void ipp_send_uri(ippeve_client_t *client);
320
static void ipp_validate_job(ippeve_client_t *client);
321
static ipp_t *load_ippserver_attributes(const char *servername, int serverport, const char *filename, cups_array_t *docformats);
322
static ipp_t *load_legacy_attributes(const char *make, const char *model, int ppm, int ppm_color, int duplex, cups_array_t *docformats);
323
#if !CUPS_LITE
324
static ipp_t *load_ppd_attributes(const char *ppdfile, cups_array_t *docformats);
325
#endif /* !CUPS_LITE */
326
#if HAVE_LIBPAM
327
static int pam_func(int, const struct pam_message **, struct pam_response **, void *);
328
#endif /* HAVE_LIBPAM */
329
static int parse_options(ippeve_client_t *client, cups_option_t **options);
330
static void process_attr_message(ippeve_job_t *job, char *message);
331
static void *process_client(ippeve_client_t *client);
332
static int process_http(ippeve_client_t *client);
333
static int process_ipp(ippeve_client_t *client);
334
static void *process_job(ippeve_job_t *job);
335
static void process_state_message(ippeve_job_t *job, char *message);
336
static int register_printer(ippeve_printer_t *printer);
337
static int respond_http(ippeve_client_t *client, http_status_t code, const char *content_coding, const char *type, size_t length);
338
static void respond_ipp(ippeve_client_t *client, ipp_status_t status, const char *message, ...) _CUPS_FORMAT(3, 4);
339
static void respond_unsupported(ippeve_client_t *client, ipp_attribute_t *attr);
340
static void run_printer(ippeve_printer_t *printer);
341
static int show_media(ippeve_client_t *client);
342
static int show_status(ippeve_client_t *client);
343
static int show_supplies(ippeve_client_t *client);
344
#ifndef _WIN32
345
static void signal_handler(int signum);
346
#endif // !_WIN32
347
static char *time_string(time_t tv, char *buffer, size_t bufsize);
348
static void usage(int status) _CUPS_NORETURN;
349
static int valid_doc_attributes(ippeve_client_t *client);
350
static int valid_job_attributes(ippeve_client_t *client);
351
352
353
/*
354
* Globals...
355
*/
356
357
#ifdef HAVE_MDNSRESPONDER
358
static DNSServiceRef DNSSDMaster = NULL;
359
#elif defined(HAVE_AVAHI)
360
static AvahiThreadedPoll *DNSSDMaster = NULL;
361
static AvahiClient *DNSSDClient = NULL;
362
#endif /* HAVE_MDNSRESPONDER */
363
364
static int KeepFiles = 0, /* Keep spooled job files? */
365
MaxVersion = 20,/* Maximum IPP version (20 = 2.0, 11 = 1.1, etc.) */
366
Verbosity = 0; /* Verbosity level */
367
static const char *PAMService = NULL;
368
/* PAM service */
369
#ifndef _WIN32
370
static int StopPrinter = 0;/* Stop the printer server? */
371
#endif // !_WIN32
372
373
374
/*
375
* 'main()' - Main entry to the sample server.
376
*/
377
378
int /* O - Exit status */
379
main(int argc, /* I - Number of command-line args */
380
char *argv[]) /* I - Command-line arguments */
381
{
382
int i; /* Looping var */
383
const char *opt, /* Current option character */
384
*attrfile = NULL, /* ippserver attributes file */
385
*command = NULL, /* Command to run with job files */
386
*device_uri = NULL, /* Device URI */
387
*output_format = NULL, /* Output format */
388
*icon = NULL, /* Icon file */
389
#ifdef HAVE_TLS
390
*keypath = NULL, /* Keychain path */
391
#endif /* HAVE_TLS */
392
*location = "", /* Location of printer */
393
*make = "Example", /* Manufacturer */
394
*model = "Printer", /* Model */
395
*name = NULL, /* Printer name */
396
#if !CUPS_LITE
397
*ppdfile = NULL, /* PPD file */
398
#endif /* !CUPS_LITE */
399
*strings = NULL, /* Strings file */
400
*subtypes = "_print"; /* DNS-SD service subtype */
401
int legacy = 0, /* Legacy mode? */
402
duplex = 0, /* Duplex mode */
403
ppm = 10, /* Pages per minute for mono */
404
ppm_color = 0, /* Pages per minute for color */
405
web_forms = 1; /* Enable website forms? */
406
ipp_t *attrs = NULL; /* Printer attributes */
407
char directory[1024] = ""; /* Spool directory */
408
cups_array_t *docformats = NULL; /* Supported formats */
409
const char *servername = NULL; /* Server host name */
410
int serverport = 0; /* Server port number (0 = auto) */
411
ippeve_printer_t *printer; /* Printer object */
412
413
414
/*
415
* Parse command-line arguments...
416
*/
417
418
for (i = 1; i < argc; i ++)
419
{
420
if (!strcmp(argv[i], "--help"))
421
{
422
usage(0);
423
}
424
else if (!strcmp(argv[i], "--no-web-forms"))
425
{
426
web_forms = 0;
427
}
428
else if (!strcmp(argv[i], "--pam-service"))
429
{
430
i ++;
431
if (i >= argc)
432
usage(1);
433
434
PAMService = argv[i];
435
}
436
else if (!strcmp(argv[i], "--version"))
437
{
438
puts(CUPS_SVERSION);
439
return (0);
440
}
441
else if (!strncmp(argv[i], "--", 2))
442
{
443
_cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."), argv[0], argv[i]);
444
usage(1);
445
}
446
else if (argv[i][0] == '-')
447
{
448
for (opt = argv[i] + 1; *opt; opt ++)
449
{
450
switch (*opt)
451
{
452
case '2' : /* -2 (enable 2-sided printing) */
453
duplex = 1;
454
legacy = 1;
455
break;
456
457
case 'A' : /* -A (enable authentication) */
458
if (!PAMService)
459
PAMService = "cups";
460
break;
461
462
case 'D' : /* -D device-uri */
463
i ++;
464
if (i >= argc)
465
usage(1);
466
467
device_uri = argv[i];
468
break;
469
470
case 'F' : /* -F output/format */
471
i ++;
472
if (i >= argc)
473
usage(1);
474
475
output_format = argv[i];
476
break;
477
478
#ifdef HAVE_TLS
479
case 'K' : /* -K keypath */
480
i ++;
481
if (i >= argc)
482
usage(1);
483
484
keypath = argv[i];
485
break;
486
#endif /* HAVE_TLS */
487
488
case 'M' : /* -M manufacturer */
489
i ++;
490
if (i >= argc)
491
usage(1);
492
493
make = argv[i];
494
legacy = 1;
495
break;
496
497
#if !CUPS_LITE
498
case 'P' : /* -P filename.ppd */
499
i ++;
500
if (i >= argc)
501
usage(1);
502
503
ppdfile = argv[i];
504
break;
505
#endif /* !CUPS_LITE */
506
507
case 'S' : /* -S filename.strings */
508
i ++;
509
if (i >= argc)
510
usage(1);
511
512
strings = argv[i];
513
break;
514
515
case 'V' : /* -V max-version */
516
i ++;
517
if (i >= argc)
518
usage(1);
519
520
if (!strcmp(argv[i], "2.0"))
521
MaxVersion = 20;
522
else if (!strcmp(argv[i], "1.1"))
523
MaxVersion = 11;
524
else
525
usage(1);
526
break;
527
528
case 'a' : /* -a attributes-file */
529
i ++;
530
if (i >= argc)
531
usage(1);
532
533
attrfile = argv[i];
534
break;
535
536
case 'c' : /* -c command */
537
i ++;
538
if (i >= argc)
539
usage(1);
540
541
command = argv[i];
542
break;
543
544
case 'd' : /* -d spool-directory */
545
i ++;
546
if (i >= argc)
547
usage(1);
548
549
strlcpy(directory, argv[i], sizeof(directory));
550
break;
551
552
case 'f' : /* -f type/subtype[,...] */
553
i ++;
554
if (i >= argc)
555
usage(1);
556
557
docformats = _cupsArrayNewStrings(argv[i], ',');
558
legacy = 1;
559
break;
560
561
case 'i' : /* -i icon.png */
562
i ++;
563
if (i >= argc)
564
usage(1);
565
566
icon = argv[i];
567
break;
568
569
case 'k' : /* -k (keep files) */
570
KeepFiles = 1;
571
break;
572
573
case 'l' : /* -l location */
574
i ++;
575
if (i >= argc)
576
usage(1);
577
578
location = argv[i];
579
break;
580
581
case 'm' : /* -m model */
582
i ++;
583
if (i >= argc)
584
usage(1);
585
586
model = argv[i];
587
legacy = 1;
588
break;
589
590
case 'n' : /* -n hostname */
591
i ++;
592
if (i >= argc)
593
usage(1);
594
595
servername = argv[i];
596
break;
597
598
case 'p' : /* -p port */
599
i ++;
600
if (i >= argc || !isdigit(argv[i][0] & 255))
601
usage(1);
602
603
serverport = atoi(argv[i]);
604
break;
605
606
case 'r' : /* -r subtype */
607
i ++;
608
if (i >= argc)
609
usage(1);
610
611
subtypes = argv[i];
612
break;
613
614
case 's' : /* -s speed[,color-speed] */
615
i ++;
616
if (i >= argc)
617
usage(1);
618
619
if (sscanf(argv[i], "%d,%d", &ppm, &ppm_color) < 1)
620
usage(1);
621
622
legacy = 1;
623
break;
624
625
case 'v' : /* -v (be verbose) */
626
Verbosity ++;
627
break;
628
629
default : /* Unknown */
630
_cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."), argv[0], *opt);
631
usage(1);
632
}
633
}
634
}
635
else if (!name)
636
{
637
name = argv[i];
638
}
639
else
640
{
641
_cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."), argv[0], argv[i]);
642
usage(1);
643
}
644
}
645
646
if (!name)
647
usage(1);
648
649
#if CUPS_LITE
650
if (attrfile != NULL && legacy)
651
usage(1);
652
#else
653
if (((ppdfile != NULL) + (attrfile != NULL) + legacy) > 1)
654
usage(1);
655
#endif /* CUPS_LITE */
656
657
/*
658
* Apply defaults as needed...
659
*/
660
661
if (!directory[0])
662
{
663
const char *tmpdir; /* Temporary directory */
664
665
#ifdef _WIN32
666
if ((tmpdir = getenv("TEMP")) == NULL)
667
tmpdir = "C:/TEMP";
668
#elif defined(__APPLE__) && TARGET_OS_OSX
669
if ((tmpdir = getenv("TMPDIR")) == NULL)
670
tmpdir = "/private/tmp";
671
#else
672
if ((tmpdir = getenv("TMPDIR")) == NULL)
673
tmpdir = "/tmp";
674
#endif /* _WIN32 */
675
676
snprintf(directory, sizeof(directory), "%s/ippeveprinter.%d", tmpdir, (int)getpid());
677
678
if (mkdir(directory, 0755) && errno != EEXIST)
679
{
680
_cupsLangPrintf(stderr, _("Unable to create spool directory \"%s\": %s"), directory, strerror(errno));
681
usage(1);
682
}
683
684
if (Verbosity)
685
_cupsLangPrintf(stderr, _("Using spool directory \"%s\"."), directory);
686
}
687
688
/*
689
* Initialize DNS-SD...
690
*/
691
692
dnssd_init();
693
694
/*
695
* Create the printer...
696
*/
697
698
if (!docformats)
699
docformats = _cupsArrayNewStrings(ppm_color > 0 ? "image/jpeg,image/pwg-raster,image/urf": "image/pwg-raster,image/urf", ',');
700
701
if (attrfile)
702
attrs = load_ippserver_attributes(servername, serverport, attrfile, docformats);
703
#if !CUPS_LITE
704
else if (ppdfile)
705
{
706
attrs = load_ppd_attributes(ppdfile, docformats);
707
708
if (!command)
709
command = "ippeveps";
710
711
if (!output_format)
712
output_format = "application/postscript";
713
}
714
#endif /* !CUPS_LITE */
715
else
716
attrs = load_legacy_attributes(make, model, ppm, ppm_color, duplex, docformats);
717
718
if ((printer = create_printer(servername, serverport, name, location, icon, strings, docformats, subtypes, directory, command, device_uri, output_format, attrs)) == NULL)
719
return (1);
720
721
printer->web_forms = web_forms;
722
723
#if !CUPS_LITE
724
if (ppdfile)
725
printer->ppdfile = strdup(ppdfile);
726
#endif /* !CUPS_LITE */
727
728
#ifdef HAVE_TLS
729
cupsSetServerCredentials(keypath, printer->hostname, 1);
730
#endif /* HAVE_TLS */
731
732
/*
733
* Run the print service...
734
*/
735
736
run_printer(printer);
737
738
/*
739
* Destroy the printer and exit...
740
*/
741
742
delete_printer(printer);
743
744
return (0);
745
}
746
747
748
/*
749
* 'authenticate_request()' - Try to authenticate the request.
750
*/
751
752
static http_status_t /* O - HTTP_STATUS_CONTINUE to keep going, otherwise status to return */
753
authenticate_request(
754
ippeve_client_t *client) /* I - Client */
755
{
756
#if HAVE_LIBPAM
757
/*
758
* If PAM isn't enabled, return 'continue' now...
759
*/
760
761
const char *authorization; /* Pointer into Authorization string */
762
int userlen; /* Username:password length */
763
pam_handle_t *pamh; /* PAM authentication handle */
764
int pamerr; /* PAM error code */
765
struct pam_conv pamdata; /* PAM conversation data */
766
ippeve_authdata_t data; /* Authentication data */
767
768
769
if (!PAMService)
770
return (HTTP_STATUS_CONTINUE);
771
772
/*
773
* Try authenticating using PAM...
774
*/
775
776
authorization = httpGetField(client->http, HTTP_FIELD_AUTHORIZATION);
777
778
if (!*authorization)
779
return (HTTP_STATUS_UNAUTHORIZED);
780
781
if (strncmp(authorization, "Basic ", 6))
782
{
783
fputs("Unsupported scheme in Authorization header.\n", stderr);
784
return (HTTP_STATUS_BAD_REQUEST);
785
}
786
787
authorization += 5;
788
while (isspace(*authorization & 255))
789
authorization ++;
790
791
userlen = sizeof(data.username);
792
httpDecode64_2(data.username, &userlen, authorization);
793
794
if ((data.password = strchr(data.username, ':')) == NULL)
795
{
796
fputs("No password in Authorization header.\n", stderr);
797
return (HTTP_STATUS_BAD_REQUEST);
798
}
799
800
*(data.password)++ = '\0';
801
802
if (!data.username[0])
803
{
804
fputs("No username in Authorization header.\n", stderr);
805
return (HTTP_STATUS_BAD_REQUEST);
806
}
807
808
pamdata.conv = pam_func;
809
pamdata.appdata_ptr = &data;
810
811
if ((pamerr = pam_start(PAMService, data.username, &pamdata, &pamh)) != PAM_SUCCESS)
812
{
813
fprintf(stderr, "pam_start() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
814
return (HTTP_STATUS_SERVER_ERROR);
815
}
816
817
if ((pamerr = pam_authenticate(pamh, PAM_SILENT)) != PAM_SUCCESS)
818
{
819
fprintf(stderr, "pam_authenticate() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
820
pam_end(pamh, 0);
821
return (HTTP_STATUS_UNAUTHORIZED);
822
}
823
824
if ((pamerr = pam_acct_mgmt(pamh, PAM_SILENT)) != PAM_SUCCESS)
825
{
826
fprintf(stderr, "pam_acct_mgmt() returned %d (%s)\n", pamerr, pam_strerror(pamh, pamerr));
827
pam_end(pamh, 0);
828
return (HTTP_STATUS_SERVER_ERROR);
829
}
830
831
strlcpy(client->username, data.username, sizeof(client->username));
832
833
pam_end(pamh, PAM_SUCCESS);
834
835
return (HTTP_STATUS_CONTINUE);
836
837
#else
838
/*
839
* No authentication support built-in, return 'continue'...
840
*/
841
842
return (HTTP_STATUS_CONTINUE);
843
#endif /* HAVE_LIBPAM */
844
}
845
846
847
/*
848
* 'clean_jobs()' - Clean out old (completed) jobs.
849
*/
850
851
static void
852
clean_jobs(ippeve_printer_t *printer) /* I - Printer */
853
{
854
ippeve_job_t *job; /* Current job */
855
time_t cleantime; /* Clean time */
856
857
858
if (cupsArrayCount(printer->jobs) == 0)
859
return;
860
861
cleantime = time(NULL) - 60;
862
863
_cupsRWLockWrite(&(printer->rwlock));
864
for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs);
865
job;
866
job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
867
if (job->completed && job->completed < cleantime)
868
{
869
cupsArrayRemove(printer->jobs, job);
870
delete_job(job);
871
}
872
else
873
break;
874
_cupsRWUnlock(&(printer->rwlock));
875
}
876
877
878
/*
879
* 'compare_jobs()' - Compare two jobs.
880
*/
881
882
static int /* O - Result of comparison */
883
compare_jobs(ippeve_job_t *a, /* I - First job */
884
ippeve_job_t *b) /* I - Second job */
885
{
886
return (b->id - a->id);
887
}
888
889
890
/*
891
* 'copy_attributes()' - Copy attributes from one request to another.
892
*/
893
894
static void
895
copy_attributes(ipp_t *to, /* I - Destination request */
896
ipp_t *from, /* I - Source request */
897
cups_array_t *ra, /* I - Requested attributes */
898
ipp_tag_t group_tag, /* I - Group to copy */
899
int quickcopy) /* I - Do a quick copy? */
900
{
901
ippeve_filter_t filter; /* Filter data */
902
903
904
filter.ra = ra;
905
filter.group_tag = group_tag;
906
907
ippCopyAttributes(to, from, quickcopy, (ipp_copycb_t)filter_cb, &filter);
908
}
909
910
911
/*
912
* 'copy_job_attrs()' - Copy job attributes to the response.
913
*/
914
915
static void
916
copy_job_attributes(
917
ippeve_client_t *client, /* I - Client */
918
ippeve_job_t *job, /* I - Job */
919
cups_array_t *ra) /* I - requested-attributes */
920
{
921
copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0);
922
923
if (!ra || cupsArrayFind(ra, "date-time-at-completed"))
924
{
925
if (job->completed)
926
ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-completed", ippTimeToDate(job->completed));
927
else
928
ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-completed");
929
}
930
931
if (!ra || cupsArrayFind(ra, "date-time-at-processing"))
932
{
933
if (job->processing)
934
ippAddDate(client->response, IPP_TAG_JOB, "date-time-at-processing", ippTimeToDate(job->processing));
935
else
936
ippAddOutOfBand(client->response, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-processing");
937
}
938
939
if (!ra || cupsArrayFind(ra, "job-impressions"))
940
ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions", job->impressions);
941
942
if (!ra || cupsArrayFind(ra, "job-impressions-completed"))
943
ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions-completed", job->impcompleted);
944
945
if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
946
ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-printer-up-time", (int)(time(NULL) - client->printer->start_time));
947
948
if (!ra || cupsArrayFind(ra, "job-state"))
949
ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state);
950
951
if (!ra || cupsArrayFind(ra, "job-state-message"))
952
{
953
if (job->message)
954
{
955
ippAddString(client->response, IPP_TAG_JOB, IPP_TAG_TEXT, "job-state-message", NULL, job->message);
956
}
957
else
958
{
959
switch (job->state)
960
{
961
case IPP_JSTATE_PENDING :
962
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job pending.");
963
break;
964
965
case IPP_JSTATE_HELD :
966
if (job->fd >= 0)
967
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job incoming.");
968
else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
969
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job held.");
970
else
971
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job created.");
972
break;
973
974
case IPP_JSTATE_PROCESSING :
975
if (job->cancel)
976
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceling.");
977
else
978
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job printing.");
979
break;
980
981
case IPP_JSTATE_STOPPED :
982
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job stopped.");
983
break;
984
985
case IPP_JSTATE_CANCELED :
986
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job canceled.");
987
break;
988
989
case IPP_JSTATE_ABORTED :
990
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job aborted.");
991
break;
992
993
case IPP_JSTATE_COMPLETED :
994
ippAddString(client->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_TEXT), "job-state-message", NULL, "Job completed.");
995
break;
996
}
997
}
998
}
999
1000
if (!ra || cupsArrayFind(ra, "job-state-reasons"))
1001
{
1002
switch (job->state)
1003
{
1004
case IPP_JSTATE_PENDING :
1005
ippAddString(client->response, IPP_TAG_JOB,
1006
IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1007
NULL, "none");
1008
break;
1009
1010
case IPP_JSTATE_HELD :
1011
if (job->fd >= 0)
1012
ippAddString(client->response, IPP_TAG_JOB,
1013
IPP_CONST_TAG(IPP_TAG_KEYWORD),
1014
"job-state-reasons", NULL, "job-incoming");
1015
else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO))
1016
ippAddString(client->response, IPP_TAG_JOB,
1017
IPP_CONST_TAG(IPP_TAG_KEYWORD),
1018
"job-state-reasons", NULL, "job-hold-until-specified");
1019
else
1020
ippAddString(client->response, IPP_TAG_JOB,
1021
IPP_CONST_TAG(IPP_TAG_KEYWORD),
1022
"job-state-reasons", NULL, "job-data-insufficient");
1023
break;
1024
1025
case IPP_JSTATE_PROCESSING :
1026
if (job->cancel)
1027
ippAddString(client->response, IPP_TAG_JOB,
1028
IPP_CONST_TAG(IPP_TAG_KEYWORD),
1029
"job-state-reasons", NULL, "processing-to-stop-point");
1030
else
1031
ippAddString(client->response, IPP_TAG_JOB,
1032
IPP_CONST_TAG(IPP_TAG_KEYWORD),
1033
"job-state-reasons", NULL, "job-printing");
1034
break;
1035
1036
case IPP_JSTATE_STOPPED :
1037
ippAddString(client->response, IPP_TAG_JOB,
1038
IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1039
NULL, "job-stopped");
1040
break;
1041
1042
case IPP_JSTATE_CANCELED :
1043
ippAddString(client->response, IPP_TAG_JOB,
1044
IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1045
NULL, "job-canceled-by-user");
1046
break;
1047
1048
case IPP_JSTATE_ABORTED :
1049
ippAddString(client->response, IPP_TAG_JOB,
1050
IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1051
NULL, "aborted-by-system");
1052
break;
1053
1054
case IPP_JSTATE_COMPLETED :
1055
ippAddString(client->response, IPP_TAG_JOB,
1056
IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-state-reasons",
1057
NULL, "job-completed-successfully");
1058
break;
1059
}
1060
}
1061
1062
if (!ra || cupsArrayFind(ra, "time-at-completed"))
1063
ippAddInteger(client->response, IPP_TAG_JOB,
1064
job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
1065
"time-at-completed", (int)(job->completed - client->printer->start_time));
1066
1067
if (!ra || cupsArrayFind(ra, "time-at-processing"))
1068
ippAddInteger(client->response, IPP_TAG_JOB,
1069
job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE,
1070
"time-at-processing", (int)(job->processing - client->printer->start_time));
1071
}
1072
1073
1074
/*
1075
* 'create_client()' - Accept a new network connection and create a client
1076
* object.
1077
*/
1078
1079
static ippeve_client_t * /* O - Client */
1080
create_client(ippeve_printer_t *printer, /* I - Printer */
1081
int sock) /* I - Listen socket */
1082
{
1083
ippeve_client_t *client; /* Client */
1084
1085
1086
if ((client = calloc(1, sizeof(ippeve_client_t))) == NULL)
1087
{
1088
perror("Unable to allocate memory for client");
1089
return (NULL);
1090
}
1091
1092
client->printer = printer;
1093
1094
/*
1095
* Accept the client and get the remote address...
1096
*/
1097
1098
if ((client->http = httpAcceptConnection(sock, 1)) == NULL)
1099
{
1100
perror("Unable to accept client connection");
1101
1102
free(client);
1103
1104
return (NULL);
1105
}
1106
1107
httpGetHostname(client->http, client->hostname, sizeof(client->hostname));
1108
1109
if (Verbosity)
1110
fprintf(stderr, "Accepted connection from %s\n", client->hostname);
1111
1112
return (client);
1113
}
1114
1115
1116
/*
1117
* 'create_job()' - Create a new job object from a Print-Job or Create-Job
1118
* request.
1119
*/
1120
1121
static ippeve_job_t * /* O - Job */
1122
create_job(ippeve_client_t *client) /* I - Client */
1123
{
1124
ippeve_job_t *job; /* Job */
1125
ipp_attribute_t *attr; /* Job attribute */
1126
char uri[1024], /* job-uri value */
1127
uuid[64]; /* job-uuid value */
1128
1129
1130
_cupsRWLockWrite(&(client->printer->rwlock));
1131
if (client->printer->active_job &&
1132
client->printer->active_job->state < IPP_JSTATE_CANCELED)
1133
{
1134
/*
1135
* Only accept a single job at a time...
1136
*/
1137
1138
_cupsRWUnlock(&(client->printer->rwlock));
1139
return (NULL);
1140
}
1141
1142
/*
1143
* Allocate and initialize the job object...
1144
*/
1145
1146
if ((job = calloc(1, sizeof(ippeve_job_t))) == NULL)
1147
{
1148
perror("Unable to allocate memory for job");
1149
_cupsRWUnlock(&(client->printer->rwlock));
1150
return (NULL);
1151
}
1152
1153
job->printer = client->printer;
1154
job->attrs = ippNew();
1155
job->state = IPP_JSTATE_HELD;
1156
job->fd = -1;
1157
1158
/*
1159
* Copy all of the job attributes...
1160
*/
1161
1162
copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
1163
1164
/*
1165
* Get the requesting-user-name, document format, and priority...
1166
*/
1167
1168
if ((attr = ippFindAttribute(client->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
1169
job->username = ippGetString(attr, 0, NULL);
1170
else
1171
job->username = "anonymous";
1172
1173
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username);
1174
1175
if (ippGetOperation(client->request) != IPP_OP_CREATE_JOB)
1176
{
1177
if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
1178
job->format = ippGetString(attr, 0, NULL);
1179
else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
1180
job->format = ippGetString(attr, 0, NULL);
1181
else
1182
job->format = "application/octet-stream";
1183
}
1184
1185
if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_INTEGER)) != NULL)
1186
job->impressions = ippGetInteger(attr, 0);
1187
1188
if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_NAME)) != NULL)
1189
job->name = ippGetString(attr, 0, NULL);
1190
1191
/*
1192
* Add job description attributes and add to the jobs array...
1193
*/
1194
1195
job->id = client->printer->next_job_id ++;
1196
1197
if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL)
1198
snprintf(uri, sizeof(uri), "%s/%d", ippGetString(attr, 0, NULL), job->id);
1199
else
1200
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, client->printer->hostname, client->printer->port, "/ipp/print/%d", job->id);
1201
1202
httpAssembleUUID(client->printer->hostname, client->printer->port, client->printer->name, job->id, uuid, sizeof(uuid));
1203
1204
ippAddDate(job->attrs, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(time(&job->created)));
1205
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1206
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri);
1207
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid);
1208
if ((attr = ippFindAttribute(client->request, "printer-uri", IPP_TAG_URI)) != NULL)
1209
{
1210
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, ippGetString(attr, 0, NULL));
1211
}
1212
else
1213
{
1214
char printer_uri[1024]; /* job-printer-uri value */
1215
1216
httpAssembleURI(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp", NULL, client->printer->hostname, client->printer->port, "/ipp/print");
1217
ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, printer_uri);
1218
}
1219
1220
ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)(job->created - client->printer->start_time));
1221
1222
cupsArrayAdd(client->printer->jobs, job);
1223
client->printer->active_job = job;
1224
1225
_cupsRWUnlock(&(client->printer->rwlock));
1226
1227
return (job);
1228
}
1229
1230
1231
/*
1232
* 'create_job_file()' - Create a file for the document in a job.
1233
*/
1234
1235
static int /* O - File descriptor or -1 on error */
1236
create_job_file(
1237
ippeve_job_t *job, /* I - Job */
1238
char *fname, /* I - Filename buffer */
1239
size_t fnamesize, /* I - Size of filename buffer */
1240
const char *directory, /* I - Directory to store in */
1241
const char *ext) /* I - Extension (`NULL` for default) */
1242
{
1243
char name[256], /* "Safe" filename */
1244
*nameptr; /* Pointer into filename */
1245
const char *job_name; /* job-name value */
1246
1247
1248
/*
1249
* Make a name from the job-name attribute...
1250
*/
1251
1252
if ((job_name = ippGetString(ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME), 0, NULL)) == NULL)
1253
job_name = "untitled";
1254
1255
for (nameptr = name; *job_name && nameptr < (name + sizeof(name) - 1); job_name ++)
1256
{
1257
if (isalnum(*job_name & 255) || *job_name == '-')
1258
{
1259
*nameptr++ = (char)tolower(*job_name & 255);
1260
}
1261
else
1262
{
1263
*nameptr++ = '_';
1264
1265
while (job_name[1] && !isalnum(job_name[1] & 255) && job_name[1] != '-')
1266
job_name ++;
1267
}
1268
}
1269
1270
*nameptr = '\0';
1271
1272
/*
1273
* Figure out the extension...
1274
*/
1275
1276
if (!ext)
1277
{
1278
if (!strcasecmp(job->format, "image/jpeg"))
1279
ext = "jpg";
1280
else if (!strcasecmp(job->format, "image/png"))
1281
ext = "png";
1282
else if (!strcasecmp(job->format, "image/pwg-raster"))
1283
ext = "pwg";
1284
else if (!strcasecmp(job->format, "image/urf"))
1285
ext = "urf";
1286
else if (!strcasecmp(job->format, "application/pdf"))
1287
ext = "pdf";
1288
else if (!strcasecmp(job->format, "application/postscript"))
1289
ext = "ps";
1290
else if (!strcasecmp(job->format, "application/vnd.hp-pcl"))
1291
ext = "pcl";
1292
else
1293
ext = "dat";
1294
}
1295
1296
/*
1297
* Create a filename with the job-id, job-name, and document-format (extension)...
1298
*/
1299
1300
snprintf(fname, fnamesize, "%s/%d-%s.%s", directory, job->id, name, ext);
1301
1302
return (open(fname, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666));
1303
}
1304
1305
1306
/*
1307
* 'create_listener()' - Create a listener socket.
1308
*/
1309
1310
static int /* O - Listener socket or -1 on error */
1311
create_listener(const char *name, /* I - Host name (`NULL` for any address) */
1312
int port, /* I - Port number */
1313
int family) /* I - Address family */
1314
{
1315
int sock; /* Listener socket */
1316
http_addrlist_t *addrlist; /* Listen address */
1317
char service[255]; /* Service port */
1318
1319
1320
snprintf(service, sizeof(service), "%d", port);
1321
if ((addrlist = httpAddrGetList(name, family, service)) == NULL)
1322
return (-1);
1323
1324
sock = httpAddrListen(&(addrlist->addr), port);
1325
1326
httpAddrFreeList(addrlist);
1327
1328
return (sock);
1329
}
1330
1331
1332
/*
1333
* 'create_media_col()' - Create a media-col value.
1334
*/
1335
1336
static ipp_t * /* O - media-col collection */
1337
create_media_col(const char *media, /* I - Media name */
1338
const char *source, /* I - Media source, if any */
1339
const char *type, /* I - Media type, if any */
1340
int width, /* I - x-dimension in 2540ths */
1341
int length, /* I - y-dimension in 2540ths */
1342
int bottom, /* I - Bottom margin in 2540ths */
1343
int left, /* I - Left margin in 2540ths */
1344
int right, /* I - Right margin in 2540ths */
1345
int top) /* I - Top margin in 2540ths */
1346
{
1347
ipp_t *media_col = ippNew(), /* media-col value */
1348
*media_size = create_media_size(width, length);
1349
/* media-size value */
1350
char media_key[256]; /* media-key value */
1351
const char *media_key_suffix = ""; /* media-key suffix */
1352
1353
1354
if (bottom == 0 && left == 0 && right == 0 && top == 0)
1355
media_key_suffix = "_borderless";
1356
1357
if (type && source)
1358
snprintf(media_key, sizeof(media_key), "%s_%s_%s%s", media, source, type, media_key_suffix);
1359
else if (type)
1360
snprintf(media_key, sizeof(media_key), "%s__%s%s", media, type, media_key_suffix);
1361
else if (source)
1362
snprintf(media_key, sizeof(media_key), "%s_%s%s", media, source, media_key_suffix);
1363
else
1364
snprintf(media_key, sizeof(media_key), "%s%s", media, media_key_suffix);
1365
1366
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL, media_key);
1367
ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
1368
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-size-name", NULL, media);
1369
if (bottom >= 0)
1370
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin", bottom);
1371
if (left >= 0)
1372
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin", left);
1373
if (right >= 0)
1374
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin", right);
1375
if (top >= 0)
1376
ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin", top);
1377
if (source)
1378
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source", NULL, source);
1379
if (type)
1380
ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type", NULL, type);
1381
1382
ippDelete(media_size);
1383
1384
return (media_col);
1385
}
1386
1387
1388
/*
1389
* 'create_media_size()' - Create a media-size value.
1390
*/
1391
1392
static ipp_t * /* O - media-col collection */
1393
create_media_size(int width, /* I - x-dimension in 2540ths */
1394
int length) /* I - y-dimension in 2540ths */
1395
{
1396
ipp_t *media_size = ippNew(); /* media-size value */
1397
1398
1399
ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", width);
1400
ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", length);
1401
1402
return (media_size);
1403
}
1404
1405
1406
/*
1407
* 'create_printer()' - Create, register, and listen for connections to a
1408
* printer object.
1409
*/
1410
1411
static ippeve_printer_t * /* O - Printer */
1412
create_printer(
1413
const char *servername, /* I - Server hostname (NULL for default) */
1414
int serverport, /* I - Server port */
1415
const char *name, /* I - printer-name */
1416
const char *location, /* I - printer-location */
1417
const char *icons, /* I - printer-icons */
1418
const char *strings, /* I - printer-strings-uri */
1419
cups_array_t *docformats, /* I - document-format-supported */
1420
const char *subtypes, /* I - DNS-SD service subtype(s) */
1421
const char *directory, /* I - Spool directory */
1422
const char *command, /* I - Command to run on job files, if any */
1423
const char *device_uri, /* I - Output device, if any */
1424
const char *output_format, /* I - Output format, if any */
1425
ipp_t *attrs) /* I - Capability attributes */
1426
{
1427
ippeve_printer_t *printer; /* Printer */
1428
int i; /* Looping var */
1429
#ifndef _WIN32
1430
char path[1024]; /* Full path to command */
1431
#endif /* !_WIN32 */
1432
unsigned char sha256[32]; /* SHA-256 digest/sum */
1433
char uuid_data[1024],/* Data to hash for printer-uuid */
1434
uuid[128], /* printer-uuid */
1435
*iconsptr; /* Pointer into icons string */
1436
int k_supported; /* Maximum file size supported */
1437
int num_formats; /* Number of supported document formats */
1438
const char *formats[100], /* Supported document formats */
1439
*format; /* Current format */
1440
int num_sup_attrs; /* Number of supported attributes */
1441
const char *sup_attrs[100];/* Supported attributes */
1442
char xxx_supported[256];
1443
/* Name of -supported attribute */
1444
_cups_globals_t *cg = _cupsGlobals();
1445
/* Global path values */
1446
#ifdef HAVE_STATVFS
1447
struct statvfs spoolinfo; /* FS info for spool directory */
1448
double spoolsize; /* FS size */
1449
#elif defined(HAVE_STATFS)
1450
struct statfs spoolinfo; /* FS info for spool directory */
1451
double spoolsize; /* FS size */
1452
#endif /* HAVE_STATVFS */
1453
static const char * const versions[] =/* ipp-versions-supported values */
1454
{
1455
"1.1",
1456
"2.0"
1457
};
1458
static const char * const features[] =/* ipp-features-supported values */
1459
{
1460
"ipp-everywhere"
1461
};
1462
static const int ops[] = /* operations-supported values */
1463
{
1464
IPP_OP_PRINT_JOB,
1465
IPP_OP_PRINT_URI,
1466
IPP_OP_VALIDATE_JOB,
1467
IPP_OP_CREATE_JOB,
1468
IPP_OP_SEND_DOCUMENT,
1469
IPP_OP_SEND_URI,
1470
IPP_OP_CANCEL_JOB,
1471
IPP_OP_GET_JOB_ATTRIBUTES,
1472
IPP_OP_GET_JOBS,
1473
IPP_OP_GET_PRINTER_ATTRIBUTES,
1474
IPP_OP_CANCEL_MY_JOBS,
1475
IPP_OP_CLOSE_JOB,
1476
IPP_OP_IDENTIFY_PRINTER
1477
};
1478
static const char * const charsets[] =/* charset-supported values */
1479
{
1480
"us-ascii",
1481
"utf-8"
1482
};
1483
static const char * const compressions[] =/* compression-supported values */
1484
{
1485
#ifdef HAVE_LIBZ
1486
"deflate",
1487
"gzip",
1488
#endif /* HAVE_LIBZ */
1489
"none"
1490
};
1491
static const char * const identify_actions[] =
1492
{
1493
"display",
1494
"sound"
1495
};
1496
static const char * const job_creation[] =
1497
{ /* job-creation-attributes-supported values */
1498
"copies",
1499
"document-access",
1500
"document-charset",
1501
"document-format",
1502
"document-message",
1503
"document-metadata",
1504
"document-name",
1505
"document-natural-language",
1506
"document-password",
1507
"finishings",
1508
"finishings-col",
1509
"ipp-attribute-fidelity",
1510
"job-account-id",
1511
"job-account-type",
1512
"job-accouunting-sheets",
1513
"job-accounting-user-id",
1514
"job-authorization-uri",
1515
"job-error-action",
1516
"job-error-sheet",
1517
"job-hold-until",
1518
"job-hold-until-time",
1519
"job-mandatory-attributes",
1520
"job-message-to-operator",
1521
"job-name",
1522
"job-pages-per-set",
1523
"job-password",
1524
"job-password-encryption",
1525
"job-phone-number",
1526
"job-priority",
1527
"job-recipient-name",
1528
"job-resource-ids",
1529
"job-sheet-message",
1530
"job-sheets",
1531
"job-sheets-col",
1532
"media",
1533
"media-col",
1534
"multiple-document-handling",
1535
"number-up",
1536
"orientation-requested",
1537
"output-bin",
1538
"output-device",
1539
"overrides",
1540
"page-delivery",
1541
"page-ranges",
1542
"presentation-direction-number-up",
1543
"print-color-mode",
1544
"print-content-optimize",
1545
"print-quality",
1546
"print-rendering-intent",
1547
"print-scaling",
1548
"printer-resolution",
1549
"proof-print",
1550
"separator-sheets",
1551
"sides",
1552
"x-image-position",
1553
"x-image-shift",
1554
"x-side1-image-shift",
1555
"x-side2-image-shift",
1556
"y-image-position",
1557
"y-image-shift",
1558
"y-side1-image-shift",
1559
"y-side2-image-shift"
1560
};
1561
static const char * const media_col_supported[] =
1562
{ /* media-col-supported values */
1563
"media-bottom-margin",
1564
"media-left-margin",
1565
"media-right-margin",
1566
"media-size",
1567
"media-size-name",
1568
"media-source",
1569
"media-top-margin",
1570
"media-type"
1571
};
1572
static const char * const multiple_document_handling[] =
1573
{ /* multiple-document-handling-supported values */
1574
"separate-documents-uncollated-copies",
1575
"separate-documents-collated-copies"
1576
};
1577
static const char * const reference_uri_schemes_supported[] =
1578
{ /* reference-uri-schemes-supported */
1579
"file",
1580
"ftp",
1581
"http"
1582
#ifdef HAVE_TLS
1583
, "https"
1584
#endif /* HAVE_TLS */
1585
};
1586
#ifdef HAVE_TLS
1587
static const char * const uri_authentication_supported[] =
1588
{ /* uri-authentication-supported values */
1589
"none",
1590
"none"
1591
};
1592
static const char * const uri_authentication_basic[] =
1593
{ /* uri-authentication-supported values with authentication */
1594
"basic",
1595
"basic"
1596
};
1597
static const char * const uri_security_supported[] =
1598
{ /* uri-security-supported values */
1599
"none",
1600
"tls"
1601
};
1602
#endif /* HAVE_TLS */
1603
static const char * const which_jobs[] =
1604
{ /* which-jobs-supported values */
1605
"completed",
1606
"not-completed",
1607
"aborted",
1608
"all",
1609
"canceled",
1610
"pending",
1611
"pending-held",
1612
"processing",
1613
"processing-stopped"
1614
};
1615
1616
1617
#ifndef _WIN32
1618
/*
1619
* If a command was specified, make sure it exists and is executable...
1620
*/
1621
1622
if (command)
1623
{
1624
if (*command == '/' || !strncmp(command, "./", 2))
1625
{
1626
if (access(command, X_OK))
1627
{
1628
_cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno));
1629
return (NULL);
1630
}
1631
}
1632
else
1633
{
1634
snprintf(path, sizeof(path), "%s/command/%s", cg->cups_serverbin, command);
1635
1636
if (access(command, X_OK))
1637
{
1638
_cupsLangPrintf(stderr, _("Unable to execute command \"%s\": %s"), command, strerror(errno));
1639
return (NULL);
1640
}
1641
1642
command = path;
1643
}
1644
}
1645
#endif /* !_WIN32 */
1646
1647
/*
1648
* Allocate memory for the printer...
1649
*/
1650
1651
if ((printer = calloc(1, sizeof(ippeve_printer_t))) == NULL)
1652
{
1653
_cupsLangPrintError(NULL, _("Unable to allocate memory for printer"));
1654
return (NULL);
1655
}
1656
1657
printer->ipv4 = -1;
1658
printer->ipv6 = -1;
1659
printer->name = strdup(name);
1660
printer->dnssd_name = strdup(name);
1661
printer->dnssd_subtypes = subtypes ? strdup(subtypes) : NULL;
1662
printer->command = command ? strdup(command) : NULL;
1663
printer->device_uri = device_uri ? strdup(device_uri) : NULL;
1664
printer->output_format = output_format ? strdup(output_format) : NULL;
1665
printer->directory = strdup(directory);
1666
printer->icons[0] = icons ? strdup(icons) : NULL;
1667
printer->strings = strings ? strdup(strings) : NULL;
1668
printer->port = serverport;
1669
printer->start_time = time(NULL);
1670
printer->config_time = printer->start_time;
1671
printer->state = IPP_PSTATE_IDLE;
1672
printer->state_reasons = IPPEVE_PREASON_NONE;
1673
printer->state_time = printer->start_time;
1674
printer->jobs = cupsArrayNew((cups_array_func_t)compare_jobs, NULL);
1675
printer->next_job_id = 1;
1676
1677
if (printer->icons[0])
1678
{
1679
/*
1680
* Extract up to 3 icons...
1681
*/
1682
1683
for (i = 1, iconsptr = strchr(icons, ','); iconsptr && i < 3; i ++, iconsptr = strchr(iconsptr, ','))
1684
{
1685
*iconsptr++ = '\0';
1686
printer->icons[i] = iconsptr;
1687
}
1688
1689
if (iconsptr)
1690
*iconsptr = '\0'; /* Strip any icons after the third... */
1691
1692
while (i < 3)
1693
{
1694
printer->icons[i] = printer->icons[i - 1];
1695
i ++;
1696
}
1697
}
1698
1699
if (servername)
1700
{
1701
printer->hostname = strdup(servername);
1702
}
1703
else
1704
{
1705
char temp[1024], /* Temporary string */
1706
*tempptr; /* Pointer into temporary string */
1707
1708
#ifdef HAVE_AVAHI
1709
const char *avahi_name = avahi_client_get_host_name_fqdn(DNSSDClient);
1710
1711
if (avahi_name)
1712
strlcpy(temp, avahi_name, sizeof(temp));
1713
else
1714
#endif /* HAVE_AVAHI */
1715
1716
if ((tempptr = strstr(httpGetHostname(NULL, temp, sizeof(temp)), ".lan")) != NULL && !tempptr[5])
1717
strlcpy(tempptr, ".local", sizeof(temp) - (size_t)(tempptr - temp));
1718
1719
printer->hostname = strdup(temp);
1720
}
1721
1722
_cupsRWInit(&(printer->rwlock));
1723
1724
/*
1725
* Create the listener sockets...
1726
*/
1727
1728
if (printer->port)
1729
{
1730
if ((printer->ipv4 = create_listener(servername, printer->port, AF_INET)) < 0)
1731
{
1732
perror("Unable to create IPv4 listener");
1733
goto bad_printer;
1734
}
1735
}
1736
else
1737
{
1738
#ifdef _WIN32
1739
/*
1740
* Windows is almost always used as a single user system, so use a default
1741
* port number of 8631.
1742
*/
1743
1744
serverport = 8631;
1745
1746
#else
1747
/*
1748
* Use 8000 + UID mod 1000 for the default port number...
1749
*/
1750
1751
serverport = 8000 + ((int)getuid() % 1000);
1752
#endif /* _WIN32 */
1753
1754
while (serverport < 10000)
1755
{
1756
if ((printer->ipv4 = create_listener(servername, serverport, AF_INET)) >= 0)
1757
break;
1758
1759
serverport ++;
1760
}
1761
1762
if (serverport < 10000)
1763
{
1764
_cupsLangPrintf(stderr, _("Listening on port %d."), serverport);
1765
printer->port = serverport;
1766
}
1767
else
1768
{
1769
perror("Unable to create IPv4 listener");
1770
goto bad_printer;
1771
}
1772
}
1773
1774
if ((printer->ipv6 = create_listener(servername, printer->port, AF_INET6)) < 0)
1775
{
1776
perror("Unable to create IPv6 listener");
1777
goto bad_printer;
1778
}
1779
1780
/*
1781
* Prepare values for the printer attributes...
1782
*/
1783
1784
snprintf(uuid_data, sizeof(uuid_data), "_IPPEVEPRINTER_:%s:%d:%s", printer->hostname, printer->port, printer->name);
1785
cupsHashData("sha2-256", (unsigned char *)uuid_data, strlen(uuid_data), sha256, sizeof(sha256));
1786
snprintf(uuid, sizeof(uuid), "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", sha256[0], sha256[1], sha256[3], sha256[4], sha256[5], sha256[6], (sha256[10] & 15) | 0x30, sha256[11], (sha256[15] & 0x3f) | 0x40, sha256[16], sha256[20], sha256[21], sha256[25], sha256[26], sha256[30], sha256[31]);
1787
1788
if (Verbosity)
1789
{
1790
#ifdef HAVE_TLS
1791
fprintf(stderr, "printer-uri-supported=\"ipp://%s:%d/ipp/print\",\"ipps://%s:%d/ipp/print\"\n", printer->hostname, printer->port, printer->hostname, printer->port);
1792
#else
1793
fprintf(stderr, "printer-uri-supported=\"ipp://%s:%d/ipp/print\"\n", printer->hostname, printer->port);
1794
#endif /* HAVE_TLS */
1795
fprintf(stderr, "printer-uuid=\"%s\"\n", uuid);
1796
}
1797
1798
/*
1799
* Get the maximum spool size based on the size of the filesystem used for
1800
* the spool directory. If the host OS doesn't support the statfs call
1801
* or the filesystem is larger than 2TiB, always report INT_MAX.
1802
*/
1803
1804
#ifdef HAVE_STATVFS
1805
if (statvfs(printer->directory, &spoolinfo))
1806
k_supported = INT_MAX;
1807
else if ((spoolsize = (double)spoolinfo.f_frsize *
1808
spoolinfo.f_blocks / 1024) > INT_MAX)
1809
k_supported = INT_MAX;
1810
else
1811
k_supported = (int)spoolsize;
1812
1813
#elif defined(HAVE_STATFS)
1814
if (statfs(printer->directory, &spoolinfo))
1815
k_supported = INT_MAX;
1816
else if ((spoolsize = (double)spoolinfo.f_bsize *
1817
spoolinfo.f_blocks / 1024) > INT_MAX)
1818
k_supported = INT_MAX;
1819
else
1820
k_supported = (int)spoolsize;
1821
1822
#else
1823
k_supported = INT_MAX;
1824
#endif /* HAVE_STATVFS */
1825
1826
/*
1827
* Assemble the final list of document formats...
1828
*/
1829
1830
if (!cupsArrayFind(docformats, (void *)"application/octet-stream"))
1831
cupsArrayAdd(docformats, (void *)"application/octet-stream");
1832
1833
for (num_formats = 0, format = (const char *)cupsArrayFirst(docformats); format && num_formats < (int)(sizeof(formats) / sizeof(formats[0])); format = (const char *)cupsArrayNext(docformats))
1834
formats[num_formats ++] = format;
1835
1836
/*
1837
* Get the list of attributes that can be used when creating a job...
1838
*/
1839
1840
num_sup_attrs = 0;
1841
sup_attrs[num_sup_attrs ++] = "document-access";
1842
sup_attrs[num_sup_attrs ++] = "document-charset";
1843
sup_attrs[num_sup_attrs ++] = "document-format";
1844
sup_attrs[num_sup_attrs ++] = "document-message";
1845
sup_attrs[num_sup_attrs ++] = "document-metadata";
1846
sup_attrs[num_sup_attrs ++] = "document-name";
1847
sup_attrs[num_sup_attrs ++] = "document-natural-language";
1848
sup_attrs[num_sup_attrs ++] = "ipp-attribute-fidelity";
1849
sup_attrs[num_sup_attrs ++] = "job-name";
1850
sup_attrs[num_sup_attrs ++] = "job-priority";
1851
1852
for (i = 0; i < (int)(sizeof(job_creation) / sizeof(job_creation[0])) && num_sup_attrs < (int)(sizeof(sup_attrs) / sizeof(sup_attrs[0])); i ++)
1853
{
1854
snprintf(xxx_supported, sizeof(xxx_supported), "%s-supported", job_creation[i]);
1855
if (ippFindAttribute(attrs, xxx_supported, IPP_TAG_ZERO))
1856
sup_attrs[num_sup_attrs ++] = job_creation[i];
1857
}
1858
1859
/*
1860
* Fill out the rest of the printer attributes.
1861
*/
1862
1863
printer->attrs = attrs;
1864
1865
/* charset-configured */
1866
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-configured", NULL, "utf-8");
1867
1868
/* charset-supported */
1869
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_CHARSET), "charset-supported", sizeof(charsets) / sizeof(charsets[0]), NULL, charsets);
1870
1871
/* compression-supported */
1872
if (!ippFindAttribute(printer->attrs, "compression-supported", IPP_TAG_ZERO))
1873
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "compression-supported", (int)(sizeof(compressions) / sizeof(compressions[0])), NULL, compressions);
1874
1875
/* document-format-default */
1876
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_MIMETYPE), "document-format-default", NULL, "application/octet-stream");
1877
1878
/* document-format-supported */
1879
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, "document-format-supported", num_formats, NULL, formats);
1880
1881
/* generated-natural-language-supported */
1882
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "generated-natural-language-supported", NULL, "en");
1883
1884
/* identify-actions-default */
1885
ippAddString (printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-default", NULL, "sound");
1886
1887
/* identify-actions-supported */
1888
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "identify-actions-supported", sizeof(identify_actions) / sizeof(identify_actions[0]), NULL, identify_actions);
1889
1890
/* ipp-features-supported */
1891
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-features-supported", sizeof(features) / sizeof(features[0]), NULL, features);
1892
1893
/* ipp-versions-supported */
1894
if (MaxVersion == 11)
1895
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", NULL, "1.1");
1896
else
1897
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "ipp-versions-supported", (int)(sizeof(versions) / sizeof(versions[0])), NULL, versions);
1898
1899
/* job-creation-attributes-supported */
1900
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "job-creation-attributes-supported", num_sup_attrs, NULL, sup_attrs);
1901
1902
/* job-ids-supported */
1903
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-ids-supported", 1);
1904
1905
/* job-k-octets-supported */
1906
ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0, k_supported);
1907
1908
/* job-priority-default */
1909
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-default", 50);
1910
1911
/* job-priority-supported */
1912
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "job-priority-supported", 1);
1913
1914
/* job-sheets-default */
1915
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-default", NULL, "none");
1916
1917
/* job-sheets-supported */
1918
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "job-sheets-supported", NULL, "none");
1919
1920
/* media-col-supported */
1921
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-col-supported", (int)(sizeof(media_col_supported) / sizeof(media_col_supported[0])), NULL, media_col_supported);
1922
1923
/* multiple-document-handling-supported */
1924
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-document-handling-supported", sizeof(multiple_document_handling) / sizeof(multiple_document_handling[0]), NULL, multiple_document_handling);
1925
1926
/* multiple-document-jobs-supported */
1927
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "multiple-document-jobs-supported", 0);
1928
1929
/* multiple-operation-time-out */
1930
ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "multiple-operation-time-out", 60);
1931
1932
/* multiple-operation-time-out-action */
1933
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "multiple-operation-time-out-action", NULL, "abort-job");
1934
1935
/* natural-language-configured */
1936
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "natural-language-configured", NULL, "en");
1937
1938
/* operations-supported */
1939
ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "operations-supported", sizeof(ops) / sizeof(ops[0]), ops);
1940
1941
/* pdl-override-supported */
1942
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pdl-override-supported", NULL, "attempted");
1943
1944
/* preferred-attributes-supported */
1945
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "preferred-attributes-supported", 0);
1946
1947
/* printer-get-attributes-supported */
1948
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-get-attributes-supported", NULL, "document-format");
1949
1950
/* printer-geo-location */
1951
ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_UNKNOWN, "printer-geo-location");
1952
1953
/* printer-is-accepting-jobs */
1954
ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
1955
1956
/* printer-info */
1957
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, name);
1958
1959
/* printer-location */
1960
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL, location);
1961
1962
/* printer-name */
1963
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", NULL, name);
1964
1965
/* printer-organization */
1966
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organization", NULL, "");
1967
1968
/* printer-organizational-unit */
1969
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-organizational-unit", NULL, "");
1970
1971
/* printer-strings-languages-supported */
1972
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_LANGUAGE, "printer-strings-languages-supported", NULL, "en");
1973
1974
/* printer-uuid */
1975
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uuid", NULL, uuid);
1976
1977
/* reference-uri-scheme-supported */
1978
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_URISCHEME), "reference-uri-schemes-supported", (int)(sizeof(reference_uri_schemes_supported) / sizeof(reference_uri_schemes_supported[0])), NULL, reference_uri_schemes_supported);
1979
1980
/* uri-authentication-supported */
1981
#ifdef HAVE_TLS
1982
if (PAMService)
1983
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_basic);
1984
else
1985
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", 2, NULL, uri_authentication_supported);
1986
#else
1987
if (PAMService)
1988
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "basic");
1989
else
1990
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-authentication-supported", NULL, "none");
1991
#endif /* HAVE_TLS */
1992
1993
/* uri-security-supported */
1994
#ifdef HAVE_TLS
1995
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", 2, NULL, uri_security_supported);
1996
#else
1997
ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "uri-security-supported", NULL, "none");
1998
#endif /* HAVE_TLS */
1999
2000
/* which-jobs-supported */
2001
ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "which-jobs-supported", sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs);
2002
2003
debug_attributes("Printer", printer->attrs, 0);
2004
2005
/*
2006
* Register the printer with DNS-SD...
2007
*/
2008
2009
if (!register_printer(printer))
2010
goto bad_printer;
2011
2012
/*
2013
* Return it!
2014
*/
2015
2016
return (printer);
2017
2018
2019
/*
2020
* If we get here we were unable to create the printer...
2021
*/
2022
2023
bad_printer:
2024
2025
delete_printer(printer);
2026
2027
return (NULL);
2028
}
2029
2030
2031
/*
2032
* 'debug_attributes()' - Print attributes in a request or response.
2033
*/
2034
2035
static void
2036
debug_attributes(const char *title, /* I - Title */
2037
ipp_t *ipp, /* I - Request/response */
2038
int type) /* I - 0 = object, 1 = request, 2 = response */
2039
{
2040
ipp_tag_t group_tag; /* Current group */
2041
ipp_attribute_t *attr; /* Current attribute */
2042
char buffer[2048]; /* String buffer for value */
2043
int major, minor; /* Version */
2044
2045
2046
if (Verbosity <= 1)
2047
return;
2048
2049
fprintf(stderr, "%s:\n", title);
2050
major = ippGetVersion(ipp, &minor);
2051
fprintf(stderr, " version=%d.%d\n", major, minor);
2052
if (type == 1)
2053
fprintf(stderr, " operation-id=%s(%04x)\n",
2054
ippOpString(ippGetOperation(ipp)), ippGetOperation(ipp));
2055
else if (type == 2)
2056
fprintf(stderr, " status-code=%s(%04x)\n",
2057
ippErrorString(ippGetStatusCode(ipp)), ippGetStatusCode(ipp));
2058
fprintf(stderr, " request-id=%d\n\n", ippGetRequestId(ipp));
2059
2060
for (attr = ippFirstAttribute(ipp), group_tag = IPP_TAG_ZERO;
2061
attr;
2062
attr = ippNextAttribute(ipp))
2063
{
2064
if (ippGetGroupTag(attr) != group_tag)
2065
{
2066
group_tag = ippGetGroupTag(attr);
2067
fprintf(stderr, " %s\n", ippTagString(group_tag));
2068
}
2069
2070
if (ippGetName(attr))
2071
{
2072
ippAttributeString(attr, buffer, sizeof(buffer));
2073
fprintf(stderr, " %s (%s%s) %s\n", ippGetName(attr),
2074
ippGetCount(attr) > 1 ? "1setOf " : "",
2075
ippTagString(ippGetValueTag(attr)), buffer);
2076
}
2077
}
2078
}
2079
2080
2081
/*
2082
* 'delete_client()' - Close the socket and free all memory used by a client
2083
* object.
2084
*/
2085
2086
static void
2087
delete_client(ippeve_client_t *client) /* I - Client */
2088
{
2089
if (Verbosity)
2090
fprintf(stderr, "Closing connection from %s\n", client->hostname);
2091
2092
/*
2093
* Flush pending writes before closing...
2094
*/
2095
2096
httpFlushWrite(client->http);
2097
2098
/*
2099
* Free memory...
2100
*/
2101
2102
httpClose(client->http);
2103
2104
ippDelete(client->request);
2105
ippDelete(client->response);
2106
2107
free(client);
2108
}
2109
2110
2111
/*
2112
* 'delete_job()' - Remove from the printer and free all memory used by a job
2113
* object.
2114
*/
2115
2116
static void
2117
delete_job(ippeve_job_t *job) /* I - Job */
2118
{
2119
if (Verbosity)
2120
fprintf(stderr, "[Job %d] Removing job from history.\n", job->id);
2121
2122
ippDelete(job->attrs);
2123
2124
if (job->message)
2125
free(job->message);
2126
2127
if (job->filename)
2128
{
2129
if (!KeepFiles)
2130
unlink(job->filename);
2131
2132
free(job->filename);
2133
}
2134
2135
free(job);
2136
}
2137
2138
2139
/*
2140
* 'delete_printer()' - Unregister, close listen sockets, and free all memory
2141
* used by a printer object.
2142
*/
2143
2144
static void
2145
delete_printer(ippeve_printer_t *printer) /* I - Printer */
2146
{
2147
if (printer->ipv4 >= 0)
2148
close(printer->ipv4);
2149
2150
if (printer->ipv6 >= 0)
2151
close(printer->ipv6);
2152
2153
#if HAVE_MDNSRESPONDER
2154
if (printer->printer_ref)
2155
DNSServiceRefDeallocate(printer->printer_ref);
2156
if (printer->ipp_ref)
2157
DNSServiceRefDeallocate(printer->ipp_ref);
2158
if (printer->ipps_ref)
2159
DNSServiceRefDeallocate(printer->ipps_ref);
2160
if (printer->http_ref)
2161
DNSServiceRefDeallocate(printer->http_ref);
2162
#elif defined(HAVE_AVAHI)
2163
avahi_threaded_poll_lock(DNSSDMaster);
2164
2165
if (printer->dnssd_ref)
2166
avahi_entry_group_free(printer->dnssd_ref);
2167
2168
avahi_threaded_poll_unlock(DNSSDMaster);
2169
#endif /* HAVE_MDNSRESPONDER */
2170
2171
if (printer->dnssd_name)
2172
free(printer->dnssd_name);
2173
if (printer->name)
2174
free(printer->name);
2175
if (printer->icons[0])
2176
free(printer->icons[0]);
2177
if (printer->strings)
2178
free(printer->strings);
2179
if (printer->command)
2180
free(printer->command);
2181
if (printer->device_uri)
2182
free(printer->device_uri);
2183
#if !CUPS_LITE
2184
if (printer->ppdfile)
2185
free(printer->ppdfile);
2186
#endif /* !CUPS_LITE */
2187
if (printer->directory)
2188
free(printer->directory);
2189
if (printer->hostname)
2190
free(printer->hostname);
2191
2192
ippDelete(printer->attrs);
2193
cupsArrayDelete(printer->jobs);
2194
2195
free(printer);
2196
}
2197
2198
2199
#ifdef HAVE_MDNSRESPONDER
2200
/*
2201
* 'dnssd_callback()' - Handle DNS-SD registration events.
2202
*/
2203
2204
static void DNSSD_API
2205
dnssd_callback(
2206
DNSServiceRef sdRef, /* I - Service reference */
2207
DNSServiceFlags flags, /* I - Status flags */
2208
DNSServiceErrorType errorCode, /* I - Error, if any */
2209
const char *name, /* I - Service name */
2210
const char *regtype, /* I - Service type */
2211
const char *domain, /* I - Domain for service */
2212
ippeve_printer_t *printer) /* I - Printer */
2213
{
2214
(void)sdRef;
2215
(void)flags;
2216
(void)domain;
2217
(void)name;
2218
2219
if (errorCode == kDNSServiceErr_NameConflict)
2220
{
2221
fputs("DNS-SD service name collision detected.\n", stderr);
2222
printer->dnssd_collision = 1;
2223
}
2224
else if (errorCode)
2225
{
2226
fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n", regtype, (int)errorCode);
2227
return;
2228
}
2229
}
2230
2231
2232
#elif defined(HAVE_AVAHI)
2233
/*
2234
* 'dnssd_callback()' - Handle DNS-SD registration events.
2235
*/
2236
2237
static void
2238
dnssd_callback(
2239
AvahiEntryGroup *srv, /* I - Service */
2240
AvahiEntryGroupState state, /* I - Registration state */
2241
void *context) /* I - Printer */
2242
{
2243
ippeve_printer_t *printer = (ippeve_printer_t *)context;
2244
/* Printer */
2245
2246
2247
(void)srv;
2248
2249
if (state == AVAHI_ENTRY_GROUP_COLLISION)
2250
{
2251
fputs("DNS-SD service name collision detected.\n", stderr);
2252
printer->dnssd_collision = 1;
2253
}
2254
}
2255
2256
2257
/*
2258
* 'dnssd_client_cb()' - Client callback for Avahi.
2259
*
2260
* Called whenever the client or server state changes...
2261
*/
2262
2263
static void
2264
dnssd_client_cb(
2265
AvahiClient *c, /* I - Client */
2266
AvahiClientState state, /* I - Current state */
2267
void *userdata) /* I - User data (printer) */
2268
{
2269
if (!c)
2270
return;
2271
2272
switch (state)
2273
{
2274
default :
2275
fprintf(stderr, "Ignored Avahi state %d.\n", state);
2276
break;
2277
2278
case AVAHI_CLIENT_FAILURE:
2279
if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
2280
{
2281
fputs("Avahi server crashed, exiting.\n", stderr);
2282
exit(1);
2283
}
2284
break;
2285
}
2286
}
2287
#endif /* HAVE_MDNSRESPONDER */
2288
2289
2290
/*
2291
* 'dnssd_init()' - Initialize the DNS-SD service connections...
2292
*/
2293
2294
static void
2295
dnssd_init(void)
2296
{
2297
#ifdef HAVE_MDNSRESPONDER
2298
if (DNSServiceCreateConnection(&DNSSDMaster) != kDNSServiceErr_NoError)
2299
{
2300
fputs("Error: Unable to initialize DNS-SD.\n", stderr);
2301
exit(1);
2302
}
2303
2304
#elif defined(HAVE_AVAHI)
2305
int error; /* Error code, if any */
2306
2307
if ((DNSSDMaster = avahi_threaded_poll_new()) == NULL)
2308
{
2309
fputs("Error: Unable to initialize DNS-SD.\n", stderr);
2310
exit(1);
2311
}
2312
2313
if ((DNSSDClient = avahi_client_new(avahi_threaded_poll_get(DNSSDMaster), AVAHI_CLIENT_NO_FAIL, dnssd_client_cb, NULL, &error)) == NULL)
2314
{
2315
fputs("Error: Unable to initialize DNS-SD.\n", stderr);
2316
exit(1);
2317
}
2318
2319
avahi_threaded_poll_start(DNSSDMaster);
2320
#endif /* HAVE_MDNSRESPONDER */
2321
}
2322
2323
2324
/*
2325
* 'filter_cb()' - Filter printer attributes based on the requested array.
2326
*/
2327
2328
static int /* O - 1 to copy, 0 to ignore */
2329
filter_cb(ippeve_filter_t *filter, /* I - Filter parameters */
2330
ipp_t *dst, /* I - Destination (unused) */
2331
ipp_attribute_t *attr) /* I - Source attribute */
2332
{
2333
/*
2334
* Filter attributes as needed...
2335
*/
2336
2337
#ifndef _WIN32 /* Avoid MS compiler bug */
2338
(void)dst;
2339
#endif /* !_WIN32 */
2340
2341
ipp_tag_t group = ippGetGroupTag(attr);
2342
const char *name = ippGetName(attr);
2343
2344
if ((filter->group_tag != IPP_TAG_ZERO && group != filter->group_tag && group != IPP_TAG_ZERO) || !name || (!strcmp(name, "media-col-database") && !cupsArrayFind(filter->ra, (void *)name)))
2345
return (0);
2346
2347
return (!filter->ra || cupsArrayFind(filter->ra, (void *)name) != NULL);
2348
}
2349
2350
2351
/*
2352
* 'find_job()' - Find a job specified in a request.
2353
*/
2354
2355
static ippeve_job_t * /* O - Job or NULL */
2356
find_job(ippeve_client_t *client) /* I - Client */
2357
{
2358
ipp_attribute_t *attr; /* job-id or job-uri attribute */
2359
ippeve_job_t key, /* Job search key */
2360
*job; /* Matching job, if any */
2361
2362
2363
if ((attr = ippFindAttribute(client->request, "job-uri", IPP_TAG_URI)) != NULL)
2364
{
2365
const char *uri = ippGetString(attr, 0, NULL);
2366
/* URI value */
2367
const char *uriptr = strrchr(uri, '/');
2368
/* Pointer to the last slash in the URI */
2369
2370
if (uriptr && isdigit(uriptr[1] & 255))
2371
key.id = atoi(uriptr + 1);
2372
else
2373
return (NULL);
2374
}
2375
else if ((attr = ippFindAttribute(client->request, "job-id", IPP_TAG_INTEGER)) != NULL)
2376
key.id = ippGetInteger(attr, 0);
2377
2378
_cupsRWLockRead(&(client->printer->rwlock));
2379
job = (ippeve_job_t *)cupsArrayFind(client->printer->jobs, &key);
2380
_cupsRWUnlock(&(client->printer->rwlock));
2381
2382
return (job);
2383
}
2384
2385
2386
/*
2387
* 'finish_document()' - Finish receiving a document file and start processing.
2388
*/
2389
2390
static void
2391
finish_document_data(
2392
ippeve_client_t *client, /* I - Client */
2393
ippeve_job_t *job) /* I - Job */
2394
{
2395
char filename[1024], /* Filename buffer */
2396
buffer[4096]; /* Copy buffer */
2397
ssize_t bytes; /* Bytes read */
2398
cups_array_t *ra; /* Attributes to send in response */
2399
_cups_thread_t t; /* Thread */
2400
2401
2402
/*
2403
* Create a file for the request data...
2404
*
2405
* TODO: Update code to support piping large raster data to the print command.
2406
*/
2407
2408
if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
2409
{
2410
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
2411
2412
goto abort_job;
2413
}
2414
2415
if (Verbosity)
2416
fprintf(stderr, "Created job file \"%s\", format \"%s\".\n", filename, job->format);
2417
2418
while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0)
2419
{
2420
if (write(job->fd, buffer, (size_t)bytes) < bytes)
2421
{
2422
int error = errno; /* Write error */
2423
2424
close(job->fd);
2425
job->fd = -1;
2426
2427
unlink(filename);
2428
2429
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2430
2431
goto abort_job;
2432
}
2433
}
2434
2435
if (bytes < 0)
2436
{
2437
/*
2438
* Got an error while reading the print data, so abort this job.
2439
*/
2440
2441
close(job->fd);
2442
job->fd = -1;
2443
2444
unlink(filename);
2445
2446
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to read print file.");
2447
2448
goto abort_job;
2449
}
2450
2451
if (close(job->fd))
2452
{
2453
int error = errno; /* Write error */
2454
2455
job->fd = -1;
2456
2457
unlink(filename);
2458
2459
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2460
2461
goto abort_job;
2462
}
2463
2464
job->fd = -1;
2465
job->filename = strdup(filename);
2466
job->state = IPP_JSTATE_PENDING;
2467
2468
/*
2469
* Process the job...
2470
*/
2471
2472
t = _cupsThreadCreate((_cups_thread_func_t)process_job, job);
2473
2474
if (t)
2475
{
2476
_cupsThreadDetach(t);
2477
}
2478
else
2479
{
2480
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job.");
2481
goto abort_job;
2482
}
2483
2484
/*
2485
* Return the job info...
2486
*/
2487
2488
respond_ipp(client, IPP_STATUS_OK, NULL);
2489
2490
ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2491
cupsArrayAdd(ra, "job-id");
2492
cupsArrayAdd(ra, "job-state");
2493
cupsArrayAdd(ra, "job-state-message");
2494
cupsArrayAdd(ra, "job-state-reasons");
2495
cupsArrayAdd(ra, "job-uri");
2496
2497
copy_job_attributes(client, job, ra);
2498
cupsArrayDelete(ra);
2499
return;
2500
2501
/*
2502
* If we get here we had to abort the job...
2503
*/
2504
2505
abort_job:
2506
2507
job->state = IPP_JSTATE_ABORTED;
2508
job->completed = time(NULL);
2509
2510
ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2511
cupsArrayAdd(ra, "job-id");
2512
cupsArrayAdd(ra, "job-state");
2513
cupsArrayAdd(ra, "job-state-reasons");
2514
cupsArrayAdd(ra, "job-uri");
2515
2516
copy_job_attributes(client, job, ra);
2517
cupsArrayDelete(ra);
2518
}
2519
2520
2521
/*
2522
* 'finish_uri()' - Finish fetching a document URI and start processing.
2523
*/
2524
2525
static void
2526
finish_document_uri(
2527
ippeve_client_t *client, /* I - Client */
2528
ippeve_job_t *job) /* I - Job */
2529
{
2530
ipp_attribute_t *uri; /* document-uri */
2531
char scheme[256], /* URI scheme */
2532
userpass[256], /* Username and password info */
2533
hostname[256], /* Hostname */
2534
resource[1024]; /* Resource path */
2535
int port; /* Port number */
2536
http_uri_status_t uri_status; /* URI decode status */
2537
http_encryption_t encryption; /* Encryption to use, if any */
2538
http_t *http; /* Connection for http/https URIs */
2539
http_status_t status; /* Access status for http/https URIs */
2540
int infile; /* Input file for local file URIs */
2541
char filename[1024], /* Filename buffer */
2542
buffer[4096]; /* Copy buffer */
2543
ssize_t bytes; /* Bytes read */
2544
ipp_attribute_t *attr; /* Current attribute */
2545
cups_array_t *ra; /* Attributes to send in response */
2546
2547
2548
/*
2549
* Do we have a file to print?
2550
*/
2551
2552
if (have_document_data(client))
2553
{
2554
flush_document_data(client);
2555
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Unexpected document data following request.");
2556
2557
goto abort_job;
2558
}
2559
2560
/*
2561
* Do we have a document URI?
2562
*/
2563
2564
if ((uri = ippFindAttribute(client->request, "document-uri", IPP_TAG_URI)) == NULL)
2565
{
2566
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri.");
2567
2568
goto abort_job;
2569
}
2570
2571
if (ippGetCount(uri) != 1)
2572
{
2573
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Too many document-uri values.");
2574
2575
goto abort_job;
2576
}
2577
2578
uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
2579
scheme, sizeof(scheme), userpass,
2580
sizeof(userpass), hostname, sizeof(hostname),
2581
&port, resource, sizeof(resource));
2582
if (uri_status < HTTP_URI_STATUS_OK)
2583
{
2584
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s", httpURIStatusString(uri_status));
2585
2586
goto abort_job;
2587
}
2588
2589
if (strcmp(scheme, "file") &&
2590
#ifdef HAVE_TLS
2591
strcmp(scheme, "https") &&
2592
#endif /* HAVE_TLS */
2593
strcmp(scheme, "http"))
2594
{
2595
respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME, "URI scheme \"%s\" not supported.", scheme);
2596
2597
goto abort_job;
2598
}
2599
2600
if (!strcmp(scheme, "file") && access(resource, R_OK))
2601
{
2602
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
2603
2604
goto abort_job;
2605
}
2606
2607
/*
2608
* Get the document format for the job...
2609
*/
2610
2611
_cupsRWLockWrite(&(client->printer->rwlock));
2612
2613
if ((attr = ippFindAttribute(job->attrs, "document-format", IPP_TAG_MIMETYPE)) != NULL)
2614
job->format = ippGetString(attr, 0, NULL);
2615
else
2616
job->format = "application/octet-stream";
2617
2618
/*
2619
* Create a file for the request data...
2620
*/
2621
2622
if ((job->fd = create_job_file(job, filename, sizeof(filename), client->printer->directory, NULL)) < 0)
2623
{
2624
_cupsRWUnlock(&(client->printer->rwlock));
2625
2626
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to create print file: %s", strerror(errno));
2627
2628
goto abort_job;
2629
}
2630
2631
_cupsRWUnlock(&(client->printer->rwlock));
2632
2633
if (!strcmp(scheme, "file"))
2634
{
2635
if ((infile = open(resource, O_RDONLY | O_BINARY)) < 0)
2636
{
2637
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to access URI: %s", strerror(errno));
2638
2639
goto abort_job;
2640
}
2641
2642
do
2643
{
2644
if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
2645
(errno == EAGAIN || errno == EINTR))
2646
{
2647
bytes = 1;
2648
}
2649
else if (bytes > 0 && write(job->fd, buffer, (size_t)bytes) < bytes)
2650
{
2651
int error = errno; /* Write error */
2652
2653
close(job->fd);
2654
job->fd = -1;
2655
2656
unlink(filename);
2657
close(infile);
2658
2659
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2660
2661
goto abort_job;
2662
}
2663
}
2664
while (bytes > 0);
2665
2666
close(infile);
2667
}
2668
else
2669
{
2670
#ifdef HAVE_TLS
2671
if (port == 443 || !strcmp(scheme, "https"))
2672
encryption = HTTP_ENCRYPTION_ALWAYS;
2673
else
2674
#endif /* HAVE_TLS */
2675
encryption = HTTP_ENCRYPTION_IF_REQUESTED;
2676
2677
if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL)
2678
{
2679
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to connect to %s: %s", hostname, cupsLastErrorString());
2680
2681
close(job->fd);
2682
job->fd = -1;
2683
2684
unlink(filename);
2685
2686
goto abort_job;
2687
}
2688
2689
httpClearFields(http);
2690
httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
2691
if (httpGet(http, resource))
2692
{
2693
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", strerror(errno));
2694
2695
close(job->fd);
2696
job->fd = -1;
2697
2698
unlink(filename);
2699
httpClose(http);
2700
2701
goto abort_job;
2702
}
2703
2704
while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
2705
2706
if (status != HTTP_STATUS_OK)
2707
{
2708
respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, "Unable to GET URI: %s", httpStatus(status));
2709
2710
close(job->fd);
2711
job->fd = -1;
2712
2713
unlink(filename);
2714
httpClose(http);
2715
2716
goto abort_job;
2717
}
2718
2719
while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
2720
{
2721
if (write(job->fd, buffer, (size_t)bytes) < bytes)
2722
{
2723
int error = errno; /* Write error */
2724
2725
close(job->fd);
2726
job->fd = -1;
2727
2728
unlink(filename);
2729
httpClose(http);
2730
2731
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL,
2732
"Unable to write print file: %s", strerror(error));
2733
2734
goto abort_job;
2735
}
2736
}
2737
2738
httpClose(http);
2739
}
2740
2741
if (close(job->fd))
2742
{
2743
int error = errno; /* Write error */
2744
2745
job->fd = -1;
2746
2747
unlink(filename);
2748
2749
respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to write print file: %s", strerror(error));
2750
2751
goto abort_job;
2752
}
2753
2754
_cupsRWLockWrite(&(client->printer->rwlock));
2755
2756
job->fd = -1;
2757
job->filename = strdup(filename);
2758
job->state = IPP_JSTATE_PENDING;
2759
2760
_cupsRWUnlock(&(client->printer->rwlock));
2761
2762
/*
2763
* Process the job...
2764
*/
2765
2766
process_job(job);
2767
2768
/*
2769
* Return the job info...
2770
*/
2771
2772
respond_ipp(client, IPP_STATUS_OK, NULL);
2773
2774
ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2775
cupsArrayAdd(ra, "job-id");
2776
cupsArrayAdd(ra, "job-state");
2777
cupsArrayAdd(ra, "job-state-reasons");
2778
cupsArrayAdd(ra, "job-uri");
2779
2780
copy_job_attributes(client, job, ra);
2781
cupsArrayDelete(ra);
2782
return;
2783
2784
/*
2785
* If we get here we had to abort the job...
2786
*/
2787
2788
abort_job:
2789
2790
job->state = IPP_JSTATE_ABORTED;
2791
job->completed = time(NULL);
2792
2793
ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
2794
cupsArrayAdd(ra, "job-id");
2795
cupsArrayAdd(ra, "job-state");
2796
cupsArrayAdd(ra, "job-state-reasons");
2797
cupsArrayAdd(ra, "job-uri");
2798
2799
copy_job_attributes(client, job, ra);
2800
cupsArrayDelete(ra);
2801
}
2802
2803
2804
/*
2805
* 'flush_document_data()' - Safely flush remaining document data.
2806
*/
2807
2808
static void
2809
flush_document_data(
2810
ippeve_client_t *client) /* I - Client */
2811
{
2812
char buffer[8192]; /* Read buffer */
2813
2814
2815
if (httpGetState(client->http) == HTTP_STATE_POST_RECV)
2816
{
2817
while (httpRead2(client->http, buffer, sizeof(buffer)) > 0);
2818
}
2819
}
2820
2821
2822
/*
2823
* 'have_document_data()' - Determine whether we have more document data.
2824
*/
2825
2826
static int /* O - 1 if data is present, 0 otherwise */
2827
have_document_data(
2828
ippeve_client_t *client) /* I - Client */
2829
{
2830
char temp; /* Data */
2831
2832
2833
if (httpGetState(client->http) != HTTP_STATE_POST_RECV)
2834
return (0);
2835
else
2836
return (httpPeek(client->http, &temp, 1) > 0);
2837
}
2838
2839
2840
/*
2841
* 'html_escape()' - Write a HTML-safe string.
2842
*/
2843
2844
static void
2845
html_escape(ippeve_client_t *client, /* I - Client */
2846
const char *s, /* I - String to write */
2847
size_t slen) /* I - Number of characters to write */
2848
{
2849
const char *start, /* Start of segment */
2850
*end; /* End of string */
2851
2852
2853
start = s;
2854
end = s + (slen > 0 ? slen : strlen(s));
2855
2856
while (*s && s < end)
2857
{
2858
if (*s == '&' || *s == '<')
2859
{
2860
if (s > start)
2861
httpWrite2(client->http, start, (size_t)(s - start));
2862
2863
if (*s == '&')
2864
httpWrite2(client->http, "&amp;", 5);
2865
else
2866
httpWrite2(client->http, "&lt;", 4);
2867
2868
start = s + 1;
2869
}
2870
2871
s ++;
2872
}
2873
2874
if (s > start)
2875
httpWrite2(client->http, start, (size_t)(s - start));
2876
}
2877
2878
2879
/*
2880
* 'html_footer()' - Show the web interface footer.
2881
*
2882
* This function also writes the trailing 0-length chunk.
2883
*/
2884
2885
static void
2886
html_footer(ippeve_client_t *client) /* I - Client */
2887
{
2888
html_printf(client,
2889
"</div>\n"
2890
"</body>\n"
2891
"</html>\n");
2892
httpWrite2(client->http, "", 0);
2893
}
2894
2895
2896
/*
2897
* 'html_header()' - Show the web interface header and title.
2898
*/
2899
2900
static void
2901
html_header(ippeve_client_t *client, /* I - Client */
2902
const char *title, /* I - Title */
2903
int refresh) /* I - Refresh timer, if any */
2904
{
2905
html_printf(client,
2906
"<!doctype html>\n"
2907
"<html>\n"
2908
"<head>\n"
2909
"<title>%s</title>\n"
2910
"<link rel=\"shortcut icon\" href=\"/icon.png\" type=\"image/png\">\n"
2911
"<link rel=\"apple-touch-icon\" href=\"/icon.png\" type=\"image/png\">\n"
2912
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\">\n", title);
2913
if (refresh > 0)
2914
html_printf(client, "<meta http-equiv=\"refresh\" content=\"%d\">\n", refresh);
2915
html_printf(client,
2916
"<meta name=\"viewport\" content=\"width=device-width\">\n"
2917
"<style>\n"
2918
"body { font-family: sans-serif; margin: 0; }\n"
2919
"div.body { padding: 0px 10px 10px; }\n"
2920
"span.badge { background: #090; border-radius: 5px; color: #fff; padding: 5px 10px; }\n"
2921
"span.bar { box-shadow: 0px 1px 5px #333; font-size: 75%%; }\n"
2922
"table.form { border-collapse: collapse; margin-left: auto; margin-right: auto; margin-top: 10px; width: auto; }\n"
2923
"table.form td, table.form th { padding: 5px 2px; }\n"
2924
"table.form td.meter { border-right: solid 1px #ccc; padding: 0px; width: 400px; }\n"
2925
"table.form th { text-align: right; }\n"
2926
"table.striped { border-bottom: solid thin black; border-collapse: collapse; width: 100%%; }\n"
2927
"table.striped tr:nth-child(even) { background: #fcfcfc; }\n"
2928
"table.striped tr:nth-child(odd) { background: #f0f0f0; }\n"
2929
"table.striped th { background: white; border-bottom: solid thin black; text-align: left; vertical-align: bottom; }\n"
2930
"table.striped td { margin: 0; padding: 5px; vertical-align: top; }\n"
2931
"table.nav { border-collapse: collapse; width: 100%%; }\n"
2932
"table.nav td { margin: 0; text-align: center; }\n"
2933
"td.nav a, td.nav a:active, td.nav a:hover, td.nav a:hover:link, td.nav a:hover:link:visited, td.nav a:link, td.nav a:link:visited, td.nav a:visited { background: inherit; color: inherit; font-size: 80%%; text-decoration: none; }\n"
2934
"td.nav { background: #333; color: #fff; padding: 4px 8px; width: 33%%; }\n"
2935
"td.nav.sel { background: #fff; color: #000; font-weight: bold; }\n"
2936
"td.nav:hover { background: #666; color: #fff; }\n"
2937
"td.nav:active { background: #000; color: #ff0; }\n"
2938
"</style>\n"
2939
"</head>\n"
2940
"<body>\n"
2941
"<table class=\"nav\"><tr>"
2942
"<td class=\"nav%s\"><a href=\"/\">Status</a></td>"
2943
"<td class=\"nav%s\"><a href=\"/supplies\">Supplies</a></td>"
2944
"<td class=\"nav%s\"><a href=\"/media\">Media</a></td>"
2945
"</tr></table>\n"
2946
"<div class=\"body\">\n", !strcmp(client->uri, "/") ? " sel" : "", !strcmp(client->uri, "/supplies") ? " sel" : "", !strcmp(client->uri, "/media") ? " sel" : "");
2947
}
2948
2949
2950
/*
2951
* 'html_printf()' - Send formatted text to the client, quoting as needed.
2952
*/
2953
2954
static void
2955
html_printf(ippeve_client_t *client, /* I - Client */
2956
const char *format, /* I - Printf-style format string */
2957
...) /* I - Additional arguments as needed */
2958
{
2959
va_list ap; /* Pointer to arguments */
2960
const char *start; /* Start of string */
2961
char size, /* Size character (h, l, L) */
2962
type; /* Format type character */
2963
int width, /* Width of field */
2964
prec; /* Number of characters of precision */
2965
char tformat[100], /* Temporary format string for sprintf() */
2966
*tptr, /* Pointer into temporary format */
2967
temp[1024]; /* Buffer for formatted numbers */
2968
char *s; /* Pointer to string */
2969
2970
2971
/*
2972
* Loop through the format string, formatting as needed...
2973
*/
2974
2975
va_start(ap, format);
2976
start = format;
2977
2978
while (*format)
2979
{
2980
if (*format == '%')
2981
{
2982
if (format > start)
2983
httpWrite2(client->http, start, (size_t)(format - start));
2984
2985
tptr = tformat;
2986
*tptr++ = *format++;
2987
2988
if (*format == '%')
2989
{
2990
httpWrite2(client->http, "%", 1);
2991
format ++;
2992
start = format;
2993
continue;
2994
}
2995
else if (strchr(" -+#\'", *format))
2996
*tptr++ = *format++;
2997
2998
if (*format == '*')
2999
{
3000
/*
3001
* Get width from argument...
3002
*/
3003
3004
format ++;
3005
width = va_arg(ap, int);
3006
3007
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
3008
tptr += strlen(tptr);
3009
}
3010
else
3011
{
3012
width = 0;
3013
3014
while (isdigit(*format & 255))
3015
{
3016
if (tptr < (tformat + sizeof(tformat) - 1))
3017
*tptr++ = *format;
3018
3019
width = width * 10 + *format++ - '0';
3020
}
3021
}
3022
3023
if (*format == '.')
3024
{
3025
if (tptr < (tformat + sizeof(tformat) - 1))
3026
*tptr++ = *format;
3027
3028
format ++;
3029
3030
if (*format == '*')
3031
{
3032
/*
3033
* Get precision from argument...
3034
*/
3035
3036
format ++;
3037
prec = va_arg(ap, int);
3038
3039
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
3040
tptr += strlen(tptr);
3041
}
3042
else
3043
{
3044
prec = 0;
3045
3046
while (isdigit(*format & 255))
3047
{
3048
if (tptr < (tformat + sizeof(tformat) - 1))
3049
*tptr++ = *format;
3050
3051
prec = prec * 10 + *format++ - '0';
3052
}
3053
}
3054
}
3055
3056
if (*format == 'l' && format[1] == 'l')
3057
{
3058
size = 'L';
3059
3060
if (tptr < (tformat + sizeof(tformat) - 2))
3061
{
3062
*tptr++ = 'l';
3063
*tptr++ = 'l';
3064
}
3065
3066
format += 2;
3067
}
3068
else if (*format == 'h' || *format == 'l' || *format == 'L')
3069
{
3070
if (tptr < (tformat + sizeof(tformat) - 1))
3071
*tptr++ = *format;
3072
3073
size = *format++;
3074
}
3075
else
3076
size = 0;
3077
3078
3079
if (!*format)
3080
{
3081
start = format;
3082
break;
3083
}
3084
3085
if (tptr < (tformat + sizeof(tformat) - 1))
3086
*tptr++ = *format;
3087
3088
type = *format++;
3089
*tptr = '\0';
3090
start = format;
3091
3092
switch (type)
3093
{
3094
case 'E' : /* Floating point formats */
3095
case 'G' :
3096
case 'e' :
3097
case 'f' :
3098
case 'g' :
3099
if ((size_t)(width + 2) > sizeof(temp))
3100
break;
3101
3102
snprintf(temp, sizeof(temp), tformat, va_arg(ap, double));
3103
3104
httpWrite2(client->http, temp, strlen(temp));
3105
break;
3106
3107
case 'B' : /* Integer formats */
3108
case 'X' :
3109
case 'b' :
3110
case 'd' :
3111
case 'i' :
3112
case 'o' :
3113
case 'u' :
3114
case 'x' :
3115
if ((size_t)(width + 2) > sizeof(temp))
3116
break;
3117
3118
# ifdef HAVE_LONG_LONG
3119
if (size == 'L')
3120
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long));
3121
else
3122
# endif /* HAVE_LONG_LONG */
3123
if (size == 'l')
3124
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long));
3125
else
3126
snprintf(temp, sizeof(temp), tformat, va_arg(ap, int));
3127
3128
httpWrite2(client->http, temp, strlen(temp));
3129
break;
3130
3131
case 'p' : /* Pointer value */
3132
if ((size_t)(width + 2) > sizeof(temp))
3133
break;
3134
3135
snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *));
3136
3137
httpWrite2(client->http, temp, strlen(temp));
3138
break;
3139
3140
case 'c' : /* Character or character array */
3141
if (width <= 1)
3142
{
3143
temp[0] = (char)va_arg(ap, int);
3144
temp[1] = '\0';
3145
html_escape(client, temp, 1);
3146
}
3147
else
3148
html_escape(client, va_arg(ap, char *), (size_t)width);
3149
break;
3150
3151
case 's' : /* String */
3152
if ((s = va_arg(ap, char *)) == NULL)
3153
s = "(null)";
3154
3155
html_escape(client, s, strlen(s));
3156
break;
3157
}
3158
}
3159
else
3160
format ++;
3161
}
3162
3163
if (format > start)
3164
httpWrite2(client->http, start, (size_t)(format - start));
3165
3166
va_end(ap);
3167
}
3168
3169
3170
/*
3171
* 'ipp_cancel_job()' - Cancel a job.
3172
*/
3173
3174
static void
3175
ipp_cancel_job(ippeve_client_t *client) /* I - Client */
3176
{
3177
ippeve_job_t *job; /* Job information */
3178
3179
3180
/*
3181
* Get the job...
3182
*/
3183
3184
if ((job = find_job(client)) == NULL)
3185
{
3186
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3187
return;
3188
}
3189
3190
/*
3191
* See if the job is already completed, canceled, or aborted; if so,
3192
* we can't cancel...
3193
*/
3194
3195
switch (job->state)
3196
{
3197
case IPP_JSTATE_CANCELED :
3198
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3199
"Job #%d is already canceled - can\'t cancel.", job->id);
3200
break;
3201
3202
case IPP_JSTATE_ABORTED :
3203
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3204
"Job #%d is already aborted - can\'t cancel.", job->id);
3205
break;
3206
3207
case IPP_JSTATE_COMPLETED :
3208
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3209
"Job #%d is already completed - can\'t cancel.", job->id);
3210
break;
3211
3212
default :
3213
/*
3214
* Cancel the job...
3215
*/
3216
3217
_cupsRWLockWrite(&(client->printer->rwlock));
3218
3219
if (job->state == IPP_JSTATE_PROCESSING ||
3220
(job->state == IPP_JSTATE_HELD && job->fd >= 0))
3221
job->cancel = 1;
3222
else
3223
{
3224
job->state = IPP_JSTATE_CANCELED;
3225
job->completed = time(NULL);
3226
}
3227
3228
_cupsRWUnlock(&(client->printer->rwlock));
3229
3230
respond_ipp(client, IPP_STATUS_OK, NULL);
3231
break;
3232
}
3233
}
3234
3235
3236
/*
3237
* 'ipp_cancel_my_jobs()' - Cancel all jobs.
3238
*
3239
* Note: Since ippeveprinter doesn't do spooling, this really just cancels the
3240
* current job.
3241
*/
3242
3243
static void
3244
ipp_cancel_my_jobs(
3245
ippeve_client_t *client) /* I - Client */
3246
{
3247
ippeve_job_t *job; /* Job information */
3248
3249
3250
_cupsRWLockWrite(&client->printer->rwlock);
3251
3252
if ((job = client->printer->active_job) != NULL)
3253
{
3254
/*
3255
* See if the job is already completed, canceled, or aborted; if so,
3256
* we can't cancel...
3257
*/
3258
3259
if (job->state < IPP_JSTATE_CANCELED)
3260
{
3261
/*
3262
* Cancel the job...
3263
*/
3264
3265
if (job->state == IPP_JSTATE_PROCESSING || (job->state == IPP_JSTATE_HELD && job->fd >= 0))
3266
{
3267
job->cancel = 1;
3268
}
3269
else
3270
{
3271
job->state = IPP_JSTATE_CANCELED;
3272
job->completed = time(NULL);
3273
}
3274
}
3275
}
3276
3277
respond_ipp(client, IPP_STATUS_OK, NULL);
3278
3279
_cupsRWUnlock(&client->printer->rwlock);
3280
}
3281
3282
3283
/*
3284
* 'ipp_close_job()' - Close an open job.
3285
*/
3286
3287
static void
3288
ipp_close_job(ippeve_client_t *client) /* I - Client */
3289
{
3290
ippeve_job_t *job; /* Job information */
3291
3292
3293
/*
3294
* Get the job...
3295
*/
3296
3297
if ((job = find_job(client)) == NULL)
3298
{
3299
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3300
return;
3301
}
3302
3303
/*
3304
* See if the job is already completed, canceled, or aborted; if so,
3305
* we can't cancel...
3306
*/
3307
3308
switch (job->state)
3309
{
3310
case IPP_JSTATE_CANCELED :
3311
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3312
"Job #%d is canceled - can\'t close.", job->id);
3313
break;
3314
3315
case IPP_JSTATE_ABORTED :
3316
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3317
"Job #%d is aborted - can\'t close.", job->id);
3318
break;
3319
3320
case IPP_JSTATE_COMPLETED :
3321
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3322
"Job #%d is completed - can\'t close.", job->id);
3323
break;
3324
3325
case IPP_JSTATE_PROCESSING :
3326
case IPP_JSTATE_STOPPED :
3327
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE,
3328
"Job #%d is already closed.", job->id);
3329
break;
3330
3331
default :
3332
respond_ipp(client, IPP_STATUS_OK, NULL);
3333
break;
3334
}
3335
}
3336
3337
3338
/*
3339
* 'ipp_create_job()' - Create a job object.
3340
*/
3341
3342
static void
3343
ipp_create_job(ippeve_client_t *client) /* I - Client */
3344
{
3345
ippeve_job_t *job; /* New job */
3346
cups_array_t *ra; /* Attributes to send in response */
3347
3348
3349
/*
3350
* Do we have a file to print?
3351
*/
3352
3353
if (have_document_data(client))
3354
{
3355
flush_document_data(client);
3356
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3357
"Unexpected document data following request.");
3358
return;
3359
}
3360
3361
/*
3362
* Validate print job attributes...
3363
*/
3364
3365
if (!valid_job_attributes(client))
3366
return;
3367
3368
/*
3369
* Create the job...
3370
*/
3371
3372
if ((job = create_job(client)) == NULL)
3373
{
3374
respond_ipp(client, IPP_STATUS_ERROR_BUSY,
3375
"Currently printing another job.");
3376
return;
3377
}
3378
3379
/*
3380
* Return the job info...
3381
*/
3382
3383
respond_ipp(client, IPP_STATUS_OK, NULL);
3384
3385
ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
3386
cupsArrayAdd(ra, "job-id");
3387
cupsArrayAdd(ra, "job-state");
3388
cupsArrayAdd(ra, "job-state-message");
3389
cupsArrayAdd(ra, "job-state-reasons");
3390
cupsArrayAdd(ra, "job-uri");
3391
3392
copy_job_attributes(client, job, ra);
3393
cupsArrayDelete(ra);
3394
}
3395
3396
3397
/*
3398
* 'ipp_get_job_attributes()' - Get the attributes for a job object.
3399
*/
3400
3401
static void
3402
ipp_get_job_attributes(
3403
ippeve_client_t *client) /* I - Client */
3404
{
3405
ippeve_job_t *job; /* Job */
3406
cups_array_t *ra; /* requested-attributes */
3407
3408
3409
if ((job = find_job(client)) == NULL)
3410
{
3411
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found.");
3412
return;
3413
}
3414
3415
respond_ipp(client, IPP_STATUS_OK, NULL);
3416
3417
ra = ippCreateRequestedArray(client->request);
3418
copy_job_attributes(client, job, ra);
3419
cupsArrayDelete(ra);
3420
}
3421
3422
3423
/*
3424
* 'ipp_get_jobs()' - Get a list of job objects.
3425
*/
3426
3427
static void
3428
ipp_get_jobs(ippeve_client_t *client) /* I - Client */
3429
{
3430
ipp_attribute_t *attr; /* Current attribute */
3431
const char *which_jobs = NULL;
3432
/* which-jobs values */
3433
int job_comparison; /* Job comparison */
3434
ipp_jstate_t job_state; /* job-state value */
3435
int first_job_id, /* First job ID */
3436
limit, /* Maximum number of jobs to return */
3437
count; /* Number of jobs that match */
3438
const char *username; /* Username */
3439
ippeve_job_t *job; /* Current job pointer */
3440
cups_array_t *ra; /* Requested attributes array */
3441
3442
3443
/*
3444
* See if the "which-jobs" attribute have been specified...
3445
*/
3446
3447
if ((attr = ippFindAttribute(client->request, "which-jobs",
3448
IPP_TAG_KEYWORD)) != NULL)
3449
{
3450
which_jobs = ippGetString(attr, 0, NULL);
3451
fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs);
3452
}
3453
3454
if (!which_jobs || !strcmp(which_jobs, "not-completed"))
3455
{
3456
job_comparison = -1;
3457
job_state = IPP_JSTATE_STOPPED;
3458
}
3459
else if (!strcmp(which_jobs, "completed"))
3460
{
3461
job_comparison = 1;
3462
job_state = IPP_JSTATE_CANCELED;
3463
}
3464
else if (!strcmp(which_jobs, "aborted"))
3465
{
3466
job_comparison = 0;
3467
job_state = IPP_JSTATE_ABORTED;
3468
}
3469
else if (!strcmp(which_jobs, "all"))
3470
{
3471
job_comparison = 1;
3472
job_state = IPP_JSTATE_PENDING;
3473
}
3474
else if (!strcmp(which_jobs, "canceled"))
3475
{
3476
job_comparison = 0;
3477
job_state = IPP_JSTATE_CANCELED;
3478
}
3479
else if (!strcmp(which_jobs, "pending"))
3480
{
3481
job_comparison = 0;
3482
job_state = IPP_JSTATE_PENDING;
3483
}
3484
else if (!strcmp(which_jobs, "pending-held"))
3485
{
3486
job_comparison = 0;
3487
job_state = IPP_JSTATE_HELD;
3488
}
3489
else if (!strcmp(which_jobs, "processing"))
3490
{
3491
job_comparison = 0;
3492
job_state = IPP_JSTATE_PROCESSING;
3493
}
3494
else if (!strcmp(which_jobs, "processing-stopped"))
3495
{
3496
job_comparison = 0;
3497
job_state = IPP_JSTATE_STOPPED;
3498
}
3499
else
3500
{
3501
respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
3502
"The which-jobs value \"%s\" is not supported.", which_jobs);
3503
ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
3504
"which-jobs", NULL, which_jobs);
3505
return;
3506
}
3507
3508
/*
3509
* See if they want to limit the number of jobs reported...
3510
*/
3511
3512
if ((attr = ippFindAttribute(client->request, "limit",
3513
IPP_TAG_INTEGER)) != NULL)
3514
{
3515
limit = ippGetInteger(attr, 0);
3516
3517
fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit);
3518
}
3519
else
3520
limit = 0;
3521
3522
if ((attr = ippFindAttribute(client->request, "first-job-id",
3523
IPP_TAG_INTEGER)) != NULL)
3524
{
3525
first_job_id = ippGetInteger(attr, 0);
3526
3527
fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname, first_job_id);
3528
}
3529
else
3530
first_job_id = 1;
3531
3532
/*
3533
* See if we only want to see jobs for a specific user...
3534
*/
3535
3536
username = NULL;
3537
3538
if ((attr = ippFindAttribute(client->request, "my-jobs",
3539
IPP_TAG_BOOLEAN)) != NULL)
3540
{
3541
int my_jobs = ippGetBoolean(attr, 0);
3542
3543
fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname, my_jobs ? "true" : "false");
3544
3545
if (my_jobs)
3546
{
3547
if ((attr = ippFindAttribute(client->request, "requesting-user-name",
3548
IPP_TAG_NAME)) == NULL)
3549
{
3550
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
3551
"Need requesting-user-name with my-jobs.");
3552
return;
3553
}
3554
3555
username = ippGetString(attr, 0, NULL);
3556
3557
fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n", client->hostname, username);
3558
}
3559
}
3560
3561
/*
3562
* OK, build a list of jobs for this printer...
3563
*/
3564
3565
ra = ippCreateRequestedArray(client->request);
3566
3567
respond_ipp(client, IPP_STATUS_OK, NULL);
3568
3569
_cupsRWLockRead(&(client->printer->rwlock));
3570
3571
for (count = 0, job = (ippeve_job_t *)cupsArrayFirst(client->printer->jobs);
3572
(limit <= 0 || count < limit) && job;
3573
job = (ippeve_job_t *)cupsArrayNext(client->printer->jobs))
3574
{
3575
/*
3576
* Filter out jobs that don't match...
3577
*/
3578
3579
if ((job_comparison < 0 && job->state > job_state) ||
3580
(job_comparison == 0 && job->state != job_state) ||
3581
(job_comparison > 0 && job->state < job_state) ||
3582
job->id < first_job_id ||
3583
(username && job->username &&
3584
strcasecmp(username, job->username)))
3585
continue;
3586
3587
if (count > 0)
3588
ippAddSeparator(client->response);
3589
3590
count ++;
3591
copy_job_attributes(client, job, ra);
3592
}
3593
3594
cupsArrayDelete(ra);
3595
3596
_cupsRWUnlock(&(client->printer->rwlock));
3597
}
3598
3599
3600
/*
3601
* 'ipp_get_printer_attributes()' - Get the attributes for a printer object.
3602
*/
3603
3604
static void
3605
ipp_get_printer_attributes(
3606
ippeve_client_t *client) /* I - Client */
3607
{
3608
cups_array_t *ra; /* Requested attributes array */
3609
ippeve_printer_t *printer; /* Printer */
3610
3611
3612
/*
3613
* Send the attributes...
3614
*/
3615
3616
ra = ippCreateRequestedArray(client->request);
3617
printer = client->printer;
3618
3619
respond_ipp(client, IPP_STATUS_OK, NULL);
3620
3621
_cupsRWLockRead(&(printer->rwlock));
3622
3623
copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO,
3624
IPP_TAG_CUPS_CONST);
3625
3626
if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
3627
ippAddDate(client->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
3628
3629
if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
3630
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", (int)(printer->config_time - printer->start_time));
3631
3632
if (!ra || cupsArrayFind(ra, "printer-current-time"))
3633
ippAddDate(client->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(time(NULL)));
3634
3635
if (!ra || cupsArrayFind(ra, "printer-icons"))
3636
{
3637
char uris[3][1024]; /* Buffers for URIs */
3638
const char *values[3]; /* Values for attribute */
3639
3640
httpAssembleURI(HTTP_URI_CODING_ALL, uris[0], sizeof(uris[0]), WEB_SCHEME, NULL, client->host_field, client->host_port, "/icon-sm.png");
3641
httpAssembleURI(HTTP_URI_CODING_ALL, uris[1], sizeof(uris[1]), WEB_SCHEME, NULL, client->host_field, client->host_port, "/icon.png");
3642
httpAssembleURI(HTTP_URI_CODING_ALL, uris[2], sizeof(uris[2]), WEB_SCHEME, NULL, client->host_field, client->host_port, "/icon-lg.png");
3643
3644
values[0] = uris[0];
3645
values[1] = uris[1];
3646
values[2] = uris[2];
3647
3648
ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons", 3, NULL, values);
3649
}
3650
3651
if (!ra || cupsArrayFind(ra, "printer-more-info"))
3652
{
3653
char uri[1024]; /* URI value */
3654
3655
httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), WEB_SCHEME, NULL, client->host_field, client->host_port, "/");
3656
ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", NULL, uri);
3657
}
3658
3659
if (!ra || cupsArrayFind(ra, "printer-state"))
3660
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
3661
3662
if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
3663
ippAddDate(client->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
3664
3665
if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
3666
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", (int)(printer->state_time - printer->start_time));
3667
3668
if (!ra || cupsArrayFind(ra, "printer-state-message"))
3669
{
3670
static const char * const messages[] = { "Idle.", "Printing.", "Stopped." };
3671
3672
ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-state-message", NULL, messages[printer->state - IPP_PSTATE_IDLE]);
3673
}
3674
3675
if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
3676
{
3677
if (printer->state_reasons == IPPEVE_PREASON_NONE)
3678
{
3679
ippAddString(client->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "printer-state-reasons", NULL, "none");
3680
}
3681
else
3682
{
3683
ipp_attribute_t *attr = NULL; /* printer-state-reasons */
3684
ippeve_preason_t bit; /* Reason bit */
3685
int i; /* Looping var */
3686
char reason[32]; /* Reason string */
3687
3688
for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
3689
{
3690
if (printer->state_reasons & bit)
3691
{
3692
snprintf(reason, sizeof(reason), "%s-%s", ippeve_preason_strings[i], printer->state == IPP_PSTATE_IDLE ? "report" : printer->state == IPP_PSTATE_PROCESSING ? "warning" : "error");
3693
if (attr)
3694
ippSetString(client->response, &attr, ippGetCount(attr), reason);
3695
else
3696
attr = ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, reason);
3697
}
3698
}
3699
}
3700
}
3701
3702
if (!ra || cupsArrayFind(ra, "printer-strings-uri"))
3703
{
3704
char uri[1024]; /* URI value */
3705
3706
httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), WEB_SCHEME, NULL, client->host_field, client->host_port, "/en.strings");
3707
ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-strings-uri", NULL, uri);
3708
}
3709
3710
if (!ra || cupsArrayFind(ra, "printer-supply-info-uri"))
3711
{
3712
char uri[1024]; /* URI value */
3713
3714
httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), WEB_SCHEME, NULL, client->host_field, client->host_port, "/supplies");
3715
ippAddString(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-supply-info-uri", NULL, uri);
3716
}
3717
3718
if (!ra || cupsArrayFind(ra, "printer-up-time"))
3719
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
3720
3721
if (!ra || cupsArrayFind(ra, "printer-uri-supported"))
3722
{
3723
char uris[2][1024]; /* Buffers for URIs */
3724
const char *values[2]; /* Values for attribute */
3725
int num_values = 0; /* Number of values */
3726
3727
httpAssembleURI(HTTP_URI_CODING_ALL, uris[0], sizeof(uris[0]), "ipp", NULL, client->host_field, client->host_port, "/ipp/print");
3728
values[num_values ++] = uris[0];
3729
3730
#ifdef HAVE_TLS
3731
httpAssembleURI(HTTP_URI_CODING_ALL, uris[1], sizeof(uris[1]), "ipps", NULL, client->host_field, client->host_port, "/ipp/print");
3732
values[num_values ++] = uris[1];
3733
#endif /* HAVE_TLS */
3734
3735
ippAddStrings(client->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", num_values, NULL, values);
3736
}
3737
3738
if (!ra || cupsArrayFind(ra, "queued-job-count"))
3739
ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "queued-job-count", printer->active_job && printer->active_job->state < IPP_JSTATE_CANCELED);
3740
3741
_cupsRWUnlock(&(printer->rwlock));
3742
3743
cupsArrayDelete(ra);
3744
}
3745
3746
3747
/*
3748
* 'ipp_identify_printer()' - Beep or display a message.
3749
*/
3750
3751
static void
3752
ipp_identify_printer(
3753
ippeve_client_t *client) /* I - Client */
3754
{
3755
ipp_attribute_t *actions, /* identify-actions */
3756
*message; /* message */
3757
3758
3759
actions = ippFindAttribute(client->request, "identify-actions", IPP_TAG_KEYWORD);
3760
message = ippFindAttribute(client->request, "message", IPP_TAG_TEXT);
3761
3762
if (!actions || ippContainsString(actions, "sound"))
3763
{
3764
#ifdef __APPLE__
3765
pid_t pid; /* Process ID for "afplay" utility */
3766
static const char * const afplay[3] =
3767
{ /* Arguments for "afplay" utility */
3768
"/usr/bin/afplay",
3769
"/System/Library/Sounds/Ping.aiff",
3770
NULL
3771
};
3772
3773
posix_spawn(&pid, afplay[0], NULL, NULL, (char **)afplay, NULL);
3774
3775
#else
3776
putchar(0x07);
3777
fflush(stdout);
3778
#endif /* __APPLE__ */
3779
}
3780
3781
if (ippContainsString(actions, "display"))
3782
printf("IDENTIFY from %s: %s\n", client->hostname, message ? ippGetString(message, 0, NULL) : "No message supplied");
3783
3784
respond_ipp(client, IPP_STATUS_OK, NULL);
3785
}
3786
3787
3788
/*
3789
* 'ipp_print_job()' - Create a job object with an attached document.
3790
*/
3791
3792
static void
3793
ipp_print_job(ippeve_client_t *client) /* I - Client */
3794
{
3795
ippeve_job_t *job; /* New job */
3796
3797
3798
/*
3799
* Validate print job attributes...
3800
*/
3801
3802
if (!valid_job_attributes(client))
3803
{
3804
flush_document_data(client);
3805
return;
3806
}
3807
3808
/*
3809
* Do we have a file to print?
3810
*/
3811
3812
if (!have_document_data(client))
3813
{
3814
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request.");
3815
return;
3816
}
3817
3818
/*
3819
* Create the job...
3820
*/
3821
3822
if ((job = create_job(client)) == NULL)
3823
{
3824
respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
3825
return;
3826
}
3827
3828
/*
3829
* Then finish getting the document data and process things...
3830
*/
3831
3832
finish_document_data(client, job);
3833
}
3834
3835
3836
/*
3837
* 'ipp_print_uri()' - Create a job object with a referenced document.
3838
*/
3839
3840
static void
3841
ipp_print_uri(ippeve_client_t *client) /* I - Client */
3842
{
3843
ippeve_job_t *job; /* New job */
3844
3845
3846
/*
3847
* Validate print job attributes...
3848
*/
3849
3850
if (!valid_job_attributes(client))
3851
{
3852
flush_document_data(client);
3853
return;
3854
}
3855
3856
/*
3857
* Create the job...
3858
*/
3859
3860
if ((job = create_job(client)) == NULL)
3861
{
3862
respond_ipp(client, IPP_STATUS_ERROR_BUSY, "Currently printing another job.");
3863
return;
3864
}
3865
3866
/*
3867
* Then finish getting the document data and process things...
3868
*/
3869
3870
finish_document_uri(client, job);
3871
}
3872
3873
3874
/*
3875
* 'ipp_send_document()' - Add an attached document to a job object created with
3876
* Create-Job.
3877
*/
3878
3879
static void
3880
ipp_send_document(
3881
ippeve_client_t *client) /* I - Client */
3882
{
3883
ippeve_job_t *job; /* Job information */
3884
ipp_attribute_t *attr; /* Current attribute */
3885
int have_data; /* Have document data? */
3886
3887
3888
/*
3889
* Get the job...
3890
*/
3891
3892
if ((job = find_job(client)) == NULL)
3893
{
3894
flush_document_data(client);
3895
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3896
return;
3897
}
3898
3899
/*
3900
* See if we already have a document for this job or the job has already
3901
* in a terminating state...
3902
*/
3903
3904
have_data = have_document_data(client);
3905
3906
if ((job->filename || job->fd >= 0) && have_data)
3907
{
3908
flush_document_data(client);
3909
respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
3910
return;
3911
}
3912
else if (job->state > IPP_JSTATE_HELD && have_data)
3913
{
3914
flush_document_data(client);
3915
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
3916
return;
3917
}
3918
3919
/*
3920
* Make sure we have the "last-document" operation attribute...
3921
*/
3922
3923
if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
3924
{
3925
flush_document_data(client);
3926
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
3927
return;
3928
}
3929
else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
3930
{
3931
flush_document_data(client);
3932
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
3933
return;
3934
}
3935
else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1)
3936
{
3937
flush_document_data(client);
3938
respond_unsupported(client, attr);
3939
return;
3940
}
3941
3942
/*
3943
* Validate document attributes...
3944
*/
3945
3946
if (have_data && !valid_doc_attributes(client))
3947
{
3948
flush_document_data(client);
3949
return;
3950
}
3951
3952
if (!have_data && !job->filename)
3953
job->state = IPP_JSTATE_ABORTED;
3954
3955
/*
3956
* Then finish getting the document data and process things...
3957
*/
3958
3959
_cupsRWLockWrite(&(client->printer->rwlock));
3960
3961
copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
3962
3963
if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
3964
job->format = ippGetString(attr, 0, NULL);
3965
else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
3966
job->format = ippGetString(attr, 0, NULL);
3967
else
3968
job->format = "application/octet-stream";
3969
3970
_cupsRWUnlock(&(client->printer->rwlock));
3971
3972
if (have_data)
3973
finish_document_data(client, job);
3974
}
3975
3976
3977
/*
3978
* 'ipp_send_uri()' - Add a referenced document to a job object created with
3979
* Create-Job.
3980
*/
3981
3982
static void
3983
ipp_send_uri(ippeve_client_t *client) /* I - Client */
3984
{
3985
ippeve_job_t *job; /* Job information */
3986
ipp_attribute_t *attr; /* Current attribute */
3987
3988
3989
/*
3990
* Get the job...
3991
*/
3992
3993
if ((job = find_job(client)) == NULL)
3994
{
3995
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist.");
3996
return;
3997
}
3998
3999
/*
4000
* See if we already have a document for this job or the job has already
4001
* in a non-terminating state...
4002
*/
4003
4004
if (job->filename || job->fd >= 0)
4005
{
4006
respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, "Multiple document jobs are not supported.");
4007
return;
4008
}
4009
else if (job->state > IPP_JSTATE_HELD)
4010
{
4011
flush_document_data(client);
4012
respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, "Job is not in a pending state.");
4013
return;
4014
}
4015
4016
if ((attr = ippFindAttribute(client->request, "last-document", IPP_TAG_ZERO)) == NULL)
4017
{
4018
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing required last-document attribute.");
4019
return;
4020
}
4021
else if (ippGetGroupTag(attr) != IPP_TAG_OPERATION)
4022
{
4023
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "The last-document attribute is not in the operation group.");
4024
return;
4025
}
4026
else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1)
4027
{
4028
respond_unsupported(client, attr);
4029
return;
4030
}
4031
4032
/*
4033
* Validate document attributes...
4034
*/
4035
4036
if (!valid_doc_attributes(client))
4037
{
4038
flush_document_data(client);
4039
return;
4040
}
4041
4042
/*
4043
* Then finish getting the document data and process things...
4044
*/
4045
4046
_cupsRWLockWrite(&(client->printer->rwlock));
4047
4048
copy_attributes(job->attrs, client->request, NULL, IPP_TAG_JOB, 0);
4049
4050
if ((attr = ippFindAttribute(job->attrs, "document-format-detected", IPP_TAG_MIMETYPE)) != NULL)
4051
job->format = ippGetString(attr, 0, NULL);
4052
else if ((attr = ippFindAttribute(job->attrs, "document-format-supplied", IPP_TAG_MIMETYPE)) != NULL)
4053
job->format = ippGetString(attr, 0, NULL);
4054
else
4055
job->format = "application/octet-stream";
4056
4057
_cupsRWUnlock(&(client->printer->rwlock));
4058
4059
finish_document_uri(client, job);
4060
}
4061
4062
4063
/*
4064
* 'ipp_validate_job()' - Validate job creation attributes.
4065
*/
4066
4067
static void
4068
ipp_validate_job(ippeve_client_t *client) /* I - Client */
4069
{
4070
if (valid_job_attributes(client))
4071
respond_ipp(client, IPP_STATUS_OK, NULL);
4072
}
4073
4074
4075
/*
4076
* 'ippserver_attr_cb()' - Determine whether an attribute should be loaded.
4077
*/
4078
4079
static int /* O - 1 to use, 0 to ignore */
4080
ippserver_attr_cb(
4081
_ipp_file_t *f, /* I - IPP file */
4082
void *user_data, /* I - User data pointer (unused) */
4083
const char *attr) /* I - Attribute name */
4084
{
4085
int i, /* Current element */
4086
result; /* Result of comparison */
4087
static const char * const ignored[] =
4088
{ /* Ignored attributes */
4089
"attributes-charset",
4090
"attributes-natural-language",
4091
"charset-configured",
4092
"charset-supported",
4093
"device-service-count",
4094
"device-uuid",
4095
"document-format-varying-attributes",
4096
"generated-natural-language-supported",
4097
"identify-actions-default",
4098
"identify-actions-supported",
4099
"ipp-features-supported",
4100
"ipp-versions-supproted",
4101
"ippget-event-life",
4102
"job-hold-until-supported",
4103
"job-hold-until-time-supported",
4104
"job-ids-supported",
4105
"job-k-octets-supported",
4106
"job-settable-attributes-supported",
4107
"multiple-document-jobs-supported",
4108
"multiple-operation-time-out",
4109
"multiple-operation-time-out-action",
4110
"natural-language-configured",
4111
"notify-attributes-supported",
4112
"notify-events-default",
4113
"notify-events-supported",
4114
"notify-lease-duration-default",
4115
"notify-lease-duration-supported",
4116
"notify-max-events-supported",
4117
"notify-pull-method-supported",
4118
"operations-supported",
4119
"printer-alert",
4120
"printer-alert-description",
4121
"printer-camera-image-uri",
4122
"printer-charge-info",
4123
"printer-charge-info-uri",
4124
"printer-config-change-date-time",
4125
"printer-config-change-time",
4126
"printer-current-time",
4127
"printer-detailed-status-messages",
4128
"printer-dns-sd-name",
4129
"printer-fax-log-uri",
4130
"printer-get-attributes-supported",
4131
"printer-icons",
4132
"printer-id",
4133
"printer-info",
4134
"printer-is-accepting-jobs",
4135
"printer-message-date-time",
4136
"printer-message-from-operator",
4137
"printer-message-time",
4138
"printer-more-info",
4139
"printer-service-type",
4140
"printer-settable-attributes-supported",
4141
"printer-state",
4142
"printer-state-message",
4143
"printer-state-reasons",
4144
"printer-static-resource-directory-uri",
4145
"printer-static-resource-k-octets-free",
4146
"printer-static-resource-k-octets-supported",
4147
"printer-strings-languages-supported",
4148
"printer-strings-uri",
4149
"printer-supply-info-uri",
4150
"printer-up-time",
4151
"printer-uri-supported",
4152
"printer-xri-supported",
4153
"queued-job-count",
4154
"reference-uri-scheme-supported",
4155
"uri-authentication-supported",
4156
"uri-security-supported",
4157
"which-jobs-supported",
4158
"xri-authentication-supported",
4159
"xri-security-supported",
4160
"xri-uri-scheme-supported"
4161
};
4162
4163
4164
(void)f;
4165
(void)user_data;
4166
4167
for (i = 0, result = 1; i < (int)(sizeof(ignored) / sizeof(ignored[0])); i ++)
4168
{
4169
if ((result = strcmp(attr, ignored[i])) <= 0)
4170
break;
4171
}
4172
4173
return (result != 0);
4174
}
4175
4176
4177
/*
4178
* 'ippserver_error_cb()' - Log an error message.
4179
*/
4180
4181
static int /* O - 1 to continue, 0 to stop */
4182
ippserver_error_cb(
4183
_ipp_file_t *f, /* I - IPP file data */
4184
void *user_data, /* I - User data pointer (unused) */
4185
const char *error) /* I - Error message */
4186
{
4187
(void)f;
4188
(void)user_data;
4189
4190
_cupsLangPrintf(stderr, "%s\n", error);
4191
4192
return (1);
4193
}
4194
4195
4196
/*
4197
* 'ippserver_token_cb()' - Process ippserver-specific config file tokens.
4198
*/
4199
4200
static int /* O - 1 to continue, 0 to stop */
4201
ippserver_token_cb(
4202
_ipp_file_t *f, /* I - IPP file data */
4203
_ipp_vars_t *vars, /* I - IPP variables */
4204
void *user_data, /* I - User data pointer (unused) */
4205
const char *token) /* I - Current token */
4206
{
4207
(void)vars;
4208
(void)user_data;
4209
4210
if (!token)
4211
{
4212
/*
4213
* NULL token means do the initial setup - create an empty IPP message and
4214
* return...
4215
*/
4216
4217
f->attrs = ippNew();
4218
f->group_tag = IPP_TAG_PRINTER;
4219
}
4220
else
4221
{
4222
_cupsLangPrintf(stderr, _("Unknown directive \"%s\" on line %d of \"%s\" ignored."), token, f->linenum, f->filename);
4223
}
4224
4225
return (1);
4226
}
4227
4228
4229
/*
4230
* 'load_ippserver_attributes()' - Load IPP attributes from an ippserver file.
4231
*/
4232
4233
static ipp_t * /* O - IPP attributes or `NULL` on error */
4234
load_ippserver_attributes(
4235
const char *servername, /* I - Server name or `NULL` for default */
4236
int serverport, /* I - Server port number */
4237
const char *filename, /* I - ippserver attribute filename */
4238
cups_array_t *docformats) /* I - document-format-supported values */
4239
{
4240
ipp_t *attrs; /* IPP attributes */
4241
_ipp_vars_t vars; /* IPP variables */
4242
char temp[256]; /* Temporary string */
4243
4244
4245
(void)docformats; /* for now */
4246
4247
/*
4248
* Setup callbacks and variables for the printer configuration file...
4249
*
4250
* The following additional variables are supported:
4251
*
4252
* - SERVERNAME: The host name of the server.
4253
* - SERVERPORT: The default port of the server.
4254
*/
4255
4256
_ippVarsInit(&vars, (_ipp_fattr_cb_t)ippserver_attr_cb, (_ipp_ferror_cb_t)ippserver_error_cb, (_ipp_ftoken_cb_t)ippserver_token_cb);
4257
4258
if (servername)
4259
{
4260
_ippVarsSet(&vars, "SERVERNAME", servername);
4261
}
4262
else
4263
{
4264
httpGetHostname(NULL, temp, sizeof(temp));
4265
_ippVarsSet(&vars, "SERVERNAME", temp);
4266
}
4267
4268
snprintf(temp, sizeof(temp), "%d", serverport);
4269
_ippVarsSet(&vars, "SERVERPORT", temp);
4270
4271
/*
4272
* Load attributes and values for the printer...
4273
*/
4274
4275
attrs = _ippFileParse(&vars, filename, NULL);
4276
4277
/*
4278
* Free memory and return...
4279
*/
4280
4281
_ippVarsDeinit(&vars);
4282
4283
return (attrs);
4284
}
4285
4286
4287
/*
4288
* 'load_legacy_attributes()' - Load IPP attributes using the old ippserver
4289
* options.
4290
*/
4291
4292
static ipp_t * /* O - IPP attributes or `NULL` on error */
4293
load_legacy_attributes(
4294
const char *make, /* I - Manufacturer name */
4295
const char *model, /* I - Model name */
4296
int ppm, /* I - pages-per-minute */
4297
int ppm_color, /* I - pages-per-minute-color */
4298
int duplex, /* I - Duplex support? */
4299
cups_array_t *docformats) /* I - document-format-supported values */
4300
{
4301
int i; /* Looping var */
4302
ipp_t *attrs, /* IPP attributes */
4303
*col; /* Collection value */
4304
ipp_attribute_t *attr; /* Current attribute */
4305
char device_id[1024],/* printer-device-id */
4306
*ptr, /* Pointer into device ID */
4307
make_model[128];/* printer-make-and-model */
4308
const char *format, /* Current document format */
4309
*prefix; /* Prefix for device ID */
4310
int num_media; /* Number of media */
4311
const char * const *media; /* List of media */
4312
int num_ready; /* Number of loaded media */
4313
const char * const *ready; /* List of loaded media */
4314
pwg_media_t *pwg; /* PWG media size information */
4315
static const char * const media_supported[] =
4316
{ /* media-supported values */
4317
"na_letter_8.5x11in", /* Letter */
4318
"na_legal_8.5x14in", /* Legal */
4319
"iso_a4_210x297mm", /* A4 */
4320
"na_number-10_4.125x9.5in", /* #10 Envelope */
4321
"iso_dl_110x220mm" /* DL Envelope */
4322
};
4323
static const char * const media_supported_color[] =
4324
{ /* media-supported values */
4325
"na_letter_8.5x11in", /* Letter */
4326
"na_legal_8.5x14in", /* Legal */
4327
"iso_a4_210x297mm", /* A4 */
4328
"na_number-10_4.125x9.5in", /* #10 Envelope */
4329
"iso_dl_110x220mm", /* DL Envelope */
4330
"na_index-3x5_3x5in", /* Photo 3x5 */
4331
"oe_photo-l_3.5x5in", /* Photo L */
4332
"na_index-4x6_4x6in", /* Photo 4x6 */
4333
"iso_a6_105x148mm", /* A6 */
4334
"na_5x7_5x7in", /* Photo 5x7 aka 2L */
4335
"iso_a5_148x210mm", /* A5 */
4336
};
4337
static const char * const media_ready[] =
4338
{ /* media-ready values */
4339
"na_letter_8.5x11in", /* Letter */
4340
"na_number-10_4.125x9.5in" /* #10 */
4341
};
4342
static const char * const media_ready_color[] =
4343
{ /* media-ready values */
4344
"na_letter_8.5x11in", /* Letter */
4345
"na_index-4x6_4x6in" /* Photo 4x6 */
4346
};
4347
static const char * const media_source_supported[] =
4348
{ /* media-source-supported values */
4349
"auto",
4350
"main",
4351
"manual",
4352
"by-pass-tray" /* AKA multi-purpose tray */
4353
};
4354
static const char * const media_source_supported_color[] =
4355
{ /* media-source-supported values */
4356
"auto",
4357
"main",
4358
"photo"
4359
};
4360
static const char * const media_type_supported[] =
4361
{ /* media-type-supported values */
4362
"auto",
4363
"cardstock",
4364
"envelope",
4365
"labels",
4366
"other",
4367
"stationery",
4368
"stationery-letterhead",
4369
"transparency"
4370
};
4371
static const char * const media_type_supported_color[] =
4372
{ /* media-type-supported values */
4373
"auto",
4374
"cardstock",
4375
"envelope",
4376
"labels",
4377
"other",
4378
"stationery",
4379
"stationery-letterhead",
4380
"transparency",
4381
"photographic-glossy",
4382
"photographic-high-gloss",
4383
"photographic-matte",
4384
"photographic-satin",
4385
"photographic-semi-gloss"
4386
};
4387
static const int media_bottom_margin_supported[] =
4388
{ /* media-bottom-margin-supported values */
4389
635 /* 1/4" */
4390
};
4391
static const int media_bottom_margin_supported_color[] =
4392
{ /* media-bottom/top-margin-supported values */
4393
0, /* Borderless */
4394
1168 /* 0.46" (common HP inkjet bottom margin) */
4395
};
4396
static const int media_lr_margin_supported[] =
4397
{ /* media-left/right-margin-supported values */
4398
340, /* 3.4mm (historical HP PCL A4 margin) */
4399
635 /* 1/4" */
4400
};
4401
static const int media_lr_margin_supported_color[] =
4402
{ /* media-left/right-margin-supported values */
4403
0, /* Borderless */
4404
340, /* 3.4mm (historical HP PCL A4 margin) */
4405
635 /* 1/4" */
4406
};
4407
static const int media_top_margin_supported[] =
4408
{ /* media-top-margin-supported values */
4409
635 /* 1/4" */
4410
};
4411
static const int media_top_margin_supported_color[] =
4412
{ /* media-top/top-margin-supported values */
4413
0, /* Borderless */
4414
102 /* 0.04" (common HP inkjet top margin */
4415
};
4416
static const int orientation_requested_supported[4] =
4417
{ /* orientation-requested-supported values */
4418
IPP_ORIENT_PORTRAIT,
4419
IPP_ORIENT_LANDSCAPE,
4420
IPP_ORIENT_REVERSE_LANDSCAPE,
4421
IPP_ORIENT_REVERSE_PORTRAIT
4422
};
4423
static const char * const overrides_supported[] =
4424
{ /* overrides-supported values */
4425
"document-numbers",
4426
"media",
4427
"media-col",
4428
"orientation-requested",
4429
"pages"
4430
};
4431
static const char * const print_color_mode_supported[] =
4432
{ /* print-color-mode-supported values */
4433
"monochrome"
4434
};
4435
static const char * const print_color_mode_supported_color[] =
4436
{ /* print-color-mode-supported values */
4437
"auto",
4438
"color",
4439
"monochrome"
4440
};
4441
static const int print_quality_supported[] =
4442
{ /* print-quality-supported values */
4443
IPP_QUALITY_DRAFT,
4444
IPP_QUALITY_NORMAL,
4445
IPP_QUALITY_HIGH
4446
};
4447
static const char * const printer_input_tray[] =
4448
{ /* printer-input-tray values */
4449
"type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4450
"type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=100;status=0;name=main",
4451
"type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=manual",
4452
"type=sheetFeedAutoNonRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=by-pass-tray"
4453
};
4454
static const char * const printer_input_tray_color[] =
4455
{ /* printer-input-tray values */
4456
"type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto",
4457
"type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=-2;status=0;name=main",
4458
"type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=25;level=-2;status=0;name=photo"
4459
};
4460
static const char * const printer_supply[] =
4461
{ /* printer-supply values */
4462
"index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
4463
"maxcapacity=100;level=25;colorantname=unknown;",
4464
"index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
4465
"maxcapacity=100;level=75;colorantname=black;"
4466
};
4467
static const char * const printer_supply_color[] =
4468
{ /* printer-supply values */
4469
"index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
4470
"maxcapacity=100;level=25;colorantname=unknown;",
4471
"index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
4472
"maxcapacity=100;level=75;colorantname=black;",
4473
"index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
4474
"maxcapacity=100;level=50;colorantname=cyan;",
4475
"index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
4476
"maxcapacity=100;level=33;colorantname=magenta;",
4477
"index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
4478
"maxcapacity=100;level=67;colorantname=yellow;"
4479
};
4480
static const char * const printer_supply_description[] =
4481
{ /* printer-supply-description values */
4482
"Toner Waste Tank",
4483
"Black Toner"
4484
};
4485
static const char * const printer_supply_description_color[] =
4486
{ /* printer-supply-description values */
4487
"Ink Waste Tank",
4488
"Black Ink",
4489
"Cyan Ink",
4490
"Magenta Ink",
4491
"Yellow Ink"
4492
};
4493
static const int pwg_raster_document_resolution_supported[] =
4494
{
4495
300,
4496
600
4497
};
4498
static const char * const pwg_raster_document_type_supported[] =
4499
{
4500
"black_1",
4501
"sgray_8"
4502
};
4503
static const char * const pwg_raster_document_type_supported_color[] =
4504
{
4505
"black_1",
4506
"sgray_8",
4507
"srgb_8",
4508
"srgb_16"
4509
};
4510
static const char * const sides_supported[] =
4511
{ /* sides-supported values */
4512
"one-sided",
4513
"two-sided-long-edge",
4514
"two-sided-short-edge"
4515
};
4516
static const char * const urf_supported[] =
4517
{ /* urf-supported values */
4518
"CP1",
4519
"IS1-4-5-19",
4520
"MT1-2-3-4-5-6",
4521
"RS600",
4522
"V1.4",
4523
"W8"
4524
};
4525
static const char * const urf_supported_color[] =
4526
{ /* urf-supported values */
4527
"CP1",
4528
"IS1-4-5-7-19",
4529
"MT1-2-3-4-5-6-8-9-10-11-12-13",
4530
"RS600",
4531
"SRGB24",
4532
"V1.4",
4533
"W8"
4534
};
4535
static const char * const urf_supported_color_duplex[] =
4536
{ /* urf-supported values */
4537
"CP1",
4538
"IS1-4-5-7-19",
4539
"MT1-2-3-4-5-6-8-9-10-11-12-13",
4540
"RS600",
4541
"SRGB24",
4542
"V1.4",
4543
"W8",
4544
"DM3"
4545
};
4546
static const char * const urf_supported_duplex[] =
4547
{ /* urf-supported values */
4548
"CP1",
4549
"IS1-4-5-19",
4550
"MT1-2-3-4-5-6",
4551
"RS600",
4552
"V1.4",
4553
"W8",
4554
"DM1"
4555
};
4556
4557
4558
attrs = ippNew();
4559
4560
if (ppm_color > 0)
4561
{
4562
num_media = (int)(sizeof(media_supported_color) / sizeof(media_supported_color[0]));
4563
media = media_supported_color;
4564
num_ready = (int)(sizeof(media_ready_color) / sizeof(media_ready_color[0]));
4565
ready = media_ready_color;
4566
}
4567
else
4568
{
4569
num_media = (int)(sizeof(media_supported) / sizeof(media_supported[0]));
4570
media = media_supported;
4571
num_ready = (int)(sizeof(media_ready) / sizeof(media_ready[0]));
4572
ready = media_ready;
4573
}
4574
4575
/* color-supported */
4576
ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", ppm_color > 0);
4577
4578
/* copies-default */
4579
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
4580
4581
/* copies-supported */
4582
ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, (cupsArrayFind(docformats, (void *)"application/pdf") != NULL || cupsArrayFind(docformats, (void *)"image/jpeg") != NULL) ? 999 : 1);
4583
4584
/* document-password-supported */
4585
if (cupsArrayFind(docformats, (void *)"application/pdf"))
4586
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 1023);
4587
4588
/* finishing-template-supported */
4589
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template-supported", NULL, "none");
4590
4591
/* finishings-col-database */
4592
col = ippNew();
4593
ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, "none");
4594
ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-database", col);
4595
ippDelete(col);
4596
4597
/* finishings-col-default */
4598
col = ippNew();
4599
ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, "none");
4600
ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col);
4601
ippDelete(col);
4602
4603
/* finishings-col-ready */
4604
col = ippNew();
4605
ippAddString(col, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishing-template", NULL, "none");
4606
ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-ready", col);
4607
ippDelete(col);
4608
4609
/* finishings-col-supported */
4610
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishings-col-supported", NULL, "finishing-template");
4611
4612
/* finishings-default */
4613
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
4614
4615
/* finishings-ready */
4616
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", IPP_FINISHINGS_NONE);
4617
4618
/* finishings-supported */
4619
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", IPP_FINISHINGS_NONE);
4620
4621
/* media-bottom-margin-supported */
4622
if (ppm_color > 0)
4623
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", (int)(sizeof(media_bottom_margin_supported) / sizeof(media_bottom_margin_supported[0])), media_bottom_margin_supported);
4624
else
4625
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", (int)(sizeof(media_bottom_margin_supported_color) / sizeof(media_bottom_margin_supported_color[0])), media_bottom_margin_supported_color);
4626
4627
/* media-col-database and media-col-default */
4628
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", num_media, NULL);
4629
for (i = 0; i < num_media; i ++)
4630
{
4631
int bottom, left, /* media-xxx-margins */
4632
right, top;
4633
const char *source; /* media-source, if any */
4634
4635
pwg = pwgMediaForPWG(media[i]);
4636
4637
if (pwg->width < 21000 && pwg->length < 21000)
4638
{
4639
source = "photo"; /* Photo size media from photo tray */
4640
bottom = /* Borderless margins */
4641
left =
4642
right =
4643
top = 0;
4644
}
4645
else if (pwg->width < 21000)
4646
{
4647
source = "by-pass-tray"; /* Envelopes from multi-purpose tray */
4648
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4649
left = /* Left/right margins are standard */
4650
right = media_lr_margin_supported[1];
4651
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4652
}
4653
else if (pwg->width == 21000)
4654
{
4655
source = NULL; /* A4 from any tray */
4656
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4657
left = /* Left/right margins are reduced */
4658
right = media_lr_margin_supported[0];
4659
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4660
}
4661
else
4662
{
4663
source = NULL; /* Other size media from any tray */
4664
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4665
left = /* Left/right margins are standard */
4666
right = media_lr_margin_supported[1];
4667
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4668
}
4669
4670
col = create_media_col(media[i], source, NULL, pwg->width, pwg->length, bottom, left, right, top);
4671
ippSetCollection(attrs, &attr, i, col);
4672
4673
ippDelete(col);
4674
}
4675
4676
/* media-col-default */
4677
pwg = pwgMediaForPWG(ready[0]);
4678
4679
if (pwg->width == 21000)
4680
col = create_media_col(ready[0], "main", "stationery", pwg->width, pwg->length, ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0], media_lr_margin_supported[0], media_lr_margin_supported[0], ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]);
4681
else
4682
col = create_media_col(ready[0], "main", "stationery", pwg->width, pwg->length, ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0], media_lr_margin_supported[1], media_lr_margin_supported[1], ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0]);
4683
4684
ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
4685
4686
ippDelete(col);
4687
4688
/* media-col-ready */
4689
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-ready", num_ready, NULL);
4690
for (i = 0; i < num_ready; i ++)
4691
{
4692
int bottom, left, /* media-xxx-margins */
4693
right, top;
4694
const char *source, /* media-source */
4695
*type; /* media-type */
4696
4697
pwg = pwgMediaForPWG(ready[i]);
4698
4699
if (pwg->width < 21000 && pwg->length < 21000)
4700
{
4701
source = "photo"; /* Photo size media from photo tray */
4702
type = "photographic-glossy"; /* Glossy photo paper */
4703
bottom = /* Borderless margins */
4704
left =
4705
right =
4706
top = 0;
4707
}
4708
else if (pwg->width < 21000)
4709
{
4710
source = "by-pass-tray"; /* Envelopes from multi-purpose tray */
4711
type = "envelope"; /* Envelope */
4712
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4713
left = /* Left/right margins are standard */
4714
right = media_lr_margin_supported[1];
4715
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4716
}
4717
else if (pwg->width == 21000)
4718
{
4719
source = "main"; /* A4 from main tray */
4720
type = "stationery"; /* Plain paper */
4721
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4722
left = /* Left/right margins are reduced */
4723
right = media_lr_margin_supported[0];
4724
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4725
}
4726
else
4727
{
4728
source = "main"; /* A4 from main tray */
4729
type = "stationery"; /* Plain paper */
4730
bottom = ppm_color > 0 ? media_bottom_margin_supported_color[1] : media_bottom_margin_supported[0];
4731
left = /* Left/right margins are standard */
4732
right = media_lr_margin_supported[1];
4733
top = ppm_color > 0 ? media_top_margin_supported_color[1] : media_top_margin_supported[0];
4734
}
4735
4736
col = create_media_col(ready[i], source, type, pwg->width, pwg->length, bottom, left, right, top);
4737
ippSetCollection(attrs, &attr, i, col);
4738
ippDelete(col);
4739
}
4740
4741
/* media-default */
4742
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-default", NULL, media[0]);
4743
4744
/* media-left/right-margin-supported */
4745
if (ppm_color > 0)
4746
{
4747
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", (int)(sizeof(media_lr_margin_supported_color) / sizeof(media_lr_margin_supported_color[0])), media_lr_margin_supported_color);
4748
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", (int)(sizeof(media_lr_margin_supported_color) / sizeof(media_lr_margin_supported_color[0])), media_lr_margin_supported_color);
4749
}
4750
else
4751
{
4752
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", (int)(sizeof(media_lr_margin_supported) / sizeof(media_lr_margin_supported[0])), media_lr_margin_supported);
4753
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", (int)(sizeof(media_lr_margin_supported) / sizeof(media_lr_margin_supported[0])), media_lr_margin_supported);
4754
}
4755
4756
/* media-ready */
4757
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", num_ready, NULL, ready);
4758
4759
/* media-supported */
4760
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-supported", num_media, NULL, media);
4761
4762
/* media-size-supported */
4763
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", num_media, NULL);
4764
for (i = 0; i < num_media; i ++)
4765
{
4766
pwg = pwgMediaForPWG(media[i]);
4767
col = create_media_size(pwg->width, pwg->length);
4768
4769
ippSetCollection(attrs, &attr, i, col);
4770
ippDelete(col);
4771
}
4772
4773
/* media-source-supported */
4774
if (ppm_color > 0)
4775
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", (int)(sizeof(media_source_supported_color) / sizeof(media_source_supported_color[0])), NULL, media_source_supported_color);
4776
else
4777
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", (int)(sizeof(media_source_supported) / sizeof(media_source_supported[0])), NULL, media_source_supported);
4778
4779
/* media-top-margin-supported */
4780
if (ppm_color > 0)
4781
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", (int)(sizeof(media_top_margin_supported) / sizeof(media_top_margin_supported[0])), media_top_margin_supported);
4782
else
4783
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", (int)(sizeof(media_top_margin_supported_color) / sizeof(media_top_margin_supported_color[0])), media_top_margin_supported_color);
4784
4785
/* media-type-supported */
4786
if (ppm_color > 0)
4787
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", (int)(sizeof(media_type_supported_color) / sizeof(media_type_supported_color[0])), NULL, media_type_supported_color);
4788
else
4789
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", (int)(sizeof(media_type_supported) / sizeof(media_type_supported[0])), NULL, media_type_supported);
4790
4791
/* orientation-requested-default */
4792
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
4793
4794
/* orientation-requested-supported */
4795
if (cupsArrayFind(docformats, (void *)"application/pdf") || cupsArrayFind(docformats, (void *)"image/jpeg"))
4796
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
4797
else
4798
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", IPP_ORIENT_PORTRAIT);
4799
4800
/* output-bin-default */
4801
if (ppm_color > 0)
4802
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-up");
4803
else
4804
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
4805
4806
/* output-bin-supported */
4807
if (ppm_color > 0)
4808
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-up");
4809
else
4810
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
4811
4812
/* overrides-supported */
4813
if (cupsArrayFind(docformats, (void *)"application/pdf"))
4814
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported);
4815
4816
/* page-ranges-supported */
4817
ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", cupsArrayFind(docformats, (void *)"application/pdf") != NULL);
4818
4819
/* pages-per-minute */
4820
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppm);
4821
4822
/* pages-per-minute-color */
4823
if (ppm_color > 0)
4824
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppm_color);
4825
4826
/* print-color-mode-default */
4827
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppm_color > 0 ? "auto" : "monochrome");
4828
4829
/* print-color-mode-supported */
4830
if (ppm_color > 0)
4831
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported_color) / sizeof(print_color_mode_supported_color[0])), NULL, print_color_mode_supported_color);
4832
else
4833
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported) / sizeof(print_color_mode_supported[0])), NULL, print_color_mode_supported);
4834
4835
/* print-content-optimize-default */
4836
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
4837
4838
/* print-content-optimize-supported */
4839
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
4840
4841
/* print-quality-default */
4842
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
4843
4844
/* print-quality-supported */
4845
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
4846
4847
/* print-rendering-intent-default */
4848
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
4849
4850
/* print-rendering-intent-supported */
4851
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
4852
4853
/* printer-device-id */
4854
snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model);
4855
ptr = device_id + strlen(device_id);
4856
prefix = "CMD:";
4857
for (format = (const char *)cupsArrayFirst(docformats); format; format = (const char *)cupsArrayNext(docformats))
4858
{
4859
if (!strcasecmp(format, "application/pdf"))
4860
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPDF", prefix);
4861
else if (!strcasecmp(format, "application/postscript"))
4862
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPS", prefix);
4863
else if (!strcasecmp(format, "application/vnd.hp-PCL"))
4864
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPCL", prefix);
4865
else if (!strcasecmp(format, "image/jpeg"))
4866
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sJPEG", prefix);
4867
else if (!strcasecmp(format, "image/png"))
4868
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPNG", prefix);
4869
else if (!strcasecmp(format, "image/pwg-raster"))
4870
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sPWG", prefix);
4871
else if (!strcasecmp(format, "image/urf"))
4872
snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%sURF", prefix);
4873
else
4874
continue;
4875
4876
ptr += strlen(ptr);
4877
prefix = ",";
4878
}
4879
if (ptr < (device_id + sizeof(device_id) - 1))
4880
{
4881
*ptr++ = ';';
4882
*ptr = '\0';
4883
}
4884
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
4885
4886
/* printer-input-tray */
4887
if (ppm_color > 0)
4888
{
4889
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray_color[0], (int)strlen(printer_input_tray_color[0]));
4890
for (i = 1; i < (int)(sizeof(printer_input_tray_color) / sizeof(printer_input_tray_color[0])); i ++)
4891
ippSetOctetString(attrs, &attr, i, printer_input_tray_color[i], (int)strlen(printer_input_tray_color[i]));
4892
}
4893
else
4894
{
4895
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray[0], (int)strlen(printer_input_tray[0]));
4896
for (i = 1; i < (int)(sizeof(printer_input_tray) / sizeof(printer_input_tray[0])); i ++)
4897
ippSetOctetString(attrs, &attr, i, printer_input_tray[i], (int)strlen(printer_input_tray[i]));
4898
}
4899
4900
/* printer-make-and-model */
4901
snprintf(make_model, sizeof(make_model), "%s %s", make, model);
4902
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, make_model);
4903
4904
/* printer-resolution-default */
4905
ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, 600, 600);
4906
4907
/* printer-resolution-supported */
4908
ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600);
4909
4910
/* printer-supply and printer-supply-description */
4911
if (ppm_color > 0)
4912
{
4913
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], (int)strlen(printer_supply_color[0]));
4914
for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
4915
ippSetOctetString(attrs, &attr, i, printer_supply_color[i], (int)strlen(printer_supply_color[i]));
4916
4917
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description_color) / sizeof(printer_supply_description_color[0])), NULL, printer_supply_description_color);
4918
}
4919
else
4920
{
4921
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], (int)strlen(printer_supply[0]));
4922
for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
4923
ippSetOctetString(attrs, &attr, i, printer_supply[i], (int)strlen(printer_supply[i]));
4924
4925
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description) / sizeof(printer_supply_description[0])), NULL, printer_supply_description);
4926
}
4927
4928
/* pwg-raster-document-xxx-supported */
4929
if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
4930
{
4931
ippAddResolutions(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", (int)(sizeof(pwg_raster_document_resolution_supported) / sizeof(pwg_raster_document_resolution_supported[0])), IPP_RES_PER_INCH, pwg_raster_document_resolution_supported, pwg_raster_document_resolution_supported);
4932
4933
if (ppm_color > 0 && duplex)
4934
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "rotated");
4935
else if (duplex)
4936
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
4937
4938
if (ppm_color > 0)
4939
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported_color) / sizeof(pwg_raster_document_type_supported_color[0])), NULL, pwg_raster_document_type_supported_color);
4940
else
4941
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported) / sizeof(pwg_raster_document_type_supported[0])), NULL, pwg_raster_document_type_supported);
4942
}
4943
4944
/* sides-default */
4945
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
4946
4947
/* sides-supported */
4948
if (duplex)
4949
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
4950
else
4951
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
4952
4953
/* urf-supported */
4954
if (cupsArrayFind(docformats, (void *)"image/urf"))
4955
{
4956
if (ppm_color > 0)
4957
{
4958
if (duplex)
4959
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color_duplex) / sizeof(urf_supported_color_duplex[0])), NULL, urf_supported_color_duplex);
4960
else
4961
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_color) / sizeof(urf_supported_color[0])), NULL, urf_supported_color);
4962
}
4963
else if (duplex)
4964
{
4965
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported_duplex) / sizeof(urf_supported_duplex[0])), NULL, urf_supported_duplex);
4966
}
4967
else
4968
{
4969
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", (int)(sizeof(urf_supported) / sizeof(urf_supported[0])), NULL, urf_supported);
4970
}
4971
}
4972
4973
return (attrs);
4974
}
4975
4976
4977
#if !CUPS_LITE
4978
/*
4979
* 'load_ppd_attributes()' - Load IPP attributes from a PPD file.
4980
*/
4981
4982
static ipp_t * /* O - IPP attributes or `NULL` on error */
4983
load_ppd_attributes(
4984
const char *ppdfile, /* I - PPD filename */
4985
cups_array_t *docformats) /* I - document-format-supported values */
4986
{
4987
int i, j; /* Looping vars */
4988
ipp_t *attrs; /* Attributes */
4989
ipp_attribute_t *attr; /* Current attribute */
4990
ipp_t *col; /* Current collection value */
4991
ppd_file_t *ppd; /* PPD data */
4992
ppd_attr_t *ppd_attr; /* PPD attribute */
4993
ppd_choice_t *ppd_choice; /* PPD choice */
4994
ppd_size_t *ppd_size; /* Default PPD size */
4995
pwg_size_t *pwg_size, /* Current PWG size */
4996
*default_size = NULL; /* Default PWG size */
4997
const char *default_source = NULL, /* Default media source */
4998
*default_type = NULL; /* Default media type */
4999
pwg_map_t *pwg_map; /* Mapping from PWG to PPD keywords */
5000
_ppd_cache_t *pc; /* PPD cache */
5001
_pwg_finishings_t *finishings; /* Current finishings value */
5002
const char *template; /* Current finishings-template value */
5003
int num_margins; /* Number of media-xxx-margin-supported values */
5004
int margins[10]; /* media-xxx-margin-supported values */
5005
int xres, /* Default horizontal resolution */
5006
yres; /* Default vertical resolution */
5007
int num_urf; /* Number of urf-supported values */
5008
const char *urf[10]; /* urf-supported values */
5009
char urf_rs[32]; /* RS value */
5010
static const int orientation_requested_supported[4] =
5011
{ /* orientation-requested-supported values */
5012
IPP_ORIENT_PORTRAIT,
5013
IPP_ORIENT_LANDSCAPE,
5014
IPP_ORIENT_REVERSE_LANDSCAPE,
5015
IPP_ORIENT_REVERSE_PORTRAIT
5016
};
5017
static const char * const overrides_supported[] =
5018
{ /* overrides-supported */
5019
"document-numbers",
5020
"media",
5021
"media-col",
5022
"orientation-requested",
5023
"pages"
5024
};
5025
static const char * const print_color_mode_supported[] =
5026
{ /* print-color-mode-supported values */
5027
"monochrome"
5028
};
5029
static const char * const print_color_mode_supported_color[] =
5030
{ /* print-color-mode-supported values */
5031
"auto",
5032
"color",
5033
"monochrome"
5034
};
5035
static const int print_quality_supported[] =
5036
{ /* print-quality-supported values */
5037
IPP_QUALITY_DRAFT,
5038
IPP_QUALITY_NORMAL,
5039
IPP_QUALITY_HIGH
5040
};
5041
static const char * const printer_supply[] =
5042
{ /* printer-supply values */
5043
"index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
5044
"maxcapacity=100;level=25;colorantname=unknown;",
5045
"index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
5046
"maxcapacity=100;level=75;colorantname=black;"
5047
};
5048
static const char * const printer_supply_color[] =
5049
{ /* printer-supply values */
5050
"index=1;class=receptacleThatIsFilled;type=wasteInk;unit=percent;"
5051
"maxcapacity=100;level=25;colorantname=unknown;",
5052
"index=2;class=supplyThatIsConsumed;type=ink;unit=percent;"
5053
"maxcapacity=100;level=75;colorantname=black;",
5054
"index=3;class=supplyThatIsConsumed;type=ink;unit=percent;"
5055
"maxcapacity=100;level=50;colorantname=cyan;",
5056
"index=4;class=supplyThatIsConsumed;type=ink;unit=percent;"
5057
"maxcapacity=100;level=33;colorantname=magenta;",
5058
"index=5;class=supplyThatIsConsumed;type=ink;unit=percent;"
5059
"maxcapacity=100;level=67;colorantname=yellow;"
5060
};
5061
static const char * const printer_supply_description[] =
5062
{ /* printer-supply-description values */
5063
"Toner Waste Tank",
5064
"Black Toner"
5065
};
5066
static const char * const printer_supply_description_color[] =
5067
{ /* printer-supply-description values */
5068
"Ink Waste Tank",
5069
"Black Ink",
5070
"Cyan Ink",
5071
"Magenta Ink",
5072
"Yellow Ink"
5073
};
5074
static const char * const pwg_raster_document_type_supported[] =
5075
{
5076
"black_1",
5077
"sgray_8"
5078
};
5079
static const char * const pwg_raster_document_type_supported_color[] =
5080
{
5081
"black_1",
5082
"sgray_8",
5083
"srgb_8",
5084
"srgb_16"
5085
};
5086
static const char * const sides_supported[] =
5087
{ /* sides-supported values */
5088
"one-sided",
5089
"two-sided-long-edge",
5090
"two-sided-short-edge"
5091
};
5092
5093
5094
/*
5095
* Open the PPD file...
5096
*/
5097
5098
if ((ppd = ppdOpenFile(ppdfile)) == NULL)
5099
{
5100
ppd_status_t status; /* Load error */
5101
5102
status = ppdLastError(&i);
5103
_cupsLangPrintf(stderr, _("ippeveprinter: Unable to open \"%s\": %s on line %d."), ppdfile, ppdErrorString(status), i);
5104
return (NULL);
5105
}
5106
5107
ppdMarkDefaults(ppd);
5108
5109
pc = _ppdCacheCreateWithPPD(ppd);
5110
5111
if ((ppd_size = ppdPageSize(ppd, NULL)) != NULL)
5112
{
5113
/*
5114
* Look up default size...
5115
*/
5116
5117
for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5118
{
5119
if (!strcmp(pwg_size->map.ppd, ppd_size->name))
5120
{
5121
default_size = pwg_size;
5122
break;
5123
}
5124
}
5125
}
5126
5127
if (!default_size)
5128
{
5129
/*
5130
* Default to A4 or Letter...
5131
*/
5132
5133
for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5134
{
5135
if (!strcmp(pwg_size->map.ppd, "Letter") || !strcmp(pwg_size->map.ppd, "A4"))
5136
{
5137
default_size = pwg_size;
5138
break;
5139
}
5140
}
5141
5142
if (!default_size)
5143
default_size = pc->sizes; /* Last resort: first size */
5144
}
5145
5146
if ((ppd_choice = ppdFindMarkedChoice(ppd, "InputSlot")) != NULL)
5147
default_source = _ppdCacheGetSource(pc, ppd_choice->choice);
5148
5149
if ((ppd_choice = ppdFindMarkedChoice(ppd, "MediaType")) != NULL)
5150
default_source = _ppdCacheGetType(pc, ppd_choice->choice);
5151
5152
if ((ppd_attr = ppdFindAttr(ppd, "DefaultResolution", NULL)) != NULL)
5153
{
5154
/*
5155
* Use the PPD-defined default resolution...
5156
*/
5157
5158
if ((i = sscanf(ppd_attr->value, "%dx%d", &xres, &yres)) == 1)
5159
yres = xres;
5160
else if (i < 0)
5161
xres = yres = 300;
5162
}
5163
else
5164
{
5165
/*
5166
* Use default of 300dpi...
5167
*/
5168
5169
xres = yres = 300;
5170
}
5171
5172
snprintf(urf_rs, sizeof(urf_rs), "RS%d", yres < xres ? yres : xres);
5173
5174
num_urf = 0;
5175
urf[num_urf ++] = "V1.4";
5176
urf[num_urf ++] = "CP1";
5177
urf[num_urf ++] = urf_rs;
5178
urf[num_urf ++] = "W8";
5179
if (pc->sides_2sided_long)
5180
urf[num_urf ++] = "DM1";
5181
if (ppd->color_device)
5182
urf[num_urf ++] = "SRGB24";
5183
5184
/*
5185
* PostScript printers accept PDF via one of the CUPS PDF to PostScript
5186
* filters, along with PostScript (of course) and JPEG...
5187
*/
5188
5189
cupsArrayAdd(docformats, "application/pdf");
5190
cupsArrayAdd(docformats, "application/postscript");
5191
cupsArrayAdd(docformats, "image/jpeg");
5192
5193
/*
5194
* Create the attributes...
5195
*/
5196
5197
attrs = ippNew();
5198
5199
/* color-supported */
5200
ippAddBoolean(attrs, IPP_TAG_PRINTER, "color-supported", (char)ppd->color_device);
5201
5202
/* copies-default */
5203
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "copies-default", 1);
5204
5205
/* copies-supported */
5206
ippAddRange(attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999);
5207
5208
/* document-password-supported */
5209
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "document-password-supported", 127);
5210
5211
/* finishing-template-supported */
5212
attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template-supported", cupsArrayCount(pc->templates) + 1, NULL, NULL);
5213
ippSetString(attrs, &attr, 0, "none");
5214
for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
5215
ippSetString(attrs, &attr, i, template);
5216
5217
/* finishings-col-database */
5218
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-database", cupsArrayCount(pc->templates) + 1, NULL);
5219
5220
col = ippNew();
5221
ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
5222
ippSetCollection(attrs, &attr, 0, col);
5223
ippDelete(col);
5224
5225
for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
5226
{
5227
col = ippNew();
5228
ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
5229
ippSetCollection(attrs, &attr, i, col);
5230
ippDelete(col);
5231
}
5232
5233
/* finishings-col-default */
5234
col = ippNew();
5235
ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
5236
ippAddCollection(attrs, IPP_TAG_PRINTER, "finishings-col-default", col);
5237
ippDelete(col);
5238
5239
/* finishings-col-ready */
5240
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "finishings-col-ready", cupsArrayCount(pc->templates) + 1, NULL);
5241
5242
col = ippNew();
5243
ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, "none");
5244
ippSetCollection(attrs, &attr, 0, col);
5245
ippDelete(col);
5246
5247
for (i = 1, template = (const char *)cupsArrayFirst(pc->templates); template; i ++, template = (const char *)cupsArrayNext(pc->templates))
5248
{
5249
col = ippNew();
5250
ippAddString(col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "finishing-template", NULL, template);
5251
ippSetCollection(attrs, &attr, i, col);
5252
ippDelete(col);
5253
}
5254
5255
/* finishings-col-supported */
5256
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "finishings-col-supported", NULL, "finishing-template");
5257
5258
/* finishings-default */
5259
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-default", IPP_FINISHINGS_NONE);
5260
5261
/* finishings-ready */
5262
attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-ready", cupsArrayCount(pc->finishings) + 1, NULL);
5263
ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
5264
for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
5265
ippSetInteger(attrs, &attr, i, (int)finishings->value);
5266
5267
/* finishings-supported */
5268
attr = ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "finishings-supported", cupsArrayCount(pc->finishings) + 1, NULL);
5269
ippSetInteger(attrs, &attr, 0, IPP_FINISHINGS_NONE);
5270
for (i = 1, finishings = (_pwg_finishings_t *)cupsArrayFirst(pc->finishings); finishings; i ++, finishings = (_pwg_finishings_t *)cupsArrayNext(pc->finishings))
5271
ippSetInteger(attrs, &attr, i, (int)finishings->value);
5272
5273
/* media-bottom-margin-supported */
5274
for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5275
{
5276
for (j = 0; j < num_margins; j ++)
5277
{
5278
if (margins[j] == pwg_size->bottom)
5279
break;
5280
}
5281
5282
if (j >= num_margins)
5283
margins[num_margins ++] = pwg_size->bottom;
5284
}
5285
5286
for (i = 0; i < (num_margins - 1); i ++)
5287
{
5288
for (j = i + 1; j < num_margins; j ++)
5289
{
5290
if (margins[i] > margins[j])
5291
{
5292
int mtemp = margins[i];
5293
5294
margins[i] = margins[j];
5295
margins[j] = mtemp;
5296
}
5297
}
5298
}
5299
5300
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-bottom-margin-supported", num_margins, margins);
5301
5302
/* media-col-database */
5303
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-col-database", pc->num_sizes, NULL);
5304
for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5305
{
5306
col = create_media_col(pwg_size->map.pwg, NULL, NULL, pwg_size->width, pwg_size->length, pwg_size->bottom, pwg_size->left, pwg_size->right, pwg_size->top);
5307
ippSetCollection(attrs, &attr, i, col);
5308
ippDelete(col);
5309
}
5310
5311
/* media-col-default */
5312
col = create_media_col(default_size->map.pwg, default_source, default_type, default_size->width, default_size->length, default_size->bottom, default_size->left, default_size->right, default_size->top);
5313
ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-default", col);
5314
ippDelete(col);
5315
5316
/* media-col-ready */
5317
col = create_media_col(default_size->map.pwg, default_source, default_type, default_size->width, default_size->length, default_size->bottom, default_size->left, default_size->right, default_size->top);
5318
ippAddCollection(attrs, IPP_TAG_PRINTER, "media-col-ready", col);
5319
ippDelete(col);
5320
5321
/* media-default */
5322
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-default", NULL, default_size->map.pwg);
5323
5324
/* media-left-margin-supported */
5325
for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5326
{
5327
for (j = 0; j < num_margins; j ++)
5328
{
5329
if (margins[j] == pwg_size->left)
5330
break;
5331
}
5332
5333
if (j >= num_margins)
5334
margins[num_margins ++] = pwg_size->left;
5335
}
5336
5337
for (i = 0; i < (num_margins - 1); i ++)
5338
{
5339
for (j = i + 1; j < num_margins; j ++)
5340
{
5341
if (margins[i] > margins[j])
5342
{
5343
int mtemp = margins[i];
5344
5345
margins[i] = margins[j];
5346
margins[j] = mtemp;
5347
}
5348
}
5349
}
5350
5351
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-left-margin-supported", num_margins, margins);
5352
5353
/* media-ready */
5354
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, default_size->map.pwg);
5355
5356
/* media-right-margin-supported */
5357
for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5358
{
5359
for (j = 0; j < num_margins; j ++)
5360
{
5361
if (margins[j] == pwg_size->right)
5362
break;
5363
}
5364
5365
if (j >= num_margins)
5366
margins[num_margins ++] = pwg_size->right;
5367
}
5368
5369
for (i = 0; i < (num_margins - 1); i ++)
5370
{
5371
for (j = i + 1; j < num_margins; j ++)
5372
{
5373
if (margins[i] > margins[j])
5374
{
5375
int mtemp = margins[i];
5376
5377
margins[i] = margins[j];
5378
margins[j] = mtemp;
5379
}
5380
}
5381
}
5382
5383
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-right-margin-supported", num_margins, margins);
5384
5385
/* media-supported */
5386
attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-supported", pc->num_sizes, NULL, NULL);
5387
for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5388
ippSetString(attrs, &attr, i, pwg_size->map.pwg);
5389
5390
/* media-size-supported */
5391
attr = ippAddCollections(attrs, IPP_TAG_PRINTER, "media-size-supported", pc->num_sizes, NULL);
5392
for (i = 0, pwg_size = pc->sizes; i < pc->num_sizes; i ++, pwg_size ++)
5393
{
5394
col = create_media_size(pwg_size->width, pwg_size->length);
5395
ippSetCollection(attrs, &attr, i, col);
5396
ippDelete(col);
5397
}
5398
5399
/* media-source-supported */
5400
if (pc->num_sources > 0)
5401
{
5402
attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-source-supported", pc->num_sources, NULL, NULL);
5403
for (i = 0, pwg_map = pc->sources; i < pc->num_sources; i ++, pwg_map ++)
5404
ippSetString(attrs, &attr, i, pwg_map->pwg);
5405
}
5406
else
5407
{
5408
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-source-supported", NULL, "auto");
5409
}
5410
5411
/* media-top-margin-supported */
5412
for (i = 0, num_margins = 0, pwg_size = pc->sizes; i < pc->num_sizes && num_margins < (int)(sizeof(margins) / sizeof(margins[0])); i ++, pwg_size ++)
5413
{
5414
for (j = 0; j < num_margins; j ++)
5415
{
5416
if (margins[j] == pwg_size->top)
5417
break;
5418
}
5419
5420
if (j >= num_margins)
5421
margins[num_margins ++] = pwg_size->top;
5422
}
5423
5424
for (i = 0; i < (num_margins - 1); i ++)
5425
{
5426
for (j = i + 1; j < num_margins; j ++)
5427
{
5428
if (margins[i] > margins[j])
5429
{
5430
int mtemp = margins[i];
5431
5432
margins[i] = margins[j];
5433
margins[j] = mtemp;
5434
}
5435
}
5436
}
5437
5438
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", num_margins, margins);
5439
5440
/* media-type-supported */
5441
if (pc->num_types > 0)
5442
{
5443
attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type-supported", pc->num_types, NULL, NULL);
5444
for (i = 0, pwg_map = pc->types; i < pc->num_types; i ++, pwg_map ++)
5445
ippSetString(attrs, &attr, i, pwg_map->pwg);
5446
}
5447
else
5448
{
5449
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "media-type-supported", NULL, "auto");
5450
}
5451
5452
/* orientation-requested-default */
5453
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-default", IPP_ORIENT_PORTRAIT);
5454
5455
/* orientation-requested-supported */
5456
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "orientation-requested-supported", (int)(sizeof(orientation_requested_supported) / sizeof(orientation_requested_supported[0])), orientation_requested_supported);
5457
5458
/* output-bin-default */
5459
if (pc->num_bins > 0)
5460
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-default", NULL, pc->bins->pwg);
5461
else
5462
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-default", NULL, "face-down");
5463
5464
/* output-bin-supported */
5465
if (pc->num_bins > 0)
5466
{
5467
attr = ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "output-bin-supported", pc->num_bins, NULL, NULL);
5468
for (i = 0, pwg_map = pc->bins; i < pc->num_bins; i ++, pwg_map ++)
5469
ippSetString(attrs, &attr, i, pwg_map->pwg);
5470
}
5471
else
5472
{
5473
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "output-bin-supported", NULL, "face-down");
5474
}
5475
5476
/* overrides-supported */
5477
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "overrides-supported", (int)(sizeof(overrides_supported) / sizeof(overrides_supported[0])), NULL, overrides_supported);
5478
5479
/* page-ranges-supported */
5480
ippAddBoolean(attrs, IPP_TAG_PRINTER, "page-ranges-supported", 1);
5481
5482
/* pages-per-minute */
5483
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute", ppd->throughput);
5484
5485
/* pages-per-minute-color */
5486
if (ppd->color_device)
5487
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "pages-per-minute-color", ppd->throughput);
5488
5489
/* print-color-mode-default */
5490
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-default", NULL, ppd->color_device ? "auto" : "monochrome");
5491
5492
/* print-color-mode-supported */
5493
if (ppd->color_device)
5494
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported_color) / sizeof(print_color_mode_supported_color[0])), NULL, print_color_mode_supported_color);
5495
else
5496
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-color-mode-supported", (int)(sizeof(print_color_mode_supported) / sizeof(print_color_mode_supported[0])), NULL, print_color_mode_supported);
5497
5498
/* print-content-optimize-default */
5499
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-default", NULL, "auto");
5500
5501
/* print-content-optimize-supported */
5502
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-content-optimize-supported", NULL, "auto");
5503
5504
/* print-quality-default */
5505
ippAddInteger(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-default", IPP_QUALITY_NORMAL);
5506
5507
/* print-quality-supported */
5508
ippAddIntegers(attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, "print-quality-supported", (int)(sizeof(print_quality_supported) / sizeof(print_quality_supported[0])), print_quality_supported);
5509
5510
/* print-rendering-intent-default */
5511
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-default", NULL, "auto");
5512
5513
/* print-rendering-intent-supported */
5514
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "print-rendering-intent-supported", NULL, "auto");
5515
5516
/* printer-device-id */
5517
if ((ppd_attr = ppdFindAttr(ppd, "1284DeviceId", NULL)) != NULL)
5518
{
5519
/*
5520
* Use the device ID string from the PPD...
5521
*/
5522
5523
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, ppd_attr->value);
5524
}
5525
else
5526
{
5527
/*
5528
* Synthesize a device ID string...
5529
*/
5530
5531
char device_id[1024]; /* Device ID string */
5532
5533
snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;CMD:PS;", ppd->manufacturer, ppd->modelname);
5534
5535
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-device-id", NULL, device_id);
5536
}
5537
5538
/* printer-input-tray */
5539
if (pc->num_sources > 0)
5540
{
5541
for (i = 0, attr = NULL; i < pc->num_sources; i ++)
5542
{
5543
char input_tray[1024]; /* printer-input-tray value */
5544
5545
if (!strcmp(pc->sources[i].pwg, "manual") || strstr(pc->sources[i].pwg, "-man") != NULL)
5546
snprintf(input_tray, sizeof(input_tray), "type=sheetFeedManual;mediafeed=0;mediaxfeed=0;maxcapacity=1;level=-2;status=0;name=%s", pc->sources[i].pwg);
5547
else
5548
snprintf(input_tray, sizeof(input_tray), "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=250;level=125;status=0;name=%s", pc->sources[i].pwg);
5549
5550
if (attr)
5551
ippSetOctetString(attrs, &attr, i, input_tray, (int)strlen(input_tray));
5552
else
5553
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", input_tray, (int)strlen(input_tray));
5554
}
5555
}
5556
else
5557
{
5558
static const char *printer_input_tray = "type=sheetFeedAutoRemovableTray;mediafeed=0;mediaxfeed=0;maxcapacity=-2;level=-2;status=0;name=auto";
5559
5560
ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-input-tray", printer_input_tray, (int)strlen(printer_input_tray));
5561
}
5562
5563
/* printer-make-and-model */
5564
ippAddString(attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-make-and-model", NULL, ppd->nickname);
5565
5566
/* printer-resolution-default */
5567
ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-default", IPP_RES_PER_INCH, xres, yres);
5568
5569
/* printer-resolution-supported */
5570
ippAddResolution(attrs, IPP_TAG_PRINTER, "printer-resolution-supported", IPP_RES_PER_INCH, xres, yres);
5571
5572
/* printer-supply and printer-supply-description */
5573
if (ppd->color_device)
5574
{
5575
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply_color[0], (int)strlen(printer_supply_color[0]));
5576
for (i = 1; i < (int)(sizeof(printer_supply_color) / sizeof(printer_supply_color[0])); i ++)
5577
ippSetOctetString(attrs, &attr, i, printer_supply_color[i], (int)strlen(printer_supply_color[i]));
5578
5579
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description_color) / sizeof(printer_supply_description_color[0])), NULL, printer_supply_description_color);
5580
}
5581
else
5582
{
5583
attr = ippAddOctetString(attrs, IPP_TAG_PRINTER, "printer-supply", printer_supply[0], (int)strlen(printer_supply[0]));
5584
for (i = 1; i < (int)(sizeof(printer_supply) / sizeof(printer_supply[0])); i ++)
5585
ippSetOctetString(attrs, &attr, i, printer_supply[i], (int)strlen(printer_supply[i]));
5586
5587
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_TEXT), "printer-supply-description", (int)(sizeof(printer_supply_description) / sizeof(printer_supply_description[0])), NULL, printer_supply_description);
5588
}
5589
5590
/* pwg-raster-document-xxx-supported */
5591
if (cupsArrayFind(docformats, (void *)"image/pwg-raster"))
5592
{
5593
ippAddResolution(attrs, IPP_TAG_PRINTER, "pwg-raster-document-resolution-supported", IPP_RES_PER_INCH, xres, yres);
5594
5595
if (pc->sides_2sided_long)
5596
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-sheet-back", NULL, "normal");
5597
5598
if (ppd->color_device)
5599
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported_color) / sizeof(pwg_raster_document_type_supported_color[0])), NULL, pwg_raster_document_type_supported_color);
5600
else
5601
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "pwg-raster-document-type-supported", (int)(sizeof(pwg_raster_document_type_supported) / sizeof(pwg_raster_document_type_supported[0])), NULL, pwg_raster_document_type_supported);
5602
}
5603
5604
/* sides-default */
5605
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-default", NULL, "one-sided");
5606
5607
/* sides-supported */
5608
if (pc->sides_2sided_long)
5609
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", (int)(sizeof(sides_supported) / sizeof(sides_supported[0])), NULL, sides_supported);
5610
else
5611
ippAddString(attrs, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_KEYWORD), "sides-supported", NULL, "one-sided");
5612
5613
/* urf-supported */
5614
if (cupsArrayFind(docformats, (void *)"image/urf"))
5615
ippAddStrings(attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "urf-supported", num_urf, NULL, urf);
5616
5617
/*
5618
* Free the PPD file and return the attributes...
5619
*/
5620
5621
_ppdCacheDestroy(pc);
5622
5623
ppdClose(ppd);
5624
5625
return (attrs);
5626
}
5627
#endif /* !CUPS_LITE */
5628
5629
5630
#if HAVE_LIBPAM
5631
/*
5632
* 'pam_func()' - PAM conversation function.
5633
*/
5634
5635
static int /* O - Success or failure */
5636
pam_func(
5637
int num_msg, /* I - Number of messages */
5638
const struct pam_message **msg, /* I - Messages */
5639
struct pam_response **resp, /* O - Responses */
5640
void *appdata_ptr)
5641
/* I - Pointer to connection */
5642
{
5643
int i; /* Looping var */
5644
struct pam_response *replies; /* Replies */
5645
ippeve_authdata_t *data; /* Pointer to auth data */
5646
5647
5648
/*
5649
* Allocate memory for the responses...
5650
*/
5651
5652
if ((replies = malloc(sizeof(struct pam_response) * (size_t)num_msg)) == NULL)
5653
return (PAM_CONV_ERR);
5654
5655
/*
5656
* Answer all of the messages...
5657
*/
5658
5659
data = (ippeve_authdata_t *)appdata_ptr;
5660
5661
for (i = 0; i < num_msg; i ++)
5662
{
5663
switch (msg[i]->msg_style)
5664
{
5665
case PAM_PROMPT_ECHO_ON:
5666
replies[i].resp_retcode = PAM_SUCCESS;
5667
replies[i].resp = strdup(data->username);
5668
break;
5669
5670
case PAM_PROMPT_ECHO_OFF:
5671
replies[i].resp_retcode = PAM_SUCCESS;
5672
replies[i].resp = strdup(data->password);
5673
break;
5674
5675
case PAM_TEXT_INFO:
5676
replies[i].resp_retcode = PAM_SUCCESS;
5677
replies[i].resp = NULL;
5678
break;
5679
5680
case PAM_ERROR_MSG:
5681
replies[i].resp_retcode = PAM_SUCCESS;
5682
replies[i].resp = NULL;
5683
break;
5684
5685
default:
5686
free(replies);
5687
return (PAM_CONV_ERR);
5688
}
5689
}
5690
5691
/*
5692
* Return the responses back to PAM...
5693
*/
5694
5695
*resp = replies;
5696
5697
return (PAM_SUCCESS);
5698
}
5699
#endif /* HAVE_LIBPAM */
5700
5701
5702
/*
5703
* 'parse_options()' - Parse URL options into CUPS options.
5704
*
5705
* The client->options string is destroyed by this function.
5706
*/
5707
5708
static int /* O - Number of options */
5709
parse_options(ippeve_client_t *client, /* I - Client */
5710
cups_option_t **options)/* O - Options */
5711
{
5712
char *name, /* Name */
5713
*value, /* Value */
5714
*next; /* Next name=value pair */
5715
int num_options = 0; /* Number of options */
5716
5717
5718
*options = NULL;
5719
5720
for (name = client->options; name && *name; name = next)
5721
{
5722
if ((value = strchr(name, '=')) == NULL)
5723
break;
5724
5725
*value++ = '\0';
5726
if ((next = strchr(value, '&')) != NULL)
5727
*next++ = '\0';
5728
5729
num_options = cupsAddOption(name, value, num_options, options);
5730
}
5731
5732
return (num_options);
5733
}
5734
5735
5736
/*
5737
* 'process_attr_message()' - Process an ATTR: message from a command.
5738
*/
5739
5740
static void
5741
process_attr_message(
5742
ippeve_job_t *job, /* I - Job */
5743
char *message) /* I - Message */
5744
{
5745
int i, /* Looping var */
5746
num_options = 0; /* Number of name=value pairs */
5747
cups_option_t *options = NULL, /* name=value pairs from message */
5748
*option; /* Current option */
5749
ipp_attribute_t *attr; /* Current attribute */
5750
5751
5752
/*
5753
* Grab attributes from the message line...
5754
*/
5755
5756
num_options = cupsParseOptions(message + 5, num_options, &options);
5757
5758
/*
5759
* Loop through the options and record them in the printer or job objects...
5760
*/
5761
5762
for (i = num_options, option = options; i > 0; i --, option ++)
5763
{
5764
if (!strcmp(option->name, "job-impressions"))
5765
{
5766
/*
5767
* Update job-impressions attribute...
5768
*/
5769
5770
job->impressions = atoi(option->value);
5771
}
5772
else if (!strcmp(option->name, "job-impressions-completed"))
5773
{
5774
/*
5775
* Update job-impressions-completed attribute...
5776
*/
5777
5778
job->impcompleted = atoi(option->value);
5779
}
5780
else if (!strncmp(option->name, "marker-", 7) || !strcmp(option->name, "printer-alert") || !strcmp(option->name, "printer-alert-description") || !strcmp(option->name, "printer-supply") || !strcmp(option->name, "printer-supply-description"))
5781
{
5782
/*
5783
* Update Printer Status attribute...
5784
*/
5785
5786
_cupsRWLockWrite(&job->printer->rwlock);
5787
5788
if ((attr = ippFindAttribute(job->printer->attrs, option->name, IPP_TAG_ZERO)) != NULL)
5789
ippDeleteAttribute(job->printer->attrs, attr);
5790
5791
cupsEncodeOption(job->printer->attrs, IPP_TAG_PRINTER, option->name, option->value);
5792
5793
_cupsRWUnlock(&job->printer->rwlock);
5794
}
5795
else
5796
{
5797
/*
5798
* Something else that isn't currently supported...
5799
*/
5800
5801
fprintf(stderr, "[Job %d] Ignoring update of attribute \"%s\" with value \"%s\".\n", job->id, option->name, option->value);
5802
}
5803
}
5804
5805
cupsFreeOptions(num_options, options);
5806
}
5807
5808
5809
/*
5810
* 'process_client()' - Process client requests on a thread.
5811
*/
5812
5813
static void * /* O - Exit status */
5814
process_client(ippeve_client_t *client) /* I - Client */
5815
{
5816
/*
5817
* Loop until we are out of requests or timeout (30 seconds)...
5818
*/
5819
5820
#ifdef HAVE_TLS
5821
int first_time = 1; /* First time request? */
5822
#endif /* HAVE_TLS */
5823
5824
while (httpWait(client->http, 30000))
5825
{
5826
#ifdef HAVE_TLS
5827
if (first_time)
5828
{
5829
/*
5830
* See if we need to negotiate a TLS connection...
5831
*/
5832
5833
char buf[1]; /* First byte from client */
5834
5835
if (recv(httpGetFd(client->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0])))
5836
{
5837
fprintf(stderr, "%s Starting HTTPS session.\n", client->hostname);
5838
5839
if (httpEncryption(client->http, HTTP_ENCRYPTION_ALWAYS))
5840
{
5841
fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
5842
break;
5843
}
5844
5845
fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
5846
}
5847
5848
first_time = 0;
5849
}
5850
#endif /* HAVE_TLS */
5851
5852
if (!process_http(client))
5853
break;
5854
}
5855
5856
/*
5857
* Close the connection to the client and return...
5858
*/
5859
5860
delete_client(client);
5861
5862
return (NULL);
5863
}
5864
5865
5866
/*
5867
* 'process_http()' - Process a HTTP request.
5868
*/
5869
5870
int /* O - 1 on success, 0 on failure */
5871
process_http(ippeve_client_t *client) /* I - Client connection */
5872
{
5873
char uri[1024]; /* URI */
5874
http_state_t http_state; /* HTTP state */
5875
http_status_t http_status; /* HTTP status */
5876
ipp_state_t ipp_state; /* State of IPP transfer */
5877
char scheme[32], /* Method/scheme */
5878
userpass[128], /* Username:password */
5879
hostname[HTTP_MAX_HOST],
5880
/* Hostname */
5881
*ptr; /* Pointer into value */
5882
int port; /* Port number */
5883
static const char * const http_states[] =
5884
{ /* Strings for logging HTTP method */
5885
"WAITING",
5886
"OPTIONS",
5887
"GET",
5888
"GET_SEND",
5889
"HEAD",
5890
"POST",
5891
"POST_RECV",
5892
"POST_SEND",
5893
"PUT",
5894
"PUT_RECV",
5895
"DELETE",
5896
"TRACE",
5897
"CONNECT",
5898
"STATUS",
5899
"UNKNOWN_METHOD",
5900
"UNKNOWN_VERSION"
5901
};
5902
5903
5904
/*
5905
* Clear state variables...
5906
*/
5907
5908
client->username[0] = '\0';
5909
5910
ippDelete(client->request);
5911
ippDelete(client->response);
5912
5913
client->request = NULL;
5914
client->response = NULL;
5915
client->operation = HTTP_STATE_WAITING;
5916
5917
/*
5918
* Read a request from the connection...
5919
*/
5920
5921
while ((http_state = httpReadRequest(client->http, uri,
5922
sizeof(uri))) == HTTP_STATE_WAITING)
5923
usleep(1);
5924
5925
/*
5926
* Parse the request line...
5927
*/
5928
5929
if (http_state == HTTP_STATE_ERROR)
5930
{
5931
if (httpError(client->http) == EPIPE)
5932
fprintf(stderr, "%s Client closed connection.\n", client->hostname);
5933
else
5934
fprintf(stderr, "%s Bad request line (%s).\n", client->hostname, strerror(httpError(client->http)));
5935
5936
return (0);
5937
}
5938
else if (http_state == HTTP_STATE_UNKNOWN_METHOD)
5939
{
5940
fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname);
5941
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5942
return (0);
5943
}
5944
else if (http_state == HTTP_STATE_UNKNOWN_VERSION)
5945
{
5946
fprintf(stderr, "%s Bad HTTP version.\n", client->hostname);
5947
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5948
return (0);
5949
}
5950
5951
fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state], uri);
5952
5953
/*
5954
* Separate the URI into its components...
5955
*/
5956
5957
if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
5958
userpass, sizeof(userpass),
5959
hostname, sizeof(hostname), &port,
5960
client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK &&
5961
(http_state != HTTP_STATE_OPTIONS || strcmp(uri, "*")))
5962
{
5963
fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri);
5964
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5965
return (0);
5966
}
5967
5968
if ((client->options = strchr(client->uri, '?')) != NULL)
5969
*(client->options)++ = '\0';
5970
5971
/*
5972
* Process the request...
5973
*/
5974
5975
client->start = time(NULL);
5976
client->operation = httpGetState(client->http);
5977
5978
/*
5979
* Parse incoming parameters until the status changes...
5980
*/
5981
5982
while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE);
5983
5984
if (http_status != HTTP_STATUS_OK)
5985
{
5986
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
5987
return (0);
5988
}
5989
5990
/*
5991
* Validate the host header...
5992
*/
5993
5994
if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] &&
5995
httpGetVersion(client->http) >= HTTP_VERSION_1_1)
5996
{
5997
/*
5998
* HTTP/1.1 and higher require the "Host:" field...
5999
*/
6000
6001
fprintf(stderr, "%s Missing Host: header.\n", client->hostname);
6002
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
6003
return (0);
6004
}
6005
6006
strlcpy(client->host_field, httpGetField(client->http, HTTP_FIELD_HOST), sizeof(client->host_field));
6007
if ((ptr = strrchr(client->host_field, ':')) != NULL)
6008
{
6009
/*
6010
* Grab port number from Host: header...
6011
*/
6012
6013
*ptr++ = '\0';
6014
client->host_port = atoi(ptr);
6015
}
6016
else
6017
{
6018
/*
6019
* Use the default port number...
6020
*/
6021
6022
client->host_port = client->printer->port;
6023
}
6024
6025
if ((ptr = strstr(client->host_field, ".local")) == NULL)
6026
ptr = strrchr(client->host_field, '.');
6027
6028
if (!isdigit(client->host_field[0] & 255) && client->host_field[0] != '[' && strcmp(client->host_field, client->printer->hostname) && strcmp(client->host_field, "localhost") &&
6029
(!ptr || (strcmp(ptr, ".local") && strcmp(ptr, ".local."))))
6030
{
6031
fprintf(stderr, "%s Bad Host: header '%s'.\n", client->hostname, client->host_field);
6032
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
6033
return (0);
6034
}
6035
6036
/*
6037
* Handle HTTP Upgrade...
6038
*/
6039
6040
if (!strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION), "Upgrade"))
6041
{
6042
#ifdef HAVE_TLS
6043
if (strstr(httpGetField(client->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(client->http))
6044
{
6045
if (!respond_http(client, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, NULL, 0))
6046
return (0);
6047
6048
fprintf(stderr, "%s Upgrading to encrypted connection.\n", client->hostname);
6049
6050
if (httpEncryption(client->http, HTTP_ENCRYPTION_REQUIRED))
6051
{
6052
fprintf(stderr, "%s Unable to encrypt connection: %s\n", client->hostname, cupsLastErrorString());
6053
return (0);
6054
}
6055
6056
fprintf(stderr, "%s Connection now encrypted.\n", client->hostname);
6057
}
6058
else
6059
#endif /* HAVE_TLS */
6060
6061
if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0))
6062
return (0);
6063
}
6064
6065
/*
6066
* Handle new transfers...
6067
*/
6068
6069
switch (client->operation)
6070
{
6071
case HTTP_STATE_OPTIONS :
6072
/*
6073
* Do OPTIONS command...
6074
*/
6075
6076
return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0));
6077
6078
case HTTP_STATE_HEAD :
6079
if (!strcmp(client->uri, "/en.strings"))
6080
return (respond_http(client, HTTP_STATUS_OK, NULL, "text/strings", 0));
6081
else if (!strcmp(client->uri, "/icon.png") || !strcmp(client->uri, "/icon-lg.png") || !strcmp(client->uri, "/icon-sm.png"))
6082
return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0));
6083
else if (!strcmp(client->uri, "/") || !strcmp(client->uri, "/media") || !strcmp(client->uri, "/supplies"))
6084
return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0));
6085
else
6086
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6087
6088
case HTTP_STATE_GET :
6089
if (!strcmp(client->uri, "/en.strings"))
6090
{
6091
/*
6092
* Send strings file.
6093
*/
6094
6095
if (client->printer->strings)
6096
{
6097
int fd; /* Icon file */
6098
struct stat fileinfo; /* Icon file information */
6099
char buffer[4096]; /* Copy buffer */
6100
ssize_t bytes; /* Bytes */
6101
6102
if (!stat(client->printer->strings, &fileinfo) && (fd = open(client->printer->strings, O_RDONLY | O_BINARY)) >= 0)
6103
{
6104
if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/strings", (size_t)fileinfo.st_size))
6105
{
6106
close(fd);
6107
return (0);
6108
}
6109
6110
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
6111
httpWrite2(client->http, buffer, (size_t)bytes);
6112
6113
httpFlushWrite(client->http);
6114
6115
close(fd);
6116
}
6117
else
6118
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6119
}
6120
else
6121
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6122
}
6123
else if (!strcmp(client->uri, "/icon.png"))
6124
{
6125
/*
6126
* Send medium PNG icon file.
6127
*/
6128
6129
if (client->printer->icons[1])
6130
{
6131
int fd; /* Icon file */
6132
struct stat fileinfo; /* Icon file information */
6133
char buffer[4096]; /* Copy buffer */
6134
ssize_t bytes; /* Bytes */
6135
6136
if (!stat(client->printer->icons[1], &fileinfo) && (fd = open(client->printer->icons[1], O_RDONLY | O_BINARY)) >= 0)
6137
{
6138
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
6139
{
6140
close(fd);
6141
return (0);
6142
}
6143
6144
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
6145
httpWrite2(client->http, buffer, (size_t)bytes);
6146
6147
httpFlushWrite(client->http);
6148
6149
close(fd);
6150
}
6151
else
6152
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6153
}
6154
else
6155
{
6156
fputs("Icon file is internal printer.png.\n", stderr);
6157
6158
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_png)))
6159
return (0);
6160
6161
httpWrite2(client->http, (const char *)printer_png, sizeof(printer_png));
6162
httpFlushWrite(client->http);
6163
}
6164
}
6165
else if (!strcmp(client->uri, "/icon-lg.png"))
6166
{
6167
/*
6168
* Send large PNG icon file.
6169
*/
6170
6171
if (client->printer->icons[2])
6172
{
6173
int fd; /* Icon file */
6174
struct stat fileinfo; /* Icon file information */
6175
char buffer[4096]; /* Copy buffer */
6176
ssize_t bytes; /* Bytes */
6177
6178
if (!stat(client->printer->icons[2], &fileinfo) && (fd = open(client->printer->icons[2], O_RDONLY | O_BINARY)) >= 0)
6179
{
6180
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
6181
{
6182
close(fd);
6183
return (0);
6184
}
6185
6186
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
6187
httpWrite2(client->http, buffer, (size_t)bytes);
6188
6189
httpFlushWrite(client->http);
6190
6191
close(fd);
6192
}
6193
else
6194
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6195
}
6196
else
6197
{
6198
fputs("Icon file is internal printer-lg.png.\n", stderr);
6199
6200
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_lg_png)))
6201
return (0);
6202
6203
httpWrite2(client->http, (const char *)printer_lg_png, sizeof(printer_lg_png));
6204
httpFlushWrite(client->http);
6205
}
6206
}
6207
else if (!strcmp(client->uri, "/icon-sm.png"))
6208
{
6209
/*
6210
* Send small PNG icon file.
6211
*/
6212
6213
if (client->printer->icons[0])
6214
{
6215
int fd; /* Icon file */
6216
struct stat fileinfo; /* Icon file information */
6217
char buffer[4096]; /* Copy buffer */
6218
ssize_t bytes; /* Bytes */
6219
6220
if (!stat(client->printer->icons[0], &fileinfo) && (fd = open(client->printer->icons[0], O_RDONLY | O_BINARY)) >= 0)
6221
{
6222
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", (size_t)fileinfo.st_size))
6223
{
6224
close(fd);
6225
return (0);
6226
}
6227
6228
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
6229
httpWrite2(client->http, buffer, (size_t)bytes);
6230
6231
httpFlushWrite(client->http);
6232
6233
close(fd);
6234
}
6235
else
6236
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6237
}
6238
else
6239
{
6240
fputs("Icon file is internal printer-sm.png.\n", stderr);
6241
6242
if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", sizeof(printer_sm_png)))
6243
return (0);
6244
6245
httpWrite2(client->http, (const char *)printer_sm_png, sizeof(printer_sm_png));
6246
httpFlushWrite(client->http);
6247
}
6248
}
6249
else
6250
{
6251
/*
6252
* Authenticate if needed...
6253
*/
6254
6255
if ((http_status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
6256
{
6257
return (respond_http(client, http_status, NULL, NULL, 0));
6258
}
6259
6260
if (!strcmp(client->uri, "/"))
6261
{
6262
/*
6263
* Show web status page...
6264
*/
6265
6266
return (show_status(client));
6267
}
6268
else if (!strcmp(client->uri, "/media"))
6269
{
6270
/*
6271
* Show web media page...
6272
*/
6273
6274
return (show_media(client));
6275
}
6276
else if (!strcmp(client->uri, "/supplies"))
6277
{
6278
/*
6279
* Show web supplies page...
6280
*/
6281
6282
return (show_supplies(client));
6283
}
6284
else
6285
return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0));
6286
}
6287
break;
6288
6289
case HTTP_STATE_POST :
6290
if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE),
6291
"application/ipp"))
6292
{
6293
/*
6294
* Not an IPP request...
6295
*/
6296
6297
return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0));
6298
}
6299
6300
/*
6301
* Read the IPP request...
6302
*/
6303
6304
client->request = ippNew();
6305
6306
while ((ipp_state = ippRead(client->http,
6307
client->request)) != IPP_STATE_DATA)
6308
{
6309
if (ipp_state == IPP_STATE_ERROR)
6310
{
6311
fprintf(stderr, "%s IPP read error (%s).\n", client->hostname, cupsLastErrorString());
6312
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
6313
return (0);
6314
}
6315
}
6316
6317
/*
6318
* Now that we have the IPP request, process the request...
6319
*/
6320
6321
return (process_ipp(client));
6322
6323
default :
6324
break; /* Anti-compiler-warning-code */
6325
}
6326
6327
return (1);
6328
}
6329
6330
6331
/*
6332
* 'process_ipp()' - Process an IPP request.
6333
*/
6334
6335
static int /* O - 1 on success, 0 on error */
6336
process_ipp(ippeve_client_t *client) /* I - Client */
6337
{
6338
ipp_tag_t group; /* Current group tag */
6339
ipp_attribute_t *attr; /* Current attribute */
6340
ipp_attribute_t *charset; /* Character set attribute */
6341
ipp_attribute_t *language; /* Language attribute */
6342
ipp_attribute_t *uri; /* Printer URI attribute */
6343
int major, minor; /* Version number */
6344
const char *name; /* Name of attribute */
6345
http_status_t status; /* Authentication status */
6346
6347
6348
debug_attributes("Request", client->request, 1);
6349
6350
/*
6351
* First build an empty response message for this request...
6352
*/
6353
6354
client->operation_id = ippGetOperation(client->request);
6355
client->response = ippNewResponse(client->request);
6356
6357
/*
6358
* Then validate the request header and required attributes...
6359
*/
6360
6361
major = ippGetVersion(client->request, &minor);
6362
6363
if (major < 1 || major > 2)
6364
{
6365
/*
6366
* Return an error, since we only support IPP 1.x and 2.x.
6367
*/
6368
6369
respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, "Bad request version number %d.%d.", major, minor);
6370
}
6371
else if ((major * 10 + minor) > MaxVersion)
6372
{
6373
if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
6374
httpFlush(client->http); /* Flush trailing (junk) data */
6375
6376
respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0);
6377
return (0);
6378
}
6379
else if (ippGetRequestId(client->request) <= 0)
6380
{
6381
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", ippGetRequestId(client->request));
6382
}
6383
else if (!ippFirstAttribute(client->request))
6384
{
6385
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No attributes in request.");
6386
}
6387
else
6388
{
6389
/*
6390
* Make sure that the attributes are provided in the correct order and
6391
* don't repeat groups...
6392
*/
6393
6394
for (attr = ippFirstAttribute(client->request),
6395
group = ippGetGroupTag(attr);
6396
attr;
6397
attr = ippNextAttribute(client->request))
6398
{
6399
if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO)
6400
{
6401
/*
6402
* Out of order; return an error...
6403
*/
6404
6405
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6406
"Attribute groups are out of order (%x < %x).",
6407
ippGetGroupTag(attr), group);
6408
break;
6409
}
6410
else
6411
group = ippGetGroupTag(attr);
6412
}
6413
6414
if (!attr)
6415
{
6416
/*
6417
* Then make sure that the first three attributes are:
6418
*
6419
* attributes-charset
6420
* attributes-natural-language
6421
* printer-uri/job-uri
6422
*/
6423
6424
attr = ippFirstAttribute(client->request);
6425
name = ippGetName(attr);
6426
if (attr && name && !strcmp(name, "attributes-charset") &&
6427
ippGetValueTag(attr) == IPP_TAG_CHARSET)
6428
charset = attr;
6429
else
6430
charset = NULL;
6431
6432
attr = ippNextAttribute(client->request);
6433
name = ippGetName(attr);
6434
6435
if (attr && name && !strcmp(name, "attributes-natural-language") &&
6436
ippGetValueTag(attr) == IPP_TAG_LANGUAGE)
6437
language = attr;
6438
else
6439
language = NULL;
6440
6441
if ((attr = ippFindAttribute(client->request, "printer-uri",
6442
IPP_TAG_URI)) != NULL)
6443
uri = attr;
6444
else if ((attr = ippFindAttribute(client->request, "job-uri",
6445
IPP_TAG_URI)) != NULL)
6446
uri = attr;
6447
else
6448
uri = NULL;
6449
6450
if (charset &&
6451
strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") &&
6452
strcasecmp(ippGetString(charset, 0, NULL), "utf-8"))
6453
{
6454
/*
6455
* Bad character set...
6456
*/
6457
6458
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6459
"Unsupported character set \"%s\".",
6460
ippGetString(charset, 0, NULL));
6461
}
6462
else if (!charset || !language || !uri)
6463
{
6464
/*
6465
* Return an error, since attributes-charset,
6466
* attributes-natural-language, and printer-uri/job-uri are required
6467
* for all operations.
6468
*/
6469
6470
respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST,
6471
"Missing required attributes.");
6472
}
6473
else
6474
{
6475
char scheme[32], /* URI scheme */
6476
userpass[32], /* Username/password in URI */
6477
host[256], /* Host name in URI */
6478
resource[256]; /* Resource path in URI */
6479
int port; /* Port number in URI */
6480
6481
name = ippGetName(uri);
6482
6483
if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL),
6484
scheme, sizeof(scheme),
6485
userpass, sizeof(userpass),
6486
host, sizeof(host), &port,
6487
resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
6488
respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES,
6489
"Bad %s value '%s'.", name, ippGetString(uri, 0, NULL));
6490
else if ((!strcmp(name, "job-uri") &&
6491
strncmp(resource, "/ipp/print/", 11)) ||
6492
(!strcmp(name, "printer-uri") &&
6493
strcmp(resource, "/ipp/print") &&
6494
(strcmp(resource, "/") || ippGetOperation(client->request) != IPP_OP_GET_PRINTER_ATTRIBUTES)))
6495
respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.",
6496
name, ippGetString(uri, 0, NULL));
6497
else if (client->operation_id != IPP_OP_GET_PRINTER_ATTRIBUTES && (status = authenticate_request(client)) != HTTP_STATUS_CONTINUE)
6498
{
6499
flush_document_data(client);
6500
6501
return (respond_http(client, status, NULL, NULL, 0));
6502
}
6503
else
6504
{
6505
/*
6506
* Handle HTTP Expect...
6507
*/
6508
6509
if (httpGetExpect(client->http))
6510
{
6511
if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE)
6512
{
6513
/*
6514
* Send 100-continue header...
6515
*/
6516
6517
if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0))
6518
return (0);
6519
}
6520
else
6521
{
6522
/*
6523
* Send 417-expectation-failed header...
6524
*/
6525
6526
if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0))
6527
return (0);
6528
6529
flush_document_data(client);
6530
return (1);
6531
}
6532
}
6533
6534
/*
6535
* Try processing the operation...
6536
*/
6537
6538
switch (client->operation_id)
6539
{
6540
case IPP_OP_PRINT_JOB :
6541
ipp_print_job(client);
6542
break;
6543
6544
case IPP_OP_PRINT_URI :
6545
ipp_print_uri(client);
6546
break;
6547
6548
case IPP_OP_VALIDATE_JOB :
6549
ipp_validate_job(client);
6550
break;
6551
6552
case IPP_OP_CREATE_JOB :
6553
ipp_create_job(client);
6554
break;
6555
6556
case IPP_OP_SEND_DOCUMENT :
6557
ipp_send_document(client);
6558
break;
6559
6560
case IPP_OP_SEND_URI :
6561
ipp_send_uri(client);
6562
break;
6563
6564
case IPP_OP_CANCEL_JOB :
6565
ipp_cancel_job(client);
6566
break;
6567
6568
case IPP_OP_CANCEL_MY_JOBS :
6569
ipp_cancel_my_jobs(client);
6570
break;
6571
6572
case IPP_OP_GET_JOB_ATTRIBUTES :
6573
ipp_get_job_attributes(client);
6574
break;
6575
6576
case IPP_OP_GET_JOBS :
6577
ipp_get_jobs(client);
6578
break;
6579
6580
case IPP_OP_GET_PRINTER_ATTRIBUTES :
6581
ipp_get_printer_attributes(client);
6582
break;
6583
6584
case IPP_OP_CLOSE_JOB :
6585
ipp_close_job(client);
6586
break;
6587
6588
case IPP_OP_IDENTIFY_PRINTER :
6589
ipp_identify_printer(client);
6590
break;
6591
6592
default :
6593
respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED,
6594
"Operation not supported.");
6595
break;
6596
}
6597
}
6598
}
6599
}
6600
}
6601
6602
/*
6603
* Send the HTTP header and return...
6604
*/
6605
6606
if (httpGetState(client->http) != HTTP_STATE_POST_SEND)
6607
httpFlush(client->http); /* Flush trailing (junk) data */
6608
6609
return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp",
6610
ippLength(client->response)));
6611
}
6612
6613
6614
/*
6615
* 'process_job()' - Process a print job.
6616
*/
6617
6618
static void * /* O - Thread exit status */
6619
process_job(ippeve_job_t *job) /* I - Job */
6620
{
6621
job->state = IPP_JSTATE_PROCESSING;
6622
job->printer->state = IPP_PSTATE_PROCESSING;
6623
job->processing = time(NULL);
6624
6625
while (job->printer->state_reasons & IPPEVE_PREASON_MEDIA_EMPTY)
6626
{
6627
job->printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
6628
6629
sleep(1);
6630
}
6631
6632
job->printer->state_reasons &= (ippeve_preason_t)~IPPEVE_PREASON_MEDIA_NEEDED;
6633
6634
if (job->printer->command)
6635
{
6636
/*
6637
* Execute a command with the job spool file and wait for it to complete...
6638
*/
6639
6640
int pid, /* Process ID */
6641
status; /* Exit status */
6642
struct timeval start, /* Start time */
6643
end; /* End time */
6644
char *myargv[3], /* Command-line arguments */
6645
*myenvp[400]; /* Environment variables */
6646
int myenvc; /* Number of environment variables */
6647
ipp_attribute_t *attr; /* Job attribute */
6648
char val[1280], /* IPP_NAME=value */
6649
*valptr; /* Pointer into string */
6650
#ifndef _WIN32
6651
int mystdout = -1; /* File for stdout */
6652
int mypipe[2]; /* Pipe for stderr */
6653
char line[2048], /* Line from stderr */
6654
*ptr, /* Pointer into line */
6655
*endptr; /* End of line */
6656
ssize_t bytes; /* Bytes read */
6657
#endif /* !_WIN32 */
6658
6659
fprintf(stderr, "[Job %d] Running command \"%s %s\".\n", job->id, job->printer->command, job->filename);
6660
gettimeofday(&start, NULL);
6661
6662
/*
6663
* Setup the command-line arguments...
6664
*/
6665
6666
myargv[0] = job->printer->command;
6667
myargv[1] = job->filename;
6668
myargv[2] = NULL;
6669
6670
/*
6671
* Copy the current environment, then add environment variables for every
6672
* Job attribute and Printer -default attributes...
6673
*/
6674
6675
for (myenvc = 0; environ[myenvc] && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); myenvc ++)
6676
myenvp[myenvc] = strdup(environ[myenvc]);
6677
6678
if (myenvc > (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 32))
6679
{
6680
fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
6681
job->state = IPP_JSTATE_ABORTED;
6682
goto error;
6683
}
6684
6685
snprintf(val, sizeof(val), "CONTENT_TYPE=%s", job->format);
6686
myenvp[myenvc ++] = strdup(val);
6687
6688
if (job->printer->device_uri)
6689
{
6690
snprintf(val, sizeof(val), "DEVICE_URI=%s", job->printer->device_uri);
6691
myenvp[myenvc ++] = strdup(val);
6692
}
6693
6694
if (job->printer->output_format)
6695
{
6696
snprintf(val, sizeof(val), "OUTPUT_TYPE=%s", job->printer->output_format);
6697
myenvp[myenvc ++] = strdup(val);
6698
}
6699
6700
#if !CUPS_LITE
6701
if (job->printer->ppdfile)
6702
{
6703
snprintf(val, sizeof(val), "PPD=%s", job->printer->ppdfile);
6704
myenvp[myenvc++] = strdup(val);
6705
}
6706
#endif /* !CUPS_LITE */
6707
6708
for (attr = ippFirstAttribute(job->printer->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->printer->attrs))
6709
{
6710
/*
6711
* Convert "attribute-name-default" to "IPP_ATTRIBUTE_NAME_DEFAULT=" and
6712
* "pwg-xxx" to "IPP_PWG_XXX", then add the value(s) from the attribute.
6713
*/
6714
6715
const char *name = ippGetName(attr),
6716
/* Attribute name */
6717
*suffix = strstr(name, "-default");
6718
/* Suffix on attribute name */
6719
6720
if (strncmp(name, "pwg-", 4) && (!suffix || suffix[8]))
6721
continue;
6722
6723
valptr = val;
6724
*valptr++ = 'I';
6725
*valptr++ = 'P';
6726
*valptr++ = 'P';
6727
*valptr++ = '_';
6728
while (*name && valptr < (val + sizeof(val) - 2))
6729
{
6730
if (*name == '-')
6731
*valptr++ = '_';
6732
else
6733
*valptr++ = (char)toupper(*name & 255);
6734
6735
name ++;
6736
}
6737
*valptr++ = '=';
6738
ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6739
6740
myenvp[myenvc++] = strdup(val);
6741
}
6742
6743
for (attr = ippFirstAttribute(job->attrs); attr && myenvc < (int)(sizeof(myenvp) / sizeof(myenvp[0]) - 1); attr = ippNextAttribute(job->attrs))
6744
{
6745
/*
6746
* Convert "attribute-name" to "IPP_ATTRIBUTE_NAME=" and then add the
6747
* value(s) from the attribute.
6748
*/
6749
6750
const char *name = ippGetName(attr);
6751
/* Attribute name */
6752
6753
if (!name)
6754
continue;
6755
6756
valptr = val;
6757
*valptr++ = 'I';
6758
*valptr++ = 'P';
6759
*valptr++ = 'P';
6760
*valptr++ = '_';
6761
while (*name && valptr < (val + sizeof(val) - 2))
6762
{
6763
if (*name == '-')
6764
*valptr++ = '_';
6765
else
6766
*valptr++ = (char)toupper(*name & 255);
6767
6768
name ++;
6769
}
6770
*valptr++ = '=';
6771
ippAttributeString(attr, valptr, sizeof(val) - (size_t)(valptr - val));
6772
6773
myenvp[myenvc++] = strdup(val);
6774
}
6775
6776
if (attr)
6777
{
6778
fprintf(stderr, "[Job %d] Too many environment variables to process job.\n", job->id);
6779
job->state = IPP_JSTATE_ABORTED;
6780
goto error;
6781
}
6782
6783
myenvp[myenvc] = NULL;
6784
6785
/*
6786
* Now run the program...
6787
*/
6788
6789
#ifdef _WIN32
6790
status = _spawnvpe(_P_WAIT, job->printer->command, myargv, myenvp);
6791
6792
#else
6793
if (job->printer->device_uri)
6794
{
6795
char scheme[32], /* URI scheme */
6796
userpass[256], /* username:password (unused) */
6797
host[256], /* Hostname or IP address */
6798
resource[256]; /* Resource path */
6799
int port; /* Port number */
6800
6801
6802
if (httpSeparateURI(HTTP_URI_CODING_ALL, job->printer->device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
6803
{
6804
fprintf(stderr, "[Job %d] Bad device URI \"%s\".\n", job->id, job->printer->device_uri);
6805
}
6806
else if (!strcmp(scheme, "file"))
6807
{
6808
struct stat fileinfo; /* See if this is a file or directory... */
6809
6810
if (stat(resource, &fileinfo))
6811
{
6812
if (errno == ENOENT)
6813
{
6814
if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) >= 0)
6815
fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
6816
else
6817
fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
6818
}
6819
else
6820
fprintf(stderr, "[Job %d] Unable to access \"%s\": %s\n", job->id, resource, strerror(errno));
6821
}
6822
else if (S_ISDIR(fileinfo.st_mode))
6823
{
6824
if ((mystdout = create_job_file(job, line, sizeof(line), resource, "prn")) >= 0)
6825
fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
6826
else
6827
fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, line, strerror(errno));
6828
}
6829
else if (!S_ISREG(fileinfo.st_mode))
6830
{
6831
if ((mystdout = open(resource, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) >= 0)
6832
fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
6833
else
6834
fprintf(stderr, "[Job %d] Unable to create \"%s\": %s\n", job->id, resource, strerror(errno));
6835
}
6836
else if ((mystdout = open(resource, O_WRONLY | O_BINARY)) >= 0)
6837
fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, resource);
6838
else
6839
fprintf(stderr, "[Job %d] Unable to open \"%s\": %s\n", job->id, resource, strerror(errno));
6840
}
6841
else if (!strcmp(scheme, "socket"))
6842
{
6843
http_addrlist_t *addrlist; /* List of addresses */
6844
char service[32]; /* Service number */
6845
6846
snprintf(service, sizeof(service), "%d", port);
6847
6848
if ((addrlist = httpAddrGetList(host, AF_UNSPEC, service)) == NULL)
6849
fprintf(stderr, "[Job %d] Unable to find \"%s\": %s\n", job->id, host, cupsLastErrorString());
6850
else if (!httpAddrConnect2(addrlist, &mystdout, 30000, &(job->cancel)))
6851
fprintf(stderr, "[Job %d] Unable to connect to \"%s\": %s\n", job->id, host, cupsLastErrorString());
6852
6853
httpAddrFreeList(addrlist);
6854
}
6855
else
6856
{
6857
fprintf(stderr, "[Job %d] Unsupported device URI scheme \"%s\".\n", job->id, scheme);
6858
}
6859
}
6860
else if ((mystdout = create_job_file(job, line, sizeof(line), job->printer->directory, "prn")) >= 0)
6861
{
6862
fprintf(stderr, "[Job %d] Saving print command output to \"%s\".\n", job->id, line);
6863
}
6864
6865
if (mystdout < 0)
6866
mystdout = open("/dev/null", O_WRONLY | O_BINARY);
6867
6868
if (pipe(mypipe))
6869
{
6870
fprintf(stderr, "[Job %d] Unable to create pipe for stderr: %s\n", job->id, strerror(errno));
6871
mypipe[0] = mypipe[1] = -1;
6872
}
6873
6874
if ((pid = fork()) == 0)
6875
{
6876
/*
6877
* Child comes here...
6878
*/
6879
6880
close(1);
6881
dup2(mystdout, 1);
6882
close(mystdout);
6883
6884
close(2);
6885
dup2(mypipe[1], 2);
6886
close(mypipe[0]);
6887
close(mypipe[1]);
6888
6889
execve(job->printer->command, myargv, myenvp);
6890
exit(errno);
6891
}
6892
else if (pid < 0)
6893
{
6894
/*
6895
* Unable to fork process...
6896
*/
6897
6898
fprintf(stderr, "[Job %d] Unable to start job processing command: %s\n", job->id, strerror(errno));
6899
status = -1;
6900
6901
close(mystdout);
6902
close(mypipe[0]);
6903
close(mypipe[1]);
6904
6905
/*
6906
* Free memory used for environment...
6907
*/
6908
6909
while (myenvc > 0)
6910
free(myenvp[-- myenvc]);
6911
}
6912
else
6913
{
6914
/*
6915
* Free memory used for environment...
6916
*/
6917
6918
while (myenvc > 0)
6919
free(myenvp[-- myenvc]);
6920
6921
/*
6922
* Close the output file in the parent process...
6923
*/
6924
6925
close(mystdout);
6926
6927
/*
6928
* If the pipe exists, read from it until EOF...
6929
*/
6930
6931
if (mypipe[0] >= 0)
6932
{
6933
close(mypipe[1]);
6934
6935
endptr = line;
6936
while ((bytes = read(mypipe[0], endptr, sizeof(line) - (size_t)(endptr - line) - 1)) > 0)
6937
{
6938
endptr += bytes;
6939
*endptr = '\0';
6940
6941
while ((ptr = strchr(line, '\n')) != NULL)
6942
{
6943
int level = 3; /* Message log level */
6944
6945
*ptr++ = '\0';
6946
6947
if (!strncmp(line, "ATTR:", 5))
6948
{
6949
/*
6950
* Process job/printer attribute updates.
6951
*/
6952
6953
process_attr_message(job, line);
6954
}
6955
else if (!strncmp(line, "DEBUG:", 6))
6956
{
6957
/*
6958
* Debug message...
6959
*/
6960
6961
level = 2;
6962
}
6963
else if (!strncmp(line, "ERROR:", 6))
6964
{
6965
/*
6966
* Error message...
6967
*/
6968
6969
level = 0;
6970
job->message = strdup(line + 6);
6971
job->msglevel = 0;
6972
}
6973
else if (!strncmp(line, "INFO:", 5))
6974
{
6975
/*
6976
* Informational/progress message...
6977
*/
6978
6979
level = 1;
6980
if (job->msglevel)
6981
{
6982
job->message = strdup(line + 5);
6983
job->msglevel = 1;
6984
}
6985
}
6986
else if (!strncmp(line, "STATE:", 6))
6987
{
6988
/*
6989
* Process printer-state-reasons keywords.
6990
*/
6991
6992
process_state_message(job, line);
6993
}
6994
6995
if (Verbosity >= level)
6996
fprintf(stderr, "[Job %d] Command - %s\n", job->id, line);
6997
6998
bytes = ptr - line;
6999
if (ptr < endptr)
7000
memmove(line, ptr, (size_t)(endptr - ptr));
7001
endptr -= bytes;
7002
*endptr = '\0';
7003
}
7004
}
7005
7006
close(mypipe[0]);
7007
}
7008
7009
/*
7010
* Wait for child to complete...
7011
*/
7012
7013
# ifdef HAVE_WAITPID
7014
while (waitpid(pid, &status, 0) < 0);
7015
# else
7016
while (wait(&status) < 0);
7017
# endif /* HAVE_WAITPID */
7018
}
7019
#endif /* _WIN32 */
7020
7021
if (status)
7022
{
7023
#ifndef _WIN32
7024
if (WIFEXITED(status))
7025
#endif /* !_WIN32 */
7026
fprintf(stderr, "[Job %d] Command \"%s\" exited with status %d.\n", job->id, job->printer->command, WEXITSTATUS(status));
7027
#ifndef _WIN32
7028
else
7029
fprintf(stderr, "[Job %d] Command \"%s\" terminated with signal %d.\n", job->id, job->printer->command, WTERMSIG(status));
7030
#endif /* !_WIN32 */
7031
job->state = IPP_JSTATE_ABORTED;
7032
}
7033
else
7034
fprintf(stderr, "[Job %d] Command \"%s\" completed successfully.\n", job->id, job->printer->command);
7035
7036
/*
7037
* Report the total processing time...
7038
*/
7039
7040
gettimeofday(&end, NULL);
7041
7042
fprintf(stderr, "[Job %d] Processing time was %.3f seconds.\n", job->id, end.tv_sec - start.tv_sec + 0.000001 * (end.tv_usec - start.tv_usec));
7043
}
7044
else
7045
{
7046
/*
7047
* Sleep for a random amount of time to simulate job processing.
7048
*/
7049
7050
sleep((unsigned)(5 + (CUPS_RAND() % 11)));
7051
}
7052
7053
if (job->cancel)
7054
job->state = IPP_JSTATE_CANCELED;
7055
else if (job->state == IPP_JSTATE_PROCESSING)
7056
job->state = IPP_JSTATE_COMPLETED;
7057
7058
error:
7059
7060
job->completed = time(NULL);
7061
job->printer->state = IPP_PSTATE_IDLE;
7062
job->printer->active_job = NULL;
7063
7064
return (NULL);
7065
}
7066
7067
7068
/*
7069
* 'process_state_message()' - Process a STATE: message from a command.
7070
*/
7071
7072
static void
7073
process_state_message(
7074
ippeve_job_t *job, /* I - Job */
7075
char *message) /* I - Message */
7076
{
7077
int i; /* Looping var */
7078
ippeve_preason_t state_reasons, /* printer-state-reasons values */
7079
bit; /* Current reason bit */
7080
char *ptr, /* Pointer into message */
7081
*next; /* Next keyword in message */
7082
int remove; /* Non-zero if we are removing keywords */
7083
7084
7085
/*
7086
* Skip leading "STATE:" and any whitespace...
7087
*/
7088
7089
for (message += 6; *message; message ++)
7090
if (*message != ' ' && *message != '\t')
7091
break;
7092
7093
/*
7094
* Support the following forms of message:
7095
*
7096
* "keyword[,keyword,...]" to set the printer-state-reasons value(s).
7097
*
7098
* "-keyword[,keyword,...]" to remove keywords.
7099
*
7100
* "+keyword[,keyword,...]" to add keywords.
7101
*
7102
* Keywords may or may not have a suffix (-report, -warning, -error) per
7103
* RFC 8011.
7104
*/
7105
7106
if (*message == '-')
7107
{
7108
remove = 1;
7109
state_reasons = job->printer->state_reasons;
7110
message ++;
7111
}
7112
else if (*message == '+')
7113
{
7114
remove = 0;
7115
state_reasons = job->printer->state_reasons;
7116
message ++;
7117
}
7118
else
7119
{
7120
remove = 0;
7121
state_reasons = IPPEVE_PREASON_NONE;
7122
}
7123
7124
while (*message)
7125
{
7126
if ((next = strchr(message, ',')) != NULL)
7127
*next++ = '\0';
7128
7129
if ((ptr = strstr(message, "-error")) != NULL)
7130
*ptr = '\0';
7131
else if ((ptr = strstr(message, "-report")) != NULL)
7132
*ptr = '\0';
7133
else if ((ptr = strstr(message, "-warning")) != NULL)
7134
*ptr = '\0';
7135
7136
for (i = 0, bit = 1; i < (int)(sizeof(ippeve_preason_strings) / sizeof(ippeve_preason_strings[0])); i ++, bit *= 2)
7137
{
7138
if (!strcmp(message, ippeve_preason_strings[i]))
7139
{
7140
if (remove)
7141
state_reasons &= ~bit;
7142
else
7143
state_reasons |= bit;
7144
}
7145
}
7146
7147
if (next)
7148
message = next;
7149
else
7150
break;
7151
}
7152
7153
job->printer->state_reasons = state_reasons;
7154
}
7155
7156
7157
/*
7158
* 'register_printer()' - Register a printer object via DNS-SD.
7159
*/
7160
7161
static int /* O - 1 on success, 0 on error */
7162
register_printer(
7163
ippeve_printer_t *printer) /* I - Printer */
7164
{
7165
#ifdef HAVE_DNSSD
7166
ippeve_txt_t ipp_txt; /* DNS-SD IPP TXT record */
7167
int i, /* Looping var */
7168
count; /* Number of values */
7169
ipp_attribute_t *color_supported,
7170
*document_format_supported,
7171
*printer_location,
7172
*printer_make_and_model,
7173
*printer_uuid,
7174
*sides_supported,
7175
*urf_supported; /* Printer attributes */
7176
const char *value; /* Value string */
7177
char adminurl[247], /* adminurl value */
7178
formats[252], /* List of supported formats */
7179
urf[252], /* List of supported URF values */
7180
*ptr; /* Pointer into string */
7181
7182
7183
if (printer->dnssd_subtypes && !strcmp(printer->dnssd_subtypes, "off"))
7184
return (1);
7185
7186
color_supported = ippFindAttribute(printer->attrs, "color-supported", IPP_TAG_BOOLEAN);
7187
document_format_supported = ippFindAttribute(printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE);
7188
printer_location = ippFindAttribute(printer->attrs, "printer-location", IPP_TAG_TEXT);
7189
printer_make_and_model = ippFindAttribute(printer->attrs, "printer-make-and-model", IPP_TAG_TEXT);
7190
printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
7191
sides_supported = ippFindAttribute(printer->attrs, "sides-supported", IPP_TAG_KEYWORD);
7192
urf_supported = ippFindAttribute(printer->attrs, "urf-supported", IPP_TAG_KEYWORD);
7193
7194
httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), WEB_SCHEME, NULL, printer->hostname, printer->port, "/");
7195
7196
for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++)
7197
{
7198
value = ippGetString(document_format_supported, i, NULL);
7199
7200
if (!strcasecmp(value, "application/octet-stream"))
7201
continue;
7202
7203
if (ptr > formats && ptr < (formats + sizeof(formats) - 1))
7204
*ptr++ = ',';
7205
7206
strlcpy(ptr, value, sizeof(formats) - (size_t)(ptr - formats));
7207
ptr += strlen(ptr);
7208
7209
if (ptr >= (formats + sizeof(formats) - 1))
7210
break;
7211
}
7212
7213
urf[0] = '\0';
7214
for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++)
7215
{
7216
value = ippGetString(urf_supported, i, NULL);
7217
7218
if (ptr > urf && ptr < (urf + sizeof(urf) - 1))
7219
*ptr++ = ',';
7220
7221
strlcpy(ptr, value, sizeof(urf) - (size_t)(ptr - urf));
7222
ptr += strlen(ptr);
7223
7224
if (ptr >= (urf + sizeof(urf) - 1))
7225
break;
7226
}
7227
7228
/*
7229
* Rename the service as needed...
7230
*/
7231
7232
if (printer->dnssd_collision)
7233
{
7234
char new_dnssd_name[256]; /* New DNS-SD name */
7235
const char *uuid = ippGetString(printer_uuid, 0, NULL);
7236
/* "printer-uuid" value */
7237
7238
_cupsRWLockWrite(&printer->rwlock);
7239
7240
snprintf(new_dnssd_name, sizeof(new_dnssd_name), "%s (%c%c%c%c%c%c)", printer->dnssd_name, toupper(uuid[39]), toupper(uuid[40]), toupper(uuid[41]), toupper(uuid[42]), toupper(uuid[43]), toupper(uuid[44]));
7241
7242
free(printer->dnssd_name);
7243
printer->dnssd_name = strdup(new_dnssd_name);
7244
7245
fprintf(stderr, "DNS-SD name collision, trying new DNS-SD service name '%s'.\n", printer->dnssd_name);
7246
7247
_cupsRWUnlock(&printer->rwlock);
7248
7249
printer->dnssd_collision = 0;
7250
}
7251
#endif /* HAVE_DNSSD */
7252
7253
#ifdef HAVE_MDNSRESPONDER
7254
DNSServiceErrorType error; /* Error from DNS-SD */
7255
char regtype[256]; /* DNS-SD service type */
7256
uint32_t ifindex; /* Interface index */
7257
7258
7259
/*
7260
* Build the TXT record for IPP...
7261
*/
7262
7263
TXTRecordCreate(&ipp_txt, 1024, NULL);
7264
TXTRecordSetValue(&ipp_txt, "rp", 9, "ipp/print");
7265
if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
7266
TXTRecordSetValue(&ipp_txt, "ty", (uint8_t)strlen(value), value);
7267
TXTRecordSetValue(&ipp_txt, "adminurl", (uint8_t)strlen(adminurl), adminurl);
7268
if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
7269
TXTRecordSetValue(&ipp_txt, "note", (uint8_t)strlen(value), value);
7270
TXTRecordSetValue(&ipp_txt, "pdl", (uint8_t)strlen(formats), formats);
7271
TXTRecordSetValue(&ipp_txt, "Color", 1, ippGetBoolean(color_supported, 0) ? "T" : "F");
7272
TXTRecordSetValue(&ipp_txt, "Duplex", 1, ippGetCount(sides_supported) > 1 ? "T" : "F");
7273
if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
7274
TXTRecordSetValue(&ipp_txt, "UUID", (uint8_t)strlen(value) - 9, value + 9);
7275
# ifdef HAVE_TLS
7276
TXTRecordSetValue(&ipp_txt, "TLS", 3, "1.2");
7277
# endif /* HAVE_TLS */
7278
if (urf[0])
7279
TXTRecordSetValue(&ipp_txt, "URF", (uint8_t)strlen(urf), urf);
7280
TXTRecordSetValue(&ipp_txt, "txtvers", 1, "1");
7281
TXTRecordSetValue(&ipp_txt, "qtotal", 1, "1");
7282
7283
/*
7284
* Register the _printer._tcp (LPD) service type with a port number of 0 to
7285
* defend our service name but not actually support LPD...
7286
*/
7287
7288
ifindex = !strcmp(printer->hostname, "localhost") ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
7289
7290
if (printer->printer_ref)
7291
DNSServiceRefDeallocate(printer->printer_ref);
7292
7293
printer->printer_ref = DNSSDMaster;
7294
7295
if ((error = DNSServiceRegister(&(printer->printer_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, "_printer._tcp", NULL /* domain */, NULL /* host */, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
7296
{
7297
_cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_printer._tcp", error);
7298
return (0);
7299
}
7300
7301
/*
7302
* Then register the _ipp._tcp (IPP) service type with the real port number to
7303
* advertise our IPP printer...
7304
*/
7305
7306
if (printer->ipp_ref)
7307
DNSServiceRefDeallocate(printer->ipp_ref);
7308
7309
printer->ipp_ref = DNSSDMaster;
7310
7311
if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
7312
snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", printer->dnssd_subtypes);
7313
else
7314
strlcpy(regtype, "_ipp._tcp", sizeof(regtype));
7315
7316
if ((error = DNSServiceRegister(&(printer->ipp_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
7317
{
7318
_cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
7319
return (0);
7320
}
7321
7322
# ifdef HAVE_TLS
7323
/*
7324
* Then register the _ipps._tcp (IPP) service type with the real port number to
7325
* advertise our IPPS printer...
7326
*/
7327
7328
if (printer->ipps_ref)
7329
DNSServiceRefDeallocate(printer->ipps_ref);
7330
7331
printer->ipps_ref = DNSSDMaster;
7332
7333
if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
7334
snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", printer->dnssd_subtypes);
7335
else
7336
strlcpy(regtype, "_ipps._tcp", sizeof(regtype));
7337
7338
if ((error = DNSServiceRegister(&(printer->ipps_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, regtype, NULL /* domain */, NULL /* host */, htons(printer->port), TXTRecordGetLength(&ipp_txt), TXTRecordGetBytesPtr(&ipp_txt), (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
7339
{
7340
_cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, regtype, error);
7341
return (0);
7342
}
7343
# endif /* HAVE_TLS */
7344
7345
/*
7346
* Similarly, register the _http._tcp,_printer (HTTP) service type with the
7347
* real port number to advertise our IPP printer...
7348
*/
7349
7350
if (printer->http_ref)
7351
DNSServiceRefDeallocate(printer->http_ref);
7352
7353
printer->http_ref = DNSSDMaster;
7354
7355
if ((error = DNSServiceRegister(&(printer->http_ref), kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, ifindex, printer->dnssd_name, "_http._tcp,_printer", NULL /* domain */, NULL /* host */, htons(printer->port), 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dnssd_callback, printer)) != kDNSServiceErr_NoError)
7356
{
7357
_cupsLangPrintf(stderr, _("Unable to register \"%s.%s\": %d"), printer->dnssd_name, "_http._tcp,_printer", error);
7358
return (0);
7359
}
7360
7361
TXTRecordDeallocate(&ipp_txt);
7362
7363
#elif defined(HAVE_AVAHI)
7364
char temp[256]; /* Subtype service string */
7365
7366
/*
7367
* Create the TXT record...
7368
*/
7369
7370
ipp_txt = NULL;
7371
ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
7372
if ((value = ippGetString(printer_make_and_model, 0, NULL)) != NULL)
7373
ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s", value);
7374
ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", adminurl);
7375
if ((value = ippGetString(printer_location, 0, NULL)) != NULL)
7376
ipp_txt = avahi_string_list_add_printf(ipp_txt, "note=%s", value);
7377
ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
7378
ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=%s", ippGetBoolean(color_supported, 0) ? "T" : "F");
7379
ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=%s", ippGetCount(sides_supported) > 1 ? "T" : "F");
7380
if ((value = ippGetString(printer_uuid, 0, NULL)) != NULL)
7381
ipp_txt = avahi_string_list_add_printf(ipp_txt, "UUID=%s", value + 9);
7382
# ifdef HAVE_TLS
7383
ipp_txt = avahi_string_list_add_printf(ipp_txt, "TLS=1.2");
7384
# endif /* HAVE_TLS */
7385
if (urf[0])
7386
ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=%s", urf);
7387
ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1");
7388
ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1");
7389
7390
/*
7391
* Register _printer._tcp (LPD) with port 0 to reserve the service name...
7392
*/
7393
7394
avahi_threaded_poll_lock(DNSSDMaster);
7395
7396
if (printer->dnssd_ref)
7397
avahi_entry_group_free(printer->dnssd_ref);
7398
7399
printer->dnssd_ref = avahi_entry_group_new(DNSSDClient, dnssd_callback, printer);
7400
7401
avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_printer._tcp", NULL, NULL, 0, NULL);
7402
7403
/*
7404
* Then register the _ipp._tcp (IPP)...
7405
*/
7406
7407
avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, NULL, printer->port, ipp_txt);
7408
if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
7409
{
7410
char *temptypes = strdup(printer->dnssd_subtypes), *start, *end;
7411
7412
for (start = temptypes; *start; start = end)
7413
{
7414
if ((end = strchr(start, ',')) != NULL)
7415
*end++ = '\0';
7416
else
7417
end = start + strlen(start);
7418
7419
snprintf(temp, sizeof(temp), "%s._sub._ipp._tcp", start);
7420
avahi_entry_group_add_service_subtype(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipp._tcp", NULL, temp);
7421
}
7422
7423
free(temptypes);
7424
}
7425
7426
#ifdef HAVE_TLS
7427
/*
7428
* _ipps._tcp (IPPS) for secure printing...
7429
*/
7430
7431
avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, NULL, printer->port, ipp_txt);
7432
if (printer->dnssd_subtypes && *(printer->dnssd_subtypes))
7433
{
7434
char *temptypes = strdup(printer->dnssd_subtypes), *start, *end;
7435
7436
for (start = temptypes; *start; start = end)
7437
{
7438
if ((end = strchr(start, ',')) != NULL)
7439
*end++ = '\0';
7440
else
7441
end = start + strlen(start);
7442
7443
snprintf(temp, sizeof(temp), "%s._sub._ipps._tcp", start);
7444
avahi_entry_group_add_service_subtype(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_ipps._tcp", NULL, temp);
7445
}
7446
7447
free(temptypes);
7448
}
7449
#endif /* HAVE_TLS */
7450
7451
/*
7452
* Finally _http.tcp (HTTP) for the web interface...
7453
*/
7454
7455
avahi_entry_group_add_service_strlst(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_http._tcp", NULL, NULL, printer->port, NULL);
7456
avahi_entry_group_add_service_subtype(printer->dnssd_ref, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, printer->dnssd_name, "_http._tcp", NULL, "_printer._sub._http._tcp");
7457
7458
/*
7459
* Commit it...
7460
*/
7461
7462
avahi_entry_group_commit(printer->dnssd_ref);
7463
avahi_threaded_poll_unlock(DNSSDMaster);
7464
7465
avahi_string_list_free(ipp_txt);
7466
#endif /* HAVE_MDNSRESPONDER */
7467
7468
return (1);
7469
}
7470
7471
7472
/*
7473
* 'respond_http()' - Send a HTTP response.
7474
*/
7475
7476
int /* O - 1 on success, 0 on failure */
7477
respond_http(
7478
ippeve_client_t *client, /* I - Client */
7479
http_status_t code, /* I - HTTP status of response */
7480
const char *content_encoding, /* I - Content-Encoding of response */
7481
const char *type, /* I - MIME media type of response */
7482
size_t length) /* I - Length of response */
7483
{
7484
char message[1024]; /* Text message */
7485
7486
7487
fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code));
7488
7489
if (code == HTTP_STATUS_CONTINUE)
7490
{
7491
/*
7492
* 100-continue doesn't send any headers...
7493
*/
7494
7495
return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0);
7496
}
7497
7498
/*
7499
* Format an error message...
7500
*/
7501
7502
if (!type && !length && code != HTTP_STATUS_OK && code != HTTP_STATUS_SWITCHING_PROTOCOLS)
7503
{
7504
snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code));
7505
7506
type = "text/plain";
7507
length = strlen(message);
7508
}
7509
else
7510
message[0] = '\0';
7511
7512
/*
7513
* Send the HTTP response header...
7514
*/
7515
7516
httpClearFields(client->http);
7517
7518
if (code == HTTP_STATUS_METHOD_NOT_ALLOWED ||
7519
client->operation == HTTP_STATE_OPTIONS)
7520
httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST");
7521
7522
if (code == HTTP_STATUS_UNAUTHORIZED)
7523
{
7524
char value[256]; /* WWW-Authenticate value */
7525
7526
snprintf(value, sizeof(value), "Basic realm=\"%s\"", PAMService);
7527
httpSetField(client->http, HTTP_FIELD_WWW_AUTHENTICATE, value);
7528
}
7529
7530
if (type)
7531
{
7532
if (!strcmp(type, "text/html"))
7533
httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE,
7534
"text/html; charset=utf-8");
7535
else
7536
httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type);
7537
7538
if (content_encoding)
7539
httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding);
7540
}
7541
7542
httpSetLength(client->http, length);
7543
7544
if (httpWriteResponse(client->http, code) < 0)
7545
return (0);
7546
7547
/*
7548
* Send the response data...
7549
*/
7550
7551
if (message[0])
7552
{
7553
/*
7554
* Send a plain text message.
7555
*/
7556
7557
if (httpPrintf(client->http, "%s", message) < 0)
7558
return (0);
7559
7560
if (httpWrite2(client->http, "", 0) < 0)
7561
return (0);
7562
}
7563
else if (client->response)
7564
{
7565
/*
7566
* Send an IPP response...
7567
*/
7568
7569
debug_attributes("Response", client->response, 2);
7570
7571
ippSetState(client->response, IPP_STATE_IDLE);
7572
7573
if (ippWrite(client->http, client->response) != IPP_STATE_DATA)
7574
return (0);
7575
}
7576
7577
return (1);
7578
}
7579
7580
7581
/*
7582
* 'respond_ipp()' - Send an IPP response.
7583
*/
7584
7585
static void
7586
respond_ipp(ippeve_client_t *client, /* I - Client */
7587
ipp_status_t status, /* I - status-code */
7588
const char *message, /* I - printf-style status-message */
7589
...) /* I - Additional args as needed */
7590
{
7591
const char *formatted = NULL; /* Formatted message */
7592
7593
7594
ippSetStatusCode(client->response, status);
7595
7596
if (message)
7597
{
7598
va_list ap; /* Pointer to additional args */
7599
ipp_attribute_t *attr; /* New status-message attribute */
7600
7601
va_start(ap, message);
7602
if ((attr = ippFindAttribute(client->response, "status-message", IPP_TAG_TEXT)) != NULL)
7603
ippSetStringfv(client->response, &attr, 0, message, ap);
7604
else
7605
attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, "status-message", NULL, message, ap);
7606
va_end(ap);
7607
7608
formatted = ippGetString(attr, 0, NULL);
7609
}
7610
7611
if (formatted)
7612
fprintf(stderr, "%s %s %s (%s)\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status), formatted);
7613
else
7614
fprintf(stderr, "%s %s %s\n", client->hostname, ippOpString(client->operation_id), ippErrorString(status));
7615
}
7616
7617
7618
/*
7619
* 'respond_unsupported()' - Respond with an unsupported attribute.
7620
*/
7621
7622
static void
7623
respond_unsupported(
7624
ippeve_client_t *client, /* I - Client */
7625
ipp_attribute_t *attr) /* I - Attribute */
7626
{
7627
ipp_attribute_t *temp; /* Copy of attribute */
7628
7629
7630
respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, "Unsupported %s %s%s value.", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)));
7631
7632
temp = ippCopyAttribute(client->response, attr, 0);
7633
ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP);
7634
}
7635
7636
7637
/*
7638
* 'run_printer()' - Run the printer service.
7639
*/
7640
7641
static void
7642
run_printer(ippeve_printer_t *printer) /* I - Printer */
7643
{
7644
int num_fds; /* Number of file descriptors */
7645
struct pollfd polldata[3]; /* poll() data */
7646
ippeve_client_t *client; /* New client */
7647
7648
7649
#ifndef _WIN32
7650
/*
7651
* Set signal handlers for SIGINT and SIGTERM...
7652
*/
7653
7654
signal(SIGINT, signal_handler);
7655
signal(SIGTERM, signal_handler);
7656
#endif // !_WIN32
7657
7658
/*
7659
* Setup poll() data for the DNS-SD service socket and IPv4/6 listeners...
7660
*/
7661
7662
polldata[0].fd = printer->ipv4;
7663
polldata[0].events = POLLIN;
7664
7665
polldata[1].fd = printer->ipv6;
7666
polldata[1].events = POLLIN;
7667
7668
num_fds = 2;
7669
7670
#ifdef HAVE_MDNSRESPONDER
7671
polldata[num_fds ].fd = DNSServiceRefSockFD(DNSSDMaster);
7672
polldata[num_fds ++].events = POLLIN;
7673
#endif /* HAVE_MDNSRESPONDER */
7674
7675
/*
7676
* Loop until we are killed or have a hard error...
7677
*/
7678
7679
for (;;)
7680
{
7681
if (poll(polldata, (nfds_t)num_fds, 1000) < 0 && errno != EINTR)
7682
{
7683
perror("poll() failed");
7684
break;
7685
}
7686
7687
#ifndef _WIN32
7688
if (StopPrinter)
7689
break;
7690
#endif // !_WIN32
7691
7692
if (polldata[0].revents & POLLIN)
7693
{
7694
if ((client = create_client(printer, printer->ipv4)) != NULL)
7695
{
7696
_cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
7697
7698
if (t)
7699
{
7700
_cupsThreadDetach(t);
7701
}
7702
else
7703
{
7704
perror("Unable to create client thread");
7705
delete_client(client);
7706
}
7707
}
7708
}
7709
7710
if (polldata[1].revents & POLLIN)
7711
{
7712
if ((client = create_client(printer, printer->ipv6)) != NULL)
7713
{
7714
_cups_thread_t t = _cupsThreadCreate((_cups_thread_func_t)process_client, client);
7715
7716
if (t)
7717
{
7718
_cupsThreadDetach(t);
7719
}
7720
else
7721
{
7722
perror("Unable to create client thread");
7723
delete_client(client);
7724
}
7725
}
7726
}
7727
7728
/*
7729
* Process DNS-SD messages...
7730
*/
7731
7732
#ifdef HAVE_MDNSRESPONDER
7733
if (polldata[2].revents & POLLIN)
7734
DNSServiceProcessResult(DNSSDMaster);
7735
#endif /* HAVE_MDNSRESPONDER */
7736
7737
#ifdef HAVE_DNSSD
7738
if (printer->dnssd_collision)
7739
register_printer(printer);
7740
#endif /* HAVE_DNSSD */
7741
7742
/*
7743
* Clean out old jobs...
7744
*/
7745
7746
clean_jobs(printer);
7747
}
7748
}
7749
7750
7751
/*
7752
* 'show_media()' - Show media load state.
7753
*/
7754
7755
static int /* O - 1 on success, 0 on failure */
7756
show_media(ippeve_client_t *client) /* I - Client connection */
7757
{
7758
ippeve_printer_t *printer = client->printer;
7759
/* Printer */
7760
int i, j, /* Looping vars */
7761
num_ready, /* Number of ready media */
7762
num_sizes, /* Number of media sizes */
7763
num_sources, /* Number of media sources */
7764
num_types; /* Number of media types */
7765
ipp_attribute_t *media_col_ready,/* media-col-ready attribute */
7766
*media_ready, /* media-ready attribute */
7767
*media_sizes, /* media-supported attribute */
7768
*media_sources, /* media-source-supported attribute */
7769
*media_types, /* media-type-supported attribute */
7770
*input_tray; /* printer-input-tray attribute */
7771
ipp_t *media_col; /* media-col value */
7772
const char *media_size, /* media value */
7773
*media_source, /* media-source value */
7774
*media_type, /* media-type value */
7775
*ready_size, /* media-col-ready media-size[-name] value */
7776
*ready_source, /* media-col-ready media-source value */
7777
*ready_tray, /* printer-input-tray value */
7778
*ready_type; /* media-col-ready media-type value */
7779
char tray_str[1024], /* printer-input-tray string value */
7780
*tray_ptr; /* Pointer into value */
7781
int tray_len; /* Length of printer-input-tray value */
7782
int ready_sheets; /* printer-input-tray sheets value */
7783
int num_options = 0;/* Number of form options */
7784
cups_option_t *options = NULL;/* Form options */
7785
static const int sheets[] = /* Number of sheets */
7786
{
7787
250,
7788
125,
7789
50,
7790
25,
7791
5,
7792
0,
7793
-2
7794
};
7795
7796
7797
if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
7798
return (0);
7799
7800
html_header(client, printer->name, 0);
7801
7802
if ((media_col_ready = ippFindAttribute(printer->attrs, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) == NULL)
7803
{
7804
html_printf(client, "<p>Error: No media-col-ready defined for printer.</p>\n");
7805
html_footer(client);
7806
return (1);
7807
}
7808
7809
media_ready = ippFindAttribute(printer->attrs, "media-ready", IPP_TAG_ZERO);
7810
7811
if ((media_sizes = ippFindAttribute(printer->attrs, "media-supported", IPP_TAG_ZERO)) == NULL)
7812
{
7813
html_printf(client, "<p>Error: No media-supported defined for printer.</p>\n");
7814
html_footer(client);
7815
return (1);
7816
}
7817
7818
if ((media_sources = ippFindAttribute(printer->attrs, "media-source-supported", IPP_TAG_ZERO)) == NULL)
7819
{
7820
html_printf(client, "<p>Error: No media-source-supported defined for printer.</p>\n");
7821
html_footer(client);
7822
return (1);
7823
}
7824
7825
if ((media_types = ippFindAttribute(printer->attrs, "media-type-supported", IPP_TAG_ZERO)) == NULL)
7826
{
7827
html_printf(client, "<p>Error: No media-type-supported defined for printer.</p>\n");
7828
html_footer(client);
7829
return (1);
7830
}
7831
7832
if ((input_tray = ippFindAttribute(printer->attrs, "printer-input-tray", IPP_TAG_STRING)) == NULL)
7833
{
7834
html_printf(client, "<p>Error: No printer-input-tray defined for printer.</p>\n");
7835
html_footer(client);
7836
return (1);
7837
}
7838
7839
num_ready = ippGetCount(media_col_ready);
7840
num_sizes = ippGetCount(media_sizes);
7841
num_sources = ippGetCount(media_sources);
7842
num_types = ippGetCount(media_types);
7843
7844
if (num_sources != ippGetCount(input_tray))
7845
{
7846
html_printf(client, "<p>Error: Different number of trays in media-source-supported and printer-input-tray defined for printer.</p>\n");
7847
html_footer(client);
7848
return (1);
7849
}
7850
7851
/*
7852
* Process form data if present...
7853
*/
7854
7855
if (printer->web_forms)
7856
num_options = parse_options(client, &options);
7857
7858
if (num_options > 0)
7859
{
7860
/*
7861
* WARNING: A real printer/server implementation MUST NOT implement
7862
* media updates via a GET request - GET requests are supposed to be
7863
* idempotent (without side-effects) and we obviously are not
7864
* authenticating access here. This form is provided solely to
7865
* enable testing and development!
7866
*/
7867
7868
char name[255]; /* Form name */
7869
const char *val; /* Form value */
7870
pwg_media_t *media; /* Media info */
7871
7872
_cupsRWLockWrite(&printer->rwlock);
7873
7874
ippDeleteAttribute(printer->attrs, media_col_ready);
7875
media_col_ready = NULL;
7876
7877
if (media_ready)
7878
{
7879
ippDeleteAttribute(printer->attrs, media_ready);
7880
media_ready = NULL;
7881
}
7882
7883
printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MEDIA_LOW | IPPEVE_PREASON_MEDIA_EMPTY | IPPEVE_PREASON_MEDIA_NEEDED);
7884
7885
for (i = 0; i < num_sources; i ++)
7886
{
7887
media_source = ippGetString(media_sources, i, NULL);
7888
7889
if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
7890
continue;
7891
7892
snprintf(name, sizeof(name), "size%d", i);
7893
if ((media_size = cupsGetOption(name, num_options, options)) != NULL && (media = pwgMediaForPWG(media_size)) != NULL)
7894
{
7895
snprintf(name, sizeof(name), "type%d", i);
7896
if ((media_type = cupsGetOption(name, num_options, options)) != NULL && !*media_type)
7897
media_type = NULL;
7898
7899
if (media_ready)
7900
ippSetString(printer->attrs, &media_ready, ippGetCount(media_ready), media_size);
7901
else
7902
media_ready = ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-ready", NULL, media_size);
7903
7904
media_col = create_media_col(media_size, media_source, media_type, media->width, media->length, -1, -1, -1, -1);
7905
7906
if (media_col_ready)
7907
ippSetCollection(printer->attrs, &media_col_ready, ippGetCount(media_col_ready), media_col);
7908
else
7909
media_col_ready = ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-ready", media_col);
7910
ippDelete(media_col);
7911
}
7912
else
7913
media = NULL;
7914
7915
snprintf(name, sizeof(name), "level%d", i);
7916
if ((val = cupsGetOption(name, num_options, options)) != NULL)
7917
ready_sheets = atoi(val);
7918
else
7919
ready_sheets = 0;
7920
7921
snprintf(tray_str, sizeof(tray_str), "type=sheetFeedAuto%sRemovableTray;mediafeed=%d;mediaxfeed=%d;maxcapacity=%d;level=%d;status=0;name=%s;", !strcmp(media_source, "by-pass-tray") ? "Non" : "", media ? media->length : 0, media ? media->width : 0, strcmp(media_source, "by-pass-tray") ? 250 : 25, ready_sheets, media_source);
7922
7923
ippSetOctetString(printer->attrs, &input_tray, i, tray_str, (int)strlen(tray_str));
7924
7925
if (ready_sheets == 0)
7926
{
7927
printer->state_reasons |= IPPEVE_PREASON_MEDIA_EMPTY;
7928
if (printer->active_job)
7929
printer->state_reasons |= IPPEVE_PREASON_MEDIA_NEEDED;
7930
}
7931
else if (ready_sheets < 25 && ready_sheets > 0)
7932
printer->state_reasons |= IPPEVE_PREASON_MEDIA_LOW;
7933
}
7934
7935
if (!media_col_ready)
7936
media_col_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-col-ready");
7937
7938
if (!media_ready)
7939
media_ready = ippAddOutOfBand(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "media-ready");
7940
7941
_cupsRWUnlock(&printer->rwlock);
7942
}
7943
7944
if (printer->web_forms)
7945
html_printf(client, "<form method=\"GET\" action=\"/media\">\n");
7946
7947
html_printf(client, "<table class=\"form\" summary=\"Media\">\n");
7948
for (i = 0; i < num_sources; i ++)
7949
{
7950
media_source = ippGetString(media_sources, i, NULL);
7951
7952
if (!strcmp(media_source, "auto") || !strcmp(media_source, "manual") || strstr(media_source, "-man") != NULL)
7953
continue;
7954
7955
for (j = 0, ready_size = NULL, ready_type = NULL; j < num_ready; j ++)
7956
{
7957
media_col = ippGetCollection(media_col_ready, j);
7958
ready_size = ippGetString(ippFindAttribute(media_col, "media-size-name", IPP_TAG_ZERO), 0, NULL);
7959
ready_source = ippGetString(ippFindAttribute(media_col, "media-source", IPP_TAG_ZERO), 0, NULL);
7960
ready_type = ippGetString(ippFindAttribute(media_col, "media-type", IPP_TAG_ZERO), 0, NULL);
7961
7962
if (ready_source && !strcmp(ready_source, media_source))
7963
break;
7964
7965
ready_source = NULL;
7966
ready_size = NULL;
7967
ready_type = NULL;
7968
}
7969
7970
html_printf(client, "<tr><th>%s:</th>", media_source);
7971
7972
/*
7973
* Media size...
7974
*/
7975
7976
if (printer->web_forms)
7977
{
7978
html_printf(client, "<td><select name=\"size%d\"><option value=\"\">None</option>", i);
7979
for (j = 0; j < num_sizes; j ++)
7980
{
7981
media_size = ippGetString(media_sizes, j, NULL);
7982
7983
html_printf(client, "<option%s>%s</option>", (ready_size && !strcmp(ready_size, media_size)) ? " selected" : "", media_size);
7984
}
7985
html_printf(client, "</select>");
7986
}
7987
else
7988
html_printf(client, "<td>%s", ready_size);
7989
7990
/*
7991
* Media type...
7992
*/
7993
7994
if (printer->web_forms)
7995
{
7996
html_printf(client, " <select name=\"type%d\"><option value=\"\">None</option>", i);
7997
for (j = 0; j < num_types; j ++)
7998
{
7999
media_type = ippGetString(media_types, j, NULL);
8000
8001
html_printf(client, "<option%s>%s</option>", (ready_type && !strcmp(ready_type, media_type)) ? " selected" : "", media_type);
8002
}
8003
html_printf(client, "</select>");
8004
}
8005
else if (ready_type)
8006
html_printf(client, ", %s", ready_type);
8007
8008
/*
8009
* Level/sheets loaded...
8010
*/
8011
8012
if ((ready_tray = ippGetOctetString(input_tray, i, &tray_len)) != NULL)
8013
{
8014
if (tray_len > (int)(sizeof(tray_str) - 1))
8015
tray_len = (int)sizeof(tray_str) - 1;
8016
memcpy(tray_str, ready_tray, (size_t)tray_len);
8017
tray_str[tray_len] = '\0';
8018
8019
if ((tray_ptr = strstr(tray_str, "level=")) != NULL)
8020
ready_sheets = atoi(tray_ptr + 6);
8021
else
8022
ready_sheets = 0;
8023
}
8024
else
8025
ready_sheets = 0;
8026
8027
if (printer->web_forms)
8028
{
8029
html_printf(client, " <select name=\"level%d\">", i);
8030
for (j = 0; j < (int)(sizeof(sheets) / sizeof(sheets[0])); j ++)
8031
{
8032
if (!strcmp(media_source, "by-pass-tray") && sheets[j] > 25)
8033
continue;
8034
8035
if (sheets[j] < 0)
8036
html_printf(client, "<option value=\"%d\"%s>Unknown</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "");
8037
else
8038
html_printf(client, "<option value=\"%d\"%s>%d sheets</option>", sheets[j], sheets[j] == ready_sheets ? " selected" : "", sheets[j]);
8039
}
8040
html_printf(client, "</select></td></tr>\n");
8041
}
8042
else if (ready_sheets == 1)
8043
html_printf(client, ", 1 sheet</td></tr>\n");
8044
else if (ready_sheets > 0)
8045
html_printf(client, ", %d sheets</td></tr>\n", ready_sheets);
8046
else
8047
html_printf(client, "</td></tr>\n");
8048
}
8049
8050
if (printer->web_forms)
8051
{
8052
html_printf(client, "<tr><td></td><td><input type=\"submit\" value=\"Update Media\">");
8053
if (num_options > 0)
8054
html_printf(client, " <span class=\"badge\" id=\"status\">Media updated.</span>\n");
8055
html_printf(client, "</td></tr></table></form>\n");
8056
8057
if (num_options > 0)
8058
html_printf(client, "<script>\n"
8059
"setTimeout(hide_status, 3000);\n"
8060
"function hide_status() {\n"
8061
" var status = document.getElementById('status');\n"
8062
" status.style.display = 'none';\n"
8063
"}\n"
8064
"</script>\n");
8065
}
8066
else
8067
html_printf(client, "</table>\n");
8068
8069
cupsFreeOptions(num_options, options);
8070
8071
html_footer(client);
8072
8073
return (1);
8074
}
8075
8076
8077
/*
8078
* 'show_status()' - Show printer/system state.
8079
*/
8080
8081
static int /* O - 1 on success, 0 on failure */
8082
show_status(ippeve_client_t *client) /* I - Client connection */
8083
{
8084
ippeve_printer_t *printer = client->printer;
8085
/* Printer */
8086
ippeve_job_t *job; /* Current job */
8087
int i; /* Looping var */
8088
ippeve_preason_t reason; /* Current reason */
8089
static const char * const reasons[] = /* Reason strings */
8090
{
8091
"Other",
8092
"Cover Open",
8093
"Input Tray Missing",
8094
"Marker Supply Empty",
8095
"Marker Supply Low",
8096
"Marker Waste Almost Full",
8097
"Marker Waste Full",
8098
"Media Empty",
8099
"Media Jam",
8100
"Media Low",
8101
"Media Needed",
8102
"Moving to Paused",
8103
"Paused",
8104
"Spool Area Full",
8105
"Toner Empty",
8106
"Toner Low"
8107
};
8108
static const char * const state_colors[] =
8109
{ /* State colors */
8110
"#0C0", /* Idle */
8111
"#EE0", /* Processing */
8112
"#C00" /* Stopped */
8113
};
8114
8115
8116
if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
8117
return (0);
8118
8119
html_header(client, printer->name, printer->state == IPP_PSTATE_PROCESSING ? 5 : 15);
8120
html_printf(client, "<h1><img style=\"background: %s; border-radius: 10px; float: left; margin-right: 10px; padding: 10px;\" src=\"/icon.png\" width=\"64\" height=\"64\">%s Jobs</h1>\n", state_colors[printer->state - IPP_PSTATE_IDLE], printer->name);
8121
html_printf(client, "<p>%s, %d job(s).", printer->state == IPP_PSTATE_IDLE ? "Idle" : printer->state == IPP_PSTATE_PROCESSING ? "Printing" : "Stopped", cupsArrayCount(printer->jobs));
8122
for (i = 0, reason = 1; i < (int)(sizeof(reasons) / sizeof(reasons[0])); i ++, reason <<= 1)
8123
if (printer->state_reasons & reason)
8124
html_printf(client, "\n<br>&nbsp;&nbsp;&nbsp;&nbsp;%s", reasons[i]);
8125
html_printf(client, "</p>\n");
8126
8127
if (cupsArrayCount(printer->jobs) > 0)
8128
{
8129
_cupsRWLockRead(&(printer->rwlock));
8130
8131
html_printf(client, "<table class=\"striped\" summary=\"Jobs\"><thead><tr><th>Job #</th><th>Name</th><th>Owner</th><th>Status</th></tr></thead><tbody>\n");
8132
for (job = (ippeve_job_t *)cupsArrayFirst(printer->jobs); job; job = (ippeve_job_t *)cupsArrayNext(printer->jobs))
8133
{
8134
char when[256], /* When job queued/started/finished */
8135
hhmmss[64]; /* Time HH:MM:SS */
8136
8137
switch (job->state)
8138
{
8139
case IPP_JSTATE_PENDING :
8140
case IPP_JSTATE_HELD :
8141
snprintf(when, sizeof(when), "Queued at %s", time_string(job->created, hhmmss, sizeof(hhmmss)));
8142
break;
8143
case IPP_JSTATE_PROCESSING :
8144
case IPP_JSTATE_STOPPED :
8145
snprintf(when, sizeof(when), "Started at %s", time_string(job->processing, hhmmss, sizeof(hhmmss)));
8146
break;
8147
case IPP_JSTATE_ABORTED :
8148
snprintf(when, sizeof(when), "Aborted at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
8149
break;
8150
case IPP_JSTATE_CANCELED :
8151
snprintf(when, sizeof(when), "Canceled at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
8152
break;
8153
case IPP_JSTATE_COMPLETED :
8154
snprintf(when, sizeof(when), "Completed at %s", time_string(job->completed, hhmmss, sizeof(hhmmss)));
8155
break;
8156
}
8157
8158
html_printf(client, "<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", job->id, job->name, job->username, when);
8159
}
8160
html_printf(client, "</tbody></table>\n");
8161
8162
_cupsRWUnlock(&(printer->rwlock));
8163
}
8164
8165
html_footer(client);
8166
8167
return (1);
8168
}
8169
8170
8171
/*
8172
* 'show_supplies()' - Show printer supplies.
8173
*/
8174
8175
static int /* O - 1 on success, 0 on failure */
8176
show_supplies(
8177
ippeve_client_t *client) /* I - Client connection */
8178
{
8179
ippeve_printer_t *printer = client->printer;
8180
/* Printer */
8181
int i, /* Looping var */
8182
num_supply; /* Number of supplies */
8183
ipp_attribute_t *supply, /* printer-supply attribute */
8184
*supply_desc; /* printer-supply-description attribute */
8185
int num_options = 0; /* Number of form options */
8186
cups_option_t *options = NULL; /* Form options */
8187
int supply_len, /* Length of supply value */
8188
level; /* Supply level */
8189
const char *supply_value; /* Supply value */
8190
char supply_text[1024], /* Supply string */
8191
*supply_ptr; /* Pointer into supply string */
8192
static const char * const printer_supply[] =
8193
{ /* printer-supply values */
8194
"index=1;class=receptacleThatIsFilled;type=wasteToner;unit=percent;"
8195
"maxcapacity=100;level=%d;colorantname=unknown;",
8196
"index=2;class=supplyThatIsConsumed;type=toner;unit=percent;"
8197
"maxcapacity=100;level=%d;colorantname=black;",
8198
"index=3;class=supplyThatIsConsumed;type=toner;unit=percent;"
8199
"maxcapacity=100;level=%d;colorantname=cyan;",
8200
"index=4;class=supplyThatIsConsumed;type=toner;unit=percent;"
8201
"maxcapacity=100;level=%d;colorantname=magenta;",
8202
"index=5;class=supplyThatIsConsumed;type=toner;unit=percent;"
8203
"maxcapacity=100;level=%d;colorantname=yellow;"
8204
};
8205
static const char * const backgrounds[] =
8206
{ /* Background colors for the supply-level bars */
8207
"#777 linear-gradient(#333,#777)",
8208
"#000 linear-gradient(#666,#000)",
8209
"#0FF linear-gradient(#6FF,#0FF)",
8210
"#F0F linear-gradient(#F6F,#F0F)",
8211
"#CC0 linear-gradient(#EE6,#EE0)"
8212
};
8213
static const char * const colors[] = /* Text colors for the supply-level bars */
8214
{
8215
"#fff",
8216
"#fff",
8217
"#000",
8218
"#000",
8219
"#000"
8220
};
8221
8222
8223
if (!respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0))
8224
return (0);
8225
8226
html_header(client, printer->name, 0);
8227
8228
if ((supply = ippFindAttribute(printer->attrs, "printer-supply", IPP_TAG_STRING)) == NULL)
8229
{
8230
html_printf(client, "<p>Error: No printer-supply defined for printer.</p>\n");
8231
html_footer(client);
8232
return (1);
8233
}
8234
8235
num_supply = ippGetCount(supply);
8236
8237
if ((supply_desc = ippFindAttribute(printer->attrs, "printer-supply-description", IPP_TAG_TEXT)) == NULL)
8238
{
8239
html_printf(client, "<p>Error: No printer-supply-description defined for printer.</p>\n");
8240
html_footer(client);
8241
return (1);
8242
}
8243
8244
if (num_supply != ippGetCount(supply_desc))
8245
{
8246
html_printf(client, "<p>Error: Different number of values for printer-supply and printer-supply-description defined for printer.</p>\n");
8247
html_footer(client);
8248
return (1);
8249
}
8250
8251
if (printer->web_forms)
8252
num_options = parse_options(client, &options);
8253
8254
if (num_options > 0)
8255
{
8256
/*
8257
* WARNING: A real printer/server implementation MUST NOT implement
8258
* supply updates via a GET request - GET requests are supposed to be
8259
* idempotent (without side-effects) and we obviously are not
8260
* authenticating access here. This form is provided solely to
8261
* enable testing and development!
8262
*/
8263
8264
char name[64]; /* Form field */
8265
const char *val; /* Form value */
8266
8267
_cupsRWLockWrite(&printer->rwlock);
8268
8269
ippDeleteAttribute(printer->attrs, supply);
8270
supply = NULL;
8271
8272
printer->state_reasons &= (ippeve_preason_t)~(IPPEVE_PREASON_MARKER_SUPPLY_EMPTY | IPPEVE_PREASON_MARKER_SUPPLY_LOW | IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL | IPPEVE_PREASON_MARKER_WASTE_FULL | IPPEVE_PREASON_TONER_EMPTY | IPPEVE_PREASON_TONER_LOW);
8273
8274
for (i = 0; i < num_supply; i ++)
8275
{
8276
snprintf(name, sizeof(name), "supply%d", i);
8277
if ((val = cupsGetOption(name, num_options, options)) != NULL)
8278
{
8279
level = atoi(val); /* New level */
8280
8281
snprintf(supply_text, sizeof(supply_text), printer_supply[i], level);
8282
if (supply)
8283
ippSetOctetString(printer->attrs, &supply, ippGetCount(supply), supply_text, (int)strlen(supply_text));
8284
else
8285
supply = ippAddOctetString(printer->attrs, IPP_TAG_PRINTER, "printer-supply", supply_text, (int)strlen(supply_text));
8286
8287
if (i == 0)
8288
{
8289
if (level == 100)
8290
printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_FULL;
8291
else if (level > 90)
8292
printer->state_reasons |= IPPEVE_PREASON_MARKER_WASTE_ALMOST_FULL;
8293
}
8294
else
8295
{
8296
if (level == 0)
8297
printer->state_reasons |= IPPEVE_PREASON_TONER_EMPTY;
8298
else if (level < 10)
8299
printer->state_reasons |= IPPEVE_PREASON_TONER_LOW;
8300
}
8301
}
8302
}
8303
8304
_cupsRWUnlock(&printer->rwlock);
8305
}
8306
8307
if (printer->web_forms)
8308
html_printf(client, "<form method=\"GET\" action=\"/supplies\">\n");
8309
8310
html_printf(client, "<table class=\"form\" summary=\"Supplies\">\n");
8311
for (i = 0; i < num_supply; i ++)
8312
{
8313
supply_value = ippGetOctetString(supply, i, &supply_len);
8314
if (supply_len > (int)(sizeof(supply_text) - 1))
8315
supply_len = (int)sizeof(supply_text) - 1;
8316
8317
memcpy(supply_text, supply_value, (size_t)supply_len);
8318
supply_text[supply_len] = '\0';
8319
8320
if ((supply_ptr = strstr(supply_text, "level=")) != NULL)
8321
level = atoi(supply_ptr + 6);
8322
else
8323
level = 50;
8324
8325
if (printer->web_forms)
8326
html_printf(client, "<tr><th>%s:</th><td><input name=\"supply%d\" size=\"3\" value=\"%d\"></td>", ippGetString(supply_desc, i, NULL), i, level);
8327
else
8328
html_printf(client, "<tr><th>%s:</th>", ippGetString(supply_desc, i, NULL));
8329
8330
if (level < 10)
8331
html_printf(client, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; padding: 5px %dpx;\"></span>&nbsp;%d%%</td></tr>\n", backgrounds[i], level * 2, level);
8332
else
8333
html_printf(client, "<td class=\"meter\"><span class=\"bar\" style=\"background: %s; color: %s; padding: 5px %dpx;\">%d%%</span></td></tr>\n", backgrounds[i], colors[i], level * 2, level);
8334
}
8335
8336
if (printer->web_forms)
8337
{
8338
html_printf(client, "<tr><td></td><td colspan=\"2\"><input type=\"submit\" value=\"Update Supplies\">");
8339
if (num_options > 0)
8340
html_printf(client, " <span class=\"badge\" id=\"status\">Supplies updated.</span>\n");
8341
html_printf(client, "</td></tr>\n</table>\n</form>\n");
8342
8343
if (num_options > 0)
8344
html_printf(client, "<script>\n"
8345
"setTimeout(hide_status, 3000);\n"
8346
"function hide_status() {\n"
8347
" var status = document.getElementById('status');\n"
8348
" status.style.display = 'none';\n"
8349
"}\n"
8350
"</script>\n");
8351
}
8352
else
8353
html_printf(client, "</table>\n");
8354
8355
cupsFreeOptions(num_options, options);
8356
8357
html_footer(client);
8358
8359
return (1);
8360
}
8361
8362
8363
#ifndef _WIN32
8364
/*
8365
* 'signal_handler()' - Handle termination signals.
8366
*/
8367
8368
static void
8369
signal_handler(int signum) /* I - Signal number (not used) */
8370
{
8371
(void)signum;
8372
8373
StopPrinter = 1;
8374
}
8375
#endif // !_WIN32
8376
8377
8378
/*
8379
* 'time_string()' - Return the local time in hours, minutes, and seconds.
8380
*/
8381
8382
static char *
8383
time_string(time_t tv, /* I - Time value */
8384
char *buffer, /* I - Buffer */
8385
size_t bufsize) /* I - Size of buffer */
8386
{
8387
struct tm date; /* Local time and date */
8388
8389
localtime_r(&tv, &date);
8390
8391
strftime(buffer, bufsize, "%X", &date);
8392
8393
return (buffer);
8394
}
8395
8396
8397
/*
8398
* 'usage()' - Show program usage.
8399
*/
8400
8401
static void
8402
usage(int status) /* O - Exit status */
8403
{
8404
_cupsLangPuts(stdout, _("Usage: ippeveprinter [options] \"name\""));
8405
_cupsLangPuts(stdout, _("Options:"));
8406
_cupsLangPuts(stdout, _("--help Show program help"));
8407
_cupsLangPuts(stdout, _("--no-web-forms Disable web forms for media and supplies"));
8408
_cupsLangPuts(stdout, _("--pam-service service Use the named PAM service"));
8409
_cupsLangPuts(stdout, _("--version Show program version"));
8410
_cupsLangPuts(stdout, _("-2 Set 2-sided printing support (default=1-sided)"));
8411
_cupsLangPuts(stdout, _("-A Enable authentication"));
8412
_cupsLangPuts(stdout, _("-D device-uri Set the device URI for the printer"));
8413
_cupsLangPuts(stdout, _("-F output-type/subtype Set the output format for the printer"));
8414
#ifdef HAVE_TLS
8415
_cupsLangPuts(stdout, _("-K keypath Set location of server X.509 certificates and keys."));
8416
#endif /* HAVE_TLS */
8417
_cupsLangPuts(stdout, _("-M manufacturer Set manufacturer name (default=Test)"));
8418
#if !CUPS_LITE
8419
_cupsLangPuts(stdout, _("-P filename.ppd Load printer attributes from PPD file"));
8420
#endif /* !CUPS_LITE */
8421
_cupsLangPuts(stdout, _("-S filename.strings Set strings file"));
8422
_cupsLangPuts(stdout, _("-V version Set default IPP version"));
8423
_cupsLangPuts(stdout, _("-a filename.conf Load printer attributes from conf file"));
8424
_cupsLangPuts(stdout, _("-c command Set print command"));
8425
_cupsLangPuts(stdout, _("-d spool-directory Set spool directory"));
8426
_cupsLangPuts(stdout, _("-f type/subtype[,...] Set supported file types"));
8427
_cupsLangPuts(stdout, _("-i iconfile.png[,...] Set icon file(s)"));
8428
_cupsLangPuts(stdout, _("-k Keep job spool files"));
8429
_cupsLangPuts(stdout, _("-l location Set location of printer"));
8430
_cupsLangPuts(stdout, _("-m model Set model name (default=Printer)"));
8431
_cupsLangPuts(stdout, _("-n hostname Set hostname for printer"));
8432
_cupsLangPuts(stdout, _("-p port Set port number for printer"));
8433
_cupsLangPuts(stdout, _("-r subtype,[subtype] Set DNS-SD service subtype"));
8434
_cupsLangPuts(stdout, _("-s speed[,color-speed] Set speed in pages per minute"));
8435
_cupsLangPuts(stdout, _("-v Be verbose"));
8436
8437
exit(status);
8438
}
8439
8440
8441
/*
8442
* 'valid_doc_attributes()' - Determine whether the document attributes are
8443
* valid.
8444
*
8445
* When one or more document attributes are invalid, this function adds a
8446
* suitable response and attributes to the unsupported group.
8447
*/
8448
8449
static int /* O - 1 if valid, 0 if not */
8450
valid_doc_attributes(
8451
ippeve_client_t *client) /* I - Client */
8452
{
8453
int valid = 1; /* Valid attributes? */
8454
ipp_op_t op = ippGetOperation(client->request);
8455
/* IPP operation */
8456
const char *op_name = ippOpString(op);
8457
/* IPP operation name */
8458
ipp_attribute_t *attr, /* Current attribute */
8459
*supported; /* xxx-supported attribute */
8460
const char *compression = NULL,
8461
/* compression value */
8462
*format = NULL; /* document-format value */
8463
8464
8465
/*
8466
* Check operation attributes...
8467
*/
8468
8469
if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL)
8470
{
8471
/*
8472
* If compression is specified, only accept a supported value in a Print-Job
8473
* or Send-Document request...
8474
*/
8475
8476
compression = ippGetString(attr, 0, NULL);
8477
supported = ippFindAttribute(client->printer->attrs,
8478
"compression-supported", IPP_TAG_KEYWORD);
8479
8480
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
8481
ippGetGroupTag(attr) != IPP_TAG_OPERATION ||
8482
(op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT &&
8483
op != IPP_OP_VALIDATE_JOB) ||
8484
!ippContainsString(supported, compression))
8485
{
8486
respond_unsupported(client, attr);
8487
valid = 0;
8488
}
8489
else
8490
{
8491
fprintf(stderr, "%s %s compression=\"%s\"\n", client->hostname, op_name, compression);
8492
8493
ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "compression-supplied", NULL, compression);
8494
8495
if (strcmp(compression, "none"))
8496
{
8497
if (Verbosity)
8498
fprintf(stderr, "Receiving job file with \"%s\" compression.\n", compression);
8499
httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression);
8500
}
8501
}
8502
}
8503
8504
/*
8505
* Is it a format we support?
8506
*/
8507
8508
if ((attr = ippFindAttribute(client->request, "document-format", IPP_TAG_ZERO)) != NULL)
8509
{
8510
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE ||
8511
ippGetGroupTag(attr) != IPP_TAG_OPERATION)
8512
{
8513
respond_unsupported(client, attr);
8514
valid = 0;
8515
}
8516
else
8517
{
8518
format = ippGetString(attr, 0, NULL);
8519
8520
fprintf(stderr, "%s %s document-format=\"%s\"\n", client->hostname, op_name, format);
8521
8522
ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, format);
8523
}
8524
}
8525
else
8526
{
8527
format = ippGetString(ippFindAttribute(client->printer->attrs, "document-format-default", IPP_TAG_MIMETYPE), 0, NULL);
8528
if (!format)
8529
format = "application/octet-stream"; /* Should never happen */
8530
8531
attr = ippAddString(client->request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, format);
8532
}
8533
8534
if (format && !strcmp(format, "application/octet-stream") && (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT))
8535
{
8536
/*
8537
* Auto-type the file using the first 8 bytes of the file...
8538
*/
8539
8540
unsigned char header[8]; /* First 8 bytes of file */
8541
8542
memset(header, 0, sizeof(header));
8543
httpPeek(client->http, (char *)header, sizeof(header));
8544
8545
fprintf(stderr, "%s %s Auto-type header: %02X%02X%02X%02X%02X%02X%02X%02X\n", client->hostname, op_name, header[0], header[1], header[2], header[3], header[4], header[5], header[6], header[7]);
8546
if (!memcmp(header, "%PDF", 4))
8547
format = "application/pdf";
8548
else if (!memcmp(header, "%!", 2))
8549
format = "application/postscript";
8550
else if (!memcmp(header, "\377\330\377", 3) && header[3] >= 0xe0 && header[3] <= 0xef)
8551
format = "image/jpeg";
8552
else if (!memcmp(header, "\211PNG", 4))
8553
format = "image/png";
8554
else if (!memcmp(header, "RaS2PwgR", 8))
8555
format = "image/pwg-raster";
8556
else if (!memcmp(header, "UNIRAST", 8))
8557
format = "image/urf";
8558
else
8559
format = NULL;
8560
8561
if (format)
8562
{
8563
fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", client->hostname, op_name, format);
8564
8565
ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, format);
8566
}
8567
}
8568
8569
if (op != IPP_OP_CREATE_JOB && (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL && !ippContainsString(supported, format))
8570
{
8571
respond_unsupported(client, attr);
8572
valid = 0;
8573
}
8574
8575
/*
8576
* document-name
8577
*/
8578
8579
if ((attr = ippFindAttribute(client->request, "document-name", IPP_TAG_NAME)) != NULL)
8580
ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
8581
8582
return (valid);
8583
}
8584
8585
8586
/*
8587
* 'valid_job_attributes()' - Determine whether the job attributes are valid.
8588
*
8589
* When one or more job attributes are invalid, this function adds a suitable
8590
* response and attributes to the unsupported group.
8591
*/
8592
8593
static int /* O - 1 if valid, 0 if not */
8594
valid_job_attributes(
8595
ippeve_client_t *client) /* I - Client */
8596
{
8597
int i, /* Looping var */
8598
count, /* Number of values */
8599
valid = 1; /* Valid attributes? */
8600
ipp_attribute_t *attr, /* Current attribute */
8601
*supported; /* xxx-supported attribute */
8602
8603
8604
/*
8605
* Check operation attributes...
8606
*/
8607
8608
valid = valid_doc_attributes(client);
8609
8610
/*
8611
* Check the various job template attributes...
8612
*/
8613
8614
if ((attr = ippFindAttribute(client->request, "copies", IPP_TAG_ZERO)) != NULL)
8615
{
8616
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
8617
ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999)
8618
{
8619
respond_unsupported(client, attr);
8620
valid = 0;
8621
}
8622
}
8623
8624
if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", IPP_TAG_ZERO)) != NULL)
8625
{
8626
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN)
8627
{
8628
respond_unsupported(client, attr);
8629
valid = 0;
8630
}
8631
}
8632
8633
if ((attr = ippFindAttribute(client->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
8634
{
8635
if (ippGetCount(attr) != 1 ||
8636
(ippGetValueTag(attr) != IPP_TAG_NAME &&
8637
ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8638
ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
8639
strcmp(ippGetString(attr, 0, NULL), "no-hold"))
8640
{
8641
respond_unsupported(client, attr);
8642
valid = 0;
8643
}
8644
}
8645
8646
if ((attr = ippFindAttribute(client->request, "job-impressions", IPP_TAG_ZERO)) != NULL)
8647
{
8648
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || ippGetInteger(attr, 0) < 0)
8649
{
8650
respond_unsupported(client, attr);
8651
valid = 0;
8652
}
8653
}
8654
8655
if ((attr = ippFindAttribute(client->request, "job-name", IPP_TAG_ZERO)) != NULL)
8656
{
8657
if (ippGetCount(attr) != 1 ||
8658
(ippGetValueTag(attr) != IPP_TAG_NAME &&
8659
ippGetValueTag(attr) != IPP_TAG_NAMELANG))
8660
{
8661
respond_unsupported(client, attr);
8662
valid = 0;
8663
}
8664
8665
ippSetGroupTag(client->request, &attr, IPP_TAG_JOB);
8666
}
8667
else
8668
ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
8669
8670
if ((attr = ippFindAttribute(client->request, "job-priority", IPP_TAG_ZERO)) != NULL)
8671
{
8672
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER ||
8673
ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100)
8674
{
8675
respond_unsupported(client, attr);
8676
valid = 0;
8677
}
8678
}
8679
8680
if ((attr = ippFindAttribute(client->request, "job-sheets", IPP_TAG_ZERO)) != NULL)
8681
{
8682
if (ippGetCount(attr) != 1 ||
8683
(ippGetValueTag(attr) != IPP_TAG_NAME &&
8684
ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8685
ippGetValueTag(attr) != IPP_TAG_KEYWORD) ||
8686
strcmp(ippGetString(attr, 0, NULL), "none"))
8687
{
8688
respond_unsupported(client, attr);
8689
valid = 0;
8690
}
8691
}
8692
8693
if ((attr = ippFindAttribute(client->request, "media", IPP_TAG_ZERO)) != NULL)
8694
{
8695
if (ippGetCount(attr) != 1 ||
8696
(ippGetValueTag(attr) != IPP_TAG_NAME &&
8697
ippGetValueTag(attr) != IPP_TAG_NAMELANG &&
8698
ippGetValueTag(attr) != IPP_TAG_KEYWORD))
8699
{
8700
respond_unsupported(client, attr);
8701
valid = 0;
8702
}
8703
else
8704
{
8705
supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
8706
8707
if (!ippContainsString(supported, ippGetString(attr, 0, NULL)))
8708
{
8709
respond_unsupported(client, attr);
8710
valid = 0;
8711
}
8712
}
8713
}
8714
8715
if ((attr = ippFindAttribute(client->request, "media-col", IPP_TAG_ZERO)) != NULL)
8716
{
8717
ipp_t *col, /* media-col collection */
8718
*size; /* media-size collection */
8719
ipp_attribute_t *member, /* Member attribute */
8720
*x_dim, /* x-dimension */
8721
*y_dim; /* y-dimension */
8722
int x_value, /* y-dimension value */
8723
y_value; /* x-dimension value */
8724
8725
if (ippGetCount(attr) != 1 ||
8726
ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION)
8727
{
8728
respond_unsupported(client, attr);
8729
valid = 0;
8730
}
8731
8732
col = ippGetCollection(attr, 0);
8733
8734
if ((member = ippFindAttribute(col, "media-size-name", IPP_TAG_ZERO)) != NULL)
8735
{
8736
if (ippGetCount(member) != 1 ||
8737
(ippGetValueTag(member) != IPP_TAG_NAME &&
8738
ippGetValueTag(member) != IPP_TAG_NAMELANG &&
8739
ippGetValueTag(member) != IPP_TAG_KEYWORD))
8740
{
8741
respond_unsupported(client, attr);
8742
valid = 0;
8743
}
8744
else
8745
{
8746
supported = ippFindAttribute(client->printer->attrs, "media-supported", IPP_TAG_KEYWORD);
8747
8748
if (!ippContainsString(supported, ippGetString(member, 0, NULL)))
8749
{
8750
respond_unsupported(client, attr);
8751
valid = 0;
8752
}
8753
}
8754
}
8755
else if ((member = ippFindAttribute(col, "media-size", IPP_TAG_BEGIN_COLLECTION)) != NULL)
8756
{
8757
if (ippGetCount(member) != 1)
8758
{
8759
respond_unsupported(client, attr);
8760
valid = 0;
8761
}
8762
else
8763
{
8764
size = ippGetCollection(member, 0);
8765
8766
if ((x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(x_dim) != 1 ||
8767
(y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_INTEGER)) == NULL || ippGetCount(y_dim) != 1)
8768
{
8769
respond_unsupported(client, attr);
8770
valid = 0;
8771
}
8772
else
8773
{
8774
x_value = ippGetInteger(x_dim, 0);
8775
y_value = ippGetInteger(y_dim, 0);
8776
supported = ippFindAttribute(client->printer->attrs, "media-size-supported", IPP_TAG_BEGIN_COLLECTION);
8777
count = ippGetCount(supported);
8778
8779
for (i = 0; i < count ; i ++)
8780
{
8781
size = ippGetCollection(supported, i);
8782
x_dim = ippFindAttribute(size, "x-dimension", IPP_TAG_ZERO);
8783
y_dim = ippFindAttribute(size, "y-dimension", IPP_TAG_ZERO);
8784
8785
if (ippContainsInteger(x_dim, x_value) && ippContainsInteger(y_dim, y_value))
8786
break;
8787
}
8788
8789
if (i >= count)
8790
{
8791
respond_unsupported(client, attr);
8792
valid = 0;
8793
}
8794
}
8795
}
8796
}
8797
}
8798
8799
if ((attr = ippFindAttribute(client->request, "multiple-document-handling", IPP_TAG_ZERO)) != NULL)
8800
{
8801
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD ||
8802
(strcmp(ippGetString(attr, 0, NULL),
8803
"separate-documents-uncollated-copies") &&
8804
strcmp(ippGetString(attr, 0, NULL),
8805
"separate-documents-collated-copies")))
8806
{
8807
respond_unsupported(client, attr);
8808
valid = 0;
8809
}
8810
}
8811
8812
if ((attr = ippFindAttribute(client->request, "orientation-requested", IPP_TAG_ZERO)) != NULL)
8813
{
8814
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8815
ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT ||
8816
ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT)
8817
{
8818
respond_unsupported(client, attr);
8819
valid = 0;
8820
}
8821
}
8822
8823
if ((attr = ippFindAttribute(client->request, "page-ranges", IPP_TAG_ZERO)) != NULL)
8824
{
8825
if (ippGetValueTag(attr) != IPP_TAG_RANGE)
8826
{
8827
respond_unsupported(client, attr);
8828
valid = 0;
8829
}
8830
}
8831
8832
if ((attr = ippFindAttribute(client->request, "print-quality", IPP_TAG_ZERO)) != NULL)
8833
{
8834
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM ||
8835
ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT ||
8836
ippGetInteger(attr, 0) > IPP_QUALITY_HIGH)
8837
{
8838
respond_unsupported(client, attr);
8839
valid = 0;
8840
}
8841
}
8842
8843
if ((attr = ippFindAttribute(client->request, "printer-resolution", IPP_TAG_ZERO)) != NULL)
8844
{
8845
supported = ippFindAttribute(client->printer->attrs, "printer-resolution-supported", IPP_TAG_RESOLUTION);
8846
8847
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_RESOLUTION ||
8848
!supported)
8849
{
8850
respond_unsupported(client, attr);
8851
valid = 0;
8852
}
8853
else
8854
{
8855
int xdpi, /* Horizontal resolution for job template attribute */
8856
ydpi, /* Vertical resolution for job template attribute */
8857
sydpi; /* Vertical resolution for supported value */
8858
ipp_res_t units, /* Units for job template attribute */
8859
sunits; /* Units for supported value */
8860
8861
xdpi = ippGetResolution(attr, 0, &ydpi, &units);
8862
count = ippGetCount(supported);
8863
8864
for (i = 0; i < count; i ++)
8865
{
8866
if (xdpi == ippGetResolution(supported, i, &sydpi, &sunits) && ydpi == sydpi && units == sunits)
8867
break;
8868
}
8869
8870
if (i >= count)
8871
{
8872
respond_unsupported(client, attr);
8873
valid = 0;
8874
}
8875
}
8876
}
8877
8878
if ((attr = ippFindAttribute(client->request, "sides", IPP_TAG_ZERO)) != NULL)
8879
{
8880
const char *sides = ippGetString(attr, 0, NULL);
8881
/* "sides" value... */
8882
8883
if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD)
8884
{
8885
respond_unsupported(client, attr);
8886
valid = 0;
8887
}
8888
else if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", IPP_TAG_KEYWORD)) != NULL)
8889
{
8890
if (!ippContainsString(supported, sides))
8891
{
8892
respond_unsupported(client, attr);
8893
valid = 0;
8894
}
8895
}
8896
else if (strcmp(sides, "one-sided"))
8897
{
8898
respond_unsupported(client, attr);
8899
valid = 0;
8900
}
8901
}
8902
8903
return (valid);
8904
}
8905
8906