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 "lib_wprint.h" 20 #include "cups.h" 21 #include "http-private.h" 22 #include "ipphelper.h" 23 #include "wprint_debug.h" 24 25 #include "ipp_print.h" 26 #include "../plugins/media.h" 27 28 #define TAG "ipphelper" 29 30 /* 31 * Get the IPP version of the given printer 32 */ 33 static status_t determine_ipp_version(char *, http_t *); 34 35 /* 36 * Tests IPP versions and sets it to the latest working version 37 */ 38 static status_t test_and_set_ipp_version(char *, http_t *, int, int); 39 40 /* 41 * Parses supported IPP versions from the IPP response and copies them into ippVersions 42 */ 43 static void parse_IPPVersions(ipp_t *response, ipp_version_supported_t *ippVersions); 44 45 /* 46 * Parses printer URIs from the IPP response and copies them into capabilities 47 */ 48 static void parse_printerUris(ipp_t *response, printer_capabilities_t *capabilities); 49 50 /* 51 * Known media sizes. 52 * 53 * A note on rounding: In some cases the Android-specified width (in mils) is rounded down. 54 * This causes artifacts in libjpeg-turbo when rendering to the correct width, so in these 55 * cases we override with a rounded-up value. 56 */ 57 struct MediaSizeTableElement SupportedMediaSizes[SUPPORTED_MEDIA_SIZE_COUNT] = { 58 { US_LETTER, "LETTER", 8500, 11000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_letter_8.5x11in" }, 59 { US_LEGAL, "LEGAL", 8500, 14000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_legal_8.5x14in" }, 60 { LEDGER, "LEDGER", 11000, 17000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_ledger_11x17in" }, 61 { INDEX_CARD_5X7, "5X7", 5000, 7000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_5x7_5x7in" }, 62 63 // Android system uses width of 11690 64 { ISO_A3, "A3", 11694, 16540, 297, 420, "iso_a3_297x420mm" }, 65 66 // Android system uses width of 8267 67 { ISO_A4, "A4", 8268, 11692, 210, 297, "iso_a4_210x297mm" }, 68 { ISO_A5, "A5", 5830, 8270, 148, 210, "iso_a5_148x210mm" }, 69 70 // Android system uses width of 10118 71 { JIS_B4, "JIS B4", 10119, 14331, 257, 364, "jis_b4_257x364mm" }, 72 73 // Android system uses width of 7165 74 { JIS_B5, "JIS B5", 7167, 10118, 182, 257, "jis_b5_182x257mm" }, 75 { US_GOVERNMENT_LETTER, "8x10", 8000, 10000, UNKNOWN_VALUE, UNKNOWN_VALUE, 76 "na_govt-letter_8x10in" }, 77 { INDEX_CARD_4X6, "4x6", 4000, 6000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_index-4x6_4x6in" }, 78 { JPN_HAGAKI_PC, "JPOST", 3940, 5830, 100, 148, "jpn_hagaki_100x148mm" }, 79 { PHOTO_89X119, "89X119", 3504, 4685, 89, 119, "om_dsc-photo_89x119mm" }, 80 { CARD_54X86, "54X86", 2126, 3386, 54, 86, "om_card_54x86mm" }, 81 { OE_PHOTO_L, "L", 3500, 5000, UNKNOWN_VALUE, UNKNOWN_VALUE, "oe_photo-l_3.5x5in" } 82 }; 83 84 typedef struct { 85 double Lower; 86 double Upper; 87 } media_dimension_mm_t; 88 89 static const char *__request_ipp_version[] = {"ipp-versions-supported"}; 90 91 static int __ipp_version_major = 2; 92 static int __ipp_version_minor = 0; 93 94 status_t set_ipp_version(ipp_t *op_to_set, char *printer_uri, http_t *http, 95 ipp_version_state use_existing_version) { 96 LOGD("set_ipp_version(): Enter %d", use_existing_version); 97 if (op_to_set == NULL) { 98 return ERROR; 99 } 100 switch (use_existing_version) { 101 case NEW_REQUEST_SEQUENCE: 102 __ipp_version_major = 2; 103 __ipp_version_minor = 0; 104 break; 105 case IPP_VERSION_RESOLVED: 106 break; 107 case IPP_VERSION_UNSUPPORTED: 108 if (determine_ipp_version(printer_uri, http) != 0) { 109 return ERROR; 110 } 111 break; 112 } 113 ippSetVersion(op_to_set, __ipp_version_major, __ipp_version_minor); 114 LOGD("set_ipp_version(): Done"); 115 return OK; 116 } 117 118 static status_t determine_ipp_version(char *printer_uri, http_t *http) { 119 LOGD("determine_ipp_version(): Enter printer_uri = %s", printer_uri); 120 121 if (http == NULL) { 122 LOGE("determine_ipp_version(): http is NULL cannot continue"); 123 return ERROR; 124 } 125 if ((test_and_set_ipp_version(printer_uri, http, 1, 1) == OK) 126 || (test_and_set_ipp_version(printer_uri, http, 1, 0) == OK) 127 || (test_and_set_ipp_version(printer_uri, http, 2, 0) == OK)) { 128 LOGD("successfully set ipp version."); 129 } else { 130 LOGD("could not get ipp version using any known ipp version."); 131 return ERROR; 132 } 133 return OK; 134 } 135 136 static status_t test_and_set_ipp_version(char *printer_uri, http_t *http, int major, int minor) { 137 status_t return_value = ERROR; 138 int service_unavailable_retry_count = 0; 139 int bad_request_retry_count = 0; 140 ipp_t *request = NULL; 141 ipp_t *response; 142 ipp_version_supported_t ippVersions; 143 char http_resource[1024]; 144 int op = IPP_GET_PRINTER_ATTRIBUTES; 145 146 LOGD("test_and_set_ipp_version(): Enter %d - %d", major, minor); 147 memset(&ippVersions, 0, sizeof(ipp_version_supported_t)); 148 getResourceFromURI(printer_uri, http_resource, 1024); 149 do { 150 request = ippNewRequest(op); 151 ippSetVersion(request, major, minor); 152 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); 153 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", 154 sizeof(__request_ipp_version) / sizeof(__request_ipp_version[0]), 155 NULL, __request_ipp_version); 156 if ((response = cupsDoRequest(http, request, http_resource)) == NULL) { 157 ipp_status_t ipp_status = cupsLastError(); 158 LOGD("test_and_set_ipp_version: response is null: ipp_status %d %s", 159 ipp_status, ippErrorString(ipp_status)); 160 if (ipp_status == IPP_INTERNAL_ERROR) { 161 LOGE("test_and_set_ipp_version: 1280 received, bailing..."); 162 break; 163 } else if ((ipp_status == IPP_SERVICE_UNAVAILABLE) && 164 (service_unavailable_retry_count < IPP_SERVICE_ERROR_MAX_RETRIES)) { 165 LOGE("test_and_set_ipp_version: 1282 received, retrying %d of %d", 166 service_unavailable_retry_count, IPP_SERVICE_ERROR_MAX_RETRIES); 167 service_unavailable_retry_count++; 168 continue; 169 } else if (ipp_status == IPP_BAD_REQUEST) { 170 LOGE("test_and_set_ipp_version: IPP_Status of IPP_BAD_REQUEST " 171 "received. retry (%d) of (%d)", bad_request_retry_count, 172 IPP_BAD_REQUEST_MAX_RETRIES); 173 if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) { 174 break; 175 } 176 bad_request_retry_count++; 177 continue; 178 } else if (ipp_status == IPP_NOT_FOUND) { 179 LOGE("test_and_set_ipp_version: IPP_Status of IPP_NOT_FOUND received"); 180 break; 181 } 182 return_value = ERROR; 183 } else { 184 ipp_status_t ipp_status = cupsLastError(); 185 LOGD("ipp CUPS last ERROR: %d, %s", ipp_status, ippErrorString(ipp_status)); 186 if (ipp_status == IPP_BAD_REQUEST) { 187 LOGD("IPP_Status of IPP_BAD_REQUEST received. retry (%d) of (%d)", 188 bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); 189 if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) { 190 break; 191 } 192 bad_request_retry_count++; 193 ippDelete(response); 194 continue; 195 } 196 197 parse_IPPVersions(response, &ippVersions); 198 if (ippVersions.supportsIpp20) { 199 __ipp_version_major = 2; 200 __ipp_version_minor = 0; 201 return_value = OK; 202 LOGD("test_and_set_ipp_version(): ipp version set to %d,%d", 203 __ipp_version_major, __ipp_version_minor); 204 } else if (ippVersions.supportsIpp11) { 205 __ipp_version_major = 1; 206 __ipp_version_minor = 1; 207 return_value = OK; 208 LOGD("test_and_set_ipp_version(): ipp version set to %d,%d", 209 __ipp_version_major, __ipp_version_minor); 210 } else if (ippVersions.supportsIpp10) { 211 __ipp_version_major = 1; 212 __ipp_version_minor = 0; 213 return_value = OK; 214 LOGD("test_and_set_ipp_version(): ipp version set to %d,%d", 215 __ipp_version_major, __ipp_version_minor); 216 } else { 217 LOGD("test_and_set_ipp_version: ipp version not found"); 218 return_value = ERROR; 219 } 220 } 221 if (response != NULL) ippDelete(response); 222 break; 223 } while (1); 224 return return_value; 225 } 226 227 ipp_status_t get_PrinterState(http_t *http, char *printer_uri, 228 printer_state_dyn_t *printer_state_dyn, ipp_pstate_t *printer_state) { 229 LOGD("get_PrinterState(): Enter"); 230 231 // Requested printer attributes 232 static const char *pattrs[] = {"printer-make-and-model", "printer-state", 233 "printer-state-message", "printer-state-reasons"}; 234 235 ipp_t *request = NULL; 236 ipp_t *response = NULL; 237 ipp_status_t ipp_status = IPP_OK; 238 int op = IPP_GET_PRINTER_ATTRIBUTES; 239 char http_resource[1024]; 240 getResourceFromURI(printer_uri, http_resource, 1024); 241 242 if (printer_state_dyn == NULL) { 243 LOGE("get_PrinterState(): printer_state_dyn is null"); 244 return ipp_status; 245 } 246 247 if (printer_state) { 248 *printer_state = IPP_PRINTER_STOPPED; 249 } else { 250 LOGE("get_PrinterState(): printer_state is null"); 251 } 252 request = ippNewRequest(op); 253 254 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); 255 256 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", 257 sizeof(pattrs) / sizeof(pattrs[0]), NULL, pattrs); 258 259 if ((response = ipp_doCupsRequest(http, request, http_resource, printer_uri)) == NULL) { 260 ipp_status = cupsLastError(); 261 LOGE("get_PrinterState(): response is null: ipp_status %d", ipp_status); 262 printer_state_dyn->printer_status = PRINT_STATUS_UNABLE_TO_CONNECT; 263 printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT; 264 } else { 265 ipp_status = cupsLastError(); 266 LOGD("ipp CUPS last ERROR: %d, %s", ipp_status, ippErrorString(ipp_status)); 267 get_PrinterStateReason(response, printer_state, printer_state_dyn); 268 LOGD("get_PrinterState(): printer_state_dyn->printer_status: %d", 269 printer_state_dyn->printer_status); 270 } 271 LOGD("get_PrinterState(): exit http->fd %d, ipp_status %d, printer_state %d", http->fd, 272 ipp_status, printer_state_dyn->printer_status); 273 274 ippDelete(request); 275 ippDelete(response); 276 return ipp_status; 277 } 278 279 void get_PrinterStateReason(ipp_t *response, ipp_pstate_t *printer_state, 280 printer_state_dyn_t *printer_state_dyn) { 281 LOGD("get_PrinterStateReason(): Enter"); 282 ipp_attribute_t *attrptr; 283 int reason_idx = 0; 284 int idx = 0; 285 ipp_pstate_t printer_ippstate = IPP_PRINTER_IDLE; 286 287 if ((attrptr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) == NULL) { 288 LOGE("get_PrinterStateReason printer-state null"); 289 printer_state_dyn->printer_status = PRINT_STATUS_UNABLE_TO_CONNECT; 290 printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT; 291 } else { 292 printer_ippstate = (ipp_pstate_t) ippGetInteger(attrptr, 0); 293 *printer_state = printer_ippstate; 294 295 LOGD("get_PrinterStateReason printer-state: %d", printer_ippstate); 296 // set the printer_status; they may be modified based on the status reasons below. 297 switch (printer_ippstate) { 298 case IPP_PRINTER_IDLE: 299 printer_state_dyn->printer_status = PRINT_STATUS_IDLE; 300 break; 301 case IPP_PRINTER_PROCESSING: 302 printer_state_dyn->printer_status = PRINT_STATUS_PRINTING; 303 break; 304 case IPP_PRINTER_STOPPED: 305 printer_state_dyn->printer_status = PRINT_STATUS_SVC_REQUEST; 306 break; 307 } 308 } 309 310 if ((attrptr = ippFindAttribute(response, "printer-state-reasons", IPP_TAG_KEYWORD)) == NULL) { 311 LOGE(" get_PrinterStateReason printer-state reason null"); 312 printer_state_dyn->printer_status = PRINT_STATUS_UNABLE_TO_CONNECT; 313 printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT; 314 } else { 315 for (idx = 0; idx < ippGetCount(attrptr); idx++) { 316 // Per RFC2911 any of these can have -error, -warning, or -report appended to end 317 LOGD("get_PrinterStateReason printer-state-reason: %s", 318 ippGetString(attrptr, idx, NULL)); 319 if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_NONE, 320 strlen(IPP_PRNT_STATE_NONE)) == 0) { 321 switch (printer_ippstate) { 322 case IPP_PRINTER_IDLE: 323 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_IDLE; 324 break; 325 case IPP_PRINTER_PROCESSING: 326 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_PRINTING; 327 break; 328 case IPP_PRINTER_STOPPED: 329 // should this be PRINT_STATUS_SVC_REQUEST 330 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_UNKNOWN; 331 break; 332 } 333 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_SPOOL_FULL, 334 strlen(IPP_PRNT_STATE_SPOOL_FULL)) == 0) { 335 switch (printer_ippstate) { 336 case IPP_PRINTER_IDLE: 337 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_UNKNOWN; 338 break; 339 case IPP_PRINTER_PROCESSING: 340 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_PRINTING; 341 break; 342 case IPP_PRINTER_STOPPED: 343 // should this be PRINT_STATUS_SVC_REQUEST 344 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_UNKNOWN; 345 break; 346 } 347 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_MARKER_SUPPLY_LOW, 348 strlen(IPP_PRNT_STATE_MARKER_SUPPLY_LOW)) == 0) { 349 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_LOW_ON_INK; 350 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_TONER_LOW, 351 strlen(IPP_PRNT_STATE_TONER_LOW)) == 0) { 352 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_LOW_ON_TONER; 353 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_OTHER_WARN, 354 strlen(IPP_PRNT_STATE_OTHER_WARN)) == 0) { 355 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_UNKNOWN; 356 } else { 357 // check blocking cases 358 if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_MEDIA_NEEDED, 359 strlen(IPP_PRNT_STATE_MEDIA_NEEDED)) == 0) { 360 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_OUT_OF_PAPER; 361 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_MEDIA_EMPTY, 362 strlen(IPP_PRNT_STATE_MEDIA_EMPTY)) == 0) { 363 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_OUT_OF_PAPER; 364 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_TONER_EMPTY, 365 strlen(IPP_PRNT_STATE_TONER_EMPTY)) == 0) { 366 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_OUT_OF_TONER; 367 } else if (strncmp(ippGetString(attrptr, idx, NULL), 368 IPP_PRNT_STATE_MARKER_SUPPLY_EMPTY, 369 strlen(IPP_PRNT_STATE_MARKER_SUPPLY_EMPTY)) == 0) { 370 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_OUT_OF_INK; 371 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_DOOR_OPEN, 372 strlen(IPP_PRNT_STATE_DOOR_OPEN)) == 0) { 373 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_DOOR_OPEN; 374 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_COVER_OPEN, 375 strlen(IPP_PRNT_STATE_COVER_OPEN)) == 0) { 376 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_DOOR_OPEN; 377 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_MEDIA_JAM, 378 strlen(IPP_PRNT_STATE_MEDIA_JAM)) == 0) { 379 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_JAMMED; 380 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_SHUTDOWN, 381 strlen(IPP_PRNT_SHUTDOWN)) == 0) { 382 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_SHUTTING_DOWN; 383 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_OTHER_ERR, 384 strlen(IPP_PRNT_STATE_OTHER_ERR)) == 0) { 385 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_SVC_REQUEST; 386 } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_PAUSED, 387 strlen(IPP_PRNT_PAUSED)) == 0) { 388 printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_UNKNOWN; 389 } 390 } 391 } // end of reasons loop 392 } 393 } 394 395 static void print_col(ipp_t *col) { 396 int i; 397 ipp_attribute_t *attr; 398 399 LOGD("{"); 400 for (attr = ippFirstAttribute(col); attr; attr = ippNextAttribute(col)) { 401 switch (ippGetValueTag(attr)) { 402 case IPP_TAG_INTEGER: 403 case IPP_TAG_ENUM: 404 for (i = 0; i < ippGetCount(attr); i++) { 405 LOGD(" %s(%s%s)= %d ", ippGetName(attr), 406 ippGetCount(attr) > 1 ? "1setOf " : "", 407 ippTagString(ippGetValueTag(attr)), ippGetInteger(attr, i)); 408 } 409 break; 410 case IPP_TAG_BOOLEAN: 411 for (i = 0; i < ippGetCount(attr); i++) { 412 if (ippGetBoolean(attr, i)) { 413 LOGD(" %s(%s%s)= true ", ippGetName(attr), 414 ippGetCount(attr) > 1 ? "1setOf " : "", 415 ippTagString(ippGetValueTag(attr))); 416 } else { 417 LOGD(" %s(%s%s)= false ", ippGetName(attr), 418 ippGetCount(attr) > 1 ? "1setOf " : "", 419 ippTagString(ippGetValueTag(attr))); 420 } 421 } 422 break; 423 case IPP_TAG_NOVALUE: 424 LOGD(" %s(%s%s)= novalue", ippGetName(attr), 425 ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); 426 break; 427 case IPP_TAG_RANGE: 428 for (i = 0; i < ippGetCount(attr); i++) { 429 int lower, upper; 430 lower = ippGetRange(attr, i, &upper); 431 LOGD(" %s(%s%s)= %d-%d ", ippGetName(attr), 432 ippGetCount(attr) > 1 ? "1setOf " : "", 433 ippTagString(ippGetValueTag(attr)), lower, upper); 434 } 435 break; 436 case IPP_TAG_RESOLUTION: 437 for (i = 0; i < ippGetCount(attr); i++) { 438 ipp_res_t units; 439 int xres, yres; 440 xres = ippGetResolution(attr, i, &yres, &units); 441 LOGD(" %s(%s%s)= %dx%d%s ", ippGetName(attr), 442 ippGetCount(attr) > 1 ? "1setOf " : "", 443 ippTagString(ippGetValueTag(attr)), xres, yres, 444 units == IPP_RES_PER_INCH ? "dpi" : "dpc"); 445 } 446 break; 447 case IPP_TAG_STRING: 448 case IPP_TAG_TEXT: 449 case IPP_TAG_NAME: 450 case IPP_TAG_KEYWORD: 451 case IPP_TAG_CHARSET: 452 case IPP_TAG_URI: 453 case IPP_TAG_MIMETYPE: 454 case IPP_TAG_LANGUAGE: 455 for (i = 0; i < ippGetCount(attr); i++) { 456 LOGD(" %s(%s%s)= \"%s\" ", ippGetName(attr), 457 ippGetCount(attr) > 1 ? "1setOf " : "", 458 ippTagString(ippGetValueTag(attr)), ippGetString(attr, i, NULL)); 459 } 460 break; 461 case IPP_TAG_TEXTLANG: 462 case IPP_TAG_NAMELANG: 463 for (i = 0; i < ippGetCount(attr); i++) { 464 const char *charset; 465 const char *text; 466 text = ippGetString(attr, i, &charset); 467 LOGD(" %s(%s%s)= \"%s\",%s ", ippGetName(attr), 468 ippGetCount(attr) > 1 ? "1setOf " : "", 469 ippTagString(ippGetValueTag(attr)), text, charset); 470 } 471 break; 472 case IPP_TAG_BEGIN_COLLECTION: 473 for (i = 0; i < ippGetCount(attr); i++) { 474 print_col(ippGetCollection(attr, i)); 475 } 476 break; 477 default: 478 break; 479 } 480 } 481 LOGD("}"); 482 } 483 484 void print_attr(ipp_attribute_t *attr) { 485 int i; 486 487 if (ippGetName(attr) == NULL) { 488 return; 489 } 490 491 switch (ippGetValueTag(attr)) { 492 case IPP_TAG_INTEGER: 493 case IPP_TAG_ENUM: 494 for (i = 0; i < ippGetCount(attr); i++) { 495 LOGD("%s (%s%s) = %d ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", 496 ippTagString(ippGetValueTag(attr)), ippGetInteger(attr, i)); 497 } 498 break; 499 case IPP_TAG_BOOLEAN: 500 for (i = 0; i < ippGetCount(attr); i++) { 501 if (ippGetBoolean(attr, i)) { 502 LOGD("%s (%s%s) = true ", ippGetName(attr), 503 ippGetCount(attr) > 1 ? "1setOf " : "", 504 ippTagString(ippGetValueTag(attr))); 505 } else { 506 LOGD("%s (%s%s) = false ", ippGetName(attr), 507 ippGetCount(attr) > 1 ? "1setOf " : "", 508 ippTagString(ippGetValueTag(attr))); 509 } 510 } 511 break; 512 case IPP_TAG_NOVALUE: 513 LOGD("%s (%s%s) = novalue", ippGetName(attr), 514 ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); 515 break; 516 case IPP_TAG_RANGE: 517 for (i = 0; i < ippGetCount(attr); i++) { 518 int lower, upper; 519 lower = ippGetRange(attr, i, &upper); 520 LOGD("%s (%s%s) = %d-%d ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", 521 ippTagString(ippGetValueTag(attr)), lower, upper); 522 } 523 break; 524 case IPP_TAG_RESOLUTION: 525 for (i = 0; i < ippGetCount(attr); i++) { 526 ipp_res_t units; 527 int xres, yres; 528 xres = ippGetResolution(attr, i, &yres, &units); 529 LOGD("%s (%s%s) = %dx%d%s ", ippGetName(attr), 530 ippGetCount(attr) > 1 ? "1setOf " : "", 531 ippTagString(ippGetValueTag(attr)), xres, yres, 532 units == IPP_RES_PER_INCH ? "dpi" : "dpc"); 533 } 534 break; 535 case IPP_TAG_STRING: 536 case IPP_TAG_TEXT: 537 case IPP_TAG_NAME: 538 case IPP_TAG_KEYWORD: 539 case IPP_TAG_CHARSET: 540 case IPP_TAG_URI: 541 case IPP_TAG_MIMETYPE: 542 case IPP_TAG_LANGUAGE: 543 for (i = 0; i < ippGetCount(attr); i++) { 544 LOGD("%s (%s%s) = \"%s\" ", ippGetName(attr), 545 ippGetCount(attr) > 1 ? "1setOf " : "", 546 ippTagString(ippGetValueTag(attr)), ippGetString(attr, i, NULL)); 547 } 548 break; 549 case IPP_TAG_TEXTLANG: 550 case IPP_TAG_NAMELANG: 551 for (i = 0; i < ippGetCount(attr); i++) { 552 const char *charset; 553 const char *text; 554 text = ippGetString(attr, i, &charset); 555 LOGD("%s (%s%s) = \"%s\",%s ", ippGetName(attr), 556 ippGetCount(attr) > 1 ? "1setOf " : "", 557 ippTagString(ippGetValueTag(attr)), text, charset); 558 } 559 break; 560 561 case IPP_TAG_BEGIN_COLLECTION: 562 for (i = 0; i < ippGetCount(attr); i++) { 563 LOGD("%s (%s%s): IPP_TAG_BEGIN_COLLECTION", ippGetName(attr), 564 ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); 565 print_col(ippGetCollection(attr, i)); 566 } 567 LOGD("%s (%s%s): IPP_TAG_END_COLLECTION", ippGetName(attr), 568 ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); 569 break; 570 571 default: 572 break; 573 } 574 } 575 576 void parse_IPPVersions(ipp_t *response, ipp_version_supported_t *ippVersions) { 577 int i; 578 ipp_attribute_t *attrptr; 579 char ipp10[] = "1.0"; 580 char ipp11[] = "1.1"; 581 char ipp20[] = "2.0"; 582 LOGD(" Entered IPPVersions"); 583 if (ippVersions != NULL) { 584 memset(ippVersions, 0, sizeof(ipp_version_supported_t)); 585 LOGD(" in get_supportedIPPVersions"); 586 attrptr = ippFindAttribute(response, "ipp-versions-supported", IPP_TAG_KEYWORD); 587 if (attrptr != NULL) { 588 LOGD(" in get_supportedIPPVersions: %d", ippGetCount(attrptr)); 589 for (i = 0; i < ippGetCount(attrptr); i++) { 590 if (strcmp(ipp10, ippGetString(attrptr, i, NULL)) == 0) { 591 ippVersions->supportsIpp10 = 1; 592 } else if (strcmp(ipp11, ippGetString(attrptr, i, NULL)) == 0) { 593 ippVersions->supportsIpp11 = 1; 594 } else if (strcmp(ipp20, ippGetString(attrptr, i, NULL)) == 0) { 595 ippVersions->supportsIpp20 = 1; 596 } else { 597 LOGD("found another ipp version. %s", ippGetString(attrptr, i, NULL)); 598 } 599 } 600 } 601 } 602 } 603 604 const char *mapDFMediaToIPPKeyword(media_size_t media_size) { 605 int i; 606 for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { 607 if (SupportedMediaSizes[i].media_size == (media_size_t) media_size) { 608 return (SupportedMediaSizes[i].PWGName); 609 } 610 } 611 return (SupportedMediaSizes[0].PWGName); 612 } 613 614 int ipp_find_media_size(const char *ipp_media_keyword, media_size_t *media_size) { 615 int i; 616 LOGD("ipp_find_media_size entry is %s", ipp_media_keyword); 617 for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { 618 if (strcmp(SupportedMediaSizes[i].PWGName, ipp_media_keyword) == 0) { 619 LOGD(" mediaArraySize: match string %s PT_size: %d", 620 SupportedMediaSizes[i].PWGName, SupportedMediaSizes[i].media_size); 621 break; 622 } 623 } 624 if (i < SUPPORTED_MEDIA_SIZE_COUNT) { 625 *media_size = SupportedMediaSizes[i].media_size; 626 return i; 627 } else { 628 return -1; 629 } 630 return -1; 631 } 632 633 /* 634 * Return a freshly allocated string copied from another string 635 */ 636 static char *substring(const char *string, int position, int length) { 637 char *pointer; 638 int c; 639 pointer = malloc(length + 1); 640 if (pointer == NULL) { 641 exit(EXIT_FAILURE); 642 } 643 644 for (c = 0; c < position - 1; c++) { 645 string++; 646 } 647 648 for (c = 0; c < length; c++) { 649 *(pointer + c) = *string; 650 string++; 651 } 652 *(pointer + c) = '\0'; 653 return pointer; 654 } 655 656 /* 657 * Parse and IPP media size and return dimensions in millimeters. 658 * Format is region_name_#x#<unit> or format is custom_(min\max)_#x#<unit> 659 */ 660 static int getMediaDimensions_mm(const char *mediaSize, media_dimension_mm_t *media_dimensions) { 661 char *tempMediaSize = NULL; 662 char *um; 663 char *buf = NULL; // custom 664 char *dim = NULL; // min 665 char *upper = NULL; // 8.5 666 const char t[2] = "_"; 667 const char x[2] = "x"; 668 char in[3] = "in"; 669 double inch_to_mm = 25.4; 670 671 tempMediaSize = malloc(strlen(mediaSize) + 1); 672 if (tempMediaSize == NULL) { 673 exit(EXIT_FAILURE); 674 } 675 strcpy(tempMediaSize, mediaSize); 676 LOGD("getMediaDimensions_mm Start media:%s", tempMediaSize); 677 678 um = substring(mediaSize, strlen(tempMediaSize) - 1, 2); 679 LOGD("getMediaDimensions um=%s", um); 680 681 // fill in buf with what we need to work with 682 buf = strtok(tempMediaSize, t); // custom 683 while (buf != NULL) { 684 if (dim != NULL) { 685 free(dim); 686 } 687 dim = malloc(strlen(buf) + 1); 688 if (dim == NULL) { 689 exit(EXIT_FAILURE); 690 } 691 strcpy(dim, buf); 692 buf = strtok(NULL, t); 693 } 694 695 if (dim != NULL) { 696 LOGD("getMediaDimensions part2=%s", dim); 697 media_dimensions->Lower = atof(strtok(dim, x)); 698 LOGD("getMediaDimensions lower=%g", media_dimensions->Lower); 699 upper = strtok(NULL, x); 700 if (upper != NULL) { 701 // Finally the upper bound 702 char *tempStr = substring(upper, 1, strlen(upper) - 2); 703 media_dimensions->Upper = atof(tempStr); 704 free(tempStr); 705 706 tempStr = substring(upper, 1, strlen(upper) - 2); 707 LOGD("getMediaDimensions part4=%s", tempStr); 708 free(tempStr); 709 710 LOGD("getMediaDimensions upper=%g", media_dimensions->Upper); 711 // finally make sure we are in mm 712 if (strcmp(um, in) == 0) { 713 LOGD("getMediaDimensions part5"); 714 media_dimensions->Lower = media_dimensions->Lower * inch_to_mm; 715 media_dimensions->Upper = media_dimensions->Upper * inch_to_mm; 716 } 717 718 if (media_dimensions->Lower > 0 && media_dimensions->Upper > 0) { 719 double dTemp = 0; 720 if (media_dimensions->Lower > media_dimensions->Upper) { 721 dTemp = media_dimensions->Lower; 722 media_dimensions->Lower = media_dimensions->Upper; 723 media_dimensions->Upper = dTemp; 724 } 725 LOGD("getMediaDimensions final lower=%g", media_dimensions->Lower); 726 LOGD("getMediaDimensions final upper=%g", media_dimensions->Upper); 727 free(tempMediaSize); 728 free(dim); 729 free(um); 730 return 1; 731 } 732 } 733 free(dim); 734 } 735 free(um); 736 free(tempMediaSize); 737 return 0; 738 } 739 740 void parse_getMediaSupported(ipp_t *response, media_supported_t *media_supported, 741 printer_capabilities_t *capabilities) { 742 int i; 743 ipp_attribute_t *attrptr; 744 int sizes_idx = 0; 745 char custom_min[] = "custom_min"; 746 char custom_max[] = "custom_max"; 747 bool apply_custom_min_max = true; 748 int iterate = 0; 749 750 char *optout_manufacture_list[7] = {"Brother", "Epson", "Fuji Xerox", "Konica Minolta", 751 "Kyocera", "Canon", "UTAX_TA"}; 752 int manufacturerlist_size = (sizeof(optout_manufacture_list) / 753 sizeof(*optout_manufacture_list)); 754 755 media_dimension_mm_t custom_min_dim; 756 media_dimension_mm_t custom_max_dim; 757 int rCustomMin = 0; 758 int rCustomMax = 0; 759 char *tempMediaSize = NULL; 760 761 char manufacturername_from_model[256]; 762 LOGD(" Entered getMediaSupported"); 763 764 media_size_t media_sizeTemp; 765 int idx = 0; 766 767 // First check printer-device-id for manufacturer exception 768 if ((attrptr = ippFindAttribute(response, "printer-device-id", IPP_TAG_TEXT)) != NULL) { 769 strlcpy(capabilities->make, ippGetString(attrptr, 0, NULL), sizeof(capabilities->make)); 770 LOGD("manufacturer_from_deviceid: %s", capabilities->make); 771 for (iterate = 0; iterate < manufacturerlist_size; iterate++) { 772 if (strcasestr(capabilities->make, optout_manufacture_list[iterate]) != NULL) { 773 LOGD("printer device id cmp: %s", strcasestr(capabilities->make, 774 optout_manufacture_list[iterate])); 775 apply_custom_min_max = false; 776 } 777 } 778 } 779 780 // Second check printer-make-and-model for manufacturer exception 781 if (apply_custom_min_max) { 782 // Get manufacturer name using printer-make-and-model 783 attrptr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT); 784 if (attrptr != NULL) { 785 strlcpy(manufacturername_from_model, ippGetString(attrptr, 0, NULL), 786 sizeof(manufacturername_from_model)); 787 LOGD("manufacturer_from_make_model: %s", manufacturername_from_model); 788 for (iterate = 0; iterate < manufacturerlist_size; iterate++) { 789 if (strcasestr(manufacturername_from_model, optout_manufacture_list[iterate]) != 790 NULL) { 791 LOGD("printer make model cmp: %s", strcasestr(manufacturername_from_model, 792 optout_manufacture_list[iterate])); 793 apply_custom_min_max = false; 794 } 795 } 796 } 797 } 798 799 if ((attrptr = ippFindAttribute(response, "media-supported", IPP_TAG_KEYWORD)) != NULL) { 800 LOGD("media-supported found; number of values %d", ippGetCount(attrptr)); 801 for (i = 0; i < ippGetCount(attrptr); i++) { 802 idx = ipp_find_media_size(ippGetString(attrptr, i, NULL), &media_sizeTemp); 803 LOGD(" Temp - i: %d idx %d keyword: %s PT_size %d", i, idx, ippGetString( 804 attrptr, i, NULL), media_sizeTemp); 805 806 // Modified since anytime the find media size returned 0 it could either mean 807 // NOT found or na_letter. 808 if (idx >= 0) { 809 media_supported->media_size[sizes_idx] = media_sizeTemp; 810 media_supported->idxKeywordTranTable[sizes_idx] = idx; 811 sizes_idx++; 812 } 813 814 if (idx == -1) { // it might be a custom range 815 if (tempMediaSize != NULL) { 816 free(tempMediaSize); 817 } 818 tempMediaSize = malloc(strlen(ippGetString(attrptr, i, NULL)) + 1); 819 if (tempMediaSize == NULL) { 820 exit(EXIT_FAILURE); 821 } 822 strcpy(tempMediaSize, ippGetString(attrptr, i, NULL)); 823 if (strstr(tempMediaSize, custom_min) != NULL) { 824 rCustomMin = getMediaDimensions_mm(tempMediaSize, &custom_min_dim); 825 } 826 if (strstr(tempMediaSize, custom_max) != NULL) { 827 rCustomMax = getMediaDimensions_mm(tempMediaSize, &custom_max_dim); 828 } 829 } 830 } 831 // if value equal to particular manufacture condition goes here 832 if (apply_custom_min_max) { 833 LOGD("*****apply custom range for this manufacture"); 834 // Now add in custom sizes if there is a custom min max range 835 media_dimension_mm_t media_dim; 836 int rMediaDim = 0; 837 LOGD("rCustomMin:%d rCustomMax:%d", rCustomMin, rCustomMax); 838 if (rCustomMin > 0 && rCustomMax > 0) { // we have custom support 839 LOGD("CustomRange minLower:%g minUpper:%g maxLower:%g maxUpper:%g", 840 custom_min_dim.Lower, custom_min_dim.Upper, custom_max_dim.Lower, 841 custom_max_dim.Upper); 842 media_dim.Lower = 0; 843 media_dim.Upper = 0; 844 for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { 845 int found; 846 int j; 847 found = 0; 848 int sizes_idx_start = sizes_idx - 1; 849 for (j = 0; j < sizes_idx_start; j++) { 850 if (i == media_supported->idxKeywordTranTable[j]) { 851 found = 1; 852 break; 853 } 854 } 855 if (found == 0) { 856 // check custom 857 if (tempMediaSize != NULL) { 858 free(tempMediaSize); 859 } 860 tempMediaSize = malloc(strlen(SupportedMediaSizes[i].PWGName) + 1); 861 if (tempMediaSize == NULL) { 862 exit(EXIT_FAILURE); 863 } 864 strcpy(tempMediaSize, SupportedMediaSizes[i].PWGName); 865 LOGD("NOT FOUND CHECKING CUSTOM:%s", tempMediaSize); 866 rMediaDim = getMediaDimensions_mm(tempMediaSize, &media_dim); 867 if (rMediaDim > 0) { 868 LOGD("CustomRange minLower:%g minUpper:%g maxLower:%g maxUpper:%g" 869 "lower:%g upper:%g", 870 custom_min_dim.Lower, custom_min_dim.Upper, 871 custom_max_dim.Lower, 872 custom_max_dim.Upper, media_dim.Lower, media_dim.Upper); 873 if (media_dim.Lower >= custom_min_dim.Lower && 874 media_dim.Lower <= custom_max_dim.Lower 875 && media_dim.Upper >= custom_min_dim.Upper && 876 media_dim.Upper <= custom_max_dim.Upper) { 877 LOGD("Add Media Size %s!!", tempMediaSize); 878 media_supported->media_size[sizes_idx] = 879 SupportedMediaSizes[i].media_size; 880 media_supported->idxKeywordTranTable[sizes_idx] = i; 881 sizes_idx++; 882 } 883 } 884 } 885 } 886 if (tempMediaSize != NULL) { 887 free(tempMediaSize); 888 tempMediaSize = NULL; 889 } 890 } 891 } else { 892 LOGD("*****Dont apply custom range for this manufacture"); 893 } 894 } else { 895 LOGD("media-supported not found"); 896 } 897 898 if (tempMediaSize) { 899 free(tempMediaSize); 900 } 901 } 902 903 static void get_supportedPrinterResolutions(ipp_attribute_t *attrptr, 904 printer_capabilities_t *capabilities) { 905 int idx = 0; 906 int i; 907 for (i = 0; i < ippGetCount(attrptr); i++) { 908 ipp_res_t units; 909 int xres, yres; 910 xres = ippGetResolution(attrptr, i, &yres, &units); 911 if (units == IPP_RES_PER_INCH) { 912 if ((idx < MAX_RESOLUTIONS_SUPPORTED) && (xres == yres)) { 913 capabilities->supportedResolutions[idx] = xres; 914 idx++; 915 } 916 } 917 } 918 capabilities->numSupportedResolutions = idx; 919 } 920 921 void getResourceFromURI(const char *uri, char *resource, int resourcelen) { 922 char scheme[1024]; 923 char username[1024]; 924 char host[1024]; 925 int port; 926 httpSeparateURI(0, uri, scheme, 1024, username, 1024, host, 1024, &port, resource, resourcelen); 927 } 928 929 /* 930 * Add a new media type to a printer's collection of supported media types 931 */ 932 static void addMediaType(printer_capabilities_t *capabilities, media_type_t mediaType) { 933 int index; 934 for (index = 0; index < capabilities->numSupportedMediaTypes; index++) { 935 // Skip if already present 936 if (capabilities->supportedMediaTypes[index] == mediaType) return; 937 } 938 939 // Add if not found and not too many 940 if (capabilities->numSupportedMediaTypes < MAX_MEDIA_TYPES_SUPPORTED) { 941 capabilities->supportedMediaTypes[capabilities->numSupportedMediaTypes++] = mediaType; 942 } else { 943 LOGI("Hit MAX_MEDIA_TYPES_SUPPORTED while adding %d", mediaType); 944 } 945 } 946 947 void parse_printerAttributes(ipp_t *response, printer_capabilities_t *capabilities) { 948 int i, j; 949 ipp_attribute_t *attrptr; 950 951 LOGD("Entered parse_printerAttributes"); 952 953 media_supported_t media_supported; 954 for (i = 0; i <= PAGE_STATUS_MAX - 1; i++) { 955 media_supported.media_size[i] = 0; 956 } 957 parse_getMediaSupported(response, &media_supported, capabilities); 958 959 parse_printerUris(response, capabilities); 960 961 LOGD("Media Supported: "); 962 int idx = 0; 963 capabilities->numSupportedMediaTypes = 0; 964 for (i = 0; i <= PAGE_STATUS_MAX - 1; i++) { 965 if (media_supported.media_size[i] != 0) { 966 capabilities->supportedMediaSizes[capabilities->numSupportedMediaSizes++] = 967 media_supported.media_size[i]; 968 idx = media_supported.idxKeywordTranTable[i]; 969 LOGD(" i %d, \tPT_Size: %d \tidx %d \tKeyword: %s", i, media_supported.media_size[i], 970 idx, SupportedMediaSizes[idx].PWGName); 971 } 972 } 973 LOGD(""); 974 975 if ((attrptr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL) { 976 LOGD("printer-name: %s", ippGetString(attrptr, 0, NULL)); 977 strlcpy(capabilities->name, ippGetString(attrptr, 0, NULL), sizeof(capabilities->name)); 978 } 979 980 if ((attrptr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT)) != NULL) { 981 strlcpy(capabilities->make, ippGetString(attrptr, 0, NULL), sizeof(capabilities->make)); 982 } 983 984 if ((attrptr = ippFindAttribute(response, "printer-uuid", IPP_TAG_URI)) != NULL) { 985 strlcpy(capabilities->uuid, ippGetString(attrptr, 0, NULL), sizeof(capabilities->uuid)); 986 } 987 988 if ((attrptr = ippFindAttribute(response, "printer-location", IPP_TAG_TEXT)) != NULL) { 989 strlcpy(capabilities->location, ippGetString(attrptr, 0, NULL), 990 sizeof(capabilities->location)); 991 } 992 993 if ((attrptr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL) { 994 strlcpy(capabilities->mediaDefault, ippGetString(attrptr, 0, NULL), 995 sizeof(capabilities->mediaDefault)); 996 } 997 998 if ((attrptr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN)) != NULL) { 999 if (ippGetBoolean(attrptr, 0)) { 1000 capabilities->color = 1; 1001 } 1002 } 1003 if ((attrptr = ippFindAttribute(response, "copies-supported", IPP_TAG_RANGE)) != NULL) { 1004 int upper = 0; 1005 for (i = 0; i < ippGetCount(attrptr); i++) { 1006 ippGetRange(attrptr, i, &upper); 1007 } 1008 if (upper > 1) { 1009 capabilities->canCopy = 1; 1010 } 1011 } 1012 if ((attrptr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) != 1013 NULL) { 1014 for (i = 0; i < ippGetCount(attrptr); i++) { 1015 if (strcmp("color", ippGetString(attrptr, i, NULL)) == 0) { 1016 capabilities->color = 1; 1017 } 1018 } 1019 } 1020 1021 char imagePCLm[] = "application/PCLm"; 1022 char imagePWG[] = "image/pwg-raster"; 1023 char imagePDF[] = "image/pdf"; 1024 char applicationPDF[] = "application/pdf"; 1025 1026 if ((attrptr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) 1027 != NULL) { 1028 for (i = 0; i < ippGetCount(attrptr); i++) { 1029 if (strcmp(imagePDF, ippGetString(attrptr, i, NULL)) == 0) { 1030 capabilities->canPrintPDF = 1; 1031 } else if (strcmp(applicationPDF, ippGetString(attrptr, i, NULL)) == 0) { 1032 capabilities->canPrintPDF = 1; 1033 } else if (strcmp(imagePCLm, ippGetString(attrptr, i, NULL)) == 0) { 1034 capabilities->canPrintPCLm = 1; 1035 } else if (strcmp(applicationPDF, ippGetString(attrptr, i, NULL)) == 0) { 1036 capabilities->canPrintPDF = 1; 1037 } else if (strcmp(imagePWG, ippGetString(attrptr, i, NULL)) == 0) { 1038 capabilities->canPrintPWG = 1; 1039 } 1040 } 1041 } 1042 1043 if ((attrptr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL) { 1044 for (i = 0; i < ippGetCount(attrptr); i++) { 1045 if (strcmp(IPP_SIDES_TWO_SIDED_SHORT_EDGE, ippGetString(attrptr, i, NULL)) == 0) { 1046 capabilities->duplex = 1; 1047 } else if (strcmp(IPP_SIDES_TWO_SIDED_LONG_EDGE, ippGetString(attrptr, i, NULL)) == 0) { 1048 capabilities->duplex = 1; 1049 } 1050 } 1051 } 1052 1053 // Look up supported media types 1054 capabilities->numSupportedMediaTypes = 0; 1055 if (((attrptr = ippFindAttribute(response, "media-type-supported", IPP_TAG_KEYWORD)) != NULL) 1056 || ((attrptr = ippFindAttribute(response, "media-type-supported", IPP_TAG_NAME)) 1057 != NULL)) { 1058 for (i = 0; i < ippGetCount(attrptr); i++) { 1059 if (strcasestr(ippGetString(attrptr, i, NULL), "photographic-glossy")) { 1060 addMediaType(capabilities, MEDIA_PHOTO_GLOSSY); 1061 } else if (strcasestr(ippGetString(attrptr, i, NULL), "photo")) { 1062 addMediaType(capabilities, MEDIA_PHOTO); 1063 } else if (strcasestr(ippGetString(attrptr, i, NULL), "stationery")) { 1064 addMediaType(capabilities, MEDIA_PLAIN); 1065 } 1066 } 1067 } 1068 1069 if (capabilities->numSupportedMediaTypes == 0) { 1070 // If no recognized media types were found, fall back to all 3 just in case 1071 addMediaType(capabilities, MEDIA_PLAIN); 1072 addMediaType(capabilities, MEDIA_PHOTO); 1073 addMediaType(capabilities, MEDIA_PHOTO_GLOSSY); 1074 } 1075 1076 capabilities->numSupportedResolutions = 0; 1077 // only appears that SMM supports the pclm-source-resolution-supported attribute 1078 // if that is not present, use the printer-resolution-supported attribute to determine 1079 // if 300DPI is supported 1080 if ((attrptr = ippFindAttribute(response, "pclm-source-resolution-supported", 1081 IPP_TAG_RESOLUTION)) != NULL) { 1082 get_supportedPrinterResolutions(attrptr, capabilities); 1083 } else if ((attrptr = ippFindAttribute(response, "printer-resolution-supported", 1084 IPP_TAG_RESOLUTION)) != NULL) { 1085 get_supportedPrinterResolutions(attrptr, capabilities); 1086 } 1087 1088 char ipp10[] = "1.0"; 1089 char ipp11[] = "1.1"; 1090 char ipp20[] = "2.0"; 1091 1092 if ((attrptr = ippFindAttribute(response, "ipp-versions-supported", IPP_TAG_KEYWORD)) != NULL) { 1093 unsigned char supportsIpp20 = 0; 1094 unsigned char supportsIpp11 = 0; 1095 unsigned char supportsIpp10 = 0; 1096 1097 for (i = 0; i < ippGetCount(attrptr); i++) { 1098 if (strcmp(ipp10, ippGetString(attrptr, i, NULL)) == 0) { 1099 supportsIpp10 = 1; 1100 } else if (strcmp(ipp11, ippGetString(attrptr, i, NULL)) == 0) { 1101 supportsIpp11 = 1; 1102 } else if (strcmp(ipp20, ippGetString(attrptr, i, NULL)) == 0) { 1103 supportsIpp20 = 1; 1104 } else { 1105 LOGD("found another ipp version. %s", ippGetString(attrptr, i, NULL)); 1106 } 1107 if (supportsIpp20) { 1108 capabilities->ippVersionMajor = 2; 1109 capabilities->ippVersionMinor = 0; 1110 } else if (supportsIpp11) { 1111 capabilities->ippVersionMajor = 1; 1112 capabilities->ippVersionMinor = 1; 1113 } else if (supportsIpp10) { 1114 capabilities->ippVersionMajor = 1; 1115 capabilities->ippVersionMinor = 0; 1116 } else { 1117 // default to 1.0 1118 capabilities->ippVersionMajor = 1; 1119 capabilities->ippVersionMinor = 0; 1120 } 1121 } 1122 } 1123 1124 char epcl10[] = "1.0"; 1125 if ((attrptr = ippFindAttribute(response, "epcl-version-supported", IPP_TAG_KEYWORD)) != NULL) { 1126 for (i = 0; i < ippGetCount(attrptr); i++) { 1127 LOGD("setting epcl_ipp_version (KEYWORD) %s", ippGetString(attrptr, i, NULL)); 1128 1129 // substring match because different devices implemented spec differently 1130 if (strstr(ippGetString(attrptr, i, NULL), epcl10) != NULL) { 1131 LOGD("setting epcl_ipp_version = 1"); 1132 capabilities->ePclIppVersion = 1; 1133 } 1134 } 1135 } 1136 1137 if ((attrptr = ippFindAttribute(response, "epcl-version-supported", IPP_TAG_TEXT)) != NULL) { 1138 for (i = 0; i < ippGetCount(attrptr); i++) { 1139 LOGD("setting epcl_ipp_verion (TEXT) %s", ippGetString(attrptr, i, NULL)); 1140 1141 // substring match because different devices implemented spec differently 1142 if (strstr(ippGetString(attrptr, i, NULL), epcl10) != NULL) { 1143 LOGD("setting epcl_ipp_verion = 1"); 1144 capabilities->ePclIppVersion = 1; 1145 } 1146 } 1147 } 1148 1149 if ((attrptr = ippFindAttribute(response, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != 1150 NULL) { 1151 for (i = 0; i < ippGetCount(attrptr); i++) { 1152 LOGD("Gathering margins supported"); 1153 1154 ipp_t *collection = ippGetCollection(attrptr, i); 1155 1156 for (j = 0, attrptr = ippFirstAttribute(collection); 1157 (j < 4) && (attrptr != NULL); attrptr = ippNextAttribute(collection)) { 1158 if (strcmp("media-top-margin", ippGetName(attrptr)) == 0) { 1159 capabilities->printerTopMargin = ippGetInteger(attrptr, 0); 1160 } else if (strcmp("media-bottom-margin", ippGetName(attrptr)) == 0) { 1161 capabilities->printerBottomMargin = ippGetInteger(attrptr, 0); 1162 } else if (strcmp("media-left-margin", ippGetName(attrptr)) == 0) { 1163 capabilities->printerLeftMargin = ippGetInteger(attrptr, 0); 1164 } else if (strcmp("media-right-margin", ippGetName(attrptr)) == 0) { 1165 capabilities->printerRightMargin = ippGetInteger(attrptr, 0); 1166 } 1167 } 1168 } 1169 } 1170 1171 if ((attrptr = ippFindAttribute(response, "media-size-name", IPP_TAG_KEYWORD)) != NULL) { 1172 capabilities->isMediaSizeNameSupported = true; 1173 } else { 1174 capabilities->isMediaSizeNameSupported = false; 1175 } 1176 1177 // is strip length supported? if so, stored in capabilities 1178 if ((attrptr = ippFindAttribute(response, "pclm-strip-height-preferred", 1179 IPP_TAG_INTEGER)) != NULL) { 1180 LOGD("pclm-strip-height-preferred=%d", ippGetInteger(attrptr, 0)); 1181 1182 // if the strip height is 0, the device wants us to send the entire page in one band 1183 // (according to ePCL spec). Since our code doesn't currently support generating an entire 1184 // page in one band, set the strip height to the default value every device *should* support 1185 // also, for some reason our code crashes when it attempts to generate strips at 512 or 1186 // above. Therefore, limiting the upper bound strip height to 256 1187 if (ippGetInteger(attrptr, 0) == 0 || ippGetInteger(attrptr, 0) > 256) { 1188 capabilities->stripHeight = STRIPE_HEIGHT; 1189 } else { 1190 capabilities->stripHeight = ippGetInteger(attrptr, 0); 1191 } 1192 } else { 1193 capabilities->stripHeight = STRIPE_HEIGHT; 1194 } 1195 1196 // what is the preferred compression method - jpeg, flate, rle 1197 if ((attrptr = ippFindAttribute(response, "pclm-compression-method-preferred", 1198 IPP_TAG_KEYWORD)) != NULL) { 1199 LOGD("pclm-compression-method-preferred=%s", ippGetString(attrptr, 0, NULL)); 1200 } 1201 1202 // is device able to rotate back page for duplex jobs? 1203 if ((attrptr = ippFindAttribute(response, "pclm-raster-back-side", IPP_TAG_KEYWORD)) != NULL) { 1204 LOGD("pclm-raster-back-side=%s", ippGetString(attrptr, 0, NULL)); 1205 if (strcmp(ippGetString(attrptr, 0, NULL), "rotated") == 0) { 1206 capabilities->canRotateDuplexBackPage = 0; 1207 LOGD("Device cannot rotate back page for duplex jobs."); 1208 } else { 1209 capabilities->canRotateDuplexBackPage = 1; 1210 } 1211 } 1212 1213 // look for full-bleed supported by looking for 0 on all margins 1214 bool topsupported = false, bottomsupported = false, rightsupported = false, 1215 leftsupported = false; 1216 if ((attrptr = ippFindAttribute(response, "media-top-margin-supported", IPP_TAG_INTEGER)) != 1217 NULL) { 1218 for (i = 0; i < ippGetCount(attrptr); i++) { 1219 if (ippGetInteger(attrptr, i) == 0) { 1220 LOGD("Top Margin Supported"); 1221 topsupported = true; 1222 break; 1223 } 1224 } 1225 } 1226 if ((attrptr = ippFindAttribute(response, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != 1227 NULL) { 1228 for (i = 0; i < ippGetCount(attrptr); i++) { 1229 if (ippGetInteger(attrptr, i) == 0) { 1230 LOGD("Bottom Margin Supported"); 1231 bottomsupported = true; 1232 break; 1233 } 1234 } 1235 } 1236 if ((attrptr = ippFindAttribute(response, "media-right-margin-supported", IPP_TAG_INTEGER)) != 1237 NULL) { 1238 for (i = 0; i < ippGetCount(attrptr); i++) { 1239 if (ippGetInteger(attrptr, i) == 0) { 1240 LOGD("Right Margin Supported"); 1241 rightsupported = true; 1242 break; 1243 } 1244 } 1245 } 1246 if ((attrptr = ippFindAttribute(response, "media-left-margin-supported", IPP_TAG_INTEGER)) != 1247 NULL) { 1248 for (i = 0; i < ippGetCount(attrptr); i++) { 1249 if (ippGetInteger(attrptr, i) == 0) { 1250 LOGD("Left Margin Supported"); 1251 leftsupported = true; 1252 break; 1253 } 1254 } 1255 } 1256 1257 if (topsupported && bottomsupported && rightsupported && leftsupported) { 1258 LOGD("full-bleed is supported"); 1259 capabilities->borderless = 1; 1260 } else { 1261 LOGD("full-bleed is NOT supported"); 1262 } 1263 1264 if ((attrptr = ippFindAttribute(response, "printer-device-id", IPP_TAG_TEXT)) != NULL) { 1265 if (strstr(ippGetString(attrptr, 0, NULL), "PCL3GUI") != NULL) { 1266 capabilities->inkjet = 1; 1267 } 1268 } else if (capabilities->borderless == 1) { 1269 capabilities->inkjet = 1; 1270 } 1271 1272 // determine if device prints pages face-down 1273 capabilities->faceDownTray = 1; 1274 if ((attrptr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_KEYWORD)) != NULL) { 1275 if (strstr(ippGetString(attrptr, 0, NULL), "face-up") != NULL) { 1276 capabilities->faceDownTray = 0; 1277 } 1278 } 1279 1280 // Determine supported document format details 1281 if ((attrptr = ippFindAttribute(response, "document-format-details-supported", 1282 IPP_TAG_KEYWORD)) != NULL) { 1283 for (i = 0; i < ippGetCount(attrptr); i++) { 1284 if (strcmp("document-source-application-name", ippGetString(attrptr, i, NULL)) == 0) { 1285 capabilities->docSourceAppName = 1; 1286 } else if ( 1287 strcmp("document-source-application-version", ippGetString(attrptr, i, NULL)) == 1288 0) { 1289 capabilities->docSourceAppVersion = 1; 1290 } else if (strcmp("document-source-os-name", ippGetString(attrptr, i, NULL)) == 0) { 1291 capabilities->docSourceOsName = 1; 1292 } else if (strcmp("document-source-os-version", ippGetString(attrptr, i, NULL)) == 0) { 1293 capabilities->docSourceOsVersion = 1; 1294 } 1295 } 1296 } 1297 debuglist_printerCapabilities(capabilities); 1298 } 1299 1300 // Used in parse_printerUris 1301 #define MAX_URIS 10 1302 typedef struct { 1303 const char *uri; 1304 int valid; 1305 } parsed_uri_t; 1306 1307 static void parse_printerUris(ipp_t *response, printer_capabilities_t *capabilities) { 1308 ipp_attribute_t *attrptr; 1309 int i; 1310 parsed_uri_t uris[MAX_URIS] = {0}; 1311 1312 if ((attrptr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL) { 1313 for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) { 1314 uris[i].uri = ippGetString(attrptr, i, NULL); 1315 uris[i].valid = true; 1316 } 1317 } 1318 1319 // If security or authentication is required (non-"none") at any URI, mark it invalid 1320 1321 if ((attrptr = ippFindAttribute(response, "uri-security-supported", IPP_TAG_KEYWORD)) != NULL) { 1322 for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) { 1323 if (strcmp("none", ippGetString(attrptr, i, NULL)) != 0) { 1324 LOGD("parse_printerUris %s invalid because sec=%s", uris[i].uri, 1325 ippGetString(attrptr, i, NULL)); 1326 uris[i].valid = false; 1327 } 1328 } 1329 } 1330 1331 if ((attrptr = ippFindAttribute(response, "uri-authentication-supported", IPP_TAG_KEYWORD)) 1332 != NULL) { 1333 for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) { 1334 // Allow "none" and "requesting-user-name" only 1335 if (strcmp("none", ippGetString(attrptr, i, NULL)) != 0 && 1336 strcmp("requesting-user-name", ippGetString(attrptr, i, NULL)) != 0) { 1337 LOGD("parse_printerUris %s invalid because auth=%s", uris[i].uri, 1338 ippGetString(attrptr, i, NULL)); 1339 uris[i].valid = false; 1340 } 1341 } 1342 } 1343 1344 // Find a valid URI and copy it into place. 1345 for (i = 0; i < MAX_URIS; i++) { 1346 if (uris[i].valid) { 1347 LOGD("parse_printerUris found %s", uris[i].uri); 1348 strlcpy(capabilities->printerUri, uris[i].uri, sizeof(capabilities->printerUri)); 1349 break; 1350 } 1351 } 1352 } 1353 1354 void debuglist_printerCapabilities(printer_capabilities_t *capabilities) { 1355 LOGD("printer make: %s", capabilities->make); 1356 LOGD("printer default media: %s", capabilities->mediaDefault); 1357 LOGD("canPrintPDF: %d", capabilities->canPrintPDF); 1358 LOGD("duplex: %d", capabilities->duplex); 1359 LOGD("canRotateDuplexBackPage: %d", capabilities->canRotateDuplexBackPage); 1360 LOGD("color: %d", capabilities->color); 1361 LOGD("canCopy: %d", capabilities->canCopy); 1362 LOGD("ippVersionMajor: %d", capabilities->ippVersionMajor); 1363 LOGD("ippVersionMinor: %d", capabilities->ippVersionMinor); 1364 LOGD("strip height: %d", capabilities->stripHeight); 1365 LOGD("faceDownTray: %d", capabilities->faceDownTray); 1366 } 1367 1368 void debuglist_printerStatus(printer_state_dyn_t *printer_state_dyn) { 1369 const char *decoded = "unknown"; 1370 if (printer_state_dyn->printer_status == PRINT_STATUS_INITIALIZING) { 1371 decoded = "Initializing"; 1372 } else if (printer_state_dyn->printer_status == PRINT_STATUS_SHUTTING_DOWN) { 1373 decoded = "Shutting Down"; 1374 } else if (printer_state_dyn->printer_status == PRINT_STATUS_UNABLE_TO_CONNECT) { 1375 decoded = "Unable To Connect"; 1376 } else if (printer_state_dyn->printer_status == PRINT_STATUS_UNKNOWN) { 1377 decoded = "Unknown"; 1378 } else if (printer_state_dyn->printer_status == PRINT_STATUS_OFFLINE) { 1379 decoded = "Offline"; 1380 } else if (printer_state_dyn->printer_status == PRINT_STATUS_IDLE) { 1381 decoded = "Idle"; 1382 } else if (printer_state_dyn->printer_status == PRINT_STATUS_PRINTING) { 1383 decoded = "Printing"; 1384 } else if (printer_state_dyn->printer_status == PRINT_STATUS_OUT_OF_PAPER) { 1385 decoded = "Out Of Paper"; 1386 } else if (printer_state_dyn->printer_status == PRINT_STATUS_OUT_OF_INK) { 1387 decoded = "Out Of Ink"; 1388 } else if (printer_state_dyn->printer_status == PRINT_STATUS_JAMMED) { 1389 decoded = "Jammed"; 1390 } else if (printer_state_dyn->printer_status == PRINT_STATUS_DOOR_OPEN) { 1391 decoded = "Door Open"; 1392 } else if (printer_state_dyn->printer_status == PRINT_STATUS_SVC_REQUEST) { 1393 decoded = "Service Request"; 1394 } 1395 LOGD("printer status: %d (%s)", printer_state_dyn->printer_status, decoded); 1396 1397 int idx = 0; 1398 for (idx = 0; idx < (PRINT_STATUS_MAX_STATE + 1); idx++) { 1399 if (PRINT_STATUS_MAX_STATE != printer_state_dyn->printer_reasons[idx]) { 1400 LOGD("printer_reasons (%d): %d", idx, printer_state_dyn->printer_reasons[idx]); 1401 } 1402 } 1403 } 1404 1405 http_t *ipp_cups_connect(const wprint_connect_info_t *connect_info, char *printer_uri, 1406 unsigned int uriLength) { 1407 const char *uri_path; 1408 http_t *curl_http = NULL; 1409 1410 if ((connect_info->uri_path == NULL) || (strlen(connect_info->uri_path) == 0)) { 1411 uri_path = DEFAULT_IPP_URI_RESOURCE; 1412 } else { 1413 uri_path = connect_info->uri_path; 1414 } 1415 1416 int ippPortNumber = ((connect_info->port_num == IPP_PORT) ? ippPort() : connect_info->port_num); 1417 1418 curl_http = httpConnect(connect_info->printer_addr, ippPortNumber); 1419 1420 httpSetTimeout(curl_http, DEFAULT_IPP_TIMEOUT, NULL, 0); 1421 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, uriLength, connect_info->uri_scheme, NULL, 1422 connect_info->printer_addr, ippPortNumber, uri_path); 1423 1424 if (curl_http == NULL) { 1425 LOGD("ipp_cups_connect failed addr=%s port=%d", connect_info->printer_addr, ippPortNumber); 1426 } 1427 return curl_http; 1428 } 1429 1430 /* 1431 * Send a request using cupsSendRequest(). Loop if we get NULL or CONTINUE. Does not delete 1432 * the request. 1433 */ 1434 static ipp_t *ippSendRequest(http_t *http, ipp_t *request, char *resource) { 1435 ipp_t *response = NULL; 1436 http_status_t result; 1437 bool retry; 1438 1439 do { 1440 retry = false; 1441 result = cupsSendRequest(http, request, resource, ippLength(request)); 1442 if (result != HTTP_ERROR) { 1443 response = cupsGetResponse(http, resource); 1444 result = httpGetStatus(http); 1445 } 1446 1447 if (result == HTTP_CONTINUE && response == NULL) { 1448 // We need to retry when this happens. 1449 LOGD("ippSendRequest: (Continue with NULL response) Retry"); 1450 retry = true; 1451 } else if (result == HTTP_ERROR || result >= HTTP_BAD_REQUEST) { 1452 _cupsSetHTTPError(result); 1453 break; 1454 } 1455 1456 if (http->state != HTTP_WAITING) { 1457 httpFlush(http); 1458 } 1459 } while (retry); 1460 1461 return response; 1462 } 1463 1464 /* 1465 * Call ippDoCupsIORequest, repeating if a failure occurs based on failure conditions, and 1466 * returning the response (or NULL if it failed). 1467 * 1468 * Does not free the request, and the caller must call ippDelete to free any valid response. 1469 */ 1470 ipp_t *ipp_doCupsRequest(http_t *http, ipp_t *request, char *http_resource, char *printer_uri) { 1471 ipp_status_t ipp_status; 1472 ipp_t *response = NULL; 1473 int service_unavailable_retry_count = 0; 1474 int bad_request_retry_count = 0; 1475 int internal_error_retry_count = 0; 1476 ipp_version_state ipp_version_supported = IPP_VERSION_RESOLVED; 1477 1478 // Fail if any of these parameters are NULL 1479 if (http == NULL || request == NULL || http_resource == NULL || printer_uri == NULL) { 1480 return NULL; 1481 } 1482 1483 do { 1484 // Give up immediately if wprint is done. 1485 if (!wprintIsRunning()) return NULL; 1486 1487 // This is a no-op until we hit the error IPP_VERSION_NOT_SUPPORTED and retry. 1488 if (set_ipp_version(request, printer_uri, http, ipp_version_supported) != 0) { 1489 // We tried to find the correct IPP version by doing a series of get attribute 1490 // requests but they all failed... we give up. 1491 LOGE("ipp_doCupsRequest: set_ipp_version!=0, version not set"); 1492 break; 1493 } 1494 1495 response = ippSendRequest(http, request, http_resource); 1496 if (response == NULL) { 1497 ipp_status = cupsLastError(); 1498 if (ipp_status == IPP_INTERNAL_ERROR || ipp_status == HTTP_ERROR) { 1499 internal_error_retry_count++; 1500 if (internal_error_retry_count > IPP_INTERNAL_ERROR_MAX_RETRIES) { 1501 break; 1502 } 1503 1504 LOGE("ipp_doCupsRequest: %s %d received, retry %d of %d", 1505 printer_uri, ipp_status, internal_error_retry_count, 1506 IPP_INTERNAL_ERROR_MAX_RETRIES); 1507 continue; 1508 } else if (ipp_status == IPP_SERVICE_UNAVAILABLE) { 1509 service_unavailable_retry_count++; 1510 if (service_unavailable_retry_count > IPP_SERVICE_ERROR_MAX_RETRIES) { 1511 break; 1512 } 1513 1514 LOGE("ipp_doCupsRequest: %s IPP_SERVICE_UNAVAILABLE received, retrying %d of %d", 1515 printer_uri, service_unavailable_retry_count, 1516 IPP_SERVICE_ERROR_MAX_RETRIES); 1517 continue; 1518 } else if (ipp_status == IPP_BAD_REQUEST) { 1519 bad_request_retry_count++; 1520 if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) { 1521 break; 1522 } 1523 1524 LOGD("ipp_doCupsRequest: %s IPP_BAD_REQUEST received. retry (%d) of (%d)", 1525 printer_uri, bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); 1526 continue; 1527 } else if (ipp_status == IPP_NOT_FOUND) { 1528 LOGE("ipp_doCupsRequest: %s IPP_NOT_FOUND received.", printer_uri); 1529 break; 1530 } 1531 } else { 1532 ipp_status = cupsLastError(); 1533 if (ipp_status == IPP_BAD_REQUEST) { 1534 bad_request_retry_count++; 1535 LOGE("ipp_doCupsRequest: %s IPP_BAD_REQUEST received. retry (%d) of (%d)", 1536 printer_uri, bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); 1537 if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) { 1538 break; 1539 } 1540 1541 ippDelete(response); 1542 response = NULL; 1543 continue; 1544 } else if (ipp_status == IPP_VERSION_NOT_SUPPORTED) { 1545 ipp_version_supported = IPP_VERSION_UNSUPPORTED; 1546 ippDelete(response); 1547 response = NULL; 1548 continue; 1549 } 1550 } 1551 break; 1552 } while (1); 1553 1554 return response; 1555 }