• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  * Copyright (C) 2016 Mopria Alliance, Inc.
4  * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include "ipp_print.h"
20 #include <math.h>
21 #include "ipphelper.h"
22 #include "wprint_debug.h"
23 
24 #include "plugins/media.h"
25 
26 #define TAG "ipp_print"
27 
28 static status_t _init(const ifc_print_job_t *this_p, const char *printer_address, int port,
29         const char *printer_uri, bool use_secure_uri);
30 
31 static status_t _validate_job(const ifc_print_job_t *this_p, wprint_job_params_t *job_params);
32 
33 static status_t _start_job(const ifc_print_job_t *this_p, const wprint_job_params_t *job_params);
34 
35 static int _send_data(const ifc_print_job_t *this_p, const char *buffer, size_t length);
36 
37 static status_t _end_job(const ifc_print_job_t *this_p);
38 
39 static void _destroy(const ifc_print_job_t *this_p);
40 
41 static const ifc_print_job_t _print_job_ifc = {
42         .init = _init, .validate_job = _validate_job, .start_job = _start_job,
43         .send_data = _send_data, .end_job = _end_job, .destroy = _destroy, .enable_timeout = NULL,
44 };
45 
46 /*
47  * Struct for handling an ipp print job
48  */
49 typedef struct {
50     http_t *http;
51     char printer_uri[1024];
52     char http_resource[1024];
53     http_status_t status;
54     ifc_print_job_t ifc;
55     const char *useragent;
56 } ipp_print_job_t;
57 
58 /*
59  * Returns a print job handle for an ipp print job
60  */
ipp_get_print_ifc(const ifc_wprint_t * wprint_ifc)61 const ifc_print_job_t *ipp_get_print_ifc(const ifc_wprint_t *wprint_ifc) {
62     LOGD("ipp_get_print_ifc: Enter");
63     ipp_print_job_t *ipp_job = (ipp_print_job_t *) malloc(sizeof(ipp_print_job_t));
64 
65     if (ipp_job == NULL) {
66         return NULL;
67     }
68 
69     memset(ipp_job, 0, sizeof(ipp_print_job_t));
70     ipp_job->status = HTTP_CONTINUE;
71 
72     memcpy(&ipp_job->ifc, &_print_job_ifc, sizeof(ifc_print_job_t));
73 
74     return &ipp_job->ifc;
75 }
76 
_init(const ifc_print_job_t * this_p,const char * printer_address,int port,const char * printer_uri,bool use_secure_uri)77 static status_t _init(const ifc_print_job_t *this_p, const char *printer_address, int port,
78         const char *printer_uri, bool use_secure_uri) {
79     LOGD("_init: Enter");
80     ipp_print_job_t *ipp_job;
81     const char *ipp_scheme;
82 
83     if (this_p == NULL) {
84         return ERROR;
85     }
86 
87     ipp_job = IMPL(ipp_print_job_t, ifc, this_p);
88     if (ipp_job->http != NULL) {
89         httpClose(ipp_job->http);
90     }
91 
92     if ((printer_uri == NULL) || (strlen(printer_uri) == 0)) {
93         printer_uri = DEFAULT_IPP_URI_RESOURCE;
94     }
95 
96     int ippPortNumber = ((port == IPP_PORT) ? ippPort() : port);
97     LOGD("Normal URI for %s:%d", printer_address, ippPortNumber);
98     ipp_scheme = (use_secure_uri) ? IPPS_PREFIX : IPP_PREFIX;
99 
100     httpAssembleURIf(HTTP_URI_CODING_ALL, ipp_job->printer_uri, sizeof(ipp_job->printer_uri),
101             ipp_scheme, NULL, printer_address, ippPortNumber, "%s", printer_uri);
102     getResourceFromURI(ipp_job->printer_uri, ipp_job->http_resource, 1024);
103     if (use_secure_uri) {
104         ipp_job->http = httpConnect2(printer_address, ippPortNumber, NULL, AF_UNSPEC,
105                 HTTP_ENCRYPTION_ALWAYS, 1, HTTP_TIMEOUT_MILLIS, NULL);
106 
107         // If ALWAYS doesn't work, fall back to REQUIRED
108         if (ipp_job->http == NULL) {
109             ipp_job->http = httpConnect2(printer_address, ippPortNumber, NULL, AF_UNSPEC,
110                     HTTP_ENCRYPTION_REQUIRED, 1, HTTP_TIMEOUT_MILLIS, NULL);
111         }
112     } else {
113         ipp_job->http = httpConnect2(printer_address, ippPortNumber, NULL, AF_UNSPEC,
114                 HTTP_ENCRYPTION_IF_REQUESTED, 1, HTTP_TIMEOUT_MILLIS, NULL);
115     }
116 
117     httpSetTimeout(ipp_job->http, DEFAULT_IPP_TIMEOUT, NULL, 0);
118 
119     return OK;
120 }
121 
_destroy(const ifc_print_job_t * this_p)122 static void _destroy(const ifc_print_job_t *this_p) {
123     LOGD("_destroy: Enter");
124     ipp_print_job_t *ipp_job;
125     if (this_p == NULL) {
126         return;
127     }
128 
129     ipp_job = IMPL(ipp_print_job_t, ifc, this_p);
130     if (ipp_job->http != NULL) {
131         httpClose(ipp_job->http);
132     }
133 
134     free(ipp_job);
135 }
136 
137 /*
138  * Outputs width, height, and name for a given media size
139  */
_get_pwg_media_size(media_size_t media_size,float * mediaWidth,float * mediaHeight,const char ** mediaSizeName)140 static void _get_pwg_media_size(media_size_t media_size, float *mediaWidth, float *mediaHeight,
141         const char **mediaSizeName) {
142     int i = 0;
143 
144     for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) {
145         if (media_size == SupportedMediaSizes[i].media_size) {
146             // Get the dimensions in 100 mm
147             if ((SupportedMediaSizes[i].WidthInMm == UNKNOWN_VALUE) ||
148                     (SupportedMediaSizes[i].HeightInMm == UNKNOWN_VALUE)) {
149                 *mediaWidth = floorf(_MI_TO_100MM(SupportedMediaSizes[i].WidthInInches));
150                 *mediaHeight = floorf(_MI_TO_100MM(SupportedMediaSizes[i].HeightInInches));
151             } else {
152                 *mediaWidth = SupportedMediaSizes[i].WidthInMm * 100;
153                 *mediaHeight = SupportedMediaSizes[i].HeightInMm * 100;
154             }
155             *mediaSizeName = (char *) SupportedMediaSizes[i].PWGName;
156 
157             LOGD("_get_pwg_media_size(): match found: %d, %s, width=%f, height=%f",
158                     media_size, SupportedMediaSizes[i].PCL6Name, *mediaWidth, *mediaHeight);
159             break;  // we found a match, so break out of loop
160         }
161     }
162 
163     if (i == SUPPORTED_MEDIA_SIZE_COUNT) {
164         // media size not found, defaulting to letter
165         LOGD("_get_pwg_media_size(): media size, %d, NOT FOUND, setting to letter", media_size);
166         _get_pwg_media_size(US_LETTER, mediaWidth, mediaHeight, mediaSizeName);
167     }
168 }
169 
170 /*
171  * Fills and returns an ipp request object with the given job parameters
172  */
_fill_job(int ipp_op,char * printer_uri,const wprint_job_params_t * job_params)173 static ipp_t *_fill_job(int ipp_op, char *printer_uri, const wprint_job_params_t *job_params) {
174     LOGD("_fill_job: Enter");
175     ipp_t *request = NULL; // IPP request object
176     ipp_attribute_t *attrptr; // Attribute pointer
177     ipp_t *col[2];
178     int col_index = -1;
179 
180     if (job_params == NULL) return NULL;
181 
182     request = ippNewRequest(ipp_op);
183     if (request == NULL) {
184         return request;
185     }
186 
187     if (set_ipp_version(request, printer_uri, NULL, IPP_VERSION_RESOLVED) != 0) {
188         ippDelete(request);
189         return NULL;
190     }
191     bool is_2_0_capable = job_params->ipp_2_0_supported;
192     bool is_ePCL_ipp_capable = job_params->epcl_ipp_supported;
193 
194     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
195             printer_uri);
196 
197     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL,
198             job_params->job_originating_user_name);
199     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, job_params->job_name);
200 
201     // Fields for Document source application and source OS
202     bool is_doc_format_details_supported = (
203             job_params->accepts_app_name ||
204                     job_params->accepts_app_version ||
205                     job_params->accepts_os_name ||
206                     job_params->accepts_os_version);
207 
208     if (is_doc_format_details_supported) {
209         ipp_t *document_format_details = ippNew();
210         if (job_params->accepts_app_name) {
211             ippAddString(document_format_details, IPP_TAG_OPERATION, IPP_TAG_NAME,
212                     "document-source-application-name", NULL, g_appName);
213         }
214         if (job_params->accepts_app_version) {
215             ippAddString(document_format_details, IPP_TAG_OPERATION, IPP_TAG_TEXT,
216                     "document-source-application-version", NULL, g_appVersion);
217         }
218         if (job_params->accepts_os_name) {
219             ippAddString(document_format_details, IPP_TAG_OPERATION, IPP_TAG_NAME,
220                     "document-source-os-name", NULL, g_osName);
221         }
222         if (job_params->accepts_os_version) {
223             char version[40];
224             sprintf(version, "%d", g_API_version);
225             ippAddString(document_format_details, IPP_TAG_OPERATION, IPP_TAG_TEXT,
226                     "document-source-os-version", NULL, version);
227         }
228 
229         ippAddCollection(request, IPP_TAG_OPERATION, "document-format-details",
230                 document_format_details);
231         ippDelete(document_format_details);
232     }
233 
234     LOGD("_fill_job: pcl_type(%d), print_format(%s)", job_params->pcl_type,
235             job_params->print_format);
236     if (strcmp(job_params->print_format, PRINT_FORMAT_PDF) == 0) {
237         if (is_2_0_capable) {
238             // document-format needs to be the very next attribute for some printers
239             ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL,
240                     PRINT_FORMAT_PDF);
241             LOGD("_fill_job: setting document-format: %s", PRINT_FORMAT_PDF);
242         } else {
243             // some earlier devices don't print pdfs when we send the other PRINT_FORMAT_PDF
244             ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL,
245                     (job_params->accepts_pclm ? PRINT_FORMAT_PCLM : PRINT_FORMAT_PDF));
246             LOGD("_fill_job: setting document-format: %s",
247                     (job_params->accepts_pclm ? PRINT_FORMAT_PCLM : PRINT_FORMAT_PDF));
248         }
249 
250         if (is_ePCL_ipp_capable) {
251             if (job_params->render_flags & RENDER_FLAG_AUTO_SCALE) {
252                 ippAddBoolean(request, IPP_TAG_JOB, "pdf-fit-to-page", 1); // true
253             }
254         }
255 
256         // Fix Orientation bug for PDF printers only.
257         if (job_params->render_flags & RENDER_FLAG_PORTRAIT_MODE) {
258             ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "orientation-requested",
259                     IPP_PRINT_ORIENTATION_PORTRAIT);
260         }
261         if (job_params->render_flags & RENDER_FLAG_LANDSCAPE_MODE) {
262             ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "orientation-requested",
263                     IPP_PRINT_ORIENTATION_LANDSCAPE);
264         }
265     } else {
266         switch (job_params->pcl_type) {
267             case PCLm:
268                 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL,
269                         PRINT_FORMAT_PCLM);
270                 LOGD("_fill_job: setting document-format: %s", PRINT_FORMAT_PCLM);
271                 if (is_ePCL_ipp_capable) {
272                     ippAddResolution(request, IPP_TAG_JOB, "pclm-source-resolution",
273                             IPP_RES_PER_INCH, job_params->pixel_units,
274                             job_params->pixel_units);
275                 }
276                 break;
277             case PCLPWG:
278                 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL,
279                         PRINT_FORMAT_PWG);
280                 LOGD("_fill_job: setting document-format: %s", PRINT_FORMAT_PWG);
281                 break;
282             default:
283                 LOGD("_fill_job: unrecognized pcl_type: %d", job_params->pcl_type);
284                 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL,
285                         PRINT_FORMAT_AUTO);
286                 break;
287         }
288 
289         if (is_ePCL_ipp_capable) {
290             ippAddBoolean(request, IPP_TAG_JOB, "margins-pre-applied", 1); // true
291         }
292     }
293 
294     // Add copies support if required and allowed
295     if (job_params->copies_supported && (strcmp(job_params->print_format, PRINT_FORMAT_PDF) == 0)) {
296         ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", job_params->num_copies);
297     }
298 
299     // Add print quality if requested
300     if (job_params->print_quality) {
301         ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
302                 job_params->print_quality);
303     }
304 
305     ippAddResolution(request, IPP_TAG_JOB, "printer-resolution", IPP_RES_PER_INCH,
306             job_params->pixel_units, job_params->pixel_units);
307     if (job_params->duplex == DUPLEX_MODE_BOOK) {
308         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_SIDES_TAG, NULL,
309                 IPP_SIDES_TWO_SIDED_LONG_EDGE);
310     } else if (job_params->duplex == DUPLEX_MODE_TABLET) {
311         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_SIDES_TAG, NULL,
312                 IPP_SIDES_TWO_SIDED_SHORT_EDGE);
313     } else {
314         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_SIDES_TAG, NULL,
315                 IPP_SIDES_ONE_SIDED);
316     }
317 
318     if (job_params->color_space == COLOR_SPACE_MONO) {
319         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_OUTPUT_MODE_TAG, NULL,
320                 IPP_OUTPUT_MODE_MONO);
321     } else {
322         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, IPP_OUTPUT_MODE_TAG, NULL,
323                 IPP_OUTPUT_MODE_COLOR);
324     }
325 
326     if (is_2_0_capable) {
327         // Not friendly to 1.1 devices.
328         if (job_params->media_tray != TRAY_SRC_AUTO_SELECT) {
329             if (job_params->media_tray == TRAY_SOURCE_PHOTO_TRAY) {
330                 col[++col_index] = ippNew();
331                 ippAddString(col[col_index], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-source", NULL,
332                         "main-tray");
333             } else if (job_params->media_tray == TRAY_SOURCE_TRAY_1) {
334                 col[++col_index] = ippNew();
335                 ippAddString(col[col_index], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-source", NULL,
336                         "main-tray");
337             }
338         }
339 
340         // MEDIA-Type is IPP 2.0 only
341         // put margins in with media-type
342         // Client-Error-Attribute-Or-Values-Not-Supported
343         col[++col_index] = ippNew();
344 
345         // set margins - if negative margins, set to full-bleed; otherwise set calculated values
346         if (job_params->borderless) {
347             LOGD("Setting Up BORDERLESS");
348             ippAddInteger(col[col_index], IPP_TAG_JOB, IPP_TAG_INTEGER, "media-bottom-margin", 0);
349             ippAddInteger(col[col_index], IPP_TAG_JOB, IPP_TAG_INTEGER, "media-top-margin", 0);
350             ippAddInteger(col[col_index], IPP_TAG_JOB, IPP_TAG_INTEGER, "media-left-margin", 0);
351             ippAddInteger(col[col_index], IPP_TAG_JOB, IPP_TAG_INTEGER, "media-right-margin", 0);
352         }
353 
354         switch (job_params->media_type) {
355             case MEDIA_AUTO:
356                 ippAddString(col[col_index], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-type", NULL,
357                              "auto");
358                 break;
359             case MEDIA_PHOTO_GLOSSY:
360                 ippAddString(col[col_index], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-type", NULL,
361                         "photographic-glossy");
362                 break;
363             case MEDIA_PHOTO:
364             case MEDIA_ADVANCED_PHOTO:
365             case MEDIA_PHOTO_MATTE:
366             case MEDIA_PREMIUM_PHOTO:
367             case MEDIA_OTHER_PHOTO:
368                 ippAddString(col[col_index], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-type", NULL,
369                         "photographic");
370                 break;
371             default:
372                 ippAddString(col[col_index], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-type", NULL,
373                         "stationery");
374                 break;
375         }
376 
377         float mediaWidth;
378         float mediaHeight;
379         const char *mediaSizeName = NULL;
380         _get_pwg_media_size(job_params->media_size, &mediaWidth, &mediaHeight, &mediaSizeName);
381         ipp_t *mediaSize = ippNew();
382 
383         if ((job_params->media_size_name) && (mediaSizeName != NULL)) {
384             ippAddString(mediaSize, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-size-name", NULL,
385                     mediaSizeName);
386         } else {
387             ippAddInteger(mediaSize, IPP_TAG_JOB, IPP_TAG_INTEGER, "x-dimension", (int) mediaWidth);
388             ippAddInteger(mediaSize, IPP_TAG_JOB, IPP_TAG_INTEGER, "y-dimension",
389                     (int) mediaHeight);
390         }
391         ippAddCollection(col[col_index], IPP_TAG_JOB, "media-size", mediaSize);
392 
393         // can either set media or media-col.
394         // if both sent, device should return client-error-bad-request
395         ippAddCollections(request, IPP_TAG_JOB, "media-col", col_index + 1, (const ipp_t **) col);
396         while (col_index >= 0) {
397             ippDelete(col[col_index--]);
398         }
399     } else {
400         ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media", NULL,
401                 mapDFMediaToIPPKeyword(job_params->media_size));
402     }
403 
404     LOGI("_fill_job (%d): request", ipp_op);
405     for (attrptr = ippFirstAttribute(request); attrptr; attrptr = ippNextAttribute(request)) {
406         print_attr(attrptr);
407     }
408 
409     return request;
410 }
411 
_validate_job(const ifc_print_job_t * this_p,wprint_job_params_t * job_params)412 static status_t  _validate_job(const ifc_print_job_t *this_p, wprint_job_params_t *job_params) {
413     LOGD("_validate_job: Enter");
414     status_t result = ERROR;
415     ipp_print_job_t *ipp_job;
416     ipp_t *response;
417     ipp_t *request = NULL;
418     ipp_status_t ipp_status;
419 
420     LOGD("_validate_job: ** validatePrintJob:  Entry");
421     do {
422         if (this_p == NULL) {
423             break;
424         }
425 
426         if (job_params == NULL) {
427             break;
428         }
429 
430         ipp_job = IMPL(ipp_print_job_t, ifc, this_p);
431         if (ipp_job->http == NULL) {
432             break;
433         }
434 
435         ipp_job->useragent = NULL;
436         if ((job_params->useragent != NULL) && (strlen(job_params->useragent) > 0)) {
437             ipp_job->useragent = job_params->useragent;
438         }
439 
440         request = _fill_job(IPP_VALIDATE_JOB, ipp_job->printer_uri, job_params);
441 
442         if (ipp_job->useragent != NULL) {
443             httpSetDefaultField(ipp_job->http, HTTP_FIELD_USER_AGENT, ipp_job->useragent);
444         }
445         if ((response = ipp_doCupsRequest(ipp_job->http, request, ipp_job->http_resource,
446                 ipp_job->printer_uri))
447                 == NULL) {
448             ipp_status = cupsLastError();
449             LOGE("_validate_job:  validatePrintJob:  response is null:  ipp_status %d %s",
450                     ipp_status, ippErrorString(ipp_status));
451         } else {
452             ipp_status = cupsLastError();
453             LOGI("_validate_job: %s ipp_status %d  %x received:", ippOpString(IPP_VALIDATE_JOB),
454                     ipp_status, ipp_status);
455             ipp_attribute_t *attrptr;
456             for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(
457                     response)) {
458                 print_attr(attrptr);
459             }
460 
461             ippDelete(response);
462         }
463 
464         LOGD("_validate_job : ipp_status: %d", ipp_status);
465         if (strncmp(ippErrorString(ipp_status), ippErrorString(IPP_OK),
466                 strlen(ippErrorString(IPP_OK))) == 0) {
467             result = OK;
468         } else {
469             result = ERROR;
470         }
471     } while (0);
472 
473     ippDelete(request);
474 
475     LOGD("_validate_job: ** validate_job result: %d", result);
476 
477     return result;
478 }
479 
_start_job(const ifc_print_job_t * this_p,const wprint_job_params_t * job_params)480 static status_t _start_job(const ifc_print_job_t *this_p, const wprint_job_params_t *job_params) {
481     LOGD("_start_job: Enter");
482     status_t result;
483     ipp_print_job_t *ipp_job;
484     ipp_t *request = NULL;
485     bool retry;
486     int failed_count = 0;
487 
488     LOGD("_start_job entry");
489     do {
490         retry = false;
491         if (this_p == NULL) {
492             LOGE("_start_job; this_p == NULL");
493             continue;
494         }
495 
496         ipp_job = IMPL(ipp_print_job_t, ifc, this_p);
497 
498         ipp_job->useragent = NULL;
499         if ((job_params->useragent != NULL) && (strlen(job_params->useragent) > 0)) {
500             ipp_job->useragent = job_params->useragent;
501         }
502         request = _fill_job(IPP_PRINT_JOB, ipp_job->printer_uri, job_params);
503 
504         if (request == NULL) {
505             continue;
506         }
507 
508         if (ipp_job->useragent != NULL) {
509             httpSetDefaultField(ipp_job->http, HTTP_FIELD_USER_AGENT, ipp_job->useragent);
510         }
511         ipp_job->status = cupsSendRequest(ipp_job->http, request, ipp_job->http_resource, 0);
512         if (ipp_job->status != HTTP_CONTINUE) {
513             failed_count++;
514             if ((failed_count == 1) &&
515                     ((ipp_job->status == HTTP_ERROR) || (ipp_job->status >= HTTP_BAD_REQUEST))) {
516                 retry = true;
517                 LOGI("_start_job retry due to internal error");
518                 // We will retry for one of these failures since we could have just
519                 // lost our connection to the server and cups will not always attempt
520                 // a reconnect for us.
521                 ippDelete(request);
522                 continue;
523             }
524         }
525         ippDelete(request);
526         LOGI("_start_job httpPrint fd %d status %d ipp_status %d", ipp_job->http->fd,
527                 ipp_job->status, cupsLastError());
528 
529         result = ((ipp_job->status == HTTP_CONTINUE) ? OK : ERROR);
530     } while (retry);
531 
532     return result;
533 }
534 
_send_data(const ifc_print_job_t * this_p,const char * buffer,size_t length)535 static int _send_data(const ifc_print_job_t *this_p, const char *buffer, size_t length) {
536     ipp_print_job_t *ipp_job;
537     if (this_p == NULL) {
538         return ERROR;
539     }
540 
541     ipp_job = IMPL(ipp_print_job_t, ifc, this_p);
542     if (ipp_job->http == NULL) {
543         return ERROR;
544     }
545 
546     if (ipp_job->status != HTTP_CONTINUE) {
547         return ERROR;
548     }
549 
550     if (length != 0) {
551         if (ipp_job->useragent != NULL) {
552             httpSetDefaultField(ipp_job->http, HTTP_FIELD_USER_AGENT, ipp_job->useragent);
553         }
554         ipp_job->status = cupsWriteRequestData(ipp_job->http, buffer, length);
555     }
556     return ((ipp_job->status == HTTP_CONTINUE) ? length : (int) ERROR);
557 }
558 
_end_job(const ifc_print_job_t * this_p)559 static status_t _end_job(const ifc_print_job_t *this_p) {
560     LOGD("_end_job: Enter");
561     status_t result = ERROR;
562     ipp_t *response;
563     ipp_attribute_t *attrptr;
564     int op = IPP_PRINT_JOB;
565     ipp_print_job_t *ipp_job;
566     int job_id = -1;
567 
568     char buffer[1024];
569 
570     if (this_p == NULL) {
571         return result;
572     }
573 
574     ipp_job = IMPL(ipp_print_job_t, ifc, this_p);
575 
576     if (ipp_job->http == NULL) {
577         return result;
578     }
579 
580     LOGD("_end_job: entry httpPrint %d", ipp_job->http->fd);
581 
582     if (ipp_job->useragent != NULL) {
583         httpSetDefaultField(ipp_job->http, HTTP_FIELD_USER_AGENT, ipp_job->useragent);
584     }
585     ipp_job->status = cupsWriteRequestData(ipp_job->http, buffer, 0);
586 
587     if (ipp_job->status != HTTP_CONTINUE) {
588         LOGE("Error: from cupsWriteRequestData http.fd %d:  status %d",
589                 ipp_job->http->fd, ipp_job->status);
590     } else {
591         result = OK;
592         LOGD("0 length Bytes sent, status %d", ipp_job->status);
593         response = cupsGetResponse(ipp_job->http, ipp_job->http_resource);
594 
595         if ((attrptr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL) {
596             LOGE("sent cupsGetResponse %s job id is null; received", ippOpString(op));
597         } else {
598             job_id = ippGetInteger(attrptr, 0);
599             LOGI("sent cupsGetResponse %s job_id %d; received", ippOpString(op), job_id);
600         }
601 
602         if (response != NULL) {
603             for (attrptr = ippFirstAttribute(response); attrptr; attrptr = ippNextAttribute(
604                     response)) {
605                 print_attr(attrptr);
606                 if (strcmp(ippGetName(attrptr), "job-state-reasons") == 0) {
607                     int i;
608                     for (i = 0; i < ippGetCount(attrptr); i++) {
609                         if (strcmp(ippGetString(attrptr, i, NULL), "job-canceled-at-device")
610                                 == 0) {
611                             result = CANCELLED;
612                             break;
613                         }
614                     }
615                 }
616             }
617             ippDelete(response);
618         }
619     }
620     LOGD("_end_job: exit status %d job_id %d", ipp_job->status, job_id);
621 
622     return result;
623 }
624