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