Index: cups-lpd.c =================================================================== --- cups-lpd.c (revision 8632) +++ cups-lpd.c (working copy) @@ -16,8 +16,10 @@ * * main() - Process an incoming LPD request... * create_job() - Create a new print job. + * hold_job() - Hold a created print job. + * release_job() - Release a held print job. + * send_doc() - Send a file to be added to a print job. * get_printer() - Get the named printer and its options. - * print_file() - Add a file to the current job. * recv_print_job() - Receive a print job from the client. * remove_jobs() - Cancel one or more jobs. * send_state() - Send the queue state. @@ -83,12 +85,14 @@ static int create_job(http_t *http, const char *dest, const char *title, const char *docname, const char *user, int num_options, cups_option_t *options); +static int hold_job(http_t * http, int job_id, const char * user); +static int release_job(http_t * http, int job_id, const char * user); +static int send_doc(http_t * http, int job_id, const char * file, + const char * docname, const char * user, + const char * format, int last); static int get_printer(http_t *http, const char *name, char *dest, int destsize, cups_option_t **options, int *accepting, int *shared, ipp_pstate_t *state); -static int print_file(http_t *http, int id, const char *filename, - const char *docname, const char *user, - const char *format, int last); static int recv_print_job(const char *name, int num_defaults, cups_option_t *defaults); static int remove_jobs(const char *name, const char *agent, @@ -370,7 +374,7 @@ NULL, title); if (docname[0]) - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", NULL, docname); cupsEncodeOptions(request, num_options, options); @@ -414,8 +418,287 @@ return (id); } +/* + * 'hold_job()' - Hold a print job, using an IPP_HOLD_JOB call. + */ +static int /* O - Success or -1 on error */ +hold_job(http_t * http, /* I - HTTP connection */ + int job_id, /* I - The job ID to hold */ + const char * user) /* I - The owner of the job */ +{ + ipp_t * request; /* IPP request */ + ipp_t * response; /* IPP response */ + char uri[HTTP_MAX_URI]; /* Printer URI */ + cups_lang_t * language; /* Language to use */ + int rtn = -1; /* Return value -1: Error 0: Success */ + + /* + * Build a standard CUPS URI for the printer and fill the standard IPP + * attributes... + */ + + if ((request = ippNew()) == NULL) + { + syslog(LOG_ERR, "Unable to create request: %s", strerror(errno)); + httpClose(http); + return (-1); + } + + request->request.op.operation_id = IPP_HOLD_JOB; + request->request.op.request_id = 0x8094; + + snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id); + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, + language != NULL ? language->language : "C"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", + NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, user); + + /* + * Do the request... + */ + + snprintf(uri, sizeof(uri), "/jobs/%d", job_id); + + response = cupsDoRequest(http, request, uri); + + if (response == NULL) + { + syslog(LOG_ERR, "Unable to hold Job %d, " + "null response to cupsDoRequest, cupsLastError() returns %s", + job_id, ippErrorString(cupsLastError())); + rtn = -1; + + } + else if (response->request.status.status_code > IPP_OK_CONFLICT) + { + syslog(LOG_ERR, "Unable to hold Job %d, " + "response->request.status.status_code = %s", + job_id, ippErrorString(response->request.status.status_code)); + rtn = -1; + + } + else + { + rtn = 0; + } + + if (response != NULL) + { + ippDelete(response); + } + + cupsLangFree(language); + + return rtn; +} + /* + * 'release_job()' - Release a print job, using an IPP_RELEASE_JOB + * call. + */ + +static int /* O - Success or -1 on error */ +release_job(http_t * http, /* I - HTTP connection */ + int job_id, /* I - The job ID to release */ + const char * user) /* I - The owner of the job */ +{ + ipp_t * request; /* IPP request */ + ipp_t * response; /* IPP response */ + char uri[HTTP_MAX_URI]; /* Printer URI */ + cups_lang_t * language; /* Language to use */ + int rtn = -1; /* Return value -1: Error 0: Success */ + + /* + * Build a standard CUPS URI for the printer and fill the standard IPP + * attributes... + */ + + if ((request = ippNew()) == NULL) + { + syslog(LOG_ERR, "Unable to create request: %s", strerror(errno)); + httpClose(http); + return (-1); + } + + request->request.op.operation_id = IPP_RELEASE_JOB; + request->request.op.request_id = 0x8095; + + snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id); + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, + language != NULL ? language->language : "C"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", + NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, user); + + /* + * Do the request... + */ + + snprintf(uri, sizeof(uri), "/jobs/%d", job_id); + + response = cupsDoRequest(http, request, uri); + + if (response == NULL) + { + syslog(LOG_ERR, "Unable to release Job %d, " + "null response to cupsDoRequest, cupsLastError() returns %s", + job_id, ippErrorString(cupsLastError())); + rtn = -1; + + } + else if (response->request.status.status_code > IPP_OK_CONFLICT) + { + syslog(LOG_ERR, "Unable to release Job %d, " + "response->request.status.status_code = %s", + job_id, ippErrorString(response->request.status.status_code)); + rtn = -1; + + } + else + { + rtn = 0; + } + + if (response != NULL) + { + ippDelete(response); + } + + cupsLangFree(language); + + return rtn; +} + +/* + * 'send_doc()' - Send a file to the cupsd using an IPP_SEND_DOCUMENT + * call. + */ + +static int /* O - Success or -1 on error */ +send_doc(http_t * http, /* I - HTTP connection */ + int job_id, /* I - The CUPS job ID to add the file to */ + const char * file, /* I - File to send to the cupsd */ + const char * docname, /* I - Name of job file */ + const char * user, /* I - The owner of the job */ + const char * format, /* I - Document format */ + int last) /* I - Is this this last file in + the job? 1 means yes */ +{ + ipp_t * request; /* IPP request */ + ipp_t * response; /* IPP response */ + char uri[HTTP_MAX_URI]; /* Printer URI */ + cups_lang_t * language; /* Language to use */ + int rtn = -1; /* Return value -1: Error 0: Success */ + + /* + * Build a standard CUPS URI for the printer and fill the standard IPP + * attributes... + */ + + if ((request = ippNew()) == NULL) + { + syslog(LOG_ERR, "Unable to create request: %s", strerror(errno)); + httpClose(http); + return (-1); + } + + request->request.op.operation_id = IPP_SEND_DOCUMENT; + request->request.op.request_id = 0x8092; + + snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id); + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, + language != NULL ? language->language : "C"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", + NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, user); + + if (docname) + { + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "document-name", NULL, docname); + } + + if (format) + { + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, + "document-format", NULL, format); + } + + if (last) { + ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1); + } + + /* + * Do the request... + */ + + snprintf(uri, sizeof(uri), "/jobs/%d", job_id); + + response = cupsDoFileRequest(http, request, uri, file); + + if (response == NULL) + { + syslog(LOG_ERR, "Unable to print file (Job %d), " + "null response to cupsDoRequest, cupsLastError() returns %s", + job_id, ippErrorString(cupsLastError())); + rtn = -1; + + } + else if (response->request.status.status_code > IPP_OK_CONFLICT) + { + syslog(LOG_ERR, "Unable to print file (Job %d), " + "response->request.status.status_code = %s", + job_id, ippErrorString(response->request.status.status_code)); + rtn = -1; + + } + else + { + rtn = 0; + } + + if (response != NULL) { + ippDelete(response); + } + + cupsLangFree(language); + + return rtn; +} + +/* * 'get_printer()' - Get the named printer and its options. */ @@ -697,9 +980,9 @@ *shared = 0; prefsurl = CFURLCreateFromFileSystemRepresentation( - kCFAllocatorDefault, - (const UInt8 *)printerprefsfile, - (CFIndex)strlen(printerprefsfile), + kCFAllocatorDefault, + (const UInt8 *)printerprefsfile, + (CFIndex)strlen(printerprefsfile), false); if (prefsurl) { @@ -707,7 +990,7 @@ prefsurl, &xmldata, NULL, NULL, NULL)) { - plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, xmldata, + plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, xmldata, kCFPropertyListImmutable, NULL); if (plist) @@ -724,7 +1007,7 @@ if (lprqarray) { - queueid = CFStringCreateWithCString(CFAllocatorGetDefault(), + queueid = CFStringCreateWithCString(CFAllocatorGetDefault(), dest, kCFStringEncodingUTF8); @@ -816,65 +1099,6 @@ /* - * 'print_file()' - Add a file to the current job. - */ - -static int /* O - 0 on success, -1 on failure */ -print_file(http_t *http, /* I - HTTP connection */ - int id, /* I - Job ID */ - const char *filename, /* I - File to print */ - const char *docname, /* I - document-name */ - const char *user, /* I - requesting-user-name */ - const char *format, /* I - document-format */ - int last) /* I - 1 = last file in job */ -{ - ipp_t *request; /* IPP request */ - char uri[HTTP_MAX_URI]; /* Printer URI */ - - - /* - * Setup the Send-Document request... - */ - - request = ippNewRequest(IPP_SEND_DOCUMENT); - - snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", id); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); - - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "requesting-user-name", NULL, user); - - if (docname) - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, - "document-name", NULL, docname); - - if (format) - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, - "document-format", NULL, format); - - if (last) - ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1); - - /* - * Do the request... - */ - - snprintf(uri, sizeof(uri), "/jobs/%d", id); - - ippDelete(cupsDoFileRequest(http, request, uri, filename)); - - if (cupsLastError() > IPP_OK_CONFLICT) - { - syslog(LOG_ERR, "Unable to send document - %s", cupsLastErrorString()); - - return (-1); - } - - return (0); -} - - -/* * 'recv_print_job()' - Receive a print job from the client. */ @@ -891,6 +1115,8 @@ FILE *fp; /* File pointer */ char filename[1024]; /* Temporary filename */ int bytes; /* Bytes received */ + int bytestotal; /* Total bytes received + for incoming file */ char line[256], /* Line from file/stdin */ command, /* Command from line */ *count, /* Number of bytes */ @@ -912,6 +1138,8 @@ int docnumber, /* Current document number */ doccount; /* Count of documents */ + fp = NULL; + id = 0; /* * Connect to the server... @@ -1073,25 +1301,27 @@ /* * Copy the data or control file from the client... */ - + bytestotal = 0; for (i = atoi(count); i > 0; i -= bytes) - { - if (i > sizeof(line)) - bytes = sizeof(line); - else - bytes = i; + { + if (i > sizeof(line)) + bytes = sizeof(line); + else + bytes = i; - if ((bytes = fread(line, 1, bytes, stdin)) > 0) - bytes = write(fd, line, bytes); + if ((bytes = fread(line, 1, bytes, stdin)) > 0) + bytes = write(fd, line, bytes); - if (bytes < 1) - { - syslog(LOG_ERR, "Error while reading file - %s", - strerror(errno)); - status = 1; - break; + if (bytes < 1) + { + syslog(LOG_ERR, "Error while reading file - %s", + strerror(errno)); + status = 1; + break; + } else { + bytestotal += bytes; + } } - } /* * Read trailing nul... @@ -1114,81 +1344,78 @@ } /* - * Close the file and send an acknowledgement... + * Close the file */ close(fd); - putchar(status); + if (!status && command == 0x3) + { + /* + * syslog the size of the data we just received + */ - if (status) - break; - } + syslog (LOG_INFO, "file_size = %d (%2f MB)", + bytestotal, (float)bytestotal/1048576); - if (!status) - { - /* - * Process the control file and print stuff... - */ - - if ((fp = fopen(control, "rb")) == NULL) - status = 1; - else + } + else if (!status && command == 0x2) { - /* - * Copy the default options... - */ + /* + * If we just received a control file, then process it right now + * to get the Job Information, and then ask cupsd to create a job + * BEFORE we receive the print data. + */ - for (i = 0; i < num_defaults; i ++) - num_options = cupsAddOption(defaults[i].name, - defaults[i].value, - num_options, &options); + if ((fp = fopen(control, "rb")) == NULL) { + status = 1; + } else { - /* - * Grab the job information... - */ + /* + * Grab the job information first... + */ - title[0] = '\0'; - user[0] = '\0'; - docname[0] = '\0'; - doccount = 0; + title[0] = '\0'; + user[0] = '\0'; + docname[0] = '\0'; + doccount = 0; - while (smart_gets(line, sizeof(line), fp) != NULL) - { - /* - * Process control lines... - */ + while (smart_gets(line, sizeof(line), fp) != NULL) + { + /* + * Process control lines... + */ - switch (line[0]) - { + switch (line[0]) + { case 'J' : /* Job name */ - strlcpy(title, line + 1, sizeof(title)); - break; + strlcpy(title, line + 1, sizeof(title)); + break; - case 'N' : /* Document name */ - strlcpy(docname, line + 1, sizeof(docname)); - break; + case 'N' : /* Document name */ + strlcpy(docname, line + 1, sizeof(docname)); + break; case 'P' : /* User identification */ - strlcpy(user, line + 1, sizeof(user)); - break; + strlcpy(user, line + 1, sizeof(user)); + break; case 'L' : /* Print banner page */ - /* - * If a banner was requested and it's not overridden by a - * command line option and the destination's default is none - * then add the standard banner... - */ + /* + * If a banner was requested and it's not overridden by a + * command line option and the destination's default is none + * then add the standard banner... + */ - if (cupsGetOption("job-sheets", num_defaults, defaults) == NULL && - ((job_sheets = cupsGetOption("job-sheets", num_options, - options)) == NULL || - !strcmp(job_sheets, "none,none"))) + if (cupsGetOption("job-sheets", num_defaults, defaults) == NULL && + ((job_sheets = cupsGetOption("job-sheets", num_options, + options)) == NULL || + !strcmp(job_sheets, "none,none"))) { num_options = cupsAddOption("job-sheets", "standard", - num_options, &options); + num_options, &options); } - break; + break; case 'c' : /* Plot CIF file */ case 'd' : /* Print DVI file */ @@ -1201,116 +1428,196 @@ case 'r' : /* File to print with FORTRAN carriage control */ case 't' : /* Print troff output file */ case 'v' : /* Print raster file */ - doccount ++; + doccount ++; - if (line[0] == 'l' && - !cupsGetOption("document-format", num_options, options)) - num_options = cupsAddOption("raw", "", num_options, &options); + if (line[0] == 'l' && + !cupsGetOption("document-format", num_options, options)) + num_options = cupsAddOption("raw", "", num_options, &options); - if (line[0] == 'p') - num_options = cupsAddOption("prettyprint", "", num_options, - &options); - break; + if (line[0] == 'p') + num_options = cupsAddOption("prettyprint", "", num_options, + &options); + break; + } + + if (status) + break; } - if (status) + /* + * We've got the job info now (though note that BSD apparent + * can send multiple command files, so this code may not be + * complete) so create a cups-job now, at the earliest + * opportunity + */ + + /* + * Check that we have a username... + */ + + if (!user[0]) + { + syslog(LOG_WARNING, "No username specified by client! " + "Using \"anonymous\"..."); + strcpy(user, "anonymous"); + } + + id = create_job(http, queue, title, docname, user, num_options, options); + if (id < 1) + { + syslog(LOG_ERR, "Error: cupsd didn't create a job to print the data."); + status = 1; break; + } + if (hold_job(http, id, user)) + { + syslog(LOG_ERR, "Error: Couldn't hold Job %d while receiving data.", id); + status = 1; + break; + } } + } /* if (command == 0x2) */ - /* - * Check that we have a username... - */ + /* + * Send acknowledgement + */ - if (!user[0]) - { - syslog(LOG_WARNING, "No username specified by client! " - "Using \"anonymous\"..."); - strcpy(user, "anonymous"); - } + putchar(status); - /* - * Create the job... - */ + if (status) + break; + } - if ((id = create_job(http, dest, title, docname, user, num_options, - options)) < 0) - status = 1; - else - { - /* - * Then print the job files... - */ + /* + * After this point, it's impossible to send an error back to the + * lpd client code (using putchar(status)) because the lpd-client + * code has already received its acknowledgement. + */ - rewind(fp); + if (!status) + { + /* + * Process the control file and print stuff... + */ - docname[0] = '\0'; - docnumber = 0; + /* If control file is open already, close it. */ + if (fp) + fclose(fp); - while (smart_gets(line, sizeof(line), fp) != NULL) + if ((fp = fopen(control, "rb")) == NULL) + status = 1; + else + { + + /* + * Then print the jobs... + */ + + rewind(fp); + + docname[0] = '\0'; + docnumber = 0; + + while (smart_gets(line, sizeof(line), fp) != NULL) + { + /* + * Process control lines... + */ + + switch (line[0]) { - /* - * Process control lines... - */ + case 'N' : /* Document name */ + strlcpy(docname, line + 1, sizeof(docname)); + break; - switch (line[0]) + case 'c' : /* Plot CIF file */ + case 'd' : /* Print DVI file */ + case 'f' : /* Print formatted file */ + case 'g' : /* Plot file */ + case 'l' : /* Print file leaving control characters (raw) */ + case 'n' : /* Print ditroff output file */ + case 'o' : /* Print PostScript output file */ + case 'p' : /* Print file with 'pr' format (prettyprint) */ + case 'r' : /* File to print with FORTRAN carriage control */ + case 't' : /* Print troff output file */ + case 'v' : /* Print raster file */ + /* + * Check that we have a username... + */ + + if (!user[0]) { - case 'N' : /* Document name */ - strlcpy(docname, line + 1, sizeof(docname)); - break; + syslog(LOG_WARNING, "No username specified by client! " + "Using \"anonymous\"..."); + strcpy(user, "anonymous"); + } - case 'c' : /* Plot CIF file */ - case 'd' : /* Print DVI file */ - case 'f' : /* Print formatted file */ - case 'g' : /* Plot file */ - case 'l' : /* Print file leaving control characters (raw) */ - case 'n' : /* Print ditroff output file */ - case 'o' : /* Print PostScript output file */ - case 'p' : /* Print file with 'pr' format (prettyprint) */ - case 'r' : /* File to print with FORTRAN carriage control */ - case 't' : /* Print troff output file */ - case 'v' : /* Print raster file */ - /* - * Figure out which file we are printing... - */ + /* + * Figure out which file we are printing... + */ - for (i = 0; i < num_data; i ++) - if (!strcmp(data[i], line + 1)) - break; + for (i = 0; i < num_data; i ++) + if (strcmp(data[i], line + 1) == 0) + break; - if (i >= num_data) - { - status = 1; - break; - } + if (i >= num_data) + { + status = 1; + break; + } - /* - * Send the print file... - */ + if (id == 0) + { + syslog(LOG_WARNING, + "%s: Job ID is 0, meaning a job was not created when the " + "control file came in but the data has been read. " + "Cannot print the data.", + __FUNCTION__); + status = 1; + break; + } - docnumber ++; + /* + * Send the "send print data" request... + */ - if (print_file(http, id, temp[i], docname, user, - cupsGetOption("document-format", num_options, - options), - docnumber == doccount)) - status = 1; - else - status = 0; + docnumber ++; - break; + if (send_doc(http, id, temp[i], docname, user, NULL, 1)) + { + syslog(LOG_ERR, "Error sending print data as Job %d.", id); + status = 1; } + else + { + if (release_job(http, id, user)) + { + syslog(LOG_ERR, "Error releasing Job %d", id); + status = 1; + } + else + { + syslog(LOG_INFO, "cupsd accepted file as Job %d", id); + status = 0; + } + } - if (status) - break; - } - } + cupsFreeOptions(num_options, options); + break; + } /* switch */ + if (status) + break; + } /* while */ + + if (status) + syslog(LOG_WARNING, "%s: An error occured which will NOT have " + "been communicated to the sending LPD client.", __FUNCTION__); + fclose(fp); } } - cupsFreeOptions(num_options, options); - httpClose(http); /*