diff options
Diffstat (limited to 'test/ippserver.c')
-rw-r--r-- | test/ippserver.c | 1285 |
1 files changed, 1171 insertions, 114 deletions
diff --git a/test/ippserver.c b/test/ippserver.c index fc10d52b9..23880a14b 100644 --- a/test/ippserver.c +++ b/test/ippserver.c @@ -1,5 +1,5 @@ /* - * "$Id: ippserver.c 9793 2011-05-20 03:49:49Z mike $" + * "$Id: ippserver.c 10031 2011-09-30 05:24:10Z mike $" * * Sample IPP/2.0 server for CUPS. * @@ -15,54 +15,62 @@ * * Contents: * - * main() - Main entry to the sample server. - * clean_jobs() - Clean out old (completed) jobs. - * compare_jobs() - Compare two jobs. - * copy_attribute() - Copy a single attribute. - * copy_attributes() - Copy attributes from one request to another. - * copy_job_attrs() - Copy job attributes to the response. - * create_client() - Accept a new network connection and create a - * client object. - * create_job() - Create a new job object from a Print-Job or - * Create-Job request. - * create_listener() - Create a listener socket. - * create_media_col() - Create a media-col value. - * create_printer() - Create, register, and listen for connections - * to a printer object. - * create_requested_array() - Create an array for requested-attributes. - * debug_attributes() - Print attributes in a request or response. - * delete_client() - Close the socket and free all memory used by - * a client object. - * delete_job() - Remove from the printer and free all memory - * used by a job object. - * delete_printer() - Unregister, close listen sockets, and free - * all memory used by a printer object. - * dnssd_callback() - Handle Bonjour registration events. - * find_job() - Find a job specified in a request. - * html_escape() - Write a HTML-safe string. - * html_printf() - Send formatted text to the client, quoting - * as needed. - * ipp_cancel_job() - Cancel a job. - * ipp_create_job() - Create a job object. - * ipp_get_job_attributes() - Get the attributes for a job object. - * ipp_get_jobs() - Get a list of job objects. + * main() - Main entry to the sample server. + * clean_jobs() - Clean out old (completed) jobs. + * compare_jobs() - Compare two jobs. + * copy_attribute() - Copy a single attribute. + * copy_attributes() - Copy attributes from one request to + * another. + * copy_job_attrs() - Copy job attributes to the response. + * create_client() - Accept a new network connection and create + * a client object. + * create_job() - Create a new job object from a Print-Job or + * Create-Job request. + * create_listener() - Create a listener socket. + * create_media_col() - Create a media-col value. + * create_printer() - Create, register, and listen for + * connections to a printer object. + * create_requested_array() - Create an array for requested-attributes. + * debug_attributes() - Print attributes in a request or response. + * delete_client() - Close the socket and free all memory used + * by a client object. + * delete_job() - Remove from the printer and free all memory + * used by a job object. + * delete_printer() - Unregister, close listen sockets, and free + * all memory used by a printer object. + * dnssd_callback() - Handle Bonjour registration events. + * find_job() - Find a job specified in a request. + * html_escape() - Write a HTML-safe string. + * html_printf() - Send formatted text to the client, quoting + * as needed. + * ipp_cancel_job() - Cancel a job. + * ipp_create_job() - Create a job object. + * ipp_get_job_attributes() - Get the attributes for a job object. + * ipp_get_jobs() - Get a list of job objects. * ipp_get_printer_attributes() - Get the attributes for a printer object. - * ipp_print_job() - Create a job object with an attached - * document. - * ipp_send_document() - Add an attached document to a job object - * created with Create-Job. - * ipp_validate_job() - Validate job creation attributes. - * process_client() - Process client requests on a thread. - * process_http() - Process a HTTP request. - * process_ipp() - Process an IPP request. - * process_job() - Process a print job. - * register_printer() - Register a printer object via Bonjour. - * respond_http() - Send a HTTP response. - * respond_ipp() - Send an IPP response. - * run_printer() - Run the printer service. - * usage() - Show program usage. - * valid_job_attributes() - Determine whether the job attributes are - * valid. + * ipp_print_job() - Create a job object with an attached + * document. + * ipp_print_uri() - Create a job object with a referenced + * document. + * ipp_send_document() - Add an attached document to a job object + * created with Create-Job. + * ipp_send_uri() - Add a referenced document to a job object + * created with Create-Job. + * ipp_validate_job() - Validate job creation attributes. + * process_client() - Process client requests on a thread. + * process_http() - Process a HTTP request. + * process_ipp() - Process an IPP request. + * process_job() - Process a print job. + * register_printer() - Register a printer object via Bonjour. + * respond_http() - Send a HTTP response. + * respond_ipp() - Send an IPP response. + * respond_unsupported() - Respond with an unsupported attribute. + * run_printer() - Run the printer service. + * usage() - Show program usage. + * valid_doc_attributes() - Determine whether the document attributes + * are valid. + * valid_job_attributes() - Determine whether the job attributes are + * valid. */ /* @@ -140,7 +148,7 @@ static const char * const media_supported[] = "na_index-3x5_3x5in", /* 3x5 */ "oe_photo-l_3.5x5in", /* L */ "na_index-4x6_4x6in", /* 4x6 */ - "na_5x7_5x7in" /* 5x7 */ + "na_5x7_5x7in" /* 5x7 aka 2L */ }; static const int media_col_sizes[][3] = { /* media-col-database sizes */ @@ -154,7 +162,7 @@ static const int media_col_sizes[][3] = { 7630, 12700, _IPP_PHOTO_ONLY }, /* 3x5 */ { 8890, 12700, _IPP_PHOTO_ONLY }, /* L */ { 10160, 15240, _IPP_PHOTO_ONLY }, /* 4x6 */ - { 12700, 17780, _IPP_PHOTO_ONLY } /* 5x7 */ + { 12700, 17780, _IPP_PHOTO_ONLY } /* 5x7 aka 2L */ }; static const char * const media_type_supported[] = /* media-type-supported values */ @@ -257,6 +265,7 @@ static _ipp_job_t *create_job(_ipp_client_t *client); static int create_listener(int family, int *port); static ipp_t *create_media_col(const char *media, const char *type, int width, int length, int margins); +static ipp_t *create_media_size(int width, int length); static _ipp_printer_t *create_printer(const char *servername, const char *name, const char *location, const char *make, const char *model, @@ -268,7 +277,8 @@ static _ipp_printer_t *create_printer(const char *servername, #endif /* HAVE_DNSSD */ const char *directory); static cups_array_t *create_requested_array(_ipp_client_t *client); -static void debug_attributes(const char *title, ipp_t *ipp); +static void debug_attributes(const char *title, ipp_t *ipp, + int response); static void delete_client(_ipp_client_t *client); static void delete_job(_ipp_job_t *job); static void delete_printer(_ipp_printer_t *printer); @@ -285,22 +295,17 @@ static _ipp_job_t *find_job(_ipp_client_t *client); static void html_escape(_ipp_client_t *client, const char *s, size_t slen); static void html_printf(_ipp_client_t *client, const char *format, - ...) -# ifdef __GNUC__ -__attribute__ ((__format__ (__printf__, 2, 3))) -# endif /* __GNUC__ */ -; + ...) __attribute__((__format__(__printf__, + 2, 3))); static void ipp_cancel_job(_ipp_client_t *client); -#if 0 static void ipp_create_job(_ipp_client_t *client); -#endif /* 0 */ static void ipp_get_job_attributes(_ipp_client_t *client); static void ipp_get_jobs(_ipp_client_t *client); static void ipp_get_printer_attributes(_ipp_client_t *client); static void ipp_print_job(_ipp_client_t *client); -#if 0 +static void ipp_print_uri(_ipp_client_t *client); static void ipp_send_document(_ipp_client_t *client); -#endif /* 0 */ +static void ipp_send_uri(_ipp_client_t *client); static void ipp_validate_job(_ipp_client_t *client); static void *process_client(_ipp_client_t *client); static int process_http(_ipp_client_t *client); @@ -317,12 +322,12 @@ static int respond_http(_ipp_client_t *client, http_status_t code, const char *type, size_t length); static void respond_ipp(_ipp_client_t *client, ipp_status_t status, const char *message, ...) -#ifdef __GNUC__ -__attribute__ ((__format__ (__printf__, 3, 4))) -#endif /* __GNUC__ */ -; + __attribute__ ((__format__ (__printf__, 3, 4))); +static void respond_unsupported(_ipp_client_t *client, + ipp_attribute_t *attr); static void run_printer(_ipp_printer_t *printer); -static void usage(int status); +static void usage(int status) __attribute__((noreturn)); +static int valid_doc_attributes(_ipp_client_t *client); static int valid_job_attributes(_ipp_client_t *client); @@ -799,7 +804,7 @@ copy_job_attributes( _ipp_job_t *job, /* I - Job */ cups_array_t *ra) /* I - requested-attributes */ { - copy_attributes(client->response, job->attrs, ra, IPP_TAG_ZERO, 0); + copy_attributes(client->response, job->attrs, ra, IPP_TAG_JOB, 0); if (!ra || cupsArrayFind(ra, "job-printer-up-time")) ippAddInteger(client->response, IPP_TAG_JOB, IPP_TAG_INTEGER, @@ -828,6 +833,10 @@ copy_job_attributes( ippAddString(client->response, IPP_TAG_JOB, IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons", NULL, "job-hold-until-specified"); + else + ippAddString(client->response, IPP_TAG_JOB, + IPP_TAG_KEYWORD | IPP_TAG_COPY, "job-state-reasons", + NULL, "job-data-insufficient"); break; case IPP_JOB_PROCESSING : @@ -899,10 +908,11 @@ create_client(_ipp_printer_t *printer, /* I - Printer */ return (NULL); } - client->printer = printer; - client->http.activity = time(NULL); - client->http.hostaddr = &(client->addr); - client->http.blocking = 1; + client->printer = printer; + client->http.activity = time(NULL); + client->http.hostaddr = &(client->addr); + client->http.blocking = 1; + client->http.wait_value = 60000; /* * Accept the client and get the remote address... @@ -1115,15 +1125,11 @@ create_media_col(const char *media, /* I - Media name */ int margins) /* I - Value for margins */ { ipp_t *media_col = ippNew(), /* media-col value */ - *media_size = ippNew(); /* media-size value */ + *media_size = create_media_size(width, length); + /* media-size value */ char media_key[256]; /* media-key value */ - ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", - width); - ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", - length); - snprintf(media_key, sizeof(media_key), "%s_%s%s", media, type, margins == 0 ? "_borderless" : ""); @@ -1148,6 +1154,26 @@ create_media_col(const char *media, /* I - Media name */ /* + * 'create_media_size()' - Create a media-size value. + */ + +static ipp_t * /* O - media-col collection */ +create_media_size(int width, /* I - x-dimension in 2540ths */ + int length) /* I - y-dimension in 2540ths */ +{ + ipp_t *media_size = ippNew(); /* media-size value */ + + + ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension", + width); + ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension", + length); + + return (media_size); +} + + +/* * 'create_printer()' - Create, register, and listen for connections to a * printer object. */ @@ -1183,8 +1209,10 @@ create_printer(const char *servername, /* I - Server hostname (NULL for default) *ptr; /* Pointer into string */ const char *prefix; /* Prefix string */ int num_database; /* Number of database values */ - ipp_attribute_t *media_col_database; + ipp_attribute_t *media_col_database, /* media-col-database value */ + *media_size_supported; + /* media-size-supported value */ ipp_t *media_col_default; /* media-col-default value */ ipp_value_t *media_col_value; @@ -1213,9 +1241,11 @@ create_printer(const char *servername, /* I - Server hostname (NULL for default) static const int ops[] = /* operations-supported values */ { IPP_PRINT_JOB, + IPP_PRINT_URI, IPP_VALIDATE_JOB, IPP_CREATE_JOB, IPP_SEND_DOCUMENT, + IPP_SEND_URI, IPP_CANCEL_JOB, IPP_GET_JOB_ATTRIBUTES, IPP_GET_JOBS, @@ -1264,6 +1294,15 @@ create_printer(const char *servername, /* I - Server hostname (NULL for default) IPP_QUALITY_NORMAL, IPP_QUALITY_HIGH }; + static const char * const reference_uri_schemes_supported[] = + { /* reference-uri-schemes-supported */ + "file", + "ftp", + "http" +#ifdef HAVE_SSL + , "https" +#endif /* HAVE_SSL */ + }; static const char * const sides_supported[] = { /* sides-supported values */ "one-sided", @@ -1605,6 +1644,18 @@ create_printer(const char *servername, /* I - Server hostname (NULL for default) (int)(sizeof(media_supported) / sizeof(media_supported[0])), NULL, media_supported); + /* media-size-supported */ + media_size_supported = ippAddCollections(printer->attrs, IPP_TAG_PRINTER, + "media-size-supported", + (int)(sizeof(media_col_sizes) / + sizeof(media_col_sizes[0])), + NULL); + for (i = 0; + i < (int)(sizeof(media_col_sizes) / sizeof(media_col_sizes[0])); + i ++) + media_size_supported->values[i].collection = + create_media_size(media_col_sizes[i][0], media_col_sizes[i][1]); + /* media-top-margin-supported */ ippAddIntegers(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "media-top-margin-supported", @@ -1612,6 +1663,13 @@ create_printer(const char *servername, /* I - Server hostname (NULL for default) sizeof(media_xxx_margin_supported[0])), media_xxx_margin_supported); + /* media-type-supported */ + ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, + "media-type-supported", + (int)(sizeof(media_type_supported) / + sizeof(media_type_supported[0])), + NULL, media_type_supported); + /* multiple-document-handling-supported */ ippAddStrings(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, "multiple-document-handling-supported", @@ -1723,6 +1781,14 @@ create_printer(const char *servername, /* I - Server hostname (NULL for default) ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri); + /* reference-uri-scheme-supported */ + ippAddStrings(printer->attrs, IPP_TAG_PRINTER, + IPP_TAG_URISCHEME | IPP_TAG_COPY, + "reference-uri-schemes-supported", + (int)(sizeof(reference_uri_schemes_supported) / + sizeof(reference_uri_schemes_supported[0])), + NULL, reference_uri_schemes_supported); + /* sides-default */ ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY, "sides-default", NULL, "one-sided"); @@ -1746,7 +1812,7 @@ create_printer(const char *servername, /* I - Server hostname (NULL for default) free(formats[0]); - debug_attributes("Printer", printer->attrs); + debug_attributes("Printer", printer->attrs, 0); #ifdef HAVE_DNSSD /* @@ -1984,7 +2050,8 @@ create_requested_array( static void debug_attributes(const char *title, /* I - Title */ - ipp_t *ipp) /* I - Request/response */ + ipp_t *ipp, /* I - Request/response */ + int type) /* I - 0 = object, 1 = request, 2 = response */ { ipp_tag_t group_tag; /* Current group */ ipp_attribute_t *attr; /* Current attribute */ @@ -1995,6 +2062,18 @@ debug_attributes(const char *title, /* I - Title */ return; fprintf(stderr, "%s:\n", title); + fprintf(stderr, " version=%d.%d\n", ipp->request.any.version[0], + ipp->request.any.version[1]); + if (type == 1) + fprintf(stderr, " operation-id=%s(%04x)\n", + ippOpString(ipp->request.op.operation_id), + ipp->request.op.operation_id); + else if (type == 2) + fprintf(stderr, " status-code=%s(%04x)\n", + ippErrorString(ipp->request.status.status_code), + ipp->request.status.status_code); + fprintf(stderr, " request-id=%d\n\n", ipp->request.any.request_id); + for (attr = ipp->attrs, group_tag = IPP_TAG_ZERO; attr; attr = attr->next) { if (attr->group_tag != group_tag) @@ -2467,7 +2546,10 @@ ipp_cancel_job(_ipp_client_t *client) /* I - Client */ */ if ((job = find_job(client)) == NULL) + { + respond_ipp(client, IPP_NOT_FOUND, "Job does not exist."); return; + } /* * See if the job is already completed, canceled, or aborted; if so, @@ -2515,7 +2597,6 @@ ipp_cancel_job(_ipp_client_t *client) /* I - Client */ } -#if 0 /* * 'ipp_create_job()' - Create a job object. */ @@ -2523,8 +2604,56 @@ ipp_cancel_job(_ipp_client_t *client) /* I - Client */ static void ipp_create_job(_ipp_client_t *client) /* I - Client */ { + _ipp_job_t *job; /* New job */ + cups_array_t *ra; /* Attributes to send in response */ + + + /* + * Validate print job attributes... + */ + + if (!valid_job_attributes(client)) + { + httpFlush(&(client->http)); + return; + } + + /* + * Do we have a file to print? + */ + + if (client->http.state == HTTP_POST_RECV) + { + respond_ipp(client, IPP_BAD_REQUEST, + "Unexpected document data following request."); + return; + } + + /* + * Create the job... + */ + + if ((job = create_job(client)) == NULL) + { + respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job."); + return; + } + + /* + * Return the job info... + */ + + respond_ipp(client, IPP_OK, NULL); + + ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); + cupsArrayAdd(ra, "job-id"); + cupsArrayAdd(ra, "job-state"); + cupsArrayAdd(ra, "job-state-reasons"); + cupsArrayAdd(ra, "job-uri"); + + copy_job_attributes(client, job, ra); + cupsArrayDelete(ra); } -#endif /* 0 */ /* @@ -2966,6 +3095,7 @@ ipp_print_job(_ipp_client_t *client) /* I - Client */ * Process the job... */ +#if 0 if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job)) { job->state = IPP_JOB_ABORTED; @@ -2973,6 +3103,10 @@ ipp_print_job(_ipp_client_t *client) /* I - Client */ return; } +#else + process_job(job); +#endif /* 0 */ + /* * Return the job info... */ @@ -2990,7 +3124,314 @@ ipp_print_job(_ipp_client_t *client) /* I - Client */ } +/* + * 'ipp_print_uri()' - Create a job object with a referenced document. + */ + +static void +ipp_print_uri(_ipp_client_t *client) /* I - Client */ +{ + _ipp_job_t *job; /* New job */ + ipp_attribute_t *uri; /* document-uri */ + char scheme[256], /* URI scheme */ + userpass[256], /* Username and password info */ + hostname[256], /* Hostname */ + resource[1024]; /* Resource path */ + int port; /* Port number */ + http_uri_status_t uri_status; /* URI decode status */ + http_encryption_t encryption; /* Encryption to use, if any */ + http_t *http; /* Connection for http/https URIs */ + http_status_t status; /* Access status for http/https URIs */ + int infile; /* Input file for local file URIs */ + char filename[1024], /* Filename buffer */ + buffer[4096]; /* Copy buffer */ + ssize_t bytes; /* Bytes read */ + cups_array_t *ra; /* Attributes to send in response */ + static const char * const uri_status_strings[] = + { /* URI decode errors */ + "URI too large.", + "Bad arguments to function.", + "Bad resource in URI.", + "Bad port number in URI.", + "Bad hostname in URI.", + "Bad username in URI.", + "Bad scheme in URI.", + "Bad/empty URI." + }; + + + /* + * Validate print job attributes... + */ + + if (!valid_job_attributes(client)) + { + httpFlush(&(client->http)); + return; + } + + /* + * Do we have a file to print? + */ + + if (client->http.state == HTTP_POST_RECV) + { + respond_ipp(client, IPP_BAD_REQUEST, + "Unexpected document data following request."); + return; + } + + /* + * Do we have a document URI? + */ + + if ((uri = ippFindAttribute(client->request, "document-uri", + IPP_TAG_URI)) == NULL) + { + respond_ipp(client, IPP_BAD_REQUEST, "Missing document-uri."); + return; + } + + if (uri->num_values != 1) + { + respond_ipp(client, IPP_BAD_REQUEST, "Too many document-uri values."); + return; + } + + uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, + scheme, sizeof(scheme), userpass, + sizeof(userpass), hostname, sizeof(hostname), + &port, resource, sizeof(resource)); + if (uri_status < HTTP_URI_OK) + { + respond_ipp(client, IPP_BAD_REQUEST, "Bad document-uri: %s", + uri_status_strings[uri_status - HTTP_URI_OVERFLOW]); + return; + } + + if (strcmp(scheme, "file") && +#ifdef HAVE_SSL + strcmp(scheme, "https") && +#endif /* HAVE_SSL */ + strcmp(scheme, "http")) + { + respond_ipp(client, IPP_URI_SCHEME, "URI scheme \"%s\" not supported.", + scheme); + return; + } + + if (!strcmp(scheme, "file") && access(resource, R_OK)) + { + respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s", + strerror(errno)); + return; + } + + /* + * Print the job... + */ + + if ((job = create_job(client)) == NULL) + { + respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job."); + return; + } + + /* + * Create a file for the request data... + */ + + if (!_cups_strcasecmp(job->format, "image/jpeg")) + snprintf(filename, sizeof(filename), "%s/%d.jpg", + client->printer->directory, job->id); + else if (!_cups_strcasecmp(job->format, "image/png")) + snprintf(filename, sizeof(filename), "%s/%d.png", + client->printer->directory, job->id); + else if (!_cups_strcasecmp(job->format, "application/pdf")) + snprintf(filename, sizeof(filename), "%s/%d.pdf", + client->printer->directory, job->id); + else if (!_cups_strcasecmp(job->format, "application/postscript")) + snprintf(filename, sizeof(filename), "%s/%d.ps", + client->printer->directory, job->id); + else + snprintf(filename, sizeof(filename), "%s/%d.prn", + client->printer->directory, job->id); + + if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) + { + job->state = IPP_JOB_ABORTED; + + respond_ipp(client, IPP_INTERNAL_ERROR, + "Unable to create print file: %s", strerror(errno)); + return; + } + + if (!strcmp(scheme, "file")) + { + if ((infile = open(resource, O_RDONLY)) < 0) + { + respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s", + strerror(errno)); + return; + } + + do + { + if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 && + (errno == EAGAIN || errno == EINTR)) + bytes = 1; + else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes) + { + int error = errno; /* Write error */ + + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + close(infile); + + respond_ipp(client, IPP_INTERNAL_ERROR, + "Unable to write print file: %s", strerror(error)); + return; + } + } + while (bytes > 0); + + close(infile); + } + else + { +#ifdef HAVE_SSL + if (port == 443 || !strcmp(scheme, "https")) + encryption = HTTP_ENCRYPT_ALWAYS; + else +#endif /* HAVE_SSL */ + encryption = HTTP_ENCRYPT_IF_REQUESTED; + + if ((http = httpConnectEncrypt(hostname, port, encryption)) == NULL) + { + respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, + "Unable to connect to %s: %s", hostname, + cupsLastErrorString()); + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + return; + } + + httpClearFields(http); + httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en"); + if (httpGet(http, resource)) + { + respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s", + strerror(errno)); + + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + httpClose(http); + return; + } + + while ((status = httpUpdate(http)) == HTTP_CONTINUE); + + if (status != HTTP_OK) + { + respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s", + httpStatus(status)); + + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + httpClose(http); + return; + } + + while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) + { + if (write(job->fd, buffer, bytes) < bytes) + { + int error = errno; /* Write error */ + + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + httpClose(http); + + respond_ipp(client, IPP_INTERNAL_ERROR, + "Unable to write print file: %s", strerror(error)); + return; + } + } + + httpClose(http); + } + + if (close(job->fd)) + { + int error = errno; /* Write error */ + + job->state = IPP_JOB_ABORTED; + job->fd = -1; + + unlink(filename); + + respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s", + strerror(error)); + return; + } + + job->fd = -1; + job->filename = strdup(filename); + job->state = IPP_JOB_PENDING; + + /* + * Process the job... + */ + #if 0 + if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job)) + { + job->state = IPP_JOB_ABORTED; + respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job."); + return; + } + +#else + process_job(job); +#endif /* 0 */ + + /* + * Return the job info... + */ + + respond_ipp(client, IPP_OK, NULL); + + ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); + cupsArrayAdd(ra, "job-id"); + cupsArrayAdd(ra, "job-state"); + cupsArrayAdd(ra, "job-state-reasons"); + cupsArrayAdd(ra, "job-uri"); + + copy_job_attributes(client, job, ra); + cupsArrayDelete(ra); +} + + /* * 'ipp_send_document()' - Add an attached document to a job object created with * Create-Job. @@ -2999,9 +3440,571 @@ ipp_print_job(_ipp_client_t *client) /* I - Client */ static void ipp_send_document(_ipp_client_t *client)/* I - Client */ { + _ipp_job_t *job; /* Job information */ + char filename[1024], /* Filename buffer */ + buffer[4096]; /* Copy buffer */ + ssize_t bytes; /* Bytes read */ + ipp_attribute_t *attr; /* Current attribute */ + cups_array_t *ra; /* Attributes to send in response */ + + + /* + * Get the job... + */ + + if ((job = find_job(client)) == NULL) + { + respond_ipp(client, IPP_NOT_FOUND, "Job does not exist."); + httpFlush(&(client->http)); + return; + } + + /* + * See if we already have a document for this job or the job has already + * in a non-pending state... + */ + + if (job->state > IPP_JOB_HELD) + { + respond_ipp(client, IPP_NOT_POSSIBLE, "Job is not in a pending state."); + httpFlush(&(client->http)); + return; + } + else if (job->filename || job->fd >= 0) + { + respond_ipp(client, IPP_MULTIPLE_JOBS_NOT_SUPPORTED, + "Multiple document jobs are not supported."); + httpFlush(&(client->http)); + return; + } + + if ((attr = ippFindAttribute(client->request, "last-document", + IPP_TAG_ZERO)) == NULL) + { + respond_ipp(client, IPP_BAD_REQUEST, + "Missing required last-document attribute."); + httpFlush(&(client->http)); + return; + } + else if (attr->value_tag != IPP_TAG_BOOLEAN || attr->num_values != 1 || + !attr->values[0].boolean) + { + respond_unsupported(client, attr); + httpFlush(&(client->http)); + return; + } + + /* + * Validate document attributes... + */ + + if (!valid_doc_attributes(client)) + { + httpFlush(&(client->http)); + return; + } + + /* + * Get the document format for the job... + */ + + _cupsRWLockWrite(&(client->printer->rwlock)); + + if ((attr = ippFindAttribute(job->attrs, "document-format", + IPP_TAG_MIMETYPE)) != NULL) + job->format = attr->values[0].string.text; + else + job->format = "application/octet-stream"; + + /* + * Create a file for the request data... + */ + + if (!_cups_strcasecmp(job->format, "image/jpeg")) + snprintf(filename, sizeof(filename), "%s/%d.jpg", + client->printer->directory, job->id); + else if (!_cups_strcasecmp(job->format, "image/png")) + snprintf(filename, sizeof(filename), "%s/%d.png", + client->printer->directory, job->id); + else if (!_cups_strcasecmp(job->format, "application/pdf")) + snprintf(filename, sizeof(filename), "%s/%d.pdf", + client->printer->directory, job->id); + else if (!_cups_strcasecmp(job->format, "application/postscript")) + snprintf(filename, sizeof(filename), "%s/%d.ps", + client->printer->directory, job->id); + else + snprintf(filename, sizeof(filename), "%s/%d.prn", + client->printer->directory, job->id); + + job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + _cupsRWUnlock(&(client->printer->rwlock)); + + if (job->fd < 0) + { + job->state = IPP_JOB_ABORTED; + + respond_ipp(client, IPP_INTERNAL_ERROR, + "Unable to create print file: %s", strerror(errno)); + return; + } + + while ((bytes = httpRead2(&(client->http), buffer, sizeof(buffer))) > 0) + { + if (write(job->fd, buffer, bytes) < bytes) + { + int error = errno; /* Write error */ + + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + + respond_ipp(client, IPP_INTERNAL_ERROR, + "Unable to write print file: %s", strerror(error)); + return; + } + } + + if (bytes < 0) + { + /* + * Got an error while reading the print data, so abort this job. + */ + + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + + respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to read print file."); + return; + } + + if (close(job->fd)) + { + int error = errno; /* Write error */ + + job->state = IPP_JOB_ABORTED; + job->fd = -1; + + unlink(filename); + + respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s", + strerror(error)); + return; + } + + _cupsRWLockWrite(&(client->printer->rwlock)); + + job->fd = -1; + job->filename = strdup(filename); + job->state = IPP_JOB_PENDING; + + _cupsRWUnlock(&(client->printer->rwlock)); + + /* + * Process the job... + */ + +#if 0 + if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job)) + { + job->state = IPP_JOB_ABORTED; + respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job."); + return; + } + +#else + process_job(job); +#endif /* 0 */ + + /* + * Return the job info... + */ + + respond_ipp(client, IPP_OK, NULL); + + ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); + cupsArrayAdd(ra, "job-id"); + cupsArrayAdd(ra, "job-state"); + cupsArrayAdd(ra, "job-state-reasons"); + cupsArrayAdd(ra, "job-uri"); + + copy_job_attributes(client, job, ra); + cupsArrayDelete(ra); } + + +/* + * 'ipp_send_uri()' - Add a referenced document to a job object created with + * Create-Job. + */ + +static void +ipp_send_uri(_ipp_client_t *client) /* I - Client */ +{ + _ipp_job_t *job; /* Job information */ + ipp_attribute_t *uri; /* document-uri */ + char scheme[256], /* URI scheme */ + userpass[256], /* Username and password info */ + hostname[256], /* Hostname */ + resource[1024]; /* Resource path */ + int port; /* Port number */ + http_uri_status_t uri_status; /* URI decode status */ + http_encryption_t encryption; /* Encryption to use, if any */ + http_t *http; /* Connection for http/https URIs */ + http_status_t status; /* Access status for http/https URIs */ + int infile; /* Input file for local file URIs */ + char filename[1024], /* Filename buffer */ + buffer[4096]; /* Copy buffer */ + ssize_t bytes; /* Bytes read */ + ipp_attribute_t *attr; /* Current attribute */ + cups_array_t *ra; /* Attributes to send in response */ + static const char * const uri_status_strings[] = + { /* URI decode errors */ + "URI too large.", + "Bad arguments to function.", + "Bad resource in URI.", + "Bad port number in URI.", + "Bad hostname in URI.", + "Bad username in URI.", + "Bad scheme in URI.", + "Bad/empty URI." + }; + + + /* + * Get the job... + */ + + if ((job = find_job(client)) == NULL) + { + respond_ipp(client, IPP_NOT_FOUND, "Job does not exist."); + httpFlush(&(client->http)); + return; + } + + /* + * See if we already have a document for this job or the job has already + * in a non-pending state... + */ + + if (job->state > IPP_JOB_HELD) + { + respond_ipp(client, IPP_NOT_POSSIBLE, "Job is not in a pending state."); + httpFlush(&(client->http)); + return; + } + else if (job->filename || job->fd >= 0) + { + respond_ipp(client, IPP_MULTIPLE_JOBS_NOT_SUPPORTED, + "Multiple document jobs are not supported."); + httpFlush(&(client->http)); + return; + } + + if ((attr = ippFindAttribute(client->request, "last-document", + IPP_TAG_ZERO)) == NULL) + { + respond_ipp(client, IPP_BAD_REQUEST, + "Missing required last-document attribute."); + httpFlush(&(client->http)); + return; + } + else if (attr->value_tag != IPP_TAG_BOOLEAN || attr->num_values != 1 || + !attr->values[0].boolean) + { + respond_unsupported(client, attr); + httpFlush(&(client->http)); + return; + } + + /* + * Validate document attributes... + */ + + if (!valid_doc_attributes(client)) + { + httpFlush(&(client->http)); + return; + } + + /* + * Do we have a file to print? + */ + + if (client->http.state == HTTP_POST_RECV) + { + respond_ipp(client, IPP_BAD_REQUEST, + "Unexpected document data following request."); + return; + } + + /* + * Do we have a document URI? + */ + + if ((uri = ippFindAttribute(client->request, "document-uri", + IPP_TAG_URI)) == NULL) + { + respond_ipp(client, IPP_BAD_REQUEST, "Missing document-uri."); + return; + } + + if (uri->num_values != 1) + { + respond_ipp(client, IPP_BAD_REQUEST, "Too many document-uri values."); + return; + } + + uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, + scheme, sizeof(scheme), userpass, + sizeof(userpass), hostname, sizeof(hostname), + &port, resource, sizeof(resource)); + if (uri_status < HTTP_URI_OK) + { + respond_ipp(client, IPP_BAD_REQUEST, "Bad document-uri: %s", + uri_status_strings[uri_status - HTTP_URI_OVERFLOW]); + return; + } + + if (strcmp(scheme, "file") && +#ifdef HAVE_SSL + strcmp(scheme, "https") && +#endif /* HAVE_SSL */ + strcmp(scheme, "http")) + { + respond_ipp(client, IPP_URI_SCHEME, "URI scheme \"%s\" not supported.", + scheme); + return; + } + + if (!strcmp(scheme, "file") && access(resource, R_OK)) + { + respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s", + strerror(errno)); + return; + } + + /* + * Get the document format for the job... + */ + + _cupsRWLockWrite(&(client->printer->rwlock)); + + if ((attr = ippFindAttribute(job->attrs, "document-format", + IPP_TAG_MIMETYPE)) != NULL) + job->format = attr->values[0].string.text; + else + job->format = "application/octet-stream"; + + /* + * Create a file for the request data... + */ + + if (!_cups_strcasecmp(job->format, "image/jpeg")) + snprintf(filename, sizeof(filename), "%s/%d.jpg", + client->printer->directory, job->id); + else if (!_cups_strcasecmp(job->format, "image/png")) + snprintf(filename, sizeof(filename), "%s/%d.png", + client->printer->directory, job->id); + else if (!_cups_strcasecmp(job->format, "application/pdf")) + snprintf(filename, sizeof(filename), "%s/%d.pdf", + client->printer->directory, job->id); + else if (!_cups_strcasecmp(job->format, "application/postscript")) + snprintf(filename, sizeof(filename), "%s/%d.ps", + client->printer->directory, job->id); + else + snprintf(filename, sizeof(filename), "%s/%d.prn", + client->printer->directory, job->id); + + job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + _cupsRWUnlock(&(client->printer->rwlock)); + + if (job->fd < 0) + { + job->state = IPP_JOB_ABORTED; + + respond_ipp(client, IPP_INTERNAL_ERROR, + "Unable to create print file: %s", strerror(errno)); + return; + } + + if (!strcmp(scheme, "file")) + { + if ((infile = open(resource, O_RDONLY)) < 0) + { + respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s", + strerror(errno)); + return; + } + + do + { + if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 && + (errno == EAGAIN || errno == EINTR)) + bytes = 1; + else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes) + { + int error = errno; /* Write error */ + + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + close(infile); + + respond_ipp(client, IPP_INTERNAL_ERROR, + "Unable to write print file: %s", strerror(error)); + return; + } + } + while (bytes > 0); + + close(infile); + } + else + { +#ifdef HAVE_SSL + if (port == 443 || !strcmp(scheme, "https")) + encryption = HTTP_ENCRYPT_ALWAYS; + else +#endif /* HAVE_SSL */ + encryption = HTTP_ENCRYPT_IF_REQUESTED; + + if ((http = httpConnectEncrypt(hostname, port, encryption)) == NULL) + { + respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, + "Unable to connect to %s: %s", hostname, + cupsLastErrorString()); + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + return; + } + + httpClearFields(http); + httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en"); + if (httpGet(http, resource)) + { + respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s", + strerror(errno)); + + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + httpClose(http); + return; + } + + while ((status = httpUpdate(http)) == HTTP_CONTINUE); + + if (status != HTTP_OK) + { + respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to GET URI: %s", + httpStatus(status)); + + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + httpClose(http); + return; + } + + while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0) + { + if (write(job->fd, buffer, bytes) < bytes) + { + int error = errno; /* Write error */ + + job->state = IPP_JOB_ABORTED; + + close(job->fd); + job->fd = -1; + + unlink(filename); + httpClose(http); + + respond_ipp(client, IPP_INTERNAL_ERROR, + "Unable to write print file: %s", strerror(error)); + return; + } + } + + httpClose(http); + } + + if (close(job->fd)) + { + int error = errno; /* Write error */ + + job->state = IPP_JOB_ABORTED; + job->fd = -1; + + unlink(filename); + + respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s", + strerror(error)); + return; + } + + _cupsRWLockWrite(&(client->printer->rwlock)); + + job->fd = -1; + job->filename = strdup(filename); + job->state = IPP_JOB_PENDING; + + _cupsRWUnlock(&(client->printer->rwlock)); + + /* + * Process the job... + */ + +#if 0 + if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job)) + { + job->state = IPP_JOB_ABORTED; + respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job."); + return; + } + +#else + process_job(job); #endif /* 0 */ + /* + * Return the job info... + */ + + respond_ipp(client, IPP_OK, NULL); + + ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); + cupsArrayAdd(ra, "job-id"); + cupsArrayAdd(ra, "job-state"); + cupsArrayAdd(ra, "job-state-reasons"); + cupsArrayAdd(ra, "job-uri"); + + copy_job_attributes(client, job, ra); + cupsArrayDelete(ra); +} + /* * 'ipp_validate_job()' - Validate job creation attributes. @@ -3010,6 +4013,8 @@ ipp_send_document(_ipp_client_t *client)/* I - Client */ static void ipp_validate_job(_ipp_client_t *client) /* I - Client */ { + if (valid_job_attributes(client)) + respond_ipp(client, IPP_OK, NULL); } @@ -3381,7 +4386,7 @@ process_http(_ipp_client_t *client) /* I - Client connection */ if (state == IPP_ERROR) { fprintf(stderr, "%s IPP read error (%s).\n", client->http.hostname, - ippOpString(client->request->request.op.operation_id)); + cupsLastErrorString()); respond_http(client, HTTP_BAD_REQUEST, NULL, 0); return (0); } @@ -3414,7 +4419,7 @@ process_ipp(_ipp_client_t *client) /* I - Client */ ipp_attribute_t *uri; /* Printer URI attribute */ - debug_attributes("Request", client->request); + debug_attributes("Request", client->request, 1); /* * First build an empty response message for this request... @@ -3571,10 +4576,26 @@ process_ipp(_ipp_client_t *client) /* I - Client */ ipp_print_job(client); break; + case IPP_PRINT_URI : + ipp_print_uri(client); + break; + case IPP_VALIDATE_JOB : ipp_validate_job(client); break; + case IPP_CREATE_JOB : + ipp_create_job(client); + break; + + case IPP_SEND_DOCUMENT : + ipp_send_document(client); + break; + + case IPP_SEND_URI : + ipp_send_uri(client); + break; + case IPP_CANCEL_JOB : ipp_cancel_job(client); break; @@ -3885,7 +4906,7 @@ respond_http(_ipp_client_t *client, /* I - Client */ * Send an IPP response... */ - debug_attributes("Response", client->response); + debug_attributes("Response", client->response, 2); client->http.data_encoding = HTTP_ENCODE_LENGTH; client->http.data_remaining = (off_t)ippLength(client->response); @@ -3949,6 +4970,24 @@ respond_ipp(_ipp_client_t *client, /* I - Client */ /* + * 'respond_unsupported()' - Respond with an unsupported attribute. + */ + +static void +respond_unsupported( + _ipp_client_t *client, /* I - Client */ + ipp_attribute_t *attr) /* I - Atribute */ +{ + if (!client->response->attrs) + respond_ipp(client, IPP_ATTRIBUTES, "Unsupported %s %s%s value.", + attr->name, attr->num_values > 1 ? "1setOf " : "", + ippTagString(attr->value_tag)); + + copy_attribute(client->response, attr, IPP_TAG_UNSUPPORTED_GROUP, 0); +} + + +/* * 'run_printer()' - Run the printer service. */ @@ -4070,37 +5109,27 @@ usage(int status) /* O - Exit status */ /* - * 'valid_job_attributes()' - Determine whether the job attributes are valid. + * 'valid_doc_attributes()' - Determine whether the document attributes are + * valid. * - * When one or more job attributes are invalid, this function adds a suitable - * response and attributes to the unsupported group. + * When one or more document attributes are invalid, this function adds a + * suitable response and attributes to the unsupported group. */ static int /* O - 1 if valid, 0 if not */ -valid_job_attributes( +valid_doc_attributes( _ipp_client_t *client) /* I - Client */ { int i; /* Looping var */ ipp_attribute_t *attr, /* Current attribute */ *supported; /* document-format-supported */ const char *format = NULL; /* document-format value */ - int valid = 1; /* Valid attributes? */ /* * Check operation attributes... */ -#define respond_unsupported(client, attr) \ - if (valid) \ - { \ - respond_ipp(client, IPP_ATTRIBUTES, "Unsupported %s %s%s value.", \ - attr->name, attr->num_values > 1 ? "1setOf " : "", \ - ippTagString(attr->value_tag)); \ - valid = 0; \ - } \ - copy_attribute(client->response, attr, IPP_TAG_UNSUPPORTED_GROUP, 0) - if ((attr = ippFindAttribute(client->request, "compression", IPP_TAG_ZERO)) != NULL) { @@ -4110,12 +5139,12 @@ valid_job_attributes( if (attr->num_values != 1 || attr->value_tag != IPP_TAG_KEYWORD || strcmp(attr->values[0].string.text, "none")) - { respond_unsupported(client, attr); - } else - fprintf(stderr, "%s Print-Job compression=\"%s\"\n", - client->http.hostname, attr->values[0].string.text); + fprintf(stderr, "%s %s compression=\"%s\"\n", + client->http.hostname, + ippOpString(client->request->request.op.operation_id), + attr->values[0].string.text); } /* @@ -4126,9 +5155,7 @@ valid_job_attributes( IPP_TAG_ZERO)) != NULL) { if (attr->num_values != 1 || attr->value_tag != IPP_TAG_MIMETYPE) - { respond_unsupported(client, attr); - } else { format = attr->values[0].string.text; @@ -4142,7 +5169,8 @@ valid_job_attributes( format = "application/octet-stream"; if (!strcmp(format, "application/octet-stream") && - client->request->request.op.operation_id != IPP_VALIDATE_JOB) + (client->request->request.op.operation_id == IPP_PRINT_JOB || + client->request->request.op.operation_id == IPP_SEND_DOCUMENT)) { /* * Auto-type the file using the first 4 bytes of the file... @@ -4169,8 +5197,8 @@ valid_job_attributes( ippOpString(client->request->request.op.operation_id), format); if (!attr) - ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, - "document-format", NULL, format); + attr = ippAddString(client->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, + "document-format", NULL, format); else { _cupsStrFree(attr->values[0].string.text); @@ -4178,7 +5206,8 @@ valid_job_attributes( } } - if ((supported = ippFindAttribute(client->printer->attrs, + if (client->request->request.op.operation_id != IPP_CREATE_JOB && + (supported = ippFindAttribute(client->printer->attrs, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL) { @@ -4186,12 +5215,38 @@ valid_job_attributes( if (!_cups_strcasecmp(format, supported->values[i].string.text)) break; - if (i >= supported->num_values) - { + if (i >= supported->num_values && attr) respond_unsupported(client, attr); - } } + return (!client->response->attrs || + !client->response->attrs->next || + !client->response->attrs->next->next); +} + + +/* + * 'valid_job_attributes()' - Determine whether the job attributes are valid. + * + * When one or more job attributes are invalid, this function adds a suitable + * response and attributes to the unsupported group. + */ + +static int /* O - 1 if valid, 0 if not */ +valid_job_attributes( + _ipp_client_t *client) /* I - Client */ +{ + int i; /* Looping var */ + ipp_attribute_t *attr, /* Current attribute */ + *supported; /* xxx-supported attribute */ + + + /* + * Check operation attributes... + */ + + valid_doc_attributes(client); + /* * Check the various job template attributes... */ @@ -4371,10 +5426,12 @@ valid_job_attributes( } } - return (valid); + return (!client->response->attrs || + !client->response->attrs->next || + !client->response->attrs->next->next); } /* - * End of "$Id: ippserver.c 9793 2011-05-20 03:49:49Z mike $". + * End of "$Id: ippserver.c 10031 2011-09-30 05:24:10Z mike $". */ |