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