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