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