1/* 2 * "$Id: ippserver.c 11098 2013-07-04 15:55:20Z msweet $" 3 * 4 * Sample IPP/2.0 server for CUPS. 5 * 6 * Copyright 2010-2013 by Apple Inc. 7 * 8 * These coded instructions, statements, and computer programs are the 9 * property of Apple Inc. and are protected by Federal copyright 10 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 11 * which should have been included with this file. If this file is 12 * file is missing or damaged, see the license at "http://www.cups.org/". 13 * 14 * This file is subject to the Apple OS-Developed Software exception. 15 * 16 * Contents: 17 * 18 * main() - Main entry to the sample server. 19 * clean_jobs() - Clean out old (completed) jobs. 20 * compare_jobs() - Compare two jobs. 21 * copy_attributes() - Copy attributes from one request to 22 * another. 23 * copy_job_attrs() - Copy job attributes to the response. 24 * create_client() - Accept a new network connection and create 25 * a client object. 26 * create_job() - Create a new job object from a Print-Job or 27 * Create-Job request. 28 * create_listener() - Create a listener socket. 29 * create_media_col() - Create a media-col value. 30 * create_printer() - Create, register, and listen for 31 * connections to a printer object. 32 * create_requested_array() - Create an array for requested-attributes. 33 * debug_attributes() - Print attributes in a request or response. 34 * delete_client() - Close the socket and free all memory used 35 * by a client object. 36 * delete_job() - Remove from the printer and free all memory 37 * used by a job object. 38 * delete_printer() - Unregister, close listen sockets, and free 39 * all memory used by a printer object. 40 * dnssd_callback() - Handle Bonjour registration events. 41 * find_job() - Find a job specified in a request. 42 * html_escape() - Write a HTML-safe string. 43 * html_printf() - Send formatted text to the client, quoting 44 * as needed. 45 * ipp_cancel_job() - Cancel a job. 46 * ipp_create_job() - Create a job object. 47 * ipp_get_job_attributes() - Get the attributes for a job object. 48 * ipp_get_jobs() - Get a list of job objects. 49 * ipp_get_printer_attributes() - Get the attributes for a printer object. 50 * ipp_print_job() - Create a job object with an attached 51 * document. 52 * ipp_print_uri() - Create a job object with a referenced 53 * document. 54 * ipp_send_document() - Add an attached document to a job object 55 * created with Create-Job. 56 * ipp_send_uri() - Add a referenced document to a job object 57 * created with Create-Job. 58 * ipp_validate_job() - Validate job creation attributes. 59 * process_client() - Process client requests on a thread. 60 * process_http() - Process a HTTP request. 61 * process_ipp() - Process an IPP request. 62 * process_job() - Process a print job. 63 * register_printer() - Register a printer object via Bonjour. 64 * respond_http() - Send a HTTP response. 65 * respond_ipp() - Send an IPP response. 66 * respond_unsupported() - Respond with an unsupported attribute. 67 * run_printer() - Run the printer service. 68 * usage() - Show program usage. 69 * valid_doc_attributes() - Determine whether the document attributes 70 * are valid. 71 * valid_job_attributes() - Determine whether the job attributes are 72 * valid. 73 */ 74 75/* 76 * Disable private and deprecated stuff so we can verify that the public API 77 * is sufficient to implement a server. 78 */ 79 80#define _IPP_PRIVATE_STRUCTURES 0 /* Disable private IPP stuff */ 81#define _CUPS_NO_DEPRECATED 1 /* Disable deprecated stuff */ 82 83 84/* 85 * Include necessary headers... 86 */ 87 88#include <cups/cups.h> /* Public API */ 89#include <config.h> /* CUPS configuration header */ 90#include <cups/string-private.h> /* For string functions */ 91#include <cups/thread-private.h> /* For multithreading functions */ 92 93#include <sys/wait.h> 94 95#ifdef HAVE_DNSSD 96# include <dns_sd.h> 97#endif /* HAVE_DNSSD */ 98#include <limits.h> 99#include <sys/stat.h> 100#include <sys/fcntl.h> 101#include <poll.h> 102#ifdef HAVE_SYS_MOUNT_H 103# include <sys/mount.h> 104#endif /* HAVE_SYS_MOUNT_H */ 105#ifdef HAVE_SYS_STATFS_H 106# include <sys/statfs.h> 107#endif /* HAVE_SYS_STATFS_H */ 108#ifdef HAVE_SYS_STATVFS_H 109# include <sys/statvfs.h> 110#endif /* HAVE_SYS_STATVFS_H */ 111#ifdef HAVE_SYS_VFS_H 112# include <sys/vfs.h> 113#endif /* HAVE_SYS_VFS_H */ 114 115 116/* 117 * Constants... 118 */ 119 120enum _ipp_preasons_e /* printer-state-reasons bit values */ 121{ 122 _IPP_PSTATE_NONE = 0x0000, /* none */ 123 _IPP_PSTATE_OTHER = 0x0001, /* other */ 124 _IPP_PSTATE_COVER_OPEN = 0x0002, /* cover-open */ 125 _IPP_PSTATE_INPUT_TRAY_MISSING = 0x0004, 126 /* input-tray-missing */ 127 _IPP_PSTATE_MARKER_SUPPLY_EMPTY = 0x0008, 128 /* marker-supply-empty */ 129 _IPP_PSTATE_MARKER_SUPPLY_LOW = 0x0010, 130 /* marker-suply-low */ 131 _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL = 0x0020, 132 /* marker-waste-almost-full */ 133 _IPP_PSTATE_MARKER_WASTE_FULL = 0x0040, 134 /* marker-waste-full */ 135 _IPP_PSTATE_MEDIA_EMPTY = 0x0080, /* media-empty */ 136 _IPP_PSTATE_MEDIA_JAM = 0x0100, /* media-jam */ 137 _IPP_PSTATE_MEDIA_LOW = 0x0200, /* media-low */ 138 _IPP_PSTATE_MEDIA_NEEDED = 0x0400, /* media-needed */ 139 _IPP_PSTATE_MOVING_TO_PAUSED = 0x0800, 140 /* moving-to-paused */ 141 _IPP_PSTATE_PAUSED = 0x1000, /* paused */ 142 _IPP_PSTATE_SPOOL_AREA_FULL = 0x2000,/* spool-area-full */ 143 _IPP_PSTATE_TONER_EMPTY = 0x4000, /* toner-empty */ 144 _IPP_PSTATE_TONER_LOW = 0x8000 /* toner-low */ 145}; 146typedef unsigned int _ipp_preasons_t; /* Bitfield for printer-state-reasons */ 147 148typedef enum _ipp_media_class_e 149{ 150 _IPP_GENERAL, /* General-purpose size */ 151 _IPP_PHOTO_ONLY, /* Photo-only size */ 152 _IPP_ENV_ONLY /* Envelope-only size */ 153} _ipp_media_class_t; 154 155static const char * const media_supported[] = 156{ /* media-supported values */ 157 "iso_a4_210x297mm", /* A4 */ 158 "iso_a5_148x210mm", /* A5 */ 159 "iso_a6_105x148mm", /* A6 */ 160 "iso_dl_110x220mm", /* DL */ 161 "na_legal_8.5x14in", /* Legal */ 162 "na_letter_8.5x11in", /* Letter */ 163 "na_number-10_4.125x9.5in", /* #10 */ 164 "na_index-3x5_3x5in", /* 3x5 */ 165 "oe_photo-l_3.5x5in", /* L */ 166 "na_index-4x6_4x6in", /* 4x6 */ 167 "na_5x7_5x7in" /* 5x7 aka 2L */ 168}; 169static const int media_col_sizes[][3] = 170{ /* media-col-database sizes */ 171 { 21000, 29700, _IPP_GENERAL }, /* A4 */ 172 { 14800, 21000, _IPP_PHOTO_ONLY }, /* A5 */ 173 { 10500, 14800, _IPP_PHOTO_ONLY }, /* A6 */ 174 { 11000, 22000, _IPP_ENV_ONLY }, /* DL */ 175 { 21590, 35560, _IPP_GENERAL }, /* Legal */ 176 { 21590, 27940, _IPP_GENERAL }, /* Letter */ 177 { 10477, 24130, _IPP_ENV_ONLY }, /* #10 */ 178 { 7630, 12700, _IPP_PHOTO_ONLY }, /* 3x5 */ 179 { 8890, 12700, _IPP_PHOTO_ONLY }, /* L */ 180 { 10160, 15240, _IPP_PHOTO_ONLY }, /* 4x6 */ 181 { 12700, 17780, _IPP_PHOTO_ONLY } /* 5x7 aka 2L */ 182}; 183static const char * const media_type_supported[] = 184 /* media-type-supported values */ 185{ 186 "auto", 187 "cardstock", 188 "envelope", 189 "labels", 190 "other", 191 "photographic-glossy", 192 "photographic-high-gloss", 193 "photographic-matte", 194 "photographic-satin", 195 "photographic-semi-gloss", 196 "stationery", 197 "stationery-letterhead", 198 "transparency" 199}; 200 201 202/* 203 * Structures... 204 */ 205 206typedef struct _ipp_job_s _ipp_job_t; 207 208typedef struct _ipp_printer_s /**** Printer data ****/ 209{ 210 int ipv4, /* IPv4 listener */ 211 ipv6; /* IPv6 listener */ 212#ifdef HAVE_DNSSD 213 DNSServiceRef common_ref, /* Shared service connection */ 214 ipp_ref, /* Bonjour IPP service */ 215# ifdef HAVE_SSL 216 ipps_ref, /* Bonjour IPPS service */ 217# endif /* HAVE_SSL */ 218 http_ref, /* Bonjour HTTP service */ 219 printer_ref; /* Bonjour LPD service */ 220 TXTRecordRef ipp_txt; /* Bonjour IPP TXT record */ 221 char *dnssd_name; /* printer-dnssd-name */ 222#endif /* HAVE_DNSSD */ 223 char *name, /* printer-name */ 224 *icon, /* Icon filename */ 225 *directory, /* Spool directory */ 226 *hostname, /* Hostname */ 227 *uri, /* printer-uri-supported */ 228 *command; /* Command to run with job file */ 229 int port; /* Port */ 230 size_t urilen; /* Length of printer URI */ 231 ipp_t *attrs; /* Static attributes */ 232 ipp_pstate_t state; /* printer-state value */ 233 _ipp_preasons_t state_reasons; /* printer-state-reasons values */ 234 cups_array_t *jobs; /* Jobs */ 235 _ipp_job_t *active_job; /* Current active/pending job */ 236 int next_job_id; /* Next job-id value */ 237 _cups_rwlock_t rwlock; /* Printer lock */ 238} _ipp_printer_t; 239 240struct _ipp_job_s /**** Job data ****/ 241{ 242 int id; /* Job ID */ 243 const char *name, /* job-name */ 244 *username, /* job-originating-user-name */ 245 *format; /* document-format */ 246 ipp_jstate_t state; /* job-state value */ 247 time_t processing, /* time-at-processing value */ 248 completed; /* time-at-completed value */ 249 ipp_t *attrs; /* Static attributes */ 250 int cancel; /* Non-zero when job canceled */ 251 char *filename; /* Print file name */ 252 int fd; /* Print file descriptor */ 253 _ipp_printer_t *printer; /* Printer */ 254}; 255 256typedef struct _ipp_client_s /**** Client data ****/ 257{ 258 http_t *http; /* HTTP connection */ 259 ipp_t *request, /* IPP request */ 260 *response; /* IPP response */ 261 time_t start; /* Request start time */ 262 http_state_t operation; /* Request operation */ 263 ipp_op_t operation_id; /* IPP operation-id */ 264 char uri[1024]; /* Request URI */ 265 http_addr_t addr; /* Client address */ 266 char hostname[256]; /* Client hostname */ 267 _ipp_printer_t *printer; /* Printer */ 268 _ipp_job_t *job; /* Current job, if any */ 269} _ipp_client_t; 270 271 272/* 273 * Local functions... 274 */ 275 276static void clean_jobs(_ipp_printer_t *printer); 277static int compare_jobs(_ipp_job_t *a, _ipp_job_t *b); 278static void copy_attributes(ipp_t *to, ipp_t *from, cups_array_t *ra, 279 ipp_tag_t group_tag, int quickcopy); 280static void copy_job_attributes(_ipp_client_t *client, 281 _ipp_job_t *job, cups_array_t *ra); 282static _ipp_client_t *create_client(_ipp_printer_t *printer, int sock); 283static _ipp_job_t *create_job(_ipp_client_t *client); 284static int create_listener(int family, int *port); 285static ipp_t *create_media_col(const char *media, const char *type, 286 int width, int length, int margins); 287static ipp_t *create_media_size(int width, int length); 288static _ipp_printer_t *create_printer(const char *servername, 289 const char *name, const char *location, 290 const char *make, const char *model, 291 const char *icon, 292 const char *docformats, int ppm, 293 int ppm_color, int duplex, int port, 294 int pin, 295#ifdef HAVE_DNSSD 296 const char *subtype, 297#endif /* HAVE_DNSSD */ 298 const char *directory, 299 const char *command); 300static void debug_attributes(const char *title, ipp_t *ipp, 301 int response); 302static void delete_client(_ipp_client_t *client); 303static void delete_job(_ipp_job_t *job); 304static void delete_printer(_ipp_printer_t *printer); 305#ifdef HAVE_DNSSD 306static void dnssd_callback(DNSServiceRef sdRef, 307 DNSServiceFlags flags, 308 DNSServiceErrorType errorCode, 309 const char *name, 310 const char *regtype, 311 const char *domain, 312 _ipp_printer_t *printer); 313#endif /* HAVE_DNSSD */ 314static _ipp_job_t *find_job(_ipp_client_t *client); 315static void html_escape(_ipp_client_t *client, const char *s, 316 size_t slen); 317static void html_printf(_ipp_client_t *client, const char *format, 318 ...) __attribute__((__format__(__printf__, 319 2, 3))); 320static void ipp_cancel_job(_ipp_client_t *client); 321static void ipp_create_job(_ipp_client_t *client); 322static void ipp_get_job_attributes(_ipp_client_t *client); 323static void ipp_get_jobs(_ipp_client_t *client); 324static void ipp_get_printer_attributes(_ipp_client_t *client); 325static void ipp_print_job(_ipp_client_t *client); 326static void ipp_print_uri(_ipp_client_t *client); 327static void ipp_send_document(_ipp_client_t *client); 328static void ipp_send_uri(_ipp_client_t *client); 329static void ipp_validate_job(_ipp_client_t *client); 330static void *process_client(_ipp_client_t *client); 331static int process_http(_ipp_client_t *client); 332static int process_ipp(_ipp_client_t *client); 333static void *process_job(_ipp_job_t *job); 334#ifdef HAVE_DNSSD 335static int register_printer(_ipp_printer_t *printer, 336 const char *location, const char *make, 337 const char *model, const char *formats, 338 const char *adminurl, int color, 339 int duplex, const char *regtype); 340#endif /* HAVE_DNSSD */ 341static int respond_http(_ipp_client_t *client, http_status_t code, 342 const char *content_coding, 343 const char *type, size_t length); 344static void respond_ipp(_ipp_client_t *client, ipp_status_t status, 345 const char *message, ...) 346 __attribute__ ((__format__ (__printf__, 3, 4))); 347static void respond_unsupported(_ipp_client_t *client, 348 ipp_attribute_t *attr); 349static void run_printer(_ipp_printer_t *printer); 350static void usage(int status) __attribute__((noreturn)); 351static int valid_doc_attributes(_ipp_client_t *client); 352static int valid_job_attributes(_ipp_client_t *client); 353 354 355/* 356 * Globals... 357 */ 358 359static int KeepFiles = 0, 360 Verbosity = 0; 361 362 363/* 364 * 'main()' - Main entry to the sample server. 365 */ 366 367int /* O - Exit status */ 368main(int argc, /* I - Number of command-line args */ 369 char *argv[]) /* I - Command-line arguments */ 370{ 371 int i; /* Looping var */ 372 const char *opt, /* Current option character */ 373 *command = NULL, /* Command to run with job files */ 374 *servername = NULL, /* Server host name */ 375 *name = NULL, /* Printer name */ 376 *location = "", /* Location of printer */ 377 *make = "Test", /* Manufacturer */ 378 *model = "Printer", /* Model */ 379 *icon = "printer.png", /* Icon file */ 380 *formats = "application/pdf,image/jpeg,image/pwg-raster"; 381 /* Supported formats */ 382#ifdef HAVE_DNSSD 383 const char *subtype = "_print"; /* Bonjour service subtype */ 384#endif /* HAVE_DNSSD */ 385 int port = 8631, /* Port number (0 = auto) */ 386 duplex = 0, /* Duplex mode */ 387 ppm = 10, /* Pages per minute for mono */ 388 ppm_color = 0, /* Pages per minute for color */ 389 pin = 0; /* PIN printing mode? */ 390 char directory[1024] = ""; /* Spool directory */ 391 _ipp_printer_t *printer; /* Printer object */ 392 393 394 /* 395 * Parse command-line arguments... 396 */ 397 398 for (i = 1; i < argc; i ++) 399 if (argv[i][0] == '-') 400 { 401 for (opt = argv[i] + 1; *opt; opt ++) 402 switch (*opt) 403 { 404 case '2' : /* -2 (enable 2-sided printing) */ 405 duplex = 1; 406 break; 407 408 case 'M' : /* -M manufacturer */ 409 i ++; 410 if (i >= argc) 411 usage(1); 412 make = argv[i]; 413 break; 414 415 case 'P' : /* -P (PIN printing mode) */ 416 pin = 1; 417 break; 418 419 case 'c' : /* -c command */ 420 i ++; 421 if (i >= argc) 422 usage(1); 423 424 command = argv[i]; 425 break; 426 427 case 'd' : /* -d spool-directory */ 428 i ++; 429 if (i >= argc) 430 usage(1); 431 strlcpy(directory, argv[i], sizeof(directory)); 432 break; 433 434 case 'f' : /* -f type/subtype[,...] */ 435 i ++; 436 if (i >= argc) 437 usage(1); 438 formats = argv[i]; 439 break; 440 441 case 'h' : /* -h (show help) */ 442 usage(0); 443 break; 444 445 case 'i' : /* -i icon.png */ 446 i ++; 447 if (i >= argc) 448 usage(1); 449 icon = argv[i]; 450 break; 451 452 case 'k' : /* -k (keep files) */ 453 KeepFiles = 1; 454 break; 455 456 case 'l' : /* -l location */ 457 i ++; 458 if (i >= argc) 459 usage(1); 460 location = argv[i]; 461 break; 462 463 case 'm' : /* -m model */ 464 i ++; 465 if (i >= argc) 466 usage(1); 467 model = argv[i]; 468 break; 469 470 case 'n' : /* -n hostname */ 471 i ++; 472 if (i >= argc) 473 usage(1); 474 servername = argv[i]; 475 break; 476 477 case 'p' : /* -p port */ 478 i ++; 479 if (i >= argc || !isdigit(argv[i][0] & 255)) 480 usage(1); 481 port = atoi(argv[i]); 482 break; 483 484#ifdef HAVE_DNSSD 485 case 'r' : /* -r subtype */ 486 i ++; 487 if (i >= argc) 488 usage(1); 489 subtype = argv[i]; 490 break; 491#endif /* HAVE_DNSSD */ 492 493 case 's' : /* -s speed[,color-speed] */ 494 i ++; 495 if (i >= argc) 496 usage(1); 497 if (sscanf(argv[i], "%d,%d", &ppm, &ppm_color) < 1) 498 usage(1); 499 break; 500 501 case 'v' : /* -v (be verbose) */ 502 Verbosity ++; 503 break; 504 505 default : /* Unknown */ 506 fprintf(stderr, "Unknown option \"-%c\".\n", *opt); 507 usage(1); 508 break; 509 } 510 } 511 else if (!name) 512 { 513 name = argv[i]; 514 } 515 else 516 { 517 fprintf(stderr, "Unexpected command-line argument \"%s\"\n", argv[i]); 518 usage(1); 519 } 520 521 if (!name) 522 usage(1); 523 524 /* 525 * Apply defaults as needed... 526 */ 527 528 if (!directory[0]) 529 { 530 snprintf(directory, sizeof(directory), "/tmp/ippserver.%d", (int)getpid()); 531 532 if (mkdir(directory, 0777) && errno != EEXIST) 533 { 534 fprintf(stderr, "Unable to create spool directory \"%s\": %s\n", 535 directory, strerror(errno)); 536 usage(1); 537 } 538 539 if (Verbosity) 540 fprintf(stderr, "Using spool directory \"%s\".\n", directory); 541 } 542 543 /* 544 * Create the printer... 545 */ 546 547 if ((printer = create_printer(servername, name, location, make, model, icon, 548 formats, ppm, ppm_color, duplex, port, pin, 549#ifdef HAVE_DNSSD 550 subtype, 551#endif /* HAVE_DNSSD */ 552 directory, command)) == NULL) 553 return (1); 554 555 /* 556 * Run the print service... 557 */ 558 559 run_printer(printer); 560 561 /* 562 * Destroy the printer and exit... 563 */ 564 565 delete_printer(printer); 566 567 return (0); 568} 569 570 571/* 572 * 'clean_jobs()' - Clean out old (completed) jobs. 573 */ 574 575static void 576clean_jobs(_ipp_printer_t *printer) /* I - Printer */ 577{ 578 _ipp_job_t *job; /* Current job */ 579 time_t cleantime; /* Clean time */ 580 581 582 if (cupsArrayCount(printer->jobs) == 0) 583 return; 584 585 cleantime = time(NULL) - 60; 586 587 _cupsRWLockWrite(&(printer->rwlock)); 588 for (job = (_ipp_job_t *)cupsArrayFirst(printer->jobs); 589 job; 590 job = (_ipp_job_t *)cupsArrayNext(printer->jobs)) 591 if (job->completed && job->completed < cleantime) 592 { 593 cupsArrayRemove(printer->jobs, job); 594 delete_job(job); 595 } 596 else 597 break; 598 _cupsRWUnlock(&(printer->rwlock)); 599} 600 601 602/* 603 * 'compare_jobs()' - Compare two jobs. 604 */ 605 606static int /* O - Result of comparison */ 607compare_jobs(_ipp_job_t *a, /* I - First job */ 608 _ipp_job_t *b) /* I - Second job */ 609{ 610 return (b->id - a->id); 611} 612 613 614/* 615 * 'copy_attributes()' - Copy attributes from one request to another. 616 */ 617 618static void 619copy_attributes(ipp_t *to, /* I - Destination request */ 620 ipp_t *from, /* I - Source request */ 621 cups_array_t *ra, /* I - Requested attributes */ 622 ipp_tag_t group_tag, /* I - Group to copy */ 623 int quickcopy) /* I - Do a quick copy? */ 624{ 625 ipp_attribute_t *fromattr; /* Source attribute */ 626 627 628 if (!to || !from) 629 return; 630 631 for (fromattr = ippFirstAttribute(from); 632 fromattr; 633 fromattr = ippNextAttribute(from)) 634 { 635 /* 636 * Filter attributes as needed... 637 */ 638 639 ipp_tag_t fromgroup = ippGetGroupTag(fromattr); 640 const char *fromname = ippGetName(fromattr); 641 642 if ((group_tag != IPP_TAG_ZERO && fromgroup != group_tag && 643 fromgroup != IPP_TAG_ZERO) || !fromname) 644 continue; 645 646 if (!ra || cupsArrayFind(ra, (void *)fromname)) 647 ippCopyAttribute(to, fromattr, quickcopy); 648 } 649} 650 651 652/* 653 * 'copy_job_attrs()' - Copy job attributes to the response. 654 */ 655 656static void 657copy_job_attributes( 658 _ipp_client_t *client, /* I - Client */ 659 _ipp_job_t *job, /* I - Job */ 660 cups_array_t *ra) /* I - requested-attributes */ 661{ 662 copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0); 663 664 if (!ra || cupsArrayFind(ra, "job-printer-up-time")) 665 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, 666 "job-printer-up-time", (int)time(NULL)); 667 668 if (!ra || cupsArrayFind(ra, "job-state")) 669 ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_ENUM, 670 "job-state", job->state); 671 672 if (!ra || cupsArrayFind(ra, "job-state-reasons")) 673 { 674 switch (job->state) 675 { 676 case IPP_JSTATE_PENDING : 677 ippAddString(client->response, IPP_TAG_JOB, 678 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "job-state-reasons", 679 NULL, "none"); 680 break; 681 682 case IPP_JSTATE_HELD : 683 if (job->fd >= 0) 684 ippAddString(client->response, IPP_TAG_JOB, 685 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 686 "job-state-reasons", NULL, "job-incoming"); 687 else if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_ZERO)) 688 ippAddString(client->response, IPP_TAG_JOB, 689 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 690 "job-state-reasons", NULL, "job-hold-until-specified"); 691 else 692 ippAddString(client->response, IPP_TAG_JOB, 693 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 694 "job-state-reasons", NULL, "job-data-insufficient"); 695 break; 696 697 case IPP_JSTATE_PROCESSING : 698 if (job->cancel) 699 ippAddString(client->response, IPP_TAG_JOB, 700 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 701 "job-state-reasons", NULL, "processing-to-stop-point"); 702 else 703 ippAddString(client->response, IPP_TAG_JOB, 704 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 705 "job-state-reasons", NULL, "job-printing"); 706 break; 707 708 case IPP_JSTATE_STOPPED : 709 ippAddString(client->response, IPP_TAG_JOB, 710 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "job-state-reasons", 711 NULL, "job-stopped"); 712 break; 713 714 case IPP_JSTATE_CANCELED : 715 ippAddString(client->response, IPP_TAG_JOB, 716 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "job-state-reasons", 717 NULL, "job-canceled-by-user"); 718 break; 719 720 case IPP_JSTATE_ABORTED : 721 ippAddString(client->response, IPP_TAG_JOB, 722 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "job-state-reasons", 723 NULL, "aborted-by-system"); 724 break; 725 726 case IPP_JSTATE_COMPLETED : 727 ippAddString(client->response, IPP_TAG_JOB, 728 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, "job-state-reasons", 729 NULL, "job-completed-successfully"); 730 break; 731 } 732 } 733 734 if (!ra || cupsArrayFind(ra, "time-at-completed")) 735 ippAddInteger(client->response, IPP_TAG_JOB, 736 job->completed ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE, 737 "time-at-completed", job->completed); 738 739 if (!ra || cupsArrayFind(ra, "time-at-processing")) 740 ippAddInteger(client->response, IPP_TAG_JOB, 741 job->processing ? IPP_TAG_INTEGER : IPP_TAG_NOVALUE, 742 "time-at-processing", job->processing); 743} 744 745 746/* 747 * 'create_client()' - Accept a new network connection and create a client 748 * object. 749 */ 750 751static _ipp_client_t * /* O - Client */ 752create_client(_ipp_printer_t *printer, /* I - Printer */ 753 int sock) /* I - Listen socket */ 754{ 755 _ipp_client_t *client; /* Client */ 756 757 758 if ((client = calloc(1, sizeof(_ipp_client_t))) == NULL) 759 { 760 perror("Unable to allocate memory for client"); 761 return (NULL); 762 } 763 764 client->printer = printer; 765 766 /* 767 * Accept the client and get the remote address... 768 */ 769 770 if ((client->http = httpAcceptConnection(sock, 1)) == NULL) 771 { 772 perror("Unable to accept client connection"); 773 774 free(client); 775 776 return (NULL); 777 } 778 779 httpGetHostname(client->http, client->hostname, sizeof(client->hostname)); 780 781 if (Verbosity) 782 fprintf(stderr, "Accepted connection from %s\n", client->hostname); 783 784 return (client); 785} 786 787 788/* 789 * 'create_job()' - Create a new job object from a Print-Job or Create-Job 790 * request. 791 */ 792 793static _ipp_job_t * /* O - Job */ 794create_job(_ipp_client_t *client) /* I - Client */ 795{ 796 _ipp_job_t *job; /* Job */ 797 ipp_attribute_t *attr; /* Job attribute */ 798 char uri[1024]; /* job-uri value */ 799 800 801 _cupsRWLockWrite(&(client->printer->rwlock)); 802 if (client->printer->active_job && 803 client->printer->active_job->state < IPP_JSTATE_CANCELED) 804 { 805 /* 806 * Only accept a single job at a time... 807 */ 808 809 _cupsRWLockWrite(&(client->printer->rwlock)); 810 return (NULL); 811 } 812 813 /* 814 * Allocate and initialize the job object... 815 */ 816 817 if ((job = calloc(1, sizeof(_ipp_job_t))) == NULL) 818 { 819 perror("Unable to allocate memory for job"); 820 return (NULL); 821 } 822 823 job->printer = client->printer; 824 job->attrs = client->request; 825 job->state = IPP_JSTATE_HELD; 826 job->fd = -1; 827 client->request = NULL; 828 829 /* 830 * Set all but the first two attributes to the job attributes group... 831 */ 832 833 for (ippFirstAttribute(job->attrs), 834 ippNextAttribute(job->attrs), 835 attr = ippNextAttribute(job->attrs); 836 attr; 837 attr = ippNextAttribute(job->attrs)) 838 ippSetGroupTag(job->attrs, &attr, IPP_TAG_JOB); 839 840 /* 841 * Get the requesting-user-name, document format, and priority... 842 */ 843 844 if ((attr = ippFindAttribute(job->attrs, "requesting-user-name", 845 IPP_TAG_NAME)) != NULL) 846 ippSetName(job->attrs, &attr, "job-originating-user-name"); 847 else 848 attr = ippAddString(job->attrs, IPP_TAG_JOB, 849 IPP_TAG_NAME | IPP_TAG_CUPS_CONST, 850 "job-originating-user-name", NULL, "anonymous"); 851 852 if (attr) 853 job->username = ippGetString(attr, 0, NULL); 854 else 855 job->username = "anonymous"; 856 857 if ((attr = ippFindAttribute(job->attrs, "document-format", 858 IPP_TAG_MIMETYPE)) != NULL) 859 job->format = ippGetString(attr, 0, NULL); 860 else 861 job->format = "application/octet-stream"; 862 863 /* 864 * Add job description attributes and add to the jobs array... 865 */ 866 867 job->id = client->printer->next_job_id ++; 868 869 snprintf(uri, sizeof(uri), "%s/%d", client->printer->uri, job->id); 870 871 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id); 872 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, uri); 873 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL, 874 client->printer->uri); 875 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", 876 (int)time(NULL)); 877 878 cupsArrayAdd(client->printer->jobs, job); 879 client->printer->active_job = job; 880 881 _cupsRWUnlock(&(client->printer->rwlock)); 882 883 return (job); 884} 885 886 887/* 888 * 'create_listener()' - Create a listener socket. 889 */ 890 891static int /* O - Listener socket or -1 on error */ 892create_listener(int family, /* I - Address family */ 893 int *port) /* IO - Port number */ 894{ 895 int sock; /* Listener socket */ 896 http_addrlist_t *addrlist; /* Listen address */ 897 char service[255]; /* Service port */ 898 899 900 if (!*port) 901 { 902 *port = 8000 + (getuid() % 1000); 903 fprintf(stderr, "Listening on port %d.\n", *port); 904 } 905 906 snprintf(service, sizeof(service), "%d", *port); 907 if ((addrlist = httpAddrGetList(NULL, family, service)) == NULL) 908 return (-1); 909 910 sock = httpAddrListen(&(addrlist->addr), *port); 911 912 httpAddrFreeList(addrlist); 913 914 return (sock); 915} 916 917 918/* 919 * 'create_media_col()' - Create a media-col value. 920 */ 921 922static ipp_t * /* O - media-col collection */ 923create_media_col(const char *media, /* I - Media name */ 924 const char *type, /* I - Nedua type */ 925 int width, /* I - x-dimension in 2540ths */ 926 int length, /* I - y-dimension in 2540ths */ 927 int margins) /* I - Value for margins */ 928{ 929 ipp_t *media_col = ippNew(), /* media-col value */ 930 *media_size = create_media_size(width, length); 931 /* media-size value */ 932 char media_key[256]; /* media-key value */ 933 934 935 snprintf(media_key, sizeof(media_key), "%s_%s%s", media, type, 936 margins == 0 ? "_borderless" : ""); 937 938 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-key", NULL, 939 media_key); 940 ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size); 941 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 942 "media-bottom-margin", margins); 943 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 944 "media-left-margin", margins); 945 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 946 "media-right-margin", margins); 947 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 948 "media-top-margin", margins); 949 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "media-type", 950 NULL, type); 951 952 ippDelete(media_size); 953 954 return (media_col); 955} 956 957 958/* 959 * 'create_media_size()' - Create a media-size value. 960 */ 961 962static ipp_t * /* O - media-col collection */ 963create_media_size(int width, /* I - x-dimension in 2540ths */ 964 int length) /* I - y-dimension in 2540ths */ 965{ 966 ipp_t *media_size = ippNew(); /* media-size value */ 967 968 969 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", 970 width); 971 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", 972 length); 973 974 return (media_size); 975} 976 977 978/* 979 * 'create_printer()' - Create, register, and listen for connections to a 980 * printer object. 981 */ 982 983static _ipp_printer_t * /* O - Printer */ 984create_printer(const char *servername, /* I - Server hostname (NULL for default) */ 985 const char *name, /* I - printer-name */ 986 const char *location, /* I - printer-location */ 987 const char *make, /* I - printer-make-and-model */ 988 const char *model, /* I - printer-make-and-model */ 989 const char *icon, /* I - printer-icons */ 990 const char *docformats, /* I - document-format-supported */ 991 int ppm, /* I - Pages per minute in grayscale */ 992 int ppm_color, /* I - Pages per minute in color (0 for gray) */ 993 int duplex, /* I - 1 = duplex, 0 = simplex */ 994 int port, /* I - Port for listeners or 0 for auto */ 995 int pin, /* I - Require PIN printing */ 996#ifdef HAVE_DNSSD 997 const char *subtype, /* I - Bonjour service subtype */ 998#endif /* HAVE_DNSSD */ 999 const char *directory, /* I - Spool directory */ 1000 const char *command) /* I - Command to run on job files */ 1001{ 1002 int i, j; /* Looping vars */ 1003 _ipp_printer_t *printer; /* Printer */ 1004 char hostname[256], /* Hostname */ 1005 uri[1024], /* Printer URI */ 1006 icons[1024], /* printer-icons URI */ 1007 adminurl[1024], /* printer-more-info URI */ 1008 device_id[1024],/* printer-device-id */ 1009 make_model[128];/* printer-make-and-model */ 1010 int num_formats; /* Number of document-format-supported values */ 1011 char *defformat, /* document-format-default value */ 1012 *formats[100], /* document-format-supported values */ 1013 *ptr; /* Pointer into string */ 1014 const char *prefix; /* Prefix string */ 1015 int num_database; /* Number of database values */ 1016 ipp_attribute_t *media_col_database, 1017 /* media-col-database value */ 1018 *media_size_supported; 1019 /* media-size-supported value */ 1020 ipp_t *media_col_default; 1021 /* media-col-default value */ 1022 int media_col_index;/* Current media-col-database value */ 1023 int k_supported; /* Maximum file size supported */ 1024#ifdef HAVE_STATVFS 1025 struct statvfs spoolinfo; /* FS info for spool directory */ 1026 double spoolsize; /* FS size */ 1027#elif defined(HAVE_STATFS) 1028 struct statfs spoolinfo; /* FS info for spool directory */ 1029 double spoolsize; /* FS size */ 1030#endif /* HAVE_STATVFS */ 1031 static const int orients[4] = /* orientation-requested-supported values */ 1032 { 1033 IPP_ORIENT_PORTRAIT, 1034 IPP_ORIENT_LANDSCAPE, 1035 IPP_ORIENT_REVERSE_LANDSCAPE, 1036 IPP_ORIENT_REVERSE_PORTRAIT 1037 }; 1038 static const char * const versions[] =/* ipp-versions-supported values */ 1039 { 1040 "1.0", 1041 "1.1", 1042 "2.0" 1043 }; 1044 static const int ops[] = /* operations-supported values */ 1045 { 1046 IPP_OP_PRINT_JOB, 1047 IPP_OP_PRINT_URI, 1048 IPP_OP_VALIDATE_JOB, 1049 IPP_OP_CREATE_JOB, 1050 IPP_OP_SEND_DOCUMENT, 1051 IPP_OP_SEND_URI, 1052 IPP_OP_CANCEL_JOB, 1053 IPP_OP_GET_JOB_ATTRIBUTES, 1054 IPP_OP_GET_JOBS, 1055 IPP_OP_GET_PRINTER_ATTRIBUTES 1056 }; 1057 static const char * const charsets[] =/* charset-supported values */ 1058 { 1059 "us-ascii", 1060 "utf-8" 1061 }; 1062 static const char * const compressions[] =/* compression-supported values */ 1063 { 1064#ifdef HAVE_LIBZ 1065 "deflate", 1066 "gzip", 1067#endif /* HAVE_LIBZ */ 1068 "none" 1069 }; 1070 static const char * const job_creation[] = 1071 { /* job-creation-attributes-supported values */ 1072 "copies", 1073 "ipp-attribute-fidelity", 1074 "job-account-id", 1075 "job-accounting-user-id", 1076 "job-name", 1077 "job-password", 1078 "job-priority", 1079 "media", 1080 "media-col", 1081 "multiple-document-handling", 1082 "orientation-requested", 1083 "print-quality", 1084 "sides" 1085 }; 1086 static const char * const media_col_supported[] = 1087 { /* media-col-supported values */ 1088 "media-bottom-margin", 1089 "media-left-margin", 1090 "media-right-margin", 1091 "media-size", 1092 "media-top-margin", 1093 "media-type" 1094 }; 1095 static const int media_xxx_margin_supported[] = 1096 { /* media-xxx-margin-supported values */ 1097 0, 1098 635 1099 }; 1100 static const char * const multiple_document_handling[] = 1101 { /* multiple-document-handling-supported values */ 1102 "separate-documents-uncollated-copies", 1103 "separate-documents-collated-copies" 1104 }; 1105 static const int print_quality_supported[] = 1106 { /* print-quality-supported values */ 1107 IPP_QUALITY_DRAFT, 1108 IPP_QUALITY_NORMAL, 1109 IPP_QUALITY_HIGH 1110 }; 1111 static const int pwg_raster_document_resolution_supported[] = 1112 { 1113 150, 1114 300, 1115 600 1116 }; 1117 static const char * const pwg_raster_document_type_supported[] = 1118 { 1119 "black-1", 1120 "cmyk-8", 1121 "sgray-8", 1122 "srgb-8", 1123 "srgb-16" 1124 }; 1125 static const char * const reference_uri_schemes_supported[] = 1126 { /* reference-uri-schemes-supported */ 1127 "file", 1128 "ftp", 1129 "http" 1130#ifdef HAVE_SSL 1131 , "https" 1132#endif /* HAVE_SSL */ 1133 }; 1134 static const char * const sides_supported[] = 1135 { /* sides-supported values */ 1136 "one-sided", 1137 "two-sided-long-edge", 1138 "two-sided-short-edge" 1139 }; 1140 static const char * const which_jobs[] = 1141 { /* which-jobs-supported values */ 1142 "completed", 1143 "not-completed", 1144 "aborted", 1145 "all", 1146 "canceled", 1147 "pending", 1148 "pending-held", 1149 "processing", 1150 "processing-stopped" 1151 }; 1152 1153 1154 /* 1155 * Allocate memory for the printer... 1156 */ 1157 1158 if ((printer = calloc(1, sizeof(_ipp_printer_t))) == NULL) 1159 { 1160 perror("Unable to allocate memory for printer"); 1161 return (NULL); 1162 } 1163 1164 printer->ipv4 = -1; 1165 printer->ipv6 = -1; 1166 printer->name = strdup(name); 1167#ifdef HAVE_DNSSD 1168 printer->dnssd_name = strdup(printer->name); 1169#endif /* HAVE_DNSSD */ 1170 printer->command = command ? strdup(command) : NULL; 1171 printer->directory = strdup(directory); 1172 printer->hostname = strdup(servername ? servername : 1173 httpGetHostname(NULL, hostname, 1174 sizeof(hostname))); 1175 printer->port = port; 1176 printer->state = IPP_PSTATE_IDLE; 1177 printer->state_reasons = _IPP_PSTATE_NONE; 1178 printer->jobs = cupsArrayNew((cups_array_func_t)compare_jobs, NULL); 1179 printer->next_job_id = 1; 1180 1181 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, 1182 printer->hostname, printer->port, "/ipp/print"); 1183 printer->uri = strdup(uri); 1184 printer->urilen = strlen(uri); 1185 1186 if (icon) 1187 printer->icon = strdup(icon); 1188 1189 _cupsRWInit(&(printer->rwlock)); 1190 1191 /* 1192 * Create the listener sockets... 1193 */ 1194 1195 if ((printer->ipv4 = create_listener(AF_INET, &(printer->port))) < 0) 1196 { 1197 perror("Unable to create IPv4 listener"); 1198 goto bad_printer; 1199 } 1200 1201 if ((printer->ipv6 = create_listener(AF_INET6, &(printer->port))) < 0) 1202 { 1203 perror("Unable to create IPv6 listener"); 1204 goto bad_printer; 1205 } 1206 1207 /* 1208 * Prepare values for the printer attributes... 1209 */ 1210 1211 httpAssembleURI(HTTP_URI_CODING_ALL, icons, sizeof(icons), "http", NULL, 1212 printer->hostname, printer->port, "/icon.png"); 1213 httpAssembleURI(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), "http", NULL, 1214 printer->hostname, printer->port, "/"); 1215 1216 if (Verbosity) 1217 { 1218 fprintf(stderr, "printer-more-info=\"%s\"\n", adminurl); 1219 fprintf(stderr, "printer-uri=\"%s\"\n", uri); 1220 } 1221 1222 snprintf(make_model, sizeof(make_model), "%s %s", make, model); 1223 1224 num_formats = 1; 1225 formats[0] = strdup(docformats); 1226 defformat = formats[0]; 1227 for (ptr = strchr(formats[0], ','); ptr; ptr = strchr(ptr, ',')) 1228 { 1229 *ptr++ = '\0'; 1230 formats[num_formats++] = ptr; 1231 1232 if (!_cups_strcasecmp(ptr, "application/octet-stream")) 1233 defformat = ptr; 1234 } 1235 1236 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", make, model); 1237 ptr = device_id + strlen(device_id); 1238 prefix = "CMD:"; 1239 for (i = 0; i < num_formats; i ++) 1240 { 1241 if (!_cups_strcasecmp(formats[i], "application/pdf")) 1242 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPDF", prefix); 1243 else if (!_cups_strcasecmp(formats[i], "application/postscript")) 1244 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPS", prefix); 1245 else if (!_cups_strcasecmp(formats[i], "application/vnd.hp-PCL")) 1246 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPCL", prefix); 1247 else if (!_cups_strcasecmp(formats[i], "image/jpeg")) 1248 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sJPEG", prefix); 1249 else if (!_cups_strcasecmp(formats[i], "image/png")) 1250 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%sPNG", prefix); 1251 else if (_cups_strcasecmp(formats[i], "application/octet-stream")) 1252 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%s%s", prefix, 1253 formats[i]); 1254 1255 ptr += strlen(ptr); 1256 prefix = ","; 1257 } 1258 strlcat(device_id, ";", sizeof(device_id)); 1259 1260 /* 1261 * Get the maximum spool size based on the size of the filesystem used for 1262 * the spool directory. If the host OS doesn't support the statfs call 1263 * or the filesystem is larger than 2TiB, always report INT_MAX. 1264 */ 1265 1266#ifdef HAVE_STATVFS 1267 if (statvfs(printer->directory, &spoolinfo)) 1268 k_supported = INT_MAX; 1269 else if ((spoolsize = (double)spoolinfo.f_frsize * 1270 spoolinfo.f_blocks / 1024) > INT_MAX) 1271 k_supported = INT_MAX; 1272 else 1273 k_supported = (int)spoolsize; 1274 1275#elif defined(HAVE_STATFS) 1276 if (statfs(printer->directory, &spoolinfo)) 1277 k_supported = INT_MAX; 1278 else if ((spoolsize = (double)spoolinfo.f_bsize * 1279 spoolinfo.f_blocks / 1024) > INT_MAX) 1280 k_supported = INT_MAX; 1281 else 1282 k_supported = (int)spoolsize; 1283 1284#else 1285 k_supported = INT_MAX; 1286#endif /* HAVE_STATVFS */ 1287 1288 /* 1289 * Create the printer attributes. This list of attributes is sorted to improve 1290 * performance when the client provides a requested-attributes attribute... 1291 */ 1292 1293 printer->attrs = ippNew(); 1294 1295 /* charset-configured */ 1296 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1297 IPP_TAG_CHARSET | IPP_TAG_CUPS_CONST, 1298 "charset-configured", NULL, "utf-8"); 1299 1300 /* charset-supported */ 1301 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1302 IPP_TAG_CHARSET | IPP_TAG_CUPS_CONST, 1303 "charset-supported", sizeof(charsets) / sizeof(charsets[0]), 1304 NULL, charsets); 1305 1306 /* color-supported */ 1307 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "color-supported", 1308 ppm_color > 0); 1309 1310 /* compression-supported */ 1311 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1312 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1313 "compression-supported", 1314 (int)(sizeof(compressions) / sizeof(compressions[0])), NULL, 1315 compressions); 1316 1317 /* copies-default */ 1318 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1319 "copies-default", 1); 1320 1321 /* copies-supported */ 1322 ippAddRange(printer->attrs, IPP_TAG_PRINTER, "copies-supported", 1, 999); 1323 1324 /* document-format-default */ 1325 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, 1326 "document-format-default", NULL, defformat); 1327 1328 /* document-format-supported */ 1329 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_MIMETYPE, 1330 "document-format-supported", num_formats, NULL, 1331 (const char * const *)formats); 1332 1333 /* finishings-default */ 1334 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, 1335 "finishings-default", IPP_FINISHINGS_NONE); 1336 1337 /* finishings-supported */ 1338 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, 1339 "finishings-supported", IPP_FINISHINGS_NONE); 1340 1341 /* generated-natural-language-supported */ 1342 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1343 IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST, 1344 "generated-natural-language-supported", NULL, "en"); 1345 1346 /* ipp-versions-supported */ 1347 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1348 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1349 "ipp-versions-supported", 1350 sizeof(versions) / sizeof(versions[0]), NULL, versions); 1351 1352 /* job-account-id-supported */ 1353 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "job-account-id-supported", 1); 1354 1355 /* job-accounting-user-id-supported */ 1356 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, 1357 "job-accounting-user-id-supported", 1); 1358 1359 /* job-creation-attributes-supported */ 1360 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1361 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1362 "job-creation-attributes-supported", 1363 sizeof(job_creation) / sizeof(job_creation[0]), 1364 NULL, job_creation); 1365 1366 /* job-k-octets-supported */ 1367 ippAddRange(printer->attrs, IPP_TAG_PRINTER, "job-k-octets-supported", 0, 1368 k_supported); 1369 1370 /* job-password-supported */ 1371 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1372 "job-password-supported", 4); 1373 1374 /* job-priority-default */ 1375 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1376 "job-priority-default", 50); 1377 1378 /* job-priority-supported */ 1379 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1380 "job-priority-supported", 100); 1381 1382 /* job-sheets-default */ 1383 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1384 IPP_TAG_NAME | IPP_TAG_CUPS_CONST, 1385 "job-sheets-default", NULL, "none"); 1386 1387 /* job-sheets-supported */ 1388 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1389 IPP_TAG_NAME | IPP_TAG_CUPS_CONST, 1390 "job-sheets-supported", NULL, "none"); 1391 1392 /* media-bottom-margin-supported */ 1393 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1394 "media-bottom-margin-supported", 1395 (int)(sizeof(media_xxx_margin_supported) / 1396 sizeof(media_xxx_margin_supported[0])), 1397 media_xxx_margin_supported); 1398 1399 /* media-col-database */ 1400 for (num_database = 0, i = 0; 1401 i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0])); 1402 i ++) 1403 { 1404 if (media_col_sizes[i][2] == _IPP_ENV_ONLY) 1405 num_database += 2; /* auto + envelope */ 1406 else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY) 1407 num_database += 12; /* auto + photographic-* + borderless */ 1408 else 1409 num_database += (int)(sizeof(media_type_supported) / 1410 sizeof(media_type_supported[0])) + 6; 1411 /* All types + borderless */ 1412 } 1413 1414 media_col_database = ippAddCollections(printer->attrs, IPP_TAG_PRINTER, 1415 "media-col-database", num_database, 1416 NULL); 1417 for (media_col_index = 0, i = 0; 1418 i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0])); 1419 i ++) 1420 { 1421 for (j = 0; 1422 j < (int)(sizeof(media_type_supported) / 1423 sizeof(media_type_supported[0])); 1424 j ++) 1425 { 1426 if (media_col_sizes[i][2] == _IPP_ENV_ONLY && 1427 strcmp(media_type_supported[j], "auto") && 1428 strcmp(media_type_supported[j], "envelope")) 1429 continue; 1430 else if (media_col_sizes[i][2] == _IPP_PHOTO_ONLY && 1431 strcmp(media_type_supported[j], "auto") && 1432 strncmp(media_type_supported[j], "photographic-", 13)) 1433 continue; 1434 1435 ippSetCollection(printer->attrs, &media_col_database, media_col_index, 1436 create_media_col(media_supported[i], 1437 media_type_supported[j], 1438 media_col_sizes[i][0], 1439 media_col_sizes[i][1], 1440 media_xxx_margin_supported[1])); 1441 media_col_index ++; 1442 1443 if (media_col_sizes[i][2] != _IPP_ENV_ONLY && 1444 (!strcmp(media_type_supported[j], "auto") || 1445 !strncmp(media_type_supported[j], "photographic-", 13))) 1446 { 1447 /* 1448 * Add borderless version for this combination... 1449 */ 1450 1451 ippSetCollection(printer->attrs, &media_col_database, media_col_index, 1452 create_media_col(media_supported[i], 1453 media_type_supported[j], 1454 media_col_sizes[i][0], 1455 media_col_sizes[i][1], 1456 media_xxx_margin_supported[0])); 1457 media_col_index ++; 1458 } 1459 } 1460 } 1461 1462 /* media-col-default */ 1463 media_col_default = create_media_col(media_supported[0], 1464 media_type_supported[0], 1465 media_col_sizes[0][0], 1466 media_col_sizes[0][1], 1467 media_xxx_margin_supported[1]); 1468 1469 ippAddCollection(printer->attrs, IPP_TAG_PRINTER, "media-col-default", 1470 media_col_default); 1471 ippDelete(media_col_default); 1472 1473 /* media-col-supported */ 1474 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1475 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1476 "media-col-supported", 1477 (int)(sizeof(media_col_supported) / 1478 sizeof(media_col_supported[0])), NULL, 1479 media_col_supported); 1480 1481 /* media-default */ 1482 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1483 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1484 "media-default", NULL, media_supported[0]); 1485 1486 /* media-left-margin-supported */ 1487 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1488 "media-left-margin-supported", 1489 (int)(sizeof(media_xxx_margin_supported) / 1490 sizeof(media_xxx_margin_supported[0])), 1491 media_xxx_margin_supported); 1492 1493 /* media-right-margin-supported */ 1494 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1495 "media-right-margin-supported", 1496 (int)(sizeof(media_xxx_margin_supported) / 1497 sizeof(media_xxx_margin_supported[0])), 1498 media_xxx_margin_supported); 1499 1500 /* media-supported */ 1501 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1502 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1503 "media-supported", 1504 (int)(sizeof(media_supported) / sizeof(media_supported[0])), 1505 NULL, media_supported); 1506 1507 /* media-size-supported */ 1508 media_size_supported = ippAddCollections(printer->attrs, IPP_TAG_PRINTER, 1509 "media-size-supported", 1510 (int)(sizeof(media_col_sizes) / 1511 sizeof(media_col_sizes[0])), 1512 NULL); 1513 for (i = 0; 1514 i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0])); 1515 i ++) 1516 ippSetCollection(printer->attrs, &media_size_supported, i, 1517 create_media_size(media_col_sizes[i][0], 1518 media_col_sizes[i][1])); 1519 1520 /* media-top-margin-supported */ 1521 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1522 "media-top-margin-supported", 1523 (int)(sizeof(media_xxx_margin_supported) / 1524 sizeof(media_xxx_margin_supported[0])), 1525 media_xxx_margin_supported); 1526 1527 /* media-type-supported */ 1528 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1529 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1530 "media-type-supported", 1531 (int)(sizeof(media_type_supported) / 1532 sizeof(media_type_supported[0])), 1533 NULL, media_type_supported); 1534 1535 /* multiple-document-handling-supported */ 1536 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1537 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1538 "multiple-document-handling-supported", 1539 sizeof(multiple_document_handling) / 1540 sizeof(multiple_document_handling[0]), NULL, 1541 multiple_document_handling); 1542 1543 /* multiple-document-jobs-supported */ 1544 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, 1545 "multiple-document-jobs-supported", 0); 1546 1547 /* natural-language-configured */ 1548 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1549 IPP_TAG_LANGUAGE | IPP_TAG_CUPS_CONST, 1550 "natural-language-configured", NULL, "en"); 1551 1552 /* number-up-default */ 1553 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1554 "number-up-default", 1); 1555 1556 /* number-up-supported */ 1557 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1558 "number-up-supported", 1); 1559 1560 /* operations-supported */ 1561 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, 1562 "operations-supported", sizeof(ops) / sizeof(ops[0]), ops); 1563 1564 /* orientation-requested-default */ 1565 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, 1566 "orientation-requested-default", 0); 1567 1568 /* orientation-requested-supported */ 1569 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, 1570 "orientation-requested-supported", 4, orients); 1571 1572 /* output-bin-default */ 1573 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1574 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1575 "output-bin-default", NULL, "face-down"); 1576 1577 /* output-bin-supported */ 1578 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1579 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1580 "output-bin-supported", NULL, "face-down"); 1581 1582 /* pages-per-minute */ 1583 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1584 "pages-per-minute", ppm); 1585 1586 /* pages-per-minute-color */ 1587 if (ppm_color > 0) 1588 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 1589 "pages-per-minute-color", ppm_color); 1590 1591 /* pdl-override-supported */ 1592 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1593 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1594 "pdl-override-supported", NULL, "attempted"); 1595 1596 /* print-quality-default */ 1597 ippAddInteger(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, 1598 "print-quality-default", IPP_QUALITY_NORMAL); 1599 1600 /* print-quality-supported */ 1601 ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_ENUM, 1602 "print-quality-supported", 1603 (int)(sizeof(print_quality_supported) / 1604 sizeof(print_quality_supported[0])), 1605 print_quality_supported); 1606 1607 /* printer-device-id */ 1608 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, 1609 "printer-device-id", NULL, device_id); 1610 1611 /* printer-icons */ 1612 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, 1613 "printer-icons", NULL, icons); 1614 1615 /* printer-is-accepting-jobs */ 1616 ippAddBoolean(printer->attrs, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1617 1); 1618 1619 /* printer-info */ 1620 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", 1621 NULL, name); 1622 1623 /* printer-location */ 1624 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, 1625 "printer-location", NULL, location); 1626 1627 /* printer-make-and-model */ 1628 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_TEXT, 1629 "printer-make-and-model", NULL, make_model); 1630 1631 /* printer-mandatory-job-attributes */ 1632 if (pin) 1633 { 1634 static const char * const names[] = 1635 { 1636 "job-accounting-user-id", 1637 "job-password" 1638 }; 1639 1640 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, 1641 "printer-mandatory-job-attributes", 1642 (int)(sizeof(names) / sizeof(names[0])), NULL, names); 1643 } 1644 1645 /* printer-more-info */ 1646 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, 1647 "printer-more-info", NULL, adminurl); 1648 1649 /* printer-name */ 1650 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-name", 1651 NULL, name); 1652 1653 /* printer-resolution-default */ 1654 ippAddResolution(printer->attrs, IPP_TAG_PRINTER, 1655 "printer-resolution-default", IPP_RES_PER_INCH, 600, 600); 1656 1657 /* printer-resolution-supported */ 1658 ippAddResolution(printer->attrs, IPP_TAG_PRINTER, 1659 "printer-resolution-supported", IPP_RES_PER_INCH, 600, 600); 1660 1661 /* printer-uri-supported */ 1662 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, 1663 "printer-uri-supported", NULL, uri); 1664 1665 /* pwg-raster-document-xxx-supported */ 1666 for (i = 0; i < num_formats; i ++) 1667 if (!_cups_strcasecmp(formats[i], "image/pwg-raster")) 1668 break; 1669 1670 if (i < num_formats) 1671 { 1672 ippAddResolutions(printer->attrs, IPP_TAG_PRINTER, 1673 "pwg-raster-document-resolution-supported", 1674 (int)(sizeof(pwg_raster_document_resolution_supported) / 1675 sizeof(pwg_raster_document_resolution_supported[0])), 1676 IPP_RES_PER_INCH, 1677 pwg_raster_document_resolution_supported, 1678 pwg_raster_document_resolution_supported); 1679 ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, 1680 "pwg-raster-document-sheet-back", NULL, "normal"); 1681 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, 1682 "pwg-raster-document-type-supported", 1683 (int)(sizeof(pwg_raster_document_type_supported) / 1684 sizeof(pwg_raster_document_type_supported[0])), NULL, 1685 pwg_raster_document_type_supported); 1686 } 1687 1688 /* reference-uri-scheme-supported */ 1689 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1690 IPP_TAG_URISCHEME | IPP_TAG_CUPS_CONST, 1691 "reference-uri-schemes-supported", 1692 (int)(sizeof(reference_uri_schemes_supported) / 1693 sizeof(reference_uri_schemes_supported[0])), 1694 NULL, reference_uri_schemes_supported); 1695 1696 /* sides-default */ 1697 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1698 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1699 "sides-default", NULL, "one-sided"); 1700 1701 /* sides-supported */ 1702 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1703 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1704 "sides-supported", duplex ? 3 : 1, NULL, sides_supported); 1705 1706 /* uri-authentication-supported */ 1707 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1708 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1709 "uri-authentication-supported", NULL, "none"); 1710 1711 /* uri-security-supported */ 1712 ippAddString(printer->attrs, IPP_TAG_PRINTER, 1713 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1714 "uri-security-supported", NULL, "none"); 1715 1716 /* which-jobs-supported */ 1717 ippAddStrings(printer->attrs, IPP_TAG_PRINTER, 1718 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 1719 "which-jobs-supported", 1720 sizeof(which_jobs) / sizeof(which_jobs[0]), NULL, which_jobs); 1721 1722 free(formats[0]); 1723 1724 debug_attributes("Printer", printer->attrs, 0); 1725 1726#ifdef HAVE_DNSSD 1727 /* 1728 * Register the printer with Bonjour... 1729 */ 1730 1731 if (!register_printer(printer, location, make, model, docformats, adminurl, 1732 ppm_color > 0, duplex, subtype)) 1733 goto bad_printer; 1734#endif /* HAVE_DNSSD */ 1735 1736 /* 1737 * Return it! 1738 */ 1739 1740 return (printer); 1741 1742 1743 /* 1744 * If we get here we were unable to create the printer... 1745 */ 1746 1747 bad_printer: 1748 1749 delete_printer(printer); 1750 return (NULL); 1751} 1752 1753 1754/* 1755 * 'debug_attributes()' - Print attributes in a request or response. 1756 */ 1757 1758static void 1759debug_attributes(const char *title, /* I - Title */ 1760 ipp_t *ipp, /* I - Request/response */ 1761 int type) /* I - 0 = object, 1 = request, 2 = response */ 1762{ 1763 ipp_tag_t group_tag; /* Current group */ 1764 ipp_attribute_t *attr; /* Current attribute */ 1765 char buffer[2048]; /* String buffer for value */ 1766 int major, minor; /* Version */ 1767 1768 1769 if (Verbosity <= 1) 1770 return; 1771 1772 fprintf(stderr, "%s:\n", title); 1773 major = ippGetVersion(ipp, &minor); 1774 fprintf(stderr, " version=%d.%d\n", major, minor); 1775 if (type == 1) 1776 fprintf(stderr, " operation-id=%s(%04x)\n", 1777 ippOpString(ippGetOperation(ipp)), ippGetOperation(ipp)); 1778 else if (type == 2) 1779 fprintf(stderr, " status-code=%s(%04x)\n", 1780 ippErrorString(ippGetStatusCode(ipp)), ippGetStatusCode(ipp)); 1781 fprintf(stderr, " request-id=%d\n\n", ippGetRequestId(ipp)); 1782 1783 for (attr = ippFirstAttribute(ipp), group_tag = IPP_TAG_ZERO; 1784 attr; 1785 attr = ippNextAttribute(ipp)) 1786 { 1787 if (ippGetGroupTag(attr) != group_tag) 1788 { 1789 group_tag = ippGetGroupTag(attr); 1790 fprintf(stderr, " %s\n", ippTagString(group_tag)); 1791 } 1792 1793 if (ippGetName(attr)) 1794 { 1795 ippAttributeString(attr, buffer, sizeof(buffer)); 1796 fprintf(stderr, " %s (%s%s) %s\n", ippGetName(attr), 1797 ippGetCount(attr) > 1 ? "1setOf " : "", 1798 ippTagString(ippGetValueTag(attr)), buffer); 1799 } 1800 } 1801} 1802 1803 1804/* 1805 * 'delete_client()' - Close the socket and free all memory used by a client 1806 * object. 1807 */ 1808 1809static void 1810delete_client(_ipp_client_t *client) /* I - Client */ 1811{ 1812 if (Verbosity) 1813 fprintf(stderr, "Closing connection from %s\n", client->hostname); 1814 1815 /* 1816 * Flush pending writes before closing... 1817 */ 1818 1819 httpFlushWrite(client->http); 1820 1821 /* 1822 * Free memory... 1823 */ 1824 1825 httpClose(client->http); 1826 1827 ippDelete(client->request); 1828 ippDelete(client->response); 1829 1830 free(client); 1831} 1832 1833 1834/* 1835 * 'delete_job()' - Remove from the printer and free all memory used by a job 1836 * object. 1837 */ 1838 1839static void 1840delete_job(_ipp_job_t *job) /* I - Job */ 1841{ 1842 if (Verbosity) 1843 fprintf(stderr, "Removing job #%d from history.\n", job->id); 1844 1845 ippDelete(job->attrs); 1846 1847 if (job->filename) 1848 { 1849 if (!KeepFiles) 1850 unlink(job->filename); 1851 1852 free(job->filename); 1853 } 1854 1855 free(job); 1856} 1857 1858 1859/* 1860 * 'delete_printer()' - Unregister, close listen sockets, and free all memory 1861 * used by a printer object. 1862 */ 1863 1864static void 1865delete_printer(_ipp_printer_t *printer) /* I - Printer */ 1866{ 1867 if (printer->ipv4 >= 0) 1868 close(printer->ipv4); 1869 1870 if (printer->ipv6 >= 0) 1871 close(printer->ipv6); 1872 1873#if HAVE_DNSSD 1874 if (printer->printer_ref) 1875 DNSServiceRefDeallocate(printer->printer_ref); 1876 1877 if (printer->ipp_ref) 1878 DNSServiceRefDeallocate(printer->ipp_ref); 1879 1880# ifdef HAVE_SSL 1881 if (printer->ipps_ref) 1882 DNSServiceRefDeallocate(printer->ipps_ref); 1883# endif /* HAVE_SSL */ 1884 if (printer->http_ref) 1885 DNSServiceRefDeallocate(printer->http_ref); 1886 1887 if (printer->common_ref) 1888 DNSServiceRefDeallocate(printer->common_ref); 1889 1890 TXTRecordDeallocate(&(printer->ipp_txt)); 1891 1892 if (printer->dnssd_name) 1893 free(printer->dnssd_name); 1894#endif /* HAVE_DNSSD */ 1895 1896 if (printer->name) 1897 free(printer->name); 1898 if (printer->icon) 1899 free(printer->icon); 1900 if (printer->command) 1901 free(printer->command); 1902 if (printer->directory) 1903 free(printer->directory); 1904 if (printer->hostname) 1905 free(printer->hostname); 1906 if (printer->uri) 1907 free(printer->uri); 1908 1909 ippDelete(printer->attrs); 1910 cupsArrayDelete(printer->jobs); 1911 1912 free(printer); 1913} 1914 1915 1916#ifdef HAVE_DNSSD 1917/* 1918 * 'dnssd_callback()' - Handle Bonjour registration events. 1919 */ 1920 1921static void 1922dnssd_callback( 1923 DNSServiceRef sdRef, /* I - Service reference */ 1924 DNSServiceFlags flags, /* I - Status flags */ 1925 DNSServiceErrorType errorCode, /* I - Error, if any */ 1926 const char *name, /* I - Service name */ 1927 const char *regtype, /* I - Service type */ 1928 const char *domain, /* I - Domain for service */ 1929 _ipp_printer_t *printer) /* I - Printer */ 1930{ 1931 if (errorCode) 1932 { 1933 fprintf(stderr, "DNSServiceRegister for %s failed with error %d.\n", 1934 regtype, (int)errorCode); 1935 return; 1936 } 1937 else if (_cups_strcasecmp(name, printer->dnssd_name)) 1938 { 1939 if (Verbosity) 1940 fprintf(stderr, "Now using DNS-SD service name \"%s\".\n", name); 1941 1942 /* No lock needed since only the main thread accesses/changes this */ 1943 free(printer->dnssd_name); 1944 printer->dnssd_name = strdup(name); 1945 } 1946} 1947#endif /* HAVE_DNSSD */ 1948 1949 1950/* 1951 * 'find_job()' - Find a job specified in a request. 1952 */ 1953 1954static _ipp_job_t * /* O - Job or NULL */ 1955find_job(_ipp_client_t *client) /* I - Client */ 1956{ 1957 ipp_attribute_t *attr; /* job-id or job-uri attribute */ 1958 _ipp_job_t key, /* Job search key */ 1959 *job; /* Matching job, if any */ 1960 1961 1962 key.id = 0; 1963 1964 if ((attr = ippFindAttribute(client->request, "job-uri", 1965 IPP_TAG_URI)) != NULL) 1966 { 1967 const char *uri = ippGetString(attr, 0, NULL); 1968 1969 if (!strncmp(uri, client->printer->uri, client->printer->urilen) && 1970 uri[client->printer->urilen] == '/') 1971 key.id = atoi(uri + client->printer->urilen + 1); 1972 } 1973 else if ((attr = ippFindAttribute(client->request, "job-id", 1974 IPP_TAG_INTEGER)) != NULL) 1975 key.id = ippGetInteger(attr, 0); 1976 1977 _cupsRWLockRead(&(client->printer->rwlock)); 1978 job = (_ipp_job_t *)cupsArrayFind(client->printer->jobs, &key); 1979 _cupsRWUnlock(&(client->printer->rwlock)); 1980 1981 return (job); 1982} 1983 1984 1985/* 1986 * 'html_escape()' - Write a HTML-safe string. 1987 */ 1988 1989static void 1990html_escape(_ipp_client_t *client, /* I - Client */ 1991 const char *s, /* I - String to write */ 1992 size_t slen) /* I - Number of characters to write */ 1993{ 1994 const char *start, /* Start of segment */ 1995 *end; /* End of string */ 1996 1997 1998 start = s; 1999 end = s + (slen > 0 ? slen : strlen(s)); 2000 2001 while (*s && s < end) 2002 { 2003 if (*s == '&' || *s == '<') 2004 { 2005 if (s > start) 2006 httpWrite2(client->http, start, s - start); 2007 2008 if (*s == '&') 2009 httpWrite2(client->http, "&", 5); 2010 else 2011 httpWrite2(client->http, "<", 4); 2012 2013 start = s + 1; 2014 } 2015 2016 s ++; 2017 } 2018 2019 if (s > start) 2020 httpWrite2(client->http, start, s - start); 2021} 2022 2023 2024/* 2025 * 'html_printf()' - Send formatted text to the client, quoting as needed. 2026 */ 2027 2028static void 2029html_printf(_ipp_client_t *client, /* I - Client */ 2030 const char *format, /* I - Printf-style format string */ 2031 ...) /* I - Additional arguments as needed */ 2032{ 2033 va_list ap; /* Pointer to arguments */ 2034 const char *start; /* Start of string */ 2035 char size, /* Size character (h, l, L) */ 2036 type; /* Format type character */ 2037 int width, /* Width of field */ 2038 prec; /* Number of characters of precision */ 2039 char tformat[100], /* Temporary format string for sprintf() */ 2040 *tptr, /* Pointer into temporary format */ 2041 temp[1024]; /* Buffer for formatted numbers */ 2042 char *s; /* Pointer to string */ 2043 2044 2045 /* 2046 * Loop through the format string, formatting as needed... 2047 */ 2048 2049 va_start(ap, format); 2050 start = format; 2051 2052 while (*format) 2053 { 2054 if (*format == '%') 2055 { 2056 if (format > start) 2057 httpWrite2(client->http, start, format - start); 2058 2059 tptr = tformat; 2060 *tptr++ = *format++; 2061 2062 if (*format == '%') 2063 { 2064 httpWrite2(client->http, "%", 1); 2065 format ++; 2066 continue; 2067 } 2068 else if (strchr(" -+#\'", *format)) 2069 *tptr++ = *format++; 2070 2071 if (*format == '*') 2072 { 2073 /* 2074 * Get width from argument... 2075 */ 2076 2077 format ++; 2078 width = va_arg(ap, int); 2079 2080 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width); 2081 tptr += strlen(tptr); 2082 } 2083 else 2084 { 2085 width = 0; 2086 2087 while (isdigit(*format & 255)) 2088 { 2089 if (tptr < (tformat + sizeof(tformat) - 1)) 2090 *tptr++ = *format; 2091 2092 width = width * 10 + *format++ - '0'; 2093 } 2094 } 2095 2096 if (*format == '.') 2097 { 2098 if (tptr < (tformat + sizeof(tformat) - 1)) 2099 *tptr++ = *format; 2100 2101 format ++; 2102 2103 if (*format == '*') 2104 { 2105 /* 2106 * Get precision from argument... 2107 */ 2108 2109 format ++; 2110 prec = va_arg(ap, int); 2111 2112 snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec); 2113 tptr += strlen(tptr); 2114 } 2115 else 2116 { 2117 prec = 0; 2118 2119 while (isdigit(*format & 255)) 2120 { 2121 if (tptr < (tformat + sizeof(tformat) - 1)) 2122 *tptr++ = *format; 2123 2124 prec = prec * 10 + *format++ - '0'; 2125 } 2126 } 2127 } 2128 2129 if (*format == 'l' && format[1] == 'l') 2130 { 2131 size = 'L'; 2132 2133 if (tptr < (tformat + sizeof(tformat) - 2)) 2134 { 2135 *tptr++ = 'l'; 2136 *tptr++ = 'l'; 2137 } 2138 2139 format += 2; 2140 } 2141 else if (*format == 'h' || *format == 'l' || *format == 'L') 2142 { 2143 if (tptr < (tformat + sizeof(tformat) - 1)) 2144 *tptr++ = *format; 2145 2146 size = *format++; 2147 } 2148 else 2149 size = 0; 2150 2151 2152 if (!*format) 2153 { 2154 start = format; 2155 break; 2156 } 2157 2158 if (tptr < (tformat + sizeof(tformat) - 1)) 2159 *tptr++ = *format; 2160 2161 type = *format++; 2162 *tptr = '\0'; 2163 start = format; 2164 2165 switch (type) 2166 { 2167 case 'E' : /* Floating point formats */ 2168 case 'G' : 2169 case 'e' : 2170 case 'f' : 2171 case 'g' : 2172 if ((width + 2) > sizeof(temp)) 2173 break; 2174 2175 sprintf(temp, tformat, va_arg(ap, double)); 2176 2177 httpWrite2(client->http, temp, strlen(temp)); 2178 break; 2179 2180 case 'B' : /* Integer formats */ 2181 case 'X' : 2182 case 'b' : 2183 case 'd' : 2184 case 'i' : 2185 case 'o' : 2186 case 'u' : 2187 case 'x' : 2188 if ((width + 2) > sizeof(temp)) 2189 break; 2190 2191# ifdef HAVE_LONG_LONG 2192 if (size == 'L') 2193 sprintf(temp, tformat, va_arg(ap, long long)); 2194 else 2195# endif /* HAVE_LONG_LONG */ 2196 if (size == 'l') 2197 sprintf(temp, tformat, va_arg(ap, long)); 2198 else 2199 sprintf(temp, tformat, va_arg(ap, int)); 2200 2201 httpWrite2(client->http, temp, strlen(temp)); 2202 break; 2203 2204 case 'p' : /* Pointer value */ 2205 if ((width + 2) > sizeof(temp)) 2206 break; 2207 2208 sprintf(temp, tformat, va_arg(ap, void *)); 2209 2210 httpWrite2(client->http, temp, strlen(temp)); 2211 break; 2212 2213 case 'c' : /* Character or character array */ 2214 if (width <= 1) 2215 { 2216 temp[0] = va_arg(ap, int); 2217 temp[1] = '\0'; 2218 html_escape(client, temp, 1); 2219 } 2220 else 2221 html_escape(client, va_arg(ap, char *), (size_t)width); 2222 break; 2223 2224 case 's' : /* String */ 2225 if ((s = va_arg(ap, char *)) == NULL) 2226 s = "(null)"; 2227 2228 html_escape(client, s, strlen(s)); 2229 break; 2230 } 2231 } 2232 else 2233 format ++; 2234 } 2235 2236 if (format > start) 2237 httpWrite2(client->http, start, format - start); 2238 2239 va_end(ap); 2240} 2241 2242 2243/* 2244 * 'ipp_cancel_job()' - Cancel a job. 2245 */ 2246 2247static void 2248ipp_cancel_job(_ipp_client_t *client) /* I - Client */ 2249{ 2250 _ipp_job_t *job; /* Job information */ 2251 2252 2253 /* 2254 * Get the job... 2255 */ 2256 2257 if ((job = find_job(client)) == NULL) 2258 { 2259 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist."); 2260 return; 2261 } 2262 2263 /* 2264 * See if the job is already completed, canceled, or aborted; if so, 2265 * we can't cancel... 2266 */ 2267 2268 switch (job->state) 2269 { 2270 case IPP_JSTATE_CANCELED : 2271 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, 2272 "Job #%d is already canceled - can\'t cancel.", job->id); 2273 break; 2274 2275 case IPP_JSTATE_ABORTED : 2276 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, 2277 "Job #%d is already aborted - can\'t cancel.", job->id); 2278 break; 2279 2280 case IPP_JSTATE_COMPLETED : 2281 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, 2282 "Job #%d is already completed - can\'t cancel.", job->id); 2283 break; 2284 2285 default : 2286 /* 2287 * Cancel the job... 2288 */ 2289 2290 _cupsRWLockWrite(&(client->printer->rwlock)); 2291 2292 if (job->state == IPP_JSTATE_PROCESSING || 2293 (job->state == IPP_JSTATE_HELD && job->fd >= 0)) 2294 job->cancel = 1; 2295 else 2296 { 2297 job->state = IPP_JSTATE_CANCELED; 2298 job->completed = time(NULL); 2299 } 2300 2301 _cupsRWUnlock(&(client->printer->rwlock)); 2302 2303 respond_ipp(client, IPP_STATUS_OK, NULL); 2304 break; 2305 } 2306} 2307 2308 2309/* 2310 * 'ipp_create_job()' - Create a job object. 2311 */ 2312 2313static void 2314ipp_create_job(_ipp_client_t *client) /* I - Client */ 2315{ 2316 _ipp_job_t *job; /* New job */ 2317 cups_array_t *ra; /* Attributes to send in response */ 2318 2319 2320 /* 2321 * Validate print job attributes... 2322 */ 2323 2324 if (!valid_job_attributes(client)) 2325 { 2326 httpFlush(client->http); 2327 return; 2328 } 2329 2330 /* 2331 * Do we have a file to print? 2332 */ 2333 2334 if (httpGetState(client->http) == HTTP_STATE_POST_RECV) 2335 { 2336 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 2337 "Unexpected document data following request."); 2338 return; 2339 } 2340 2341 /* 2342 * Create the job... 2343 */ 2344 2345 if ((job = create_job(client)) == NULL) 2346 { 2347 respond_ipp(client, IPP_STATUS_ERROR_BUSY, 2348 "Currently printing another job."); 2349 return; 2350 } 2351 2352 /* 2353 * Return the job info... 2354 */ 2355 2356 respond_ipp(client, IPP_STATUS_OK, NULL); 2357 2358 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); 2359 cupsArrayAdd(ra, "job-id"); 2360 cupsArrayAdd(ra, "job-state"); 2361 cupsArrayAdd(ra, "job-state-reasons"); 2362 cupsArrayAdd(ra, "job-uri"); 2363 2364 copy_job_attributes(client, job, ra); 2365 cupsArrayDelete(ra); 2366} 2367 2368 2369/* 2370 * 'ipp_get_job_attributes()' - Get the attributes for a job object. 2371 */ 2372 2373static void 2374ipp_get_job_attributes( 2375 _ipp_client_t *client) /* I - Client */ 2376{ 2377 _ipp_job_t *job; /* Job */ 2378 cups_array_t *ra; /* requested-attributes */ 2379 2380 2381 if ((job = find_job(client)) == NULL) 2382 { 2383 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job not found."); 2384 return; 2385 } 2386 2387 respond_ipp(client, IPP_STATUS_OK, NULL); 2388 2389 ra = ippCreateRequestedArray(client->request); 2390 copy_job_attributes(client, job, ra); 2391 cupsArrayDelete(ra); 2392} 2393 2394 2395/* 2396 * 'ipp_get_jobs()' - Get a list of job objects. 2397 */ 2398 2399static void 2400ipp_get_jobs(_ipp_client_t *client) /* I - Client */ 2401{ 2402 ipp_attribute_t *attr; /* Current attribute */ 2403 const char *which_jobs = NULL; 2404 /* which-jobs values */ 2405 int job_comparison; /* Job comparison */ 2406 ipp_jstate_t job_state; /* job-state value */ 2407 int first_job_id, /* First job ID */ 2408 limit, /* Maximum number of jobs to return */ 2409 count; /* Number of jobs that match */ 2410 const char *username; /* Username */ 2411 _ipp_job_t *job; /* Current job pointer */ 2412 cups_array_t *ra; /* Requested attributes array */ 2413 2414 2415 /* 2416 * See if the "which-jobs" attribute have been specified... 2417 */ 2418 2419 if ((attr = ippFindAttribute(client->request, "which-jobs", 2420 IPP_TAG_KEYWORD)) != NULL) 2421 { 2422 which_jobs = ippGetString(attr, 0, NULL); 2423 fprintf(stderr, "%s Get-Jobs which-jobs=%s", client->hostname, which_jobs); 2424 } 2425 2426 if (!which_jobs || !strcmp(which_jobs, "not-completed")) 2427 { 2428 job_comparison = -1; 2429 job_state = IPP_JSTATE_STOPPED; 2430 } 2431 else if (!strcmp(which_jobs, "completed")) 2432 { 2433 job_comparison = 1; 2434 job_state = IPP_JSTATE_CANCELED; 2435 } 2436 else if (!strcmp(which_jobs, "aborted")) 2437 { 2438 job_comparison = 0; 2439 job_state = IPP_JSTATE_ABORTED; 2440 } 2441 else if (!strcmp(which_jobs, "all")) 2442 { 2443 job_comparison = 1; 2444 job_state = IPP_JSTATE_PENDING; 2445 } 2446 else if (!strcmp(which_jobs, "canceled")) 2447 { 2448 job_comparison = 0; 2449 job_state = IPP_JSTATE_CANCELED; 2450 } 2451 else if (!strcmp(which_jobs, "pending")) 2452 { 2453 job_comparison = 0; 2454 job_state = IPP_JSTATE_PENDING; 2455 } 2456 else if (!strcmp(which_jobs, "pending-held")) 2457 { 2458 job_comparison = 0; 2459 job_state = IPP_JSTATE_HELD; 2460 } 2461 else if (!strcmp(which_jobs, "processing")) 2462 { 2463 job_comparison = 0; 2464 job_state = IPP_JSTATE_PROCESSING; 2465 } 2466 else if (!strcmp(which_jobs, "processing-stopped")) 2467 { 2468 job_comparison = 0; 2469 job_state = IPP_JSTATE_STOPPED; 2470 } 2471 else 2472 { 2473 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, 2474 "The which-jobs value \"%s\" is not supported.", which_jobs); 2475 ippAddString(client->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD, 2476 "which-jobs", NULL, which_jobs); 2477 return; 2478 } 2479 2480 /* 2481 * See if they want to limit the number of jobs reported... 2482 */ 2483 2484 if ((attr = ippFindAttribute(client->request, "limit", 2485 IPP_TAG_INTEGER)) != NULL) 2486 { 2487 limit = ippGetInteger(attr, 0); 2488 2489 fprintf(stderr, "%s Get-Jobs limit=%d", client->hostname, limit); 2490 } 2491 else 2492 limit = 0; 2493 2494 if ((attr = ippFindAttribute(client->request, "first-job-id", 2495 IPP_TAG_INTEGER)) != NULL) 2496 { 2497 first_job_id = ippGetInteger(attr, 0); 2498 2499 fprintf(stderr, "%s Get-Jobs first-job-id=%d", client->hostname, 2500 first_job_id); 2501 } 2502 else 2503 first_job_id = 1; 2504 2505 /* 2506 * See if we only want to see jobs for a specific user... 2507 */ 2508 2509 username = NULL; 2510 2511 if ((attr = ippFindAttribute(client->request, "my-jobs", 2512 IPP_TAG_BOOLEAN)) != NULL) 2513 { 2514 int my_jobs = ippGetBoolean(attr, 0); 2515 2516 fprintf(stderr, "%s Get-Jobs my-jobs=%s\n", client->hostname, 2517 my_jobs ? "true" : "false"); 2518 2519 if (my_jobs) 2520 { 2521 if ((attr = ippFindAttribute(client->request, "requesting-user-name", 2522 IPP_TAG_NAME)) == NULL) 2523 { 2524 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 2525 "Need requesting-user-name with my-jobs."); 2526 return; 2527 } 2528 2529 username = ippGetString(attr, 0, NULL); 2530 2531 fprintf(stderr, "%s Get-Jobs requesting-user-name=\"%s\"\n", 2532 client->hostname, username); 2533 } 2534 } 2535 2536 /* 2537 * OK, build a list of jobs for this printer... 2538 */ 2539 2540 ra = ippCreateRequestedArray(client->request); 2541 2542 respond_ipp(client, IPP_STATUS_OK, NULL); 2543 2544 _cupsRWLockRead(&(client->printer->rwlock)); 2545 2546 for (count = 0, job = (_ipp_job_t *)cupsArrayFirst(client->printer->jobs); 2547 (limit <= 0 || count < limit) && job; 2548 job = (_ipp_job_t *)cupsArrayNext(client->printer->jobs)) 2549 { 2550 /* 2551 * Filter out jobs that don't match... 2552 */ 2553 2554 if ((job_comparison < 0 && job->state > job_state) || 2555 (job_comparison == 0 && job->state != job_state) || 2556 (job_comparison > 0 && job->state < job_state) || 2557 job->id < first_job_id || 2558 (username && job->username && 2559 _cups_strcasecmp(username, job->username))) 2560 continue; 2561 2562 if (count > 0) 2563 ippAddSeparator(client->response); 2564 2565 count ++; 2566 copy_job_attributes(client, job, ra); 2567 } 2568 2569 cupsArrayDelete(ra); 2570 2571 _cupsRWUnlock(&(client->printer->rwlock)); 2572} 2573 2574 2575/* 2576 * 'ipp_get_printer_attributes()' - Get the attributes for a printer object. 2577 */ 2578 2579static void 2580ipp_get_printer_attributes( 2581 _ipp_client_t *client) /* I - Client */ 2582{ 2583 cups_array_t *ra; /* Requested attributes array */ 2584 _ipp_printer_t *printer; /* Printer */ 2585 2586 2587 /* 2588 * Send the attributes... 2589 */ 2590 2591 ra = ippCreateRequestedArray(client->request); 2592 printer = client->printer; 2593 2594 respond_ipp(client, IPP_STATUS_OK, NULL); 2595 2596 _cupsRWLockRead(&(printer->rwlock)); 2597 2598 copy_attributes(client->response, printer->attrs, ra, IPP_TAG_ZERO, 2599 IPP_TAG_CUPS_CONST); 2600 2601 if (!ra || cupsArrayFind(ra, "printer-state")) 2602 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, 2603 "printer-state", printer->state); 2604 2605 if (!ra || cupsArrayFind(ra, "printer-state-reasons")) 2606 { 2607 if (printer->state_reasons == _IPP_PSTATE_NONE) 2608 ippAddString(client->response, IPP_TAG_PRINTER, 2609 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 2610 "printer-state-reasons", NULL, "none"); 2611 else 2612 { 2613 int num_reasons = 0;/* Number of reasons */ 2614 const char *reasons[32]; /* Reason strings */ 2615 2616 if (printer->state_reasons & _IPP_PSTATE_OTHER) 2617 reasons[num_reasons ++] = "other"; 2618 if (printer->state_reasons & _IPP_PSTATE_COVER_OPEN) 2619 reasons[num_reasons ++] = "cover-open"; 2620 if (printer->state_reasons & _IPP_PSTATE_INPUT_TRAY_MISSING) 2621 reasons[num_reasons ++] = "input-tray-missing"; 2622 if (printer->state_reasons & _IPP_PSTATE_MARKER_SUPPLY_EMPTY) 2623 reasons[num_reasons ++] = "marker-supply-empty-warning"; 2624 if (printer->state_reasons & _IPP_PSTATE_MARKER_SUPPLY_LOW) 2625 reasons[num_reasons ++] = "marker-supply-low-report"; 2626 if (printer->state_reasons & _IPP_PSTATE_MARKER_WASTE_ALMOST_FULL) 2627 reasons[num_reasons ++] = "marker-waste-almost-full-report"; 2628 if (printer->state_reasons & _IPP_PSTATE_MARKER_WASTE_FULL) 2629 reasons[num_reasons ++] = "marker-waste-full-warning"; 2630 if (printer->state_reasons & _IPP_PSTATE_MEDIA_EMPTY) 2631 reasons[num_reasons ++] = "media-empty-warning"; 2632 if (printer->state_reasons & _IPP_PSTATE_MEDIA_JAM) 2633 reasons[num_reasons ++] = "media-jam-warning"; 2634 if (printer->state_reasons & _IPP_PSTATE_MEDIA_LOW) 2635 reasons[num_reasons ++] = "media-low-report"; 2636 if (printer->state_reasons & _IPP_PSTATE_MEDIA_NEEDED) 2637 reasons[num_reasons ++] = "media-needed-report"; 2638 if (printer->state_reasons & _IPP_PSTATE_MOVING_TO_PAUSED) 2639 reasons[num_reasons ++] = "moving-to-paused"; 2640 if (printer->state_reasons & _IPP_PSTATE_PAUSED) 2641 reasons[num_reasons ++] = "paused"; 2642 if (printer->state_reasons & _IPP_PSTATE_SPOOL_AREA_FULL) 2643 reasons[num_reasons ++] = "spool-area-full"; 2644 if (printer->state_reasons & _IPP_PSTATE_TONER_EMPTY) 2645 reasons[num_reasons ++] = "toner-empty-warning"; 2646 if (printer->state_reasons & _IPP_PSTATE_TONER_LOW) 2647 reasons[num_reasons ++] = "toner-low-report"; 2648 2649 ippAddStrings(client->response, IPP_TAG_PRINTER, 2650 IPP_TAG_KEYWORD | IPP_TAG_CUPS_CONST, 2651 "printer-state-reasons", num_reasons, NULL, reasons); 2652 } 2653 } 2654 2655 if (!ra || cupsArrayFind(ra, "printer-up-time")) 2656 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 2657 "printer-up-time", (int)time(NULL)); 2658 2659 if (!ra || cupsArrayFind(ra, "queued-job-count")) 2660 ippAddInteger(client->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, 2661 "queued-job-count", 2662 printer->active_job && 2663 printer->active_job->state < IPP_JSTATE_CANCELED); 2664 2665 _cupsRWUnlock(&(printer->rwlock)); 2666 2667 cupsArrayDelete(ra); 2668} 2669 2670 2671/* 2672 * 'ipp_print_job()' - Create a job object with an attached document. 2673 */ 2674 2675static void 2676ipp_print_job(_ipp_client_t *client) /* I - Client */ 2677{ 2678 _ipp_job_t *job; /* New job */ 2679 char filename[1024], /* Filename buffer */ 2680 buffer[4096]; /* Copy buffer */ 2681 ssize_t bytes; /* Bytes read */ 2682 cups_array_t *ra; /* Attributes to send in response */ 2683 2684 2685 /* 2686 * Validate print job attributes... 2687 */ 2688 2689 if (!valid_job_attributes(client)) 2690 { 2691 httpFlush(client->http); 2692 return; 2693 } 2694 2695 /* 2696 * Do we have a file to print? 2697 */ 2698 2699 if (httpGetState(client->http) == HTTP_STATE_POST_SEND) 2700 { 2701 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "No file in request."); 2702 return; 2703 } 2704 2705 /* 2706 * Print the job... 2707 */ 2708 2709 if ((job = create_job(client)) == NULL) 2710 { 2711 respond_ipp(client, IPP_STATUS_ERROR_BUSY, 2712 "Currently printing another job."); 2713 return; 2714 } 2715 2716 /* 2717 * Create a file for the request data... 2718 */ 2719 2720 if (!_cups_strcasecmp(job->format, "image/jpeg")) 2721 snprintf(filename, sizeof(filename), "%s/%d.jpg", 2722 client->printer->directory, job->id); 2723 else if (!_cups_strcasecmp(job->format, "image/png")) 2724 snprintf(filename, sizeof(filename), "%s/%d.png", 2725 client->printer->directory, job->id); 2726 else if (!_cups_strcasecmp(job->format, "image/pwg-raster")) 2727 snprintf(filename, sizeof(filename), "%s/%d.ras", 2728 client->printer->directory, job->id); 2729 else if (!_cups_strcasecmp(job->format, "application/pdf")) 2730 snprintf(filename, sizeof(filename), "%s/%d.pdf", 2731 client->printer->directory, job->id); 2732 else if (!_cups_strcasecmp(job->format, "application/postscript")) 2733 snprintf(filename, sizeof(filename), "%s/%d.ps", 2734 client->printer->directory, job->id); 2735 else 2736 snprintf(filename, sizeof(filename), "%s/%d.prn", 2737 client->printer->directory, job->id); 2738 2739 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) 2740 { 2741 job->state = IPP_JSTATE_ABORTED; 2742 2743 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 2744 "Unable to create print file: %s", strerror(errno)); 2745 return; 2746 } 2747 2748 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0) 2749 { 2750 if (write(job->fd, buffer, bytes) < bytes) 2751 { 2752 int error = errno; /* Write error */ 2753 2754 job->state = IPP_JSTATE_ABORTED; 2755 2756 close(job->fd); 2757 job->fd = -1; 2758 2759 unlink(filename); 2760 2761 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 2762 "Unable to write print file: %s", strerror(error)); 2763 return; 2764 } 2765 } 2766 2767 if (bytes < 0) 2768 { 2769 /* 2770 * Got an error while reading the print data, so abort this job. 2771 */ 2772 2773 job->state = IPP_JSTATE_ABORTED; 2774 2775 close(job->fd); 2776 job->fd = -1; 2777 2778 unlink(filename); 2779 2780 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 2781 "Unable to read print file."); 2782 return; 2783 } 2784 2785 if (close(job->fd)) 2786 { 2787 int error = errno; /* Write error */ 2788 2789 job->state = IPP_JSTATE_ABORTED; 2790 job->fd = -1; 2791 2792 unlink(filename); 2793 2794 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 2795 "Unable to write print file: %s", strerror(error)); 2796 return; 2797 } 2798 2799 job->fd = -1; 2800 job->filename = strdup(filename); 2801 job->state = IPP_JSTATE_PENDING; 2802 2803 /* 2804 * Process the job... 2805 */ 2806 2807#if 0 2808 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job)) 2809 { 2810 job->state = IPP_JSTATE_ABORTED; 2811 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job."); 2812 return; 2813 } 2814 2815#else 2816 process_job(job); 2817#endif /* 0 */ 2818 2819 /* 2820 * Return the job info... 2821 */ 2822 2823 respond_ipp(client, IPP_STATUS_OK, NULL); 2824 2825 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); 2826 cupsArrayAdd(ra, "job-id"); 2827 cupsArrayAdd(ra, "job-state"); 2828 cupsArrayAdd(ra, "job-state-reasons"); 2829 cupsArrayAdd(ra, "job-uri"); 2830 2831 copy_job_attributes(client, job, ra); 2832 cupsArrayDelete(ra); 2833} 2834 2835 2836/* 2837 * 'ipp_print_uri()' - Create a job object with a referenced document. 2838 */ 2839 2840static void 2841ipp_print_uri(_ipp_client_t *client) /* I - Client */ 2842{ 2843 _ipp_job_t *job; /* New job */ 2844 ipp_attribute_t *uri; /* document-uri */ 2845 char scheme[256], /* URI scheme */ 2846 userpass[256], /* Username and password info */ 2847 hostname[256], /* Hostname */ 2848 resource[1024]; /* Resource path */ 2849 int port; /* Port number */ 2850 http_uri_status_t uri_status; /* URI decode status */ 2851 http_encryption_t encryption; /* Encryption to use, if any */ 2852 http_t *http; /* Connection for http/https URIs */ 2853 http_status_t status; /* Access status for http/https URIs */ 2854 int infile; /* Input file for local file URIs */ 2855 char filename[1024], /* Filename buffer */ 2856 buffer[4096]; /* Copy buffer */ 2857 ssize_t bytes; /* Bytes read */ 2858 cups_array_t *ra; /* Attributes to send in response */ 2859 static const char * const uri_status_strings[] = 2860 { /* URI decode errors */ 2861 "URI too large.", 2862 "Bad arguments to function.", 2863 "Bad resource in URI.", 2864 "Bad port number in URI.", 2865 "Bad hostname in URI.", 2866 "Bad username in URI.", 2867 "Bad scheme in URI.", 2868 "Bad/empty URI." 2869 }; 2870 2871 2872 /* 2873 * Validate print job attributes... 2874 */ 2875 2876 if (!valid_job_attributes(client)) 2877 { 2878 httpFlush(client->http); 2879 return; 2880 } 2881 2882 /* 2883 * Do we have a file to print? 2884 */ 2885 2886 if (httpGetState(client->http) == HTTP_STATE_POST_RECV) 2887 { 2888 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 2889 "Unexpected document data following request."); 2890 return; 2891 } 2892 2893 /* 2894 * Do we have a document URI? 2895 */ 2896 2897 if ((uri = ippFindAttribute(client->request, "document-uri", 2898 IPP_TAG_URI)) == NULL) 2899 { 2900 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri."); 2901 return; 2902 } 2903 2904 if (ippGetCount(uri) != 1) 2905 { 2906 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 2907 "Too many document-uri values."); 2908 return; 2909 } 2910 2911 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL), 2912 scheme, sizeof(scheme), userpass, 2913 sizeof(userpass), hostname, sizeof(hostname), 2914 &port, resource, sizeof(resource)); 2915 if (uri_status < HTTP_URI_STATUS_OK) 2916 { 2917 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s", 2918 uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]); 2919 return; 2920 } 2921 2922 if (strcmp(scheme, "file") && 2923#ifdef HAVE_SSL 2924 strcmp(scheme, "https") && 2925#endif /* HAVE_SSL */ 2926 strcmp(scheme, "http")) 2927 { 2928 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME, 2929 "URI scheme \"%s\" not supported.", scheme); 2930 return; 2931 } 2932 2933 if (!strcmp(scheme, "file") && access(resource, R_OK)) 2934 { 2935 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, 2936 "Unable to access URI: %s", strerror(errno)); 2937 return; 2938 } 2939 2940 /* 2941 * Print the job... 2942 */ 2943 2944 if ((job = create_job(client)) == NULL) 2945 { 2946 respond_ipp(client, IPP_STATUS_ERROR_BUSY, 2947 "Currently printing another job."); 2948 return; 2949 } 2950 2951 /* 2952 * Create a file for the request data... 2953 */ 2954 2955 if (!_cups_strcasecmp(job->format, "image/jpeg")) 2956 snprintf(filename, sizeof(filename), "%s/%d.jpg", 2957 client->printer->directory, job->id); 2958 else if (!_cups_strcasecmp(job->format, "image/png")) 2959 snprintf(filename, sizeof(filename), "%s/%d.png", 2960 client->printer->directory, job->id); 2961 else if (!_cups_strcasecmp(job->format, "application/pdf")) 2962 snprintf(filename, sizeof(filename), "%s/%d.pdf", 2963 client->printer->directory, job->id); 2964 else if (!_cups_strcasecmp(job->format, "application/postscript")) 2965 snprintf(filename, sizeof(filename), "%s/%d.ps", 2966 client->printer->directory, job->id); 2967 else 2968 snprintf(filename, sizeof(filename), "%s/%d.prn", 2969 client->printer->directory, job->id); 2970 2971 if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) 2972 { 2973 job->state = IPP_JSTATE_ABORTED; 2974 2975 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 2976 "Unable to create print file: %s", strerror(errno)); 2977 return; 2978 } 2979 2980 if (!strcmp(scheme, "file")) 2981 { 2982 if ((infile = open(resource, O_RDONLY)) < 0) 2983 { 2984 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, 2985 "Unable to access URI: %s", strerror(errno)); 2986 return; 2987 } 2988 2989 do 2990 { 2991 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 && 2992 (errno == EAGAIN || errno == EINTR)) 2993 bytes = 1; 2994 else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes) 2995 { 2996 int error = errno; /* Write error */ 2997 2998 job->state = IPP_JSTATE_ABORTED; 2999 3000 close(job->fd); 3001 job->fd = -1; 3002 3003 unlink(filename); 3004 close(infile); 3005 3006 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3007 "Unable to write print file: %s", strerror(error)); 3008 return; 3009 } 3010 } 3011 while (bytes > 0); 3012 3013 close(infile); 3014 } 3015 else 3016 { 3017#ifdef HAVE_SSL 3018 if (port == 443 || !strcmp(scheme, "https")) 3019 encryption = HTTP_ENCRYPTION_ALWAYS; 3020 else 3021#endif /* HAVE_SSL */ 3022 encryption = HTTP_ENCRYPTION_IF_REQUESTED; 3023 3024 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 3025 1, 30000, NULL)) == NULL) 3026 { 3027 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, 3028 "Unable to connect to %s: %s", hostname, 3029 cupsLastErrorString()); 3030 job->state = IPP_JSTATE_ABORTED; 3031 3032 close(job->fd); 3033 job->fd = -1; 3034 3035 unlink(filename); 3036 return; 3037 } 3038 3039 httpClearFields(http); 3040 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en"); 3041 if (httpGet(http, resource)) 3042 { 3043 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, 3044 "Unable to GET URI: %s", strerror(errno)); 3045 3046 job->state = IPP_JSTATE_ABORTED; 3047 3048 close(job->fd); 3049 job->fd = -1; 3050 3051 unlink(filename); 3052 httpClose(http); 3053 return; 3054 } 3055 3056 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); 3057 3058 if (status != HTTP_STATUS_OK) 3059 { 3060 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, 3061 "Unable to GET URI: %s", httpStatus(status)); 3062 3063 job->state = IPP_JSTATE_ABORTED; 3064 3065 close(job->fd); 3066 job->fd = -1; 3067 3068 unlink(filename); 3069 httpClose(http); 3070 return; 3071 } 3072 3073 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) 3074 { 3075 if (write(job->fd, buffer, bytes) < bytes) 3076 { 3077 int error = errno; /* Write error */ 3078 3079 job->state = IPP_JSTATE_ABORTED; 3080 3081 close(job->fd); 3082 job->fd = -1; 3083 3084 unlink(filename); 3085 httpClose(http); 3086 3087 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3088 "Unable to write print file: %s", strerror(error)); 3089 return; 3090 } 3091 } 3092 3093 httpClose(http); 3094 } 3095 3096 if (close(job->fd)) 3097 { 3098 int error = errno; /* Write error */ 3099 3100 job->state = IPP_JSTATE_ABORTED; 3101 job->fd = -1; 3102 3103 unlink(filename); 3104 3105 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3106 "Unable to write print file: %s", strerror(error)); 3107 return; 3108 } 3109 3110 job->fd = -1; 3111 job->filename = strdup(filename); 3112 job->state = IPP_JSTATE_PENDING; 3113 3114 /* 3115 * Process the job... 3116 */ 3117 3118#if 0 3119 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job)) 3120 { 3121 job->state = IPP_JSTATE_ABORTED; 3122 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job."); 3123 return; 3124 } 3125 3126#else 3127 process_job(job); 3128#endif /* 0 */ 3129 3130 /* 3131 * Return the job info... 3132 */ 3133 3134 respond_ipp(client, IPP_STATUS_OK, NULL); 3135 3136 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); 3137 cupsArrayAdd(ra, "job-id"); 3138 cupsArrayAdd(ra, "job-state"); 3139 cupsArrayAdd(ra, "job-state-reasons"); 3140 cupsArrayAdd(ra, "job-uri"); 3141 3142 copy_job_attributes(client, job, ra); 3143 cupsArrayDelete(ra); 3144} 3145 3146 3147/* 3148 * 'ipp_send_document()' - Add an attached document to a job object created with 3149 * Create-Job. 3150 */ 3151 3152static void 3153ipp_send_document(_ipp_client_t *client)/* I - Client */ 3154{ 3155 _ipp_job_t *job; /* Job information */ 3156 char filename[1024], /* Filename buffer */ 3157 buffer[4096]; /* Copy buffer */ 3158 ssize_t bytes; /* Bytes read */ 3159 ipp_attribute_t *attr; /* Current attribute */ 3160 cups_array_t *ra; /* Attributes to send in response */ 3161 3162 3163 /* 3164 * Get the job... 3165 */ 3166 3167 if ((job = find_job(client)) == NULL) 3168 { 3169 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist."); 3170 httpFlush(client->http); 3171 return; 3172 } 3173 3174 /* 3175 * See if we already have a document for this job or the job has already 3176 * in a non-pending state... 3177 */ 3178 3179 if (job->state > IPP_JSTATE_HELD) 3180 { 3181 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, 3182 "Job is not in a pending state."); 3183 httpFlush(client->http); 3184 return; 3185 } 3186 else if (job->filename || job->fd >= 0) 3187 { 3188 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, 3189 "Multiple document jobs are not supported."); 3190 httpFlush(client->http); 3191 return; 3192 } 3193 3194 if ((attr = ippFindAttribute(client->request, "last-document", 3195 IPP_TAG_ZERO)) == NULL) 3196 { 3197 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 3198 "Missing required last-document attribute."); 3199 httpFlush(client->http); 3200 return; 3201 } 3202 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 || 3203 !ippGetBoolean(attr, 0)) 3204 { 3205 respond_unsupported(client, attr); 3206 httpFlush(client->http); 3207 return; 3208 } 3209 3210 /* 3211 * Validate document attributes... 3212 */ 3213 3214 if (!valid_doc_attributes(client)) 3215 { 3216 httpFlush(client->http); 3217 return; 3218 } 3219 3220 /* 3221 * Get the document format for the job... 3222 */ 3223 3224 _cupsRWLockWrite(&(client->printer->rwlock)); 3225 3226 if ((attr = ippFindAttribute(job->attrs, "document-format", 3227 IPP_TAG_MIMETYPE)) != NULL) 3228 job->format = ippGetString(attr, 0, NULL); 3229 else 3230 job->format = "application/octet-stream"; 3231 3232 /* 3233 * Create a file for the request data... 3234 */ 3235 3236 if (!_cups_strcasecmp(job->format, "image/jpeg")) 3237 snprintf(filename, sizeof(filename), "%s/%d.jpg", 3238 client->printer->directory, job->id); 3239 else if (!_cups_strcasecmp(job->format, "image/png")) 3240 snprintf(filename, sizeof(filename), "%s/%d.png", 3241 client->printer->directory, job->id); 3242 else if (!_cups_strcasecmp(job->format, "application/pdf")) 3243 snprintf(filename, sizeof(filename), "%s/%d.pdf", 3244 client->printer->directory, job->id); 3245 else if (!_cups_strcasecmp(job->format, "application/postscript")) 3246 snprintf(filename, sizeof(filename), "%s/%d.ps", 3247 client->printer->directory, job->id); 3248 else 3249 snprintf(filename, sizeof(filename), "%s/%d.prn", 3250 client->printer->directory, job->id); 3251 3252 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); 3253 3254 _cupsRWUnlock(&(client->printer->rwlock)); 3255 3256 if (job->fd < 0) 3257 { 3258 job->state = IPP_JSTATE_ABORTED; 3259 3260 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3261 "Unable to create print file: %s", strerror(errno)); 3262 return; 3263 } 3264 3265 while ((bytes = httpRead2(client->http, buffer, sizeof(buffer))) > 0) 3266 { 3267 if (write(job->fd, buffer, bytes) < bytes) 3268 { 3269 int error = errno; /* Write error */ 3270 3271 job->state = IPP_JSTATE_ABORTED; 3272 3273 close(job->fd); 3274 job->fd = -1; 3275 3276 unlink(filename); 3277 3278 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3279 "Unable to write print file: %s", strerror(error)); 3280 return; 3281 } 3282 } 3283 3284 if (bytes < 0) 3285 { 3286 /* 3287 * Got an error while reading the print data, so abort this job. 3288 */ 3289 3290 job->state = IPP_JSTATE_ABORTED; 3291 3292 close(job->fd); 3293 job->fd = -1; 3294 3295 unlink(filename); 3296 3297 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3298 "Unable to read print file."); 3299 return; 3300 } 3301 3302 if (close(job->fd)) 3303 { 3304 int error = errno; /* Write error */ 3305 3306 job->state = IPP_JSTATE_ABORTED; 3307 job->fd = -1; 3308 3309 unlink(filename); 3310 3311 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3312 "Unable to write print file: %s", strerror(error)); 3313 return; 3314 } 3315 3316 _cupsRWLockWrite(&(client->printer->rwlock)); 3317 3318 job->fd = -1; 3319 job->filename = strdup(filename); 3320 job->state = IPP_JSTATE_PENDING; 3321 3322 _cupsRWUnlock(&(client->printer->rwlock)); 3323 3324 /* 3325 * Process the job... 3326 */ 3327 3328#if 0 3329 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job)) 3330 { 3331 job->state = IPP_JSTATE_ABORTED; 3332 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job."); 3333 return; 3334 } 3335 3336#else 3337 process_job(job); 3338#endif /* 0 */ 3339 3340 /* 3341 * Return the job info... 3342 */ 3343 3344 respond_ipp(client, IPP_STATUS_OK, NULL); 3345 3346 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); 3347 cupsArrayAdd(ra, "job-id"); 3348 cupsArrayAdd(ra, "job-state"); 3349 cupsArrayAdd(ra, "job-state-reasons"); 3350 cupsArrayAdd(ra, "job-uri"); 3351 3352 copy_job_attributes(client, job, ra); 3353 cupsArrayDelete(ra); 3354} 3355 3356 3357/* 3358 * 'ipp_send_uri()' - Add a referenced document to a job object created with 3359 * Create-Job. 3360 */ 3361 3362static void 3363ipp_send_uri(_ipp_client_t *client) /* I - Client */ 3364{ 3365 _ipp_job_t *job; /* Job information */ 3366 ipp_attribute_t *uri; /* document-uri */ 3367 char scheme[256], /* URI scheme */ 3368 userpass[256], /* Username and password info */ 3369 hostname[256], /* Hostname */ 3370 resource[1024]; /* Resource path */ 3371 int port; /* Port number */ 3372 http_uri_status_t uri_status; /* URI decode status */ 3373 http_encryption_t encryption; /* Encryption to use, if any */ 3374 http_t *http; /* Connection for http/https URIs */ 3375 http_status_t status; /* Access status for http/https URIs */ 3376 int infile; /* Input file for local file URIs */ 3377 char filename[1024], /* Filename buffer */ 3378 buffer[4096]; /* Copy buffer */ 3379 ssize_t bytes; /* Bytes read */ 3380 ipp_attribute_t *attr; /* Current attribute */ 3381 cups_array_t *ra; /* Attributes to send in response */ 3382 static const char * const uri_status_strings[] = 3383 { /* URI decode errors */ 3384 "URI too large.", 3385 "Bad arguments to function.", 3386 "Bad resource in URI.", 3387 "Bad port number in URI.", 3388 "Bad hostname in URI.", 3389 "Bad username in URI.", 3390 "Bad scheme in URI.", 3391 "Bad/empty URI." 3392 }; 3393 3394 3395 /* 3396 * Get the job... 3397 */ 3398 3399 if ((job = find_job(client)) == NULL) 3400 { 3401 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "Job does not exist."); 3402 httpFlush(client->http); 3403 return; 3404 } 3405 3406 /* 3407 * See if we already have a document for this job or the job has already 3408 * in a non-pending state... 3409 */ 3410 3411 if (job->state > IPP_JSTATE_HELD) 3412 { 3413 respond_ipp(client, IPP_STATUS_ERROR_NOT_POSSIBLE, 3414 "Job is not in a pending state."); 3415 httpFlush(client->http); 3416 return; 3417 } 3418 else if (job->filename || job->fd >= 0) 3419 { 3420 respond_ipp(client, IPP_STATUS_ERROR_MULTIPLE_JOBS_NOT_SUPPORTED, 3421 "Multiple document jobs are not supported."); 3422 httpFlush(client->http); 3423 return; 3424 } 3425 3426 if ((attr = ippFindAttribute(client->request, "last-document", 3427 IPP_TAG_ZERO)) == NULL) 3428 { 3429 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 3430 "Missing required last-document attribute."); 3431 httpFlush(client->http); 3432 return; 3433 } 3434 else if (ippGetValueTag(attr) != IPP_TAG_BOOLEAN || ippGetCount(attr) != 1 || 3435 !ippGetBoolean(attr, 0)) 3436 { 3437 respond_unsupported(client, attr); 3438 httpFlush(client->http); 3439 return; 3440 } 3441 3442 /* 3443 * Validate document attributes... 3444 */ 3445 3446 if (!valid_doc_attributes(client)) 3447 { 3448 httpFlush(client->http); 3449 return; 3450 } 3451 3452 /* 3453 * Do we have a file to print? 3454 */ 3455 3456 if (httpGetState(client->http) == HTTP_STATE_POST_RECV) 3457 { 3458 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 3459 "Unexpected document data following request."); 3460 return; 3461 } 3462 3463 /* 3464 * Do we have a document URI? 3465 */ 3466 3467 if ((uri = ippFindAttribute(client->request, "document-uri", 3468 IPP_TAG_URI)) == NULL) 3469 { 3470 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Missing document-uri."); 3471 return; 3472 } 3473 3474 if (ippGetCount(uri) != 1) 3475 { 3476 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 3477 "Too many document-uri values."); 3478 return; 3479 } 3480 3481 uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL), 3482 scheme, sizeof(scheme), userpass, 3483 sizeof(userpass), hostname, sizeof(hostname), 3484 &port, resource, sizeof(resource)); 3485 if (uri_status < HTTP_URI_STATUS_OK) 3486 { 3487 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad document-uri: %s", 3488 uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]); 3489 return; 3490 } 3491 3492 if (strcmp(scheme, "file") && 3493#ifdef HAVE_SSL 3494 strcmp(scheme, "https") && 3495#endif /* HAVE_SSL */ 3496 strcmp(scheme, "http")) 3497 { 3498 respond_ipp(client, IPP_STATUS_ERROR_URI_SCHEME, 3499 "URI scheme \"%s\" not supported.", scheme); 3500 return; 3501 } 3502 3503 if (!strcmp(scheme, "file") && access(resource, R_OK)) 3504 { 3505 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, 3506 "Unable to access URI: %s", strerror(errno)); 3507 return; 3508 } 3509 3510 /* 3511 * Get the document format for the job... 3512 */ 3513 3514 _cupsRWLockWrite(&(client->printer->rwlock)); 3515 3516 if ((attr = ippFindAttribute(job->attrs, "document-format", 3517 IPP_TAG_MIMETYPE)) != NULL) 3518 job->format = ippGetString(attr, 0, NULL); 3519 else 3520 job->format = "application/octet-stream"; 3521 3522 /* 3523 * Create a file for the request data... 3524 */ 3525 3526 if (!_cups_strcasecmp(job->format, "image/jpeg")) 3527 snprintf(filename, sizeof(filename), "%s/%d.jpg", 3528 client->printer->directory, job->id); 3529 else if (!_cups_strcasecmp(job->format, "image/png")) 3530 snprintf(filename, sizeof(filename), "%s/%d.png", 3531 client->printer->directory, job->id); 3532 else if (!_cups_strcasecmp(job->format, "application/pdf")) 3533 snprintf(filename, sizeof(filename), "%s/%d.pdf", 3534 client->printer->directory, job->id); 3535 else if (!_cups_strcasecmp(job->format, "application/postscript")) 3536 snprintf(filename, sizeof(filename), "%s/%d.ps", 3537 client->printer->directory, job->id); 3538 else 3539 snprintf(filename, sizeof(filename), "%s/%d.prn", 3540 client->printer->directory, job->id); 3541 3542 job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); 3543 3544 _cupsRWUnlock(&(client->printer->rwlock)); 3545 3546 if (job->fd < 0) 3547 { 3548 job->state = IPP_JSTATE_ABORTED; 3549 3550 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3551 "Unable to create print file: %s", strerror(errno)); 3552 return; 3553 } 3554 3555 if (!strcmp(scheme, "file")) 3556 { 3557 if ((infile = open(resource, O_RDONLY)) < 0) 3558 { 3559 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, 3560 "Unable to access URI: %s", strerror(errno)); 3561 return; 3562 } 3563 3564 do 3565 { 3566 if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 && 3567 (errno == EAGAIN || errno == EINTR)) 3568 bytes = 1; 3569 else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes) 3570 { 3571 int error = errno; /* Write error */ 3572 3573 job->state = IPP_JSTATE_ABORTED; 3574 3575 close(job->fd); 3576 job->fd = -1; 3577 3578 unlink(filename); 3579 close(infile); 3580 3581 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3582 "Unable to write print file: %s", strerror(error)); 3583 return; 3584 } 3585 } 3586 while (bytes > 0); 3587 3588 close(infile); 3589 } 3590 else 3591 { 3592#ifdef HAVE_SSL 3593 if (port == 443 || !strcmp(scheme, "https")) 3594 encryption = HTTP_ENCRYPTION_ALWAYS; 3595 else 3596#endif /* HAVE_SSL */ 3597 encryption = HTTP_ENCRYPTION_IF_REQUESTED; 3598 3599 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 3600 1, 30000, NULL)) == NULL) 3601 { 3602 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, 3603 "Unable to connect to %s: %s", hostname, 3604 cupsLastErrorString()); 3605 job->state = IPP_JSTATE_ABORTED; 3606 3607 close(job->fd); 3608 job->fd = -1; 3609 3610 unlink(filename); 3611 return; 3612 } 3613 3614 httpClearFields(http); 3615 httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en"); 3616 if (httpGet(http, resource)) 3617 { 3618 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, 3619 "Unable to GET URI: %s", strerror(errno)); 3620 3621 job->state = IPP_JSTATE_ABORTED; 3622 3623 close(job->fd); 3624 job->fd = -1; 3625 3626 unlink(filename); 3627 httpClose(http); 3628 return; 3629 } 3630 3631 while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); 3632 3633 if (status != HTTP_STATUS_OK) 3634 { 3635 respond_ipp(client, IPP_STATUS_ERROR_DOCUMENT_ACCESS, 3636 "Unable to GET URI: %s", httpStatus(status)); 3637 3638 job->state = IPP_JSTATE_ABORTED; 3639 3640 close(job->fd); 3641 job->fd = -1; 3642 3643 unlink(filename); 3644 httpClose(http); 3645 return; 3646 } 3647 3648 while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) 3649 { 3650 if (write(job->fd, buffer, bytes) < bytes) 3651 { 3652 int error = errno; /* Write error */ 3653 3654 job->state = IPP_JSTATE_ABORTED; 3655 3656 close(job->fd); 3657 job->fd = -1; 3658 3659 unlink(filename); 3660 httpClose(http); 3661 3662 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3663 "Unable to write print file: %s", strerror(error)); 3664 return; 3665 } 3666 } 3667 3668 httpClose(http); 3669 } 3670 3671 if (close(job->fd)) 3672 { 3673 int error = errno; /* Write error */ 3674 3675 job->state = IPP_JSTATE_ABORTED; 3676 job->fd = -1; 3677 3678 unlink(filename); 3679 3680 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, 3681 "Unable to write print file: %s", strerror(error)); 3682 return; 3683 } 3684 3685 _cupsRWLockWrite(&(client->printer->rwlock)); 3686 3687 job->fd = -1; 3688 job->filename = strdup(filename); 3689 job->state = IPP_JSTATE_PENDING; 3690 3691 _cupsRWUnlock(&(client->printer->rwlock)); 3692 3693 /* 3694 * Process the job... 3695 */ 3696 3697#if 0 3698 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job)) 3699 { 3700 job->state = IPP_JSTATE_ABORTED; 3701 respond_ipp(client, IPP_STATUS_ERROR_INTERNAL, "Unable to process job."); 3702 return; 3703 } 3704 3705#else 3706 process_job(job); 3707#endif /* 0 */ 3708 3709 /* 3710 * Return the job info... 3711 */ 3712 3713 respond_ipp(client, IPP_STATUS_OK, NULL); 3714 3715 ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); 3716 cupsArrayAdd(ra, "job-id"); 3717 cupsArrayAdd(ra, "job-state"); 3718 cupsArrayAdd(ra, "job-state-reasons"); 3719 cupsArrayAdd(ra, "job-uri"); 3720 3721 copy_job_attributes(client, job, ra); 3722 cupsArrayDelete(ra); 3723} 3724 3725 3726/* 3727 * 'ipp_validate_job()' - Validate job creation attributes. 3728 */ 3729 3730static void 3731ipp_validate_job(_ipp_client_t *client) /* I - Client */ 3732{ 3733 if (valid_job_attributes(client)) 3734 respond_ipp(client, IPP_STATUS_OK, NULL); 3735} 3736 3737 3738/* 3739 * 'process_client()' - Process client requests on a thread. 3740 */ 3741 3742static void * /* O - Exit status */ 3743process_client(_ipp_client_t *client) /* I - Client */ 3744{ 3745 /* 3746 * Loop until we are out of requests or timeout (30 seconds)... 3747 */ 3748 3749 while (httpWait(client->http, 30000)) 3750 if (!process_http(client)) 3751 break; 3752 3753 /* 3754 * Close the conection to the client and return... 3755 */ 3756 3757 delete_client(client); 3758 3759 return (NULL); 3760} 3761 3762 3763/* 3764 * 'process_http()' - Process a HTTP request. 3765 */ 3766 3767int /* O - 1 on success, 0 on failure */ 3768process_http(_ipp_client_t *client) /* I - Client connection */ 3769{ 3770 char uri[1024]; /* URI */ 3771 http_state_t http_state; /* HTTP state */ 3772 http_status_t http_status; /* HTTP status */ 3773 ipp_state_t ipp_state; /* State of IPP transfer */ 3774 char scheme[32], /* Method/scheme */ 3775 userpass[128], /* Username:password */ 3776 hostname[HTTP_MAX_HOST]; 3777 /* Hostname */ 3778 int port; /* Port number */ 3779 const char *encoding; /* Content-Encoding value */ 3780 static const char * const http_states[] = 3781 { /* Strings for logging HTTP method */ 3782 "WAITING", 3783 "OPTIONS", 3784 "GET", 3785 "GET_SEND", 3786 "HEAD", 3787 "POST", 3788 "POST_RECV", 3789 "POST_SEND", 3790 "PUT", 3791 "PUT_RECV", 3792 "DELETE", 3793 "TRACE", 3794 "CONNECT", 3795 "STATUS", 3796 "UNKNOWN_METHOD", 3797 "UNKNOWN_VERSION" 3798 }; 3799 3800 3801 /* 3802 * Clear state variables... 3803 */ 3804 3805 ippDelete(client->request); 3806 ippDelete(client->response); 3807 3808 client->request = NULL; 3809 client->response = NULL; 3810 client->operation = HTTP_STATE_WAITING; 3811 3812 /* 3813 * Read a request from the connection... 3814 */ 3815 3816 while ((http_state = httpReadRequest(client->http, uri, 3817 sizeof(uri))) == HTTP_STATE_WAITING) 3818 usleep(1); 3819 3820 /* 3821 * Parse the request line... 3822 */ 3823 3824 if (http_state == HTTP_STATE_ERROR) 3825 { 3826 if (httpError(client->http) == EPIPE) 3827 fprintf(stderr, "%s Client closed connection.\n", client->hostname); 3828 else 3829 fprintf(stderr, "%s Bad request line (%s).\n", client->hostname, 3830 strerror(httpError(client->http))); 3831 3832 return (0); 3833 } 3834 else if (http_state == HTTP_STATE_UNKNOWN_METHOD) 3835 { 3836 fprintf(stderr, "%s Bad/unknown operation.\n", client->hostname); 3837 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); 3838 return (0); 3839 } 3840 else if (http_state == HTTP_STATE_UNKNOWN_VERSION) 3841 { 3842 fprintf(stderr, "%s Bad HTTP version.\n", client->hostname); 3843 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); 3844 return (0); 3845 } 3846 3847 fprintf(stderr, "%s %s %s\n", client->hostname, http_states[http_state], 3848 uri); 3849 3850 /* 3851 * Separate the URI into its components... 3852 */ 3853 3854 if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme), 3855 userpass, sizeof(userpass), 3856 hostname, sizeof(hostname), &port, 3857 client->uri, sizeof(client->uri)) < HTTP_URI_STATUS_OK) 3858 { 3859 fprintf(stderr, "%s Bad URI \"%s\".\n", client->hostname, uri); 3860 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); 3861 return (0); 3862 } 3863 3864 /* 3865 * Process the request... 3866 */ 3867 3868 client->start = time(NULL); 3869 client->operation = httpGetState(client->http); 3870 3871 /* 3872 * Parse incoming parameters until the status changes... 3873 */ 3874 3875 while ((http_status = httpUpdate(client->http)) == HTTP_STATUS_CONTINUE); 3876 3877 if (http_status != HTTP_STATUS_OK) 3878 { 3879 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); 3880 return (0); 3881 } 3882 3883 if (!httpGetField(client->http, HTTP_FIELD_HOST)[0] && 3884 httpGetVersion(client->http) >= HTTP_VERSION_1_1) 3885 { 3886 /* 3887 * HTTP/1.1 and higher require the "Host:" field... 3888 */ 3889 3890 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); 3891 return (0); 3892 } 3893 3894 /* 3895 * Handle HTTP Upgrade... 3896 */ 3897 3898 if (!_cups_strcasecmp(httpGetField(client->http, HTTP_FIELD_CONNECTION), 3899 "Upgrade")) 3900 { 3901 if (!respond_http(client, HTTP_STATUS_NOT_IMPLEMENTED, NULL, NULL, 0)) 3902 return (0); 3903 } 3904 3905 /* 3906 * Handle HTTP Expect... 3907 */ 3908 3909 if (httpGetExpect(client->http) && 3910 (client->operation == HTTP_STATE_POST || 3911 client->operation == HTTP_STATE_PUT)) 3912 { 3913 if (httpGetExpect(client->http) == HTTP_STATUS_CONTINUE) 3914 { 3915 /* 3916 * Send 100-continue header... 3917 */ 3918 3919 if (!respond_http(client, HTTP_STATUS_CONTINUE, NULL, NULL, 0)) 3920 return (0); 3921 } 3922 else 3923 { 3924 /* 3925 * Send 417-expectation-failed header... 3926 */ 3927 3928 if (!respond_http(client, HTTP_STATUS_EXPECTATION_FAILED, NULL, NULL, 0)) 3929 return (0); 3930 } 3931 } 3932 3933 /* 3934 * Handle new transfers... 3935 */ 3936 3937 encoding = httpGetContentEncoding(client->http); 3938 3939 switch (client->operation) 3940 { 3941 case HTTP_STATE_OPTIONS : 3942 /* 3943 * Do HEAD/OPTIONS command... 3944 */ 3945 3946 return (respond_http(client, HTTP_STATUS_OK, NULL, NULL, 0)); 3947 3948 case HTTP_STATE_HEAD : 3949 if (!strcmp(client->uri, "/icon.png")) 3950 return (respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 0)); 3951 else if (!strcmp(client->uri, "/")) 3952 return (respond_http(client, HTTP_STATUS_OK, NULL, "text/html", 0)); 3953 else 3954 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0)); 3955 break; 3956 3957 case HTTP_STATE_GET : 3958 if (!strcmp(client->uri, "/icon.png")) 3959 { 3960 /* 3961 * Send PNG icon file. 3962 */ 3963 3964 int fd; /* Icon file */ 3965 struct stat fileinfo; /* Icon file information */ 3966 char buffer[4096]; /* Copy buffer */ 3967 ssize_t bytes; /* Bytes */ 3968 3969 fprintf(stderr, "Icon file is \"%s\".\n", client->printer->icon); 3970 3971 if (!stat(client->printer->icon, &fileinfo) && 3972 (fd = open(client->printer->icon, O_RDONLY)) >= 0) 3973 { 3974 if (!respond_http(client, HTTP_STATUS_OK, NULL, "image/png", 3975 fileinfo.st_size)) 3976 { 3977 close(fd); 3978 return (0); 3979 } 3980 3981 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) 3982 httpWrite2(client->http, buffer, bytes); 3983 3984 httpFlushWrite(client->http); 3985 3986 close(fd); 3987 } 3988 else 3989 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0)); 3990 } 3991 else if (!strcmp(client->uri, "/")) 3992 { 3993 /* 3994 * Show web status page... 3995 */ 3996 3997 if (!respond_http(client, HTTP_STATUS_OK, encoding, "text/html", 0)) 3998 return (0); 3999 4000 html_printf(client, 4001 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " 4002 "\"http://www.w3.org/TR/html4/strict.dtd\">\n" 4003 "<html>\n" 4004 "<head>\n" 4005 "<title>%s</title>\n" 4006 "<link rel=\"SHORTCUT ICON\" href=\"/icon.png\" " 4007 "type=\"image/png\">\n" 4008 "</head>\n" 4009 "<body>\n" 4010 "</body>\n" 4011 "<h1><img align=\"right\" src=\"/icon.png\">%s</h1>\n" 4012 "<p>%s, %d job(s).</p>\n" 4013 "</body>\n" 4014 "</html>\n", 4015 client->printer->name, client->printer->name, 4016 client->printer->state == IPP_PSTATE_IDLE ? "Idle" : 4017 client->printer->state == IPP_PSTATE_PROCESSING ? 4018 "Printing" : "Stopped", 4019 cupsArrayCount(client->printer->jobs)); 4020 httpWrite2(client->http, "", 0); 4021 4022 return (1); 4023 } 4024 else 4025 return (respond_http(client, HTTP_STATUS_NOT_FOUND, NULL, NULL, 0)); 4026 break; 4027 4028 case HTTP_STATE_POST : 4029 if (strcmp(httpGetField(client->http, HTTP_FIELD_CONTENT_TYPE), 4030 "application/ipp")) 4031 { 4032 /* 4033 * Not an IPP request... 4034 */ 4035 4036 return (respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0)); 4037 } 4038 4039 /* 4040 * Read the IPP request... 4041 */ 4042 4043 client->request = ippNew(); 4044 4045 while ((ipp_state = ippRead(client->http, 4046 client->request)) != IPP_STATE_DATA) 4047 { 4048 if (ipp_state == IPP_STATE_ERROR) 4049 { 4050 fprintf(stderr, "%s IPP read error (%s).\n", client->hostname, 4051 cupsLastErrorString()); 4052 respond_http(client, HTTP_STATUS_BAD_REQUEST, NULL, NULL, 0); 4053 return (0); 4054 } 4055 } 4056 4057 /* 4058 * Now that we have the IPP request, process the request... 4059 */ 4060 4061 return (process_ipp(client)); 4062 4063 default : 4064 break; /* Anti-compiler-warning-code */ 4065 } 4066 4067 return (1); 4068} 4069 4070 4071/* 4072 * 'process_ipp()' - Process an IPP request. 4073 */ 4074 4075static int /* O - 1 on success, 0 on error */ 4076process_ipp(_ipp_client_t *client) /* I - Client */ 4077{ 4078 ipp_tag_t group; /* Current group tag */ 4079 ipp_attribute_t *attr; /* Current attribute */ 4080 ipp_attribute_t *charset; /* Character set attribute */ 4081 ipp_attribute_t *language; /* Language attribute */ 4082 ipp_attribute_t *uri; /* Printer URI attribute */ 4083 int major, minor; /* Version number */ 4084 const char *name; /* Name of attribute */ 4085 4086 4087 debug_attributes("Request", client->request, 1); 4088 4089 /* 4090 * First build an empty response message for this request... 4091 */ 4092 4093 client->operation_id = ippGetOperation(client->request); 4094 client->response = ippNewResponse(client->request); 4095 4096 /* 4097 * Then validate the request header and required attributes... 4098 */ 4099 4100 major = ippGetVersion(client->request, &minor); 4101 4102 if (major < 1 || major > 2) 4103 { 4104 /* 4105 * Return an error, since we only support IPP 1.x and 2.x. 4106 */ 4107 4108 respond_ipp(client, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, 4109 "Bad request version number %d.%d.", major, minor); 4110 } 4111 else if (ippGetRequestId(client->request) <= 0) 4112 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, "Bad request-id %d.", 4113 ippGetRequestId(client->request)); 4114 else if (!ippFirstAttribute(client->request)) 4115 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 4116 "No attributes in request."); 4117 else 4118 { 4119 /* 4120 * Make sure that the attributes are provided in the correct order and 4121 * don't repeat groups... 4122 */ 4123 4124 for (attr = ippFirstAttribute(client->request), 4125 group = ippGetGroupTag(attr); 4126 attr; 4127 attr = ippNextAttribute(client->request)) 4128 { 4129 if (ippGetGroupTag(attr) < group && ippGetGroupTag(attr) != IPP_TAG_ZERO) 4130 { 4131 /* 4132 * Out of order; return an error... 4133 */ 4134 4135 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 4136 "Attribute groups are out of order (%x < %x).", 4137 ippGetGroupTag(attr), group); 4138 break; 4139 } 4140 else 4141 group = ippGetGroupTag(attr); 4142 } 4143 4144 if (!attr) 4145 { 4146 /* 4147 * Then make sure that the first three attributes are: 4148 * 4149 * attributes-charset 4150 * attributes-natural-language 4151 * printer-uri/job-uri 4152 */ 4153 4154 attr = ippFirstAttribute(client->request); 4155 name = ippGetName(attr); 4156 if (attr && name && !strcmp(name, "attributes-charset") && 4157 ippGetValueTag(attr) == IPP_TAG_CHARSET) 4158 charset = attr; 4159 else 4160 charset = NULL; 4161 4162 attr = ippNextAttribute(client->request); 4163 name = ippGetName(attr); 4164 4165 if (attr && name && !strcmp(name, "attributes-natural-language") && 4166 ippGetValueTag(attr) == IPP_TAG_LANGUAGE) 4167 language = attr; 4168 else 4169 language = NULL; 4170 4171 if ((attr = ippFindAttribute(client->request, "printer-uri", 4172 IPP_TAG_URI)) != NULL) 4173 uri = attr; 4174 else if ((attr = ippFindAttribute(client->request, "job-uri", 4175 IPP_TAG_URI)) != NULL) 4176 uri = attr; 4177 else 4178 uri = NULL; 4179 4180 if (charset && 4181 _cups_strcasecmp(ippGetString(charset, 0, NULL), "us-ascii") && 4182 _cups_strcasecmp(ippGetString(charset, 0, NULL), "utf-8")) 4183 { 4184 /* 4185 * Bad character set... 4186 */ 4187 4188 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 4189 "Unsupported character set \"%s\".", 4190 ippGetString(charset, 0, NULL)); 4191 } 4192 else if (!charset || !language || !uri) 4193 { 4194 /* 4195 * Return an error, since attributes-charset, 4196 * attributes-natural-language, and printer-uri/job-uri are required 4197 * for all operations. 4198 */ 4199 4200 respond_ipp(client, IPP_STATUS_ERROR_BAD_REQUEST, 4201 "Missing required attributes."); 4202 } 4203 else 4204 { 4205 char scheme[32], /* URI scheme */ 4206 userpass[32], /* Username/password in URI */ 4207 host[256], /* Host name in URI */ 4208 resource[256]; /* Resource path in URI */ 4209 int port; /* Port number in URI */ 4210 4211 name = ippGetName(uri); 4212 4213 if (httpSeparateURI(HTTP_URI_CODING_ALL, ippGetString(uri, 0, NULL), 4214 scheme, sizeof(scheme), 4215 userpass, sizeof(userpass), 4216 host, sizeof(host), &port, 4217 resource, sizeof(resource)) < HTTP_URI_STATUS_OK) 4218 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, 4219 "Bad %s value '%s'.", name, ippGetString(uri, 0, NULL)); 4220 else if ((!strcmp(name, "job-uri") && 4221 strncmp(resource, "/ipp/print/", 11)) || 4222 (!strcmp(name, "printer-uri") && 4223 strcmp(resource, "/ipp/print"))) 4224 respond_ipp(client, IPP_STATUS_ERROR_NOT_FOUND, "%s %s not found.", 4225 name, ippGetString(uri, 0, NULL)); 4226 else 4227 { 4228 /* 4229 * Try processing the operation... 4230 */ 4231 4232 switch (ippGetOperation(client->request)) 4233 { 4234 case IPP_OP_PRINT_JOB : 4235 ipp_print_job(client); 4236 break; 4237 4238 case IPP_OP_PRINT_URI : 4239 ipp_print_uri(client); 4240 break; 4241 4242 case IPP_OP_VALIDATE_JOB : 4243 ipp_validate_job(client); 4244 break; 4245 4246 case IPP_OP_CREATE_JOB : 4247 ipp_create_job(client); 4248 break; 4249 4250 case IPP_OP_SEND_DOCUMENT : 4251 ipp_send_document(client); 4252 break; 4253 4254 case IPP_OP_SEND_URI : 4255 ipp_send_uri(client); 4256 break; 4257 4258 case IPP_OP_CANCEL_JOB : 4259 ipp_cancel_job(client); 4260 break; 4261 4262 case IPP_OP_GET_JOB_ATTRIBUTES : 4263 ipp_get_job_attributes(client); 4264 break; 4265 4266 case IPP_OP_GET_JOBS : 4267 ipp_get_jobs(client); 4268 break; 4269 4270 case IPP_OP_GET_PRINTER_ATTRIBUTES : 4271 ipp_get_printer_attributes(client); 4272 break; 4273 4274 default : 4275 respond_ipp(client, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, 4276 "Operation not supported."); 4277 break; 4278 } 4279 } 4280 } 4281 } 4282 } 4283 4284 /* 4285 * Send the HTTP header and return... 4286 */ 4287 4288 if (httpGetState(client->http) != HTTP_STATE_POST_SEND) 4289 httpFlush(client->http); /* Flush trailing (junk) data */ 4290 4291 return (respond_http(client, HTTP_STATUS_OK, NULL, "application/ipp", 4292 ippLength(client->response))); 4293} 4294 4295 4296/* 4297 * 'process_job()' - Process a print job. 4298 */ 4299 4300static void * /* O - Thread exit status */ 4301process_job(_ipp_job_t *job) /* I - Job */ 4302{ 4303 job->state = IPP_JSTATE_PROCESSING; 4304 job->printer->state = IPP_PSTATE_PROCESSING; 4305 4306 if (job->printer->command) 4307 { 4308 /* 4309 * Execute a command with the job spool file and wait for it to complete... 4310 */ 4311 4312 int pid, /* Process ID */ 4313 status; /* Exit status */ 4314 time_t start, /* Start time */ 4315 end; /* End time */ 4316 4317 fprintf(stderr, "Running command \"%s %s\".\n", job->printer->command, 4318 job->filename); 4319 time(&start); 4320 4321 if ((pid = fork()) == 0) 4322 { 4323 /* 4324 * Child comes here... 4325 */ 4326 4327 execlp(job->printer->command, job->printer->command, job->filename, 4328 (void *)NULL); 4329 exit(errno); 4330 } 4331 else if (pid < 0) 4332 { 4333 /* 4334 * Unable to fork process... 4335 */ 4336 4337 perror("Unable to start job processing command"); 4338 } 4339 else 4340 { 4341 /* 4342 * Wait for child to complete... 4343 */ 4344 4345#ifdef HAVE_WAITPID 4346 while (waitpid(pid, &status, 0) < 0); 4347#else 4348 while (wait(&status) < 0); 4349#endif /* HAVE_WAITPID */ 4350 4351 if (status) 4352 { 4353 if (WIFEXITED(status)) 4354 fprintf(stderr, "Command \"%s\" exited with status %d.\n", 4355 job->printer->command, WEXITSTATUS(status)); 4356 else 4357 fprintf(stderr, "Command \"%s\" terminated with signal %d.\n", 4358 job->printer->command, WTERMSIG(status)); 4359 } 4360 else 4361 fprintf(stderr, "Command \"%s\" completed successfully.\n", 4362 job->printer->command); 4363 } 4364 4365 /* 4366 * Make sure processing takes at least 5 seconds... 4367 */ 4368 4369 time(&end); 4370 if ((end - start) < 5) 4371 sleep(5); 4372 } 4373 else 4374 { 4375 /* 4376 * Sleep for a random amount of time to simulate job processing. 4377 */ 4378 4379 sleep(5 + (rand() % 11)); 4380 } 4381 4382 if (job->cancel) 4383 job->state = IPP_JSTATE_CANCELED; 4384 else 4385 job->state = IPP_JSTATE_COMPLETED; 4386 4387 job->completed = time(NULL); 4388 job->printer->state = IPP_PSTATE_IDLE; 4389 job->printer->active_job = NULL; 4390 4391 return (NULL); 4392} 4393 4394 4395#ifdef HAVE_DNSSD 4396/* 4397 * 'register_printer()' - Register a printer object via Bonjour. 4398 */ 4399 4400static int /* O - 1 on success, 0 on error */ 4401register_printer( 4402 _ipp_printer_t *printer, /* I - Printer */ 4403 const char *location, /* I - Location */ 4404 const char *make, /* I - Manufacturer */ 4405 const char *model, /* I - Model name */ 4406 const char *formats, /* I - Supported formats */ 4407 const char *adminurl, /* I - Web interface URL */ 4408 int color, /* I - 1 = color, 0 = monochrome */ 4409 int duplex, /* I - 1 = duplex, 0 = simplex */ 4410 const char *subtype) /* I - Service subtype */ 4411{ 4412 DNSServiceErrorType error; /* Error from Bonjour */ 4413 char make_model[256],/* Make and model together */ 4414 product[256], /* Product string */ 4415 regtype[256]; /* Bonjour service type */ 4416 4417 4418 /* 4419 * Build the TXT record for IPP... 4420 */ 4421 4422 snprintf(make_model, sizeof(make_model), "%s %s", make, model); 4423 snprintf(product, sizeof(product), "(%s)", model); 4424 4425 TXTRecordCreate(&(printer->ipp_txt), 1024, NULL); 4426 TXTRecordSetValue(&(printer->ipp_txt), "rp", 9, "ipp/print"); 4427 TXTRecordSetValue(&(printer->ipp_txt), "ty", (uint8_t)strlen(make_model), 4428 make_model); 4429 TXTRecordSetValue(&(printer->ipp_txt), "adminurl", (uint8_t)strlen(adminurl), 4430 adminurl); 4431 if (*location) 4432 TXTRecordSetValue(&(printer->ipp_txt), "note", (uint8_t)strlen(location), 4433 location); 4434 TXTRecordSetValue(&(printer->ipp_txt), "product", (uint8_t)strlen(product), 4435 product); 4436 TXTRecordSetValue(&(printer->ipp_txt), "pdl", (uint8_t)strlen(formats), 4437 formats); 4438 TXTRecordSetValue(&(printer->ipp_txt), "Color", 1, color ? "T" : "F"); 4439 TXTRecordSetValue(&(printer->ipp_txt), "Duplex", 1, duplex ? "T" : "F"); 4440 TXTRecordSetValue(&(printer->ipp_txt), "usb_MFG", (uint8_t)strlen(make), 4441 make); 4442 TXTRecordSetValue(&(printer->ipp_txt), "usb_MDL", (uint8_t)strlen(model), 4443 model); 4444 4445 /* 4446 * Create a shared service reference for Bonjour... 4447 */ 4448 4449 if ((error = DNSServiceCreateConnection(&(printer->common_ref))) 4450 != kDNSServiceErr_NoError) 4451 { 4452 fprintf(stderr, "Unable to create mDNSResponder connection: %d\n", error); 4453 return (0); 4454 } 4455 4456 /* 4457 * Register the _printer._tcp (LPD) service type with a port number of 0 to 4458 * defend our service name but not actually support LPD... 4459 */ 4460 4461 printer->printer_ref = printer->common_ref; 4462 4463 if ((error = DNSServiceRegister(&(printer->printer_ref), 4464 kDNSServiceFlagsShareConnection, 4465 0 /* interfaceIndex */, printer->dnssd_name, 4466 "_printer._tcp", NULL /* domain */, 4467 NULL /* host */, 0 /* port */, 0 /* txtLen */, 4468 NULL /* txtRecord */, 4469 (DNSServiceRegisterReply)dnssd_callback, 4470 printer)) != kDNSServiceErr_NoError) 4471 { 4472 fprintf(stderr, "Unable to register \"%s._printer._tcp\": %d\n", 4473 printer->dnssd_name, error); 4474 return (0); 4475 } 4476 4477 /* 4478 * Then register the _ipp._tcp (IPP) service type with the real port number to 4479 * advertise our IPP printer... 4480 */ 4481 4482 printer->ipp_ref = printer->common_ref; 4483 4484 if (subtype && *subtype) 4485 snprintf(regtype, sizeof(regtype), "_ipp._tcp,%s", subtype); 4486 else 4487 strlcpy(regtype, "_ipp._tcp", sizeof(regtype)); 4488 4489 if ((error = DNSServiceRegister(&(printer->ipp_ref), 4490 kDNSServiceFlagsShareConnection, 4491 0 /* interfaceIndex */, printer->dnssd_name, 4492 regtype, NULL /* domain */, 4493 NULL /* host */, htons(printer->port), 4494 TXTRecordGetLength(&(printer->ipp_txt)), 4495 TXTRecordGetBytesPtr(&(printer->ipp_txt)), 4496 (DNSServiceRegisterReply)dnssd_callback, 4497 printer)) != kDNSServiceErr_NoError) 4498 { 4499 fprintf(stderr, "Unable to register \"%s.%s\": %d\n", 4500 printer->dnssd_name, regtype, error); 4501 return (0); 4502 } 4503 4504# if 0 /* ifdef HAVE_SSL */ 4505 /* 4506 * Then register the _ipps._tcp (IPP) service type with the real port number to 4507 * advertise our IPP printer... 4508 */ 4509 4510 printer->ipps_ref = printer->common_ref; 4511 4512 if (subtype && *subtype) 4513 snprintf(regtype, sizeof(regtype), "_ipps._tcp,%s", subtype); 4514 else 4515 strlcpy(regtype, "_ipps._tcp", sizeof(regtype)); 4516 4517 if ((error = DNSServiceRegister(&(printer->ipps_ref), 4518 kDNSServiceFlagsShareConnection, 4519 0 /* interfaceIndex */, printer->dnssd_name, 4520 regtype, NULL /* domain */, 4521 NULL /* host */, htons(printer->port), 4522 TXTRecordGetLength(&(printer->ipp_txt)), 4523 TXTRecordGetBytesPtr(&(printer->ipp_txt)), 4524 (DNSServiceRegisterReply)dnssd_callback, 4525 printer)) != kDNSServiceErr_NoError) 4526 { 4527 fprintf(stderr, "Unable to register \"%s.%s\": %d\n", 4528 printer->dnssd_name, regtype, error); 4529 return (0); 4530 } 4531# endif /* HAVE_SSL */ 4532 4533 /* 4534 * Similarly, register the _http._tcp,_printer (HTTP) service type with the 4535 * real port number to advertise our IPP printer... 4536 */ 4537 4538 printer->http_ref = printer->common_ref; 4539 4540 if ((error = DNSServiceRegister(&(printer->http_ref), 4541 kDNSServiceFlagsShareConnection, 4542 0 /* interfaceIndex */, printer->dnssd_name, 4543 "_http._tcp,_printer", NULL /* domain */, 4544 NULL /* host */, htons(printer->port), 4545 0 /* txtLen */, NULL, /* txtRecord */ 4546 (DNSServiceRegisterReply)dnssd_callback, 4547 printer)) != kDNSServiceErr_NoError) 4548 { 4549 fprintf(stderr, "Unable to register \"%s.%s\": %d\n", 4550 printer->dnssd_name, regtype, error); 4551 return (0); 4552 } 4553 4554 return (1); 4555} 4556#endif /* HAVE_DNSSD */ 4557 4558 4559/* 4560 * 'respond_http()' - Send a HTTP response. 4561 */ 4562 4563int /* O - 1 on success, 0 on failure */ 4564respond_http( 4565 _ipp_client_t *client, /* I - Client */ 4566 http_status_t code, /* I - HTTP status of response */ 4567 const char *content_encoding, /* I - Content-Encoding of response */ 4568 const char *type, /* I - MIME media type of response */ 4569 size_t length) /* I - Length of response */ 4570{ 4571 char message[1024]; /* Text message */ 4572 4573 4574 fprintf(stderr, "%s %s\n", client->hostname, httpStatus(code)); 4575 4576 if (code == HTTP_STATUS_CONTINUE) 4577 { 4578 /* 4579 * 100-continue doesn't send any headers... 4580 */ 4581 4582 return (httpWriteResponse(client->http, HTTP_STATUS_CONTINUE) == 0); 4583 } 4584 4585 /* 4586 * Format an error message... 4587 */ 4588 4589 if (!type && !length && code != HTTP_STATUS_OK) 4590 { 4591 snprintf(message, sizeof(message), "%d - %s\n", code, httpStatus(code)); 4592 4593 type = "text/plain"; 4594 length = strlen(message); 4595 } 4596 else 4597 message[0] = '\0'; 4598 4599 /* 4600 * Send the HTTP response header... 4601 */ 4602 4603 httpClearFields(client->http); 4604 4605 if (code == HTTP_STATUS_METHOD_NOT_ALLOWED || 4606 client->operation == HTTP_STATE_OPTIONS) 4607 httpSetField(client->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST"); 4608 4609 if (type) 4610 { 4611 if (!strcmp(type, "text/html")) 4612 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, 4613 "text/html; charset=utf-8"); 4614 else 4615 httpSetField(client->http, HTTP_FIELD_CONTENT_TYPE, type); 4616 4617 if (content_encoding) 4618 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, content_encoding); 4619 } 4620 4621 httpSetLength(client->http, length); 4622 4623 if (httpWriteResponse(client->http, code) < 0) 4624 return (0); 4625 4626 /* 4627 * Send the response data... 4628 */ 4629 4630 if (message[0]) 4631 { 4632 /* 4633 * Send a plain text message. 4634 */ 4635 4636 if (httpPrintf(client->http, "%s", message) < 0) 4637 return (0); 4638 4639 if (httpWrite2(client->http, "", 0) < 0) 4640 return (0); 4641 } 4642 else if (client->response) 4643 { 4644 /* 4645 * Send an IPP response... 4646 */ 4647 4648 debug_attributes("Response", client->response, 2); 4649 4650 ippSetState(client->response, IPP_STATE_IDLE); 4651 4652 if (ippWrite(client->http, client->response) != IPP_STATE_DATA) 4653 return (0); 4654 } 4655 4656 return (1); 4657} 4658 4659 4660/* 4661 * 'respond_ipp()' - Send an IPP response. 4662 */ 4663 4664static void 4665respond_ipp(_ipp_client_t *client, /* I - Client */ 4666 ipp_status_t status, /* I - status-code */ 4667 const char *message, /* I - printf-style status-message */ 4668 ...) /* I - Additional args as needed */ 4669{ 4670 const char *formatted = NULL; /* Formatted message */ 4671 4672 4673 ippSetStatusCode(client->response, status); 4674 4675 if (message) 4676 { 4677 va_list ap; /* Pointer to additional args */ 4678 ipp_attribute_t *attr; /* New status-message attribute */ 4679 4680 va_start(ap, message); 4681 if ((attr = ippFindAttribute(client->response, "status-message", 4682 IPP_TAG_TEXT)) != NULL) 4683 ippSetStringfv(client->response, &attr, 0, message, ap); 4684 else 4685 attr = ippAddStringfv(client->response, IPP_TAG_OPERATION, IPP_TAG_TEXT, 4686 "status-message", NULL, message, ap); 4687 va_end(ap); 4688 4689 formatted = ippGetString(attr, 0, NULL); 4690 } 4691 4692 if (formatted) 4693 fprintf(stderr, "%s %s %s (%s)\n", client->hostname, 4694 ippOpString(client->operation_id), ippErrorString(status), 4695 formatted); 4696 else 4697 fprintf(stderr, "%s %s %s\n", client->hostname, 4698 ippOpString(client->operation_id), ippErrorString(status)); 4699} 4700 4701 4702/* 4703 * 'respond_unsupported()' - Respond with an unsupported attribute. 4704 */ 4705 4706static void 4707respond_unsupported( 4708 _ipp_client_t *client, /* I - Client */ 4709 ipp_attribute_t *attr) /* I - Atribute */ 4710{ 4711 ipp_attribute_t *temp; /* Copy of attribute */ 4712 4713 4714 respond_ipp(client, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, 4715 "Unsupported %s %s%s value.", ippGetName(attr), 4716 ippGetCount(attr) > 1 ? "1setOf " : "", 4717 ippTagString(ippGetValueTag(attr))); 4718 4719 temp = ippCopyAttribute(client->response, attr, 0); 4720 ippSetGroupTag(client->response, &temp, IPP_TAG_UNSUPPORTED_GROUP); 4721} 4722 4723 4724/* 4725 * 'run_printer()' - Run the printer service. 4726 */ 4727 4728static void 4729run_printer(_ipp_printer_t *printer) /* I - Printer */ 4730{ 4731 int num_fds; /* Number of file descriptors */ 4732 struct pollfd polldata[3]; /* poll() data */ 4733 int timeout; /* Timeout for poll() */ 4734 _ipp_client_t *client; /* New client */ 4735 4736 4737 /* 4738 * Setup poll() data for the Bonjour service socket and IPv4/6 listeners... 4739 */ 4740 4741 polldata[0].fd = printer->ipv4; 4742 polldata[0].events = POLLIN; 4743 4744 polldata[1].fd = printer->ipv6; 4745 polldata[1].events = POLLIN; 4746 4747 num_fds = 2; 4748 4749#ifdef HAVE_DNSSD 4750 polldata[num_fds ].fd = DNSServiceRefSockFD(printer->common_ref); 4751 polldata[num_fds ++].events = POLLIN; 4752#endif /* HAVE_DNSSD */ 4753 4754 /* 4755 * Loop until we are killed or have a hard error... 4756 */ 4757 4758 for (;;) 4759 { 4760 if (cupsArrayCount(printer->jobs)) 4761 timeout = 10; 4762 else 4763 timeout = -1; 4764 4765 if (poll(polldata, num_fds, timeout) < 0 && errno != EINTR) 4766 { 4767 perror("poll() failed"); 4768 break; 4769 } 4770 4771 if (polldata[0].revents & POLLIN) 4772 { 4773 if ((client = create_client(printer, printer->ipv4)) != NULL) 4774 { 4775 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client)) 4776 { 4777 perror("Unable to create client thread"); 4778 delete_client(client); 4779 } 4780 } 4781 } 4782 4783 if (polldata[1].revents & POLLIN) 4784 { 4785 if ((client = create_client(printer, printer->ipv6)) != NULL) 4786 { 4787 if (!_cupsThreadCreate((_cups_thread_func_t)process_client, client)) 4788 { 4789 perror("Unable to create client thread"); 4790 delete_client(client); 4791 } 4792 } 4793 } 4794 4795#ifdef HAVE_DNSSD 4796 if (polldata[2].revents & POLLIN) 4797 DNSServiceProcessResult(printer->common_ref); 4798#endif /* HAVE_DNSSD */ 4799 4800 /* 4801 * Clean out old jobs... 4802 */ 4803 4804 clean_jobs(printer); 4805 } 4806} 4807 4808 4809/* 4810 * 'usage()' - Show program usage. 4811 */ 4812 4813static void 4814usage(int status) /* O - Exit status */ 4815{ 4816 if (!status) 4817 { 4818 puts(CUPS_SVERSION " - Copyright 2010-2013 by Apple Inc. All rights " 4819 "reserved."); 4820 puts(""); 4821 } 4822 4823 puts("Usage: ippserver [options] \"name\""); 4824 puts(""); 4825 puts("Options:"); 4826 puts("-2 Supports 2-sided printing (default=1-sided)"); 4827 puts("-M manufacturer Manufacturer name (default=Test)"); 4828 puts("-P PIN printing mode"); 4829 puts("-c command Run command for every print job"); 4830 printf("-d spool-directory Spool directory " 4831 "(default=/tmp/ippserver.%d)\n", (int)getpid()); 4832 puts("-f type/subtype[,...] List of supported types " 4833 "(default=application/pdf,image/jpeg)"); 4834 puts("-h Show program help"); 4835 puts("-i iconfile.png PNG icon file (default=printer.png)"); 4836 puts("-k Keep job spool files"); 4837 puts("-l location Location of printer (default=empty string)"); 4838 puts("-m model Model name (default=Printer)"); 4839 puts("-n hostname Hostname for printer"); 4840 puts("-p port Port number (default=auto)"); 4841#ifdef HAVE_DNSSD 4842 puts("-r subtype Bonjour service subtype (default=_print)"); 4843#endif /* HAVE_DNSSD */ 4844 puts("-s speed[,color-speed] Speed in pages per minute (default=10,0)"); 4845 puts("-v[vvv] Be (very) verbose"); 4846 4847 exit(status); 4848} 4849 4850 4851/* 4852 * 'valid_doc_attributes()' - Determine whether the document attributes are 4853 * valid. 4854 * 4855 * When one or more document attributes are invalid, this function adds a 4856 * suitable response and attributes to the unsupported group. 4857 */ 4858 4859static int /* O - 1 if valid, 0 if not */ 4860valid_doc_attributes( 4861 _ipp_client_t *client) /* I - Client */ 4862{ 4863 int valid = 1; /* Valid attributes? */ 4864 ipp_op_t op = ippGetOperation(client->request); 4865 /* IPP operation */ 4866 const char *op_name = ippOpString(op); 4867 /* IPP operation name */ 4868 ipp_attribute_t *attr, /* Current attribute */ 4869 *supported; /* xxx-supported attribute */ 4870 const char *compression = NULL, 4871 /* compression value */ 4872 *format = NULL; /* document-format value */ 4873 4874 4875 /* 4876 * Check operation attributes... 4877 */ 4878 4879 if ((attr = ippFindAttribute(client->request, "compression", 4880 IPP_TAG_ZERO)) != NULL) 4881 { 4882 /* 4883 * If compression is specified, only accept a supported value in a Print-Job 4884 * or Send-Document request... 4885 */ 4886 4887 compression = ippGetString(attr, 0, NULL); 4888 supported = ippFindAttribute(client->printer->attrs, 4889 "compression-supported", IPP_TAG_KEYWORD); 4890 4891 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD || 4892 ippGetGroupTag(attr) != IPP_TAG_OPERATION || 4893 (op != IPP_OP_PRINT_JOB && op != IPP_OP_SEND_DOCUMENT && 4894 op != IPP_OP_VALIDATE_JOB) || 4895 !ippContainsString(supported, compression)) 4896 { 4897 respond_unsupported(client, attr); 4898 valid = 0; 4899 } 4900 else 4901 { 4902 fprintf(stderr, "%s %s compression=\"%s\"\n", 4903 client->hostname, op_name, compression); 4904 4905 if (strcmp(compression, "none")) 4906 httpSetField(client->http, HTTP_FIELD_CONTENT_ENCODING, compression); 4907 } 4908 } 4909 4910 /* 4911 * Is it a format we support? 4912 */ 4913 4914 if ((attr = ippFindAttribute(client->request, "document-format", 4915 IPP_TAG_ZERO)) != NULL) 4916 { 4917 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_MIMETYPE || 4918 ippGetGroupTag(attr) != IPP_TAG_OPERATION) 4919 { 4920 respond_unsupported(client, attr); 4921 valid = 0; 4922 } 4923 else 4924 { 4925 format = ippGetString(attr, 0, NULL); 4926 4927 fprintf(stderr, "%s %s document-format=\"%s\"\n", 4928 client->hostname, op_name, format); 4929 } 4930 } 4931 else 4932 { 4933 format = ippGetString(ippFindAttribute(client->printer->attrs, 4934 "document-format-default", 4935 IPP_TAG_MIMETYPE), 0, NULL); 4936 if (!format) 4937 format = "application/octet-stream"; /* Should never happen */ 4938 4939 attr = ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, 4940 "document-format", NULL, format); 4941 } 4942 4943 if (!strcmp(format, "application/octet-stream") && 4944 (ippGetOperation(client->request) == IPP_OP_PRINT_JOB || 4945 ippGetOperation(client->request) == IPP_OP_SEND_DOCUMENT)) 4946 { 4947 /* 4948 * Auto-type the file using the first 4 bytes of the file... 4949 */ 4950 4951 unsigned char header[4]; /* First 4 bytes of file */ 4952 4953 memset(header, 0, sizeof(header)); 4954 httpPeek(client->http, (char *)header, sizeof(header)); 4955 4956 if (!memcmp(header, "%PDF", 4)) 4957 format = "application/pdf"; 4958 else if (!memcmp(header, "%!", 2)) 4959 format = "application/postscript"; 4960 else if (!memcmp(header, "\377\330\377", 3) && 4961 header[3] >= 0xe0 && header[3] <= 0xef) 4962 format = "image/jpeg"; 4963 else if (!memcmp(header, "\211PNG", 4)) 4964 format = "image/png"; 4965 4966 if (format) 4967 fprintf(stderr, "%s %s Auto-typed document-format=\"%s\"\n", 4968 client->hostname, op_name, format); 4969 4970 if (!attr) 4971 attr = ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, 4972 "document-format", NULL, format); 4973 else 4974 ippSetString(client->request, &attr, 0, format); 4975 } 4976 4977 if (op != IPP_OP_CREATE_JOB && 4978 (supported = ippFindAttribute(client->printer->attrs, 4979 "document-format-supported", 4980 IPP_TAG_MIMETYPE)) != NULL && 4981 !ippContainsString(supported, format)) 4982 { 4983 respond_unsupported(client, attr); 4984 valid = 0; 4985 } 4986 4987 return (valid); 4988} 4989 4990 4991/* 4992 * 'valid_job_attributes()' - Determine whether the job attributes are valid. 4993 * 4994 * When one or more job attributes are invalid, this function adds a suitable 4995 * response and attributes to the unsupported group. 4996 */ 4997 4998static int /* O - 1 if valid, 0 if not */ 4999valid_job_attributes( 5000 _ipp_client_t *client) /* I - Client */ 5001{ 5002 int i, /* Looping var */ 5003 valid = 1; /* Valid attributes? */ 5004 ipp_attribute_t *attr, /* Current attribute */ 5005 *supported; /* xxx-supported attribute */ 5006 5007 5008 /* 5009 * Check operation attributes... 5010 */ 5011 5012 valid = valid_doc_attributes(client); 5013 5014 /* 5015 * Check the various job template attributes... 5016 */ 5017 5018 if ((attr = ippFindAttribute(client->request, "copies", 5019 IPP_TAG_ZERO)) != NULL) 5020 { 5021 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || 5022 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 999) 5023 { 5024 respond_unsupported(client, attr); 5025 valid = 0; 5026 } 5027 } 5028 5029 if ((attr = ippFindAttribute(client->request, "ipp-attribute-fidelity", 5030 IPP_TAG_ZERO)) != NULL) 5031 { 5032 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_BOOLEAN) 5033 { 5034 respond_unsupported(client, attr); 5035 valid = 0; 5036 } 5037 } 5038 5039 if ((attr = ippFindAttribute(client->request, "job-hold-until", 5040 IPP_TAG_ZERO)) != NULL) 5041 { 5042 if (ippGetCount(attr) != 1 || 5043 (ippGetValueTag(attr) != IPP_TAG_NAME && 5044 ippGetValueTag(attr) != IPP_TAG_NAMELANG && 5045 ippGetValueTag(attr) != IPP_TAG_KEYWORD) || 5046 strcmp(ippGetString(attr, 0, NULL), "no-hold")) 5047 { 5048 respond_unsupported(client, attr); 5049 valid = 0; 5050 } 5051 } 5052 5053 if ((attr = ippFindAttribute(client->request, "job-name", 5054 IPP_TAG_ZERO)) != NULL) 5055 { 5056 if (ippGetCount(attr) != 1 || 5057 (ippGetValueTag(attr) != IPP_TAG_NAME && 5058 ippGetValueTag(attr) != IPP_TAG_NAMELANG)) 5059 { 5060 respond_unsupported(client, attr); 5061 valid = 0; 5062 } 5063 } 5064 5065 if ((attr = ippFindAttribute(client->request, "job-priority", 5066 IPP_TAG_ZERO)) != NULL) 5067 { 5068 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_INTEGER || 5069 ippGetInteger(attr, 0) < 1 || ippGetInteger(attr, 0) > 100) 5070 { 5071 respond_unsupported(client, attr); 5072 valid = 0; 5073 } 5074 } 5075 5076 if ((attr = ippFindAttribute(client->request, "job-sheets", 5077 IPP_TAG_ZERO)) != NULL) 5078 { 5079 if (ippGetCount(attr) != 1 || 5080 (ippGetValueTag(attr) != IPP_TAG_NAME && 5081 ippGetValueTag(attr) != IPP_TAG_NAMELANG && 5082 ippGetValueTag(attr) != IPP_TAG_KEYWORD) || 5083 strcmp(ippGetString(attr, 0, NULL), "none")) 5084 { 5085 respond_unsupported(client, attr); 5086 valid = 0; 5087 } 5088 } 5089 5090 if ((attr = ippFindAttribute(client->request, "media", 5091 IPP_TAG_ZERO)) != NULL) 5092 { 5093 if (ippGetCount(attr) != 1 || 5094 (ippGetValueTag(attr) != IPP_TAG_NAME && 5095 ippGetValueTag(attr) != IPP_TAG_NAMELANG && 5096 ippGetValueTag(attr) != IPP_TAG_KEYWORD)) 5097 { 5098 respond_unsupported(client, attr); 5099 valid = 0; 5100 } 5101 else 5102 { 5103 for (i = 0; 5104 i < (int)(sizeof(media_supported) / sizeof(media_supported[0])); 5105 i ++) 5106 if (!strcmp(ippGetString(attr, 0, NULL), media_supported[i])) 5107 break; 5108 5109 if (i >= (int)(sizeof(media_supported) / sizeof(media_supported[0]))) 5110 { 5111 respond_unsupported(client, attr); 5112 valid = 0; 5113 } 5114 } 5115 } 5116 5117 if ((attr = ippFindAttribute(client->request, "media-col", 5118 IPP_TAG_ZERO)) != NULL) 5119 { 5120 if (ippGetCount(attr) != 1 || 5121 ippGetValueTag(attr) != IPP_TAG_BEGIN_COLLECTION) 5122 { 5123 respond_unsupported(client, attr); 5124 valid = 0; 5125 } 5126 /* TODO: check for valid media-col */ 5127 } 5128 5129 if ((attr = ippFindAttribute(client->request, "multiple-document-handling", 5130 IPP_TAG_ZERO)) != NULL) 5131 { 5132 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD || 5133 (strcmp(ippGetString(attr, 0, NULL), 5134 "separate-documents-uncollated-copies") && 5135 strcmp(ippGetString(attr, 0, NULL), 5136 "separate-documents-collated-copies"))) 5137 { 5138 respond_unsupported(client, attr); 5139 valid = 0; 5140 } 5141 } 5142 5143 if ((attr = ippFindAttribute(client->request, "orientation-requested", 5144 IPP_TAG_ZERO)) != NULL) 5145 { 5146 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM || 5147 ippGetInteger(attr, 0) < IPP_ORIENT_PORTRAIT || 5148 ippGetInteger(attr, 0) > IPP_ORIENT_REVERSE_PORTRAIT) 5149 { 5150 respond_unsupported(client, attr); 5151 valid = 0; 5152 } 5153 } 5154 5155 if ((attr = ippFindAttribute(client->request, "page-ranges", 5156 IPP_TAG_ZERO)) != NULL) 5157 { 5158 respond_unsupported(client, attr); 5159 valid = 0; 5160 } 5161 5162 if ((attr = ippFindAttribute(client->request, "print-quality", 5163 IPP_TAG_ZERO)) != NULL) 5164 { 5165 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_ENUM || 5166 ippGetInteger(attr, 0) < IPP_QUALITY_DRAFT || 5167 ippGetInteger(attr, 0) > IPP_QUALITY_HIGH) 5168 { 5169 respond_unsupported(client, attr); 5170 valid = 0; 5171 } 5172 } 5173 5174 if ((attr = ippFindAttribute(client->request, "printer-resolution", 5175 IPP_TAG_ZERO)) != NULL) 5176 { 5177 respond_unsupported(client, attr); 5178 valid = 0; 5179 } 5180 5181 if ((attr = ippFindAttribute(client->request, "sides", 5182 IPP_TAG_ZERO)) != NULL) 5183 { 5184 const char *sides = NULL; /* "sides" value... */ 5185 5186 if (ippGetCount(attr) != 1 || ippGetValueTag(attr) != IPP_TAG_KEYWORD) 5187 { 5188 respond_unsupported(client, attr); 5189 valid = 0; 5190 } 5191 5192 sides = ippGetString(attr, 0, NULL); 5193 5194 if ((supported = ippFindAttribute(client->printer->attrs, "sides-supported", 5195 IPP_TAG_KEYWORD)) != NULL) 5196 { 5197 if (!ippContainsString(supported, sides)) 5198 { 5199 respond_unsupported(client, attr); 5200 valid = 0; 5201 } 5202 } 5203 else if (strcmp(sides, "one-sided")) 5204 { 5205 respond_unsupported(client, attr); 5206 valid = 0; 5207 } 5208 } 5209 5210 return (valid); 5211} 5212 5213 5214/* 5215 * End of "$Id: ippserver.c 11098 2013-07-04 15:55:20Z msweet $". 5216 */ 5217