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 void parse_getMediaSupported(ipp_t *response, media_supported_t *media_supported) { 634 int i; 635 ipp_attribute_t *attrptr; 636 int sizes_idx = 0; 637 LOGD(" Entered getMediaSupported"); 638 639 media_size_t media_sizeTemp; 640 int idx = 0; 641 642 if ((attrptr = ippFindAttribute(response, "media-supported", IPP_TAG_KEYWORD)) != NULL) { 643 LOGD("media-supported found; number of values %d", ippGetCount(attrptr)); 644 for (i = 0; i < ippGetCount(attrptr); i++) { 645 idx = ipp_find_media_size(ippGetString(attrptr, i, NULL), &media_sizeTemp); 646 LOGD(" Temp - i: %d idx %d keyword: %s PT_size %d", i, idx, ippGetString( 647 attrptr, i, NULL), media_sizeTemp); 648 649 // Modified since anytime the find media size returned 0 it could either mean 650 // NOT found or na_letter. 651 if (idx >= 0) { 652 media_supported->media_size[sizes_idx] = media_sizeTemp; 653 media_supported->idxKeywordTranTable[sizes_idx] = idx; 654 sizes_idx++; 655 } 656 } 657 } else { 658 LOGD("media-supported not found"); 659 } 660 } 661 662 static void get_supportedPrinterResolutions(ipp_attribute_t *attrptr, 663 printer_capabilities_t *capabilities) { 664 int idx = 0; 665 int i; 666 for (i = 0; i < ippGetCount(attrptr); i++) { 667 ipp_res_t units; 668 int xres, yres; 669 xres = ippGetResolution(attrptr, i, &yres, &units); 670 if (units == IPP_RES_PER_INCH) { 671 if ((idx < MAX_RESOLUTIONS_SUPPORTED) && (xres == yres)) { 672 capabilities->supportedResolutions[idx] = xres; 673 idx++; 674 } 675 } 676 } 677 capabilities->numSupportedResolutions = idx; 678 } 679 680 void getResourceFromURI(const char *uri, char *resource, int resourcelen) { 681 char scheme[1024]; 682 char username[1024]; 683 char host[1024]; 684 int port; 685 httpSeparateURI(0, uri, scheme, 1024, username, 1024, host, 1024, &port, resource, resourcelen); 686 } 687 688 /* 689 * Add a new media type to a printer's collection of supported media types 690 */ 691 static void addMediaType(printer_capabilities_t *capabilities, media_type_t mediaType) { 692 int index; 693 for (index = 0; index < capabilities->numSupportedMediaTypes; index++) { 694 // Skip if already present 695 if (capabilities->supportedMediaTypes[index] == mediaType) return; 696 } 697 698 // Add if not found and not too many 699 if (capabilities->numSupportedMediaTypes < MAX_MEDIA_TYPES_SUPPORTED) { 700 capabilities->supportedMediaTypes[capabilities->numSupportedMediaTypes++] = mediaType; 701 } else { 702 LOGI("Hit MAX_MEDIA_TYPES_SUPPORTED while adding %d", mediaType); 703 } 704 } 705 706 void parse_printerAttributes(ipp_t *response, printer_capabilities_t *capabilities) { 707 int i, j; 708 ipp_attribute_t *attrptr; 709 710 LOGD("Entered parse_printerAttributes"); 711 712 media_supported_t media_supported; 713 for (i = 0; i <= PAGE_STATUS_MAX - 1; i++) { 714 media_supported.media_size[i] = 0; 715 } 716 parse_getMediaSupported(response, &media_supported); 717 718 parse_printerUris(response, capabilities); 719 720 LOGD("Media Supported: "); 721 int idx = 0; 722 capabilities->numSupportedMediaTypes = 0; 723 for (i = 0; i <= PAGE_STATUS_MAX - 1; i++) { 724 if (media_supported.media_size[i] != 0) { 725 capabilities->supportedMediaSizes[capabilities->numSupportedMediaSizes++] = 726 media_supported.media_size[i]; 727 idx = media_supported.idxKeywordTranTable[i]; 728 LOGD(" i %d, \tPT_Size: %d \tidx %d \tKeyword: %s", i, media_supported.media_size[i], 729 idx, SupportedMediaSizes[idx].PWGName); 730 } 731 } 732 733 if ((attrptr = ippFindAttribute(response, "printer-dns-sd-name", IPP_TAG_NAME)) != NULL) { 734 strlcpy(capabilities->name, ippGetString(attrptr, 0, NULL), sizeof(capabilities->name)); 735 } 736 737 if (!capabilities->name[0]) { 738 if ((attrptr = ippFindAttribute(response, "printer-info", IPP_TAG_TEXT)) != NULL) { 739 strlcpy(capabilities->name, ippGetString(attrptr, 0, NULL), sizeof(capabilities->name)); 740 } 741 } 742 743 if (!capabilities->name[0]) { 744 if ((attrptr = ippFindAttribute(response, "printer-name", IPP_TAG_TEXT)) != NULL) { 745 strlcpy(capabilities->name, ippGetString(attrptr, 0, NULL), sizeof(capabilities->name)); 746 } 747 } 748 749 if ((attrptr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT)) != NULL) { 750 strlcpy(capabilities->make, ippGetString(attrptr, 0, NULL), sizeof(capabilities->make)); 751 } 752 753 if ((attrptr = ippFindAttribute(response, "printer-uuid", IPP_TAG_URI)) != NULL) { 754 strlcpy(capabilities->uuid, ippGetString(attrptr, 0, NULL), sizeof(capabilities->uuid)); 755 } 756 757 if ((attrptr = ippFindAttribute(response, "printer-location", IPP_TAG_TEXT)) != NULL) { 758 strlcpy(capabilities->location, ippGetString(attrptr, 0, NULL), 759 sizeof(capabilities->location)); 760 } 761 762 if ((attrptr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL) { 763 strlcpy(capabilities->mediaDefault, ippGetString(attrptr, 0, NULL), 764 sizeof(capabilities->mediaDefault)); 765 } 766 767 if ((attrptr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN)) != NULL) { 768 if (ippGetBoolean(attrptr, 0)) { 769 capabilities->color = 1; 770 } 771 } 772 if ((attrptr = ippFindAttribute(response, "copies-supported", IPP_TAG_RANGE)) != NULL) { 773 int upper = 0; 774 for (i = 0; i < ippGetCount(attrptr); i++) { 775 ippGetRange(attrptr, i, &upper); 776 } 777 if (upper > 1) { 778 capabilities->canCopy = 1; 779 } 780 } 781 if ((attrptr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) != 782 NULL) { 783 for (i = 0; i < ippGetCount(attrptr); i++) { 784 if (strcmp("color", ippGetString(attrptr, i, NULL)) == 0) { 785 capabilities->color = 1; 786 } 787 } 788 } 789 790 char imagePCLm[] = "application/PCLm"; 791 char imagePWG[] = "image/pwg-raster"; 792 char imagePDF[] = "image/pdf"; 793 char applicationPDF[] = "application/pdf"; 794 795 if ((attrptr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) 796 != NULL) { 797 for (i = 0; i < ippGetCount(attrptr); i++) { 798 if (strcmp(imagePDF, ippGetString(attrptr, i, NULL)) == 0) { 799 capabilities->canPrintPDF = 1; 800 } else if (strcmp(applicationPDF, ippGetString(attrptr, i, NULL)) == 0) { 801 capabilities->canPrintPDF = 1; 802 } else if (strcmp(imagePCLm, ippGetString(attrptr, i, NULL)) == 0) { 803 capabilities->canPrintPCLm = 1; 804 } else if (strcmp(applicationPDF, ippGetString(attrptr, i, NULL)) == 0) { 805 capabilities->canPrintPDF = 1; 806 } else if (strcmp(imagePWG, ippGetString(attrptr, i, NULL)) == 0) { 807 capabilities->canPrintPWG = 1; 808 } 809 } 810 } 811 812 if ((attrptr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL) { 813 for (i = 0; i < ippGetCount(attrptr); i++) { 814 if (strcmp(IPP_SIDES_TWO_SIDED_SHORT_EDGE, ippGetString(attrptr, i, NULL)) == 0) { 815 capabilities->duplex = 1; 816 } else if (strcmp(IPP_SIDES_TWO_SIDED_LONG_EDGE, ippGetString(attrptr, i, NULL)) == 0) { 817 capabilities->duplex = 1; 818 } 819 } 820 } 821 822 // Look up supported media types 823 capabilities->numSupportedMediaTypes = 0; 824 if (((attrptr = ippFindAttribute(response, "media-type-supported", IPP_TAG_KEYWORD)) != NULL) 825 || ((attrptr = ippFindAttribute(response, "media-type-supported", IPP_TAG_NAME)) 826 != NULL)) { 827 for (i = 0; i < ippGetCount(attrptr); i++) { 828 if (strcasestr(ippGetString(attrptr, i, NULL), "photographic-glossy")) { 829 addMediaType(capabilities, MEDIA_PHOTO_GLOSSY); 830 } else if (strcasestr(ippGetString(attrptr, i, NULL), "photo")) { 831 addMediaType(capabilities, MEDIA_PHOTO); 832 } else if (strcasestr(ippGetString(attrptr, i, NULL), "stationery")) { 833 addMediaType(capabilities, MEDIA_PLAIN); 834 } 835 } 836 } 837 838 if (capabilities->numSupportedMediaTypes == 0) { 839 // If no recognized media types were found, fall back to all 3 just in case 840 addMediaType(capabilities, MEDIA_PLAIN); 841 addMediaType(capabilities, MEDIA_PHOTO); 842 addMediaType(capabilities, MEDIA_PHOTO_GLOSSY); 843 } 844 845 capabilities->numSupportedResolutions = 0; 846 // only appears that SMM supports the pclm-source-resolution-supported attribute 847 // if that is not present, use the printer-resolution-supported attribute to determine 848 // if 300DPI is supported 849 if ((attrptr = ippFindAttribute(response, "pclm-source-resolution-supported", 850 IPP_TAG_RESOLUTION)) != NULL) { 851 get_supportedPrinterResolutions(attrptr, capabilities); 852 } else if ((attrptr = ippFindAttribute(response, "printer-resolution-supported", 853 IPP_TAG_RESOLUTION)) != NULL) { 854 get_supportedPrinterResolutions(attrptr, capabilities); 855 } 856 857 char ipp10[] = "1.0"; 858 char ipp11[] = "1.1"; 859 char ipp20[] = "2.0"; 860 861 if ((attrptr = ippFindAttribute(response, "ipp-versions-supported", IPP_TAG_KEYWORD)) != NULL) { 862 unsigned char supportsIpp20 = 0; 863 unsigned char supportsIpp11 = 0; 864 unsigned char supportsIpp10 = 0; 865 866 for (i = 0; i < ippGetCount(attrptr); i++) { 867 if (strcmp(ipp10, ippGetString(attrptr, i, NULL)) == 0) { 868 supportsIpp10 = 1; 869 } else if (strcmp(ipp11, ippGetString(attrptr, i, NULL)) == 0) { 870 supportsIpp11 = 1; 871 } else if (strcmp(ipp20, ippGetString(attrptr, i, NULL)) == 0) { 872 supportsIpp20 = 1; 873 } else { 874 LOGD("found another ipp version. %s", ippGetString(attrptr, i, NULL)); 875 } 876 if (supportsIpp20) { 877 capabilities->ippVersionMajor = 2; 878 capabilities->ippVersionMinor = 0; 879 } else if (supportsIpp11) { 880 capabilities->ippVersionMajor = 1; 881 capabilities->ippVersionMinor = 1; 882 } else if (supportsIpp10) { 883 capabilities->ippVersionMajor = 1; 884 capabilities->ippVersionMinor = 0; 885 } else { 886 // default to 1.0 887 capabilities->ippVersionMajor = 1; 888 capabilities->ippVersionMinor = 0; 889 } 890 } 891 } 892 893 char epcl10[] = "1.0"; 894 if ((attrptr = ippFindAttribute(response, "epcl-version-supported", IPP_TAG_KEYWORD)) != NULL) { 895 for (i = 0; i < ippGetCount(attrptr); i++) { 896 LOGD("setting epcl_ipp_version (KEYWORD) %s", ippGetString(attrptr, i, NULL)); 897 898 // substring match because different devices implemented spec differently 899 if (strstr(ippGetString(attrptr, i, NULL), epcl10) != NULL) { 900 LOGD("setting epcl_ipp_version = 1"); 901 capabilities->ePclIppVersion = 1; 902 } 903 } 904 } 905 906 if ((attrptr = ippFindAttribute(response, "epcl-version-supported", IPP_TAG_TEXT)) != NULL) { 907 for (i = 0; i < ippGetCount(attrptr); i++) { 908 LOGD("setting epcl_ipp_verion (TEXT) %s", ippGetString(attrptr, i, NULL)); 909 910 // substring match because different devices implemented spec differently 911 if (strstr(ippGetString(attrptr, i, NULL), epcl10) != NULL) { 912 LOGD("setting epcl_ipp_verion = 1"); 913 capabilities->ePclIppVersion = 1; 914 } 915 } 916 } 917 918 if ((attrptr = ippFindAttribute(response, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != 919 NULL) { 920 for (i = 0; i < ippGetCount(attrptr); i++) { 921 LOGD("Gathering margins supported"); 922 923 ipp_t *collection = ippGetCollection(attrptr, i); 924 925 for (j = 0, attrptr = ippFirstAttribute(collection); 926 (j < 4) && (attrptr != NULL); attrptr = ippNextAttribute(collection)) { 927 if (strcmp("media-top-margin", ippGetName(attrptr)) == 0) { 928 capabilities->printerTopMargin = ippGetInteger(attrptr, 0); 929 } else if (strcmp("media-bottom-margin", ippGetName(attrptr)) == 0) { 930 capabilities->printerBottomMargin = ippGetInteger(attrptr, 0); 931 } else if (strcmp("media-left-margin", ippGetName(attrptr)) == 0) { 932 capabilities->printerLeftMargin = ippGetInteger(attrptr, 0); 933 } else if (strcmp("media-right-margin", ippGetName(attrptr)) == 0) { 934 capabilities->printerRightMargin = ippGetInteger(attrptr, 0); 935 } 936 } 937 } 938 } 939 940 if ((attrptr = ippFindAttribute(response, "media-size-name", IPP_TAG_KEYWORD)) != NULL) { 941 capabilities->isMediaSizeNameSupported = true; 942 } else { 943 capabilities->isMediaSizeNameSupported = false; 944 } 945 946 // is strip length supported? if so, stored in capabilities 947 if ((attrptr = ippFindAttribute(response, "pclm-strip-height-preferred", 948 IPP_TAG_INTEGER)) != NULL) { 949 LOGD("pclm-strip-height-preferred=%d", ippGetInteger(attrptr, 0)); 950 951 // if the strip height is 0, the device wants us to send the entire page in one band 952 // (according to ePCL spec). Since our code doesn't currently support generating an entire 953 // page in one band, set the strip height to the default value every device *should* support 954 // also, for some reason our code crashes when it attempts to generate strips at 512 or 955 // above. Therefore, limiting the upper bound strip height to 256 956 if (ippGetInteger(attrptr, 0) == 0 || ippGetInteger(attrptr, 0) > 256) { 957 capabilities->stripHeight = STRIPE_HEIGHT; 958 } else { 959 capabilities->stripHeight = ippGetInteger(attrptr, 0); 960 } 961 } else { 962 capabilities->stripHeight = STRIPE_HEIGHT; 963 } 964 965 // what is the preferred compression method - jpeg, flate, rle 966 if ((attrptr = ippFindAttribute(response, "pclm-compression-method-preferred", 967 IPP_TAG_KEYWORD)) != NULL) { 968 LOGD("pclm-compression-method-preferred=%s", ippGetString(attrptr, 0, NULL)); 969 } 970 971 // is device able to rotate back page for duplex jobs? 972 if ((attrptr = ippFindAttribute(response, "pclm-raster-back-side", IPP_TAG_KEYWORD)) != NULL) { 973 LOGD("pclm-raster-back-side=%s", ippGetString(attrptr, 0, NULL)); 974 if (strcmp(ippGetString(attrptr, 0, NULL), "rotated") == 0) { 975 capabilities->canRotateDuplexBackPage = 0; 976 LOGD("Device cannot rotate back page for duplex jobs."); 977 } else { 978 capabilities->canRotateDuplexBackPage = 1; 979 } 980 } 981 982 // look for full-bleed supported by looking for 0 on all margins 983 bool topsupported = false, bottomsupported = false, rightsupported = false, 984 leftsupported = false; 985 if ((attrptr = ippFindAttribute(response, "media-top-margin-supported", IPP_TAG_INTEGER)) != 986 NULL) { 987 for (i = 0; i < ippGetCount(attrptr); i++) { 988 if (ippGetInteger(attrptr, i) == 0) { 989 LOGD("Top Margin Supported"); 990 topsupported = true; 991 break; 992 } 993 } 994 } 995 if ((attrptr = ippFindAttribute(response, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != 996 NULL) { 997 for (i = 0; i < ippGetCount(attrptr); i++) { 998 if (ippGetInteger(attrptr, i) == 0) { 999 LOGD("Bottom Margin Supported"); 1000 bottomsupported = true; 1001 break; 1002 } 1003 } 1004 } 1005 if ((attrptr = ippFindAttribute(response, "media-right-margin-supported", IPP_TAG_INTEGER)) != 1006 NULL) { 1007 for (i = 0; i < ippGetCount(attrptr); i++) { 1008 if (ippGetInteger(attrptr, i) == 0) { 1009 LOGD("Right Margin Supported"); 1010 rightsupported = true; 1011 break; 1012 } 1013 } 1014 } 1015 if ((attrptr = ippFindAttribute(response, "media-left-margin-supported", IPP_TAG_INTEGER)) != 1016 NULL) { 1017 for (i = 0; i < ippGetCount(attrptr); i++) { 1018 if (ippGetInteger(attrptr, i) == 0) { 1019 LOGD("Left Margin Supported"); 1020 leftsupported = true; 1021 break; 1022 } 1023 } 1024 } 1025 1026 if (topsupported && bottomsupported && rightsupported && leftsupported) { 1027 LOGD("full-bleed is supported"); 1028 capabilities->borderless = 1; 1029 } else { 1030 LOGD("full-bleed is NOT supported"); 1031 } 1032 1033 if ((attrptr = ippFindAttribute(response, "printer-device-id", IPP_TAG_TEXT)) != NULL) { 1034 if (strstr(ippGetString(attrptr, 0, NULL), "PCL3GUI") != NULL) { 1035 capabilities->inkjet = 1; 1036 } 1037 } else if (capabilities->borderless == 1) { 1038 capabilities->inkjet = 1; 1039 } 1040 1041 // determine if device prints pages face-down 1042 capabilities->faceDownTray = 1; 1043 if ((attrptr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_KEYWORD)) != NULL) { 1044 if (strstr(ippGetString(attrptr, 0, NULL), "face-up") != NULL) { 1045 capabilities->faceDownTray = 0; 1046 } 1047 } 1048 1049 // Determine supported document format details 1050 if ((attrptr = ippFindAttribute(response, "document-format-details-supported", 1051 IPP_TAG_KEYWORD)) != NULL) { 1052 for (i = 0; i < ippGetCount(attrptr); i++) { 1053 if (strcmp("document-source-application-name", ippGetString(attrptr, i, NULL)) == 0) { 1054 capabilities->docSourceAppName = 1; 1055 } else if ( 1056 strcmp("document-source-application-version", ippGetString(attrptr, i, NULL)) == 1057 0) { 1058 capabilities->docSourceAppVersion = 1; 1059 } else if (strcmp("document-source-os-name", ippGetString(attrptr, i, NULL)) == 0) { 1060 capabilities->docSourceOsName = 1; 1061 } else if (strcmp("document-source-os-version", ippGetString(attrptr, i, NULL)) == 0) { 1062 capabilities->docSourceOsVersion = 1; 1063 } 1064 } 1065 } 1066 debuglist_printerCapabilities(capabilities); 1067 } 1068 1069 // Used in parse_printerUris 1070 #define MAX_URIS 10 1071 typedef struct { 1072 const char *uri; 1073 int valid; 1074 } parsed_uri_t; 1075 1076 static void parse_printerUris(ipp_t *response, printer_capabilities_t *capabilities) { 1077 ipp_attribute_t *attrptr; 1078 int i; 1079 parsed_uri_t uris[MAX_URIS] = {0}; 1080 1081 if ((attrptr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL) { 1082 for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) { 1083 uris[i].uri = ippGetString(attrptr, i, NULL); 1084 uris[i].valid = true; 1085 } 1086 } 1087 1088 // If security or authentication is required (non-"none") at any URI, mark it invalid 1089 1090 if ((attrptr = ippFindAttribute(response, "uri-security-supported", IPP_TAG_KEYWORD)) != NULL) { 1091 for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) { 1092 if (strcmp("none", ippGetString(attrptr, i, NULL)) != 0) { 1093 LOGD("parse_printerUris %s invalid because sec=%s", uris[i].uri, 1094 ippGetString(attrptr, i, NULL)); 1095 uris[i].valid = false; 1096 } 1097 } 1098 } 1099 1100 if ((attrptr = ippFindAttribute(response, "uri-authentication-supported", IPP_TAG_KEYWORD)) 1101 != NULL) { 1102 for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) { 1103 // Allow "none" and "requesting-user-name" only 1104 if (strcmp("none", ippGetString(attrptr, i, NULL)) != 0 && 1105 strcmp("requesting-user-name", ippGetString(attrptr, i, NULL)) != 0) { 1106 LOGD("parse_printerUris %s invalid because auth=%s", uris[i].uri, 1107 ippGetString(attrptr, i, NULL)); 1108 uris[i].valid = false; 1109 } 1110 } 1111 } 1112 1113 // Find a valid URI and copy it into place. 1114 for (i = 0; i < MAX_URIS; i++) { 1115 if (uris[i].valid) { 1116 LOGD("parse_printerUris found %s", uris[i].uri); 1117 strlcpy(capabilities->printerUri, uris[i].uri, sizeof(capabilities->printerUri)); 1118 break; 1119 } 1120 } 1121 } 1122 1123 void debuglist_printerCapabilities(printer_capabilities_t *capabilities) { 1124 LOGD("printer make: %s", capabilities->make); 1125 LOGD("printer default media: %s", capabilities->mediaDefault); 1126 LOGD("canPrintPDF: %d", capabilities->canPrintPDF); 1127 LOGD("duplex: %d", capabilities->duplex); 1128 LOGD("canRotateDuplexBackPage: %d", capabilities->canRotateDuplexBackPage); 1129 LOGD("color: %d", capabilities->color); 1130 LOGD("canCopy: %d", capabilities->canCopy); 1131 LOGD("ippVersionMajor: %d", capabilities->ippVersionMajor); 1132 LOGD("ippVersionMinor: %d", capabilities->ippVersionMinor); 1133 LOGD("strip height: %d", capabilities->stripHeight); 1134 LOGD("faceDownTray: %d", capabilities->faceDownTray); 1135 } 1136 1137 void debuglist_printerStatus(printer_state_dyn_t *printer_state_dyn) { 1138 const char *decoded = "unknown"; 1139 if (printer_state_dyn->printer_status == PRINT_STATUS_INITIALIZING) { 1140 decoded = "Initializing"; 1141 } else if (printer_state_dyn->printer_status == PRINT_STATUS_SHUTTING_DOWN) { 1142 decoded = "Shutting Down"; 1143 } else if (printer_state_dyn->printer_status == PRINT_STATUS_UNABLE_TO_CONNECT) { 1144 decoded = "Unable To Connect"; 1145 } else if (printer_state_dyn->printer_status == PRINT_STATUS_UNKNOWN) { 1146 decoded = "Unknown"; 1147 } else if (printer_state_dyn->printer_status == PRINT_STATUS_OFFLINE) { 1148 decoded = "Offline"; 1149 } else if (printer_state_dyn->printer_status == PRINT_STATUS_IDLE) { 1150 decoded = "Idle"; 1151 } else if (printer_state_dyn->printer_status == PRINT_STATUS_PRINTING) { 1152 decoded = "Printing"; 1153 } else if (printer_state_dyn->printer_status == PRINT_STATUS_OUT_OF_PAPER) { 1154 decoded = "Out Of Paper"; 1155 } else if (printer_state_dyn->printer_status == PRINT_STATUS_OUT_OF_INK) { 1156 decoded = "Out Of Ink"; 1157 } else if (printer_state_dyn->printer_status == PRINT_STATUS_JAMMED) { 1158 decoded = "Jammed"; 1159 } else if (printer_state_dyn->printer_status == PRINT_STATUS_DOOR_OPEN) { 1160 decoded = "Door Open"; 1161 } else if (printer_state_dyn->printer_status == PRINT_STATUS_SVC_REQUEST) { 1162 decoded = "Service Request"; 1163 } 1164 LOGD("printer status: %d (%s)", printer_state_dyn->printer_status, decoded); 1165 1166 int idx = 0; 1167 for (idx = 0; idx < (PRINT_STATUS_MAX_STATE + 1); idx++) { 1168 if (PRINT_STATUS_MAX_STATE != printer_state_dyn->printer_reasons[idx]) { 1169 LOGD("printer_reasons (%d): %d", idx, printer_state_dyn->printer_reasons[idx]); 1170 } 1171 } 1172 } 1173 1174 http_t *ipp_cups_connect(const wprint_connect_info_t *connect_info, char *printer_uri, 1175 unsigned int uriLength) { 1176 const char *uri_path; 1177 http_t *curl_http = NULL; 1178 1179 if ((connect_info->uri_path == NULL) || (strlen(connect_info->uri_path) == 0)) { 1180 uri_path = DEFAULT_IPP_URI_RESOURCE; 1181 } else { 1182 uri_path = connect_info->uri_path; 1183 } 1184 1185 int ippPortNumber = ((connect_info->port_num == IPP_PORT) ? ippPort() : connect_info->port_num); 1186 1187 if (strstr(connect_info->uri_scheme,IPPS_PREFIX) != NULL) { 1188 curl_http = httpConnectEncrypt(connect_info->printer_addr, ippPortNumber, HTTP_ENCRYPTION_ALWAYS); 1189 1190 // If ALWAYS doesn't work, fall back to REQUIRED 1191 if (curl_http == NULL) { 1192 curl_http = httpConnectEncrypt(connect_info->printer_addr, ippPortNumber, HTTP_ENCRYPT_REQUIRED); 1193 } 1194 } else { 1195 curl_http = httpConnectEncrypt(connect_info->printer_addr, ippPortNumber, HTTP_ENCRYPTION_IF_REQUESTED); 1196 } 1197 1198 httpSetTimeout(curl_http, (double)connect_info->timeout / 1000, NULL, 0); 1199 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, uriLength, connect_info->uri_scheme, NULL, 1200 connect_info->printer_addr, ippPortNumber, uri_path); 1201 1202 if (curl_http == NULL) { 1203 LOGD("ipp_cups_connect failed addr=%s port=%d", connect_info->printer_addr, ippPortNumber); 1204 } 1205 return curl_http; 1206 } 1207 1208 /* 1209 * Send a request using cupsSendRequest(). Loop if we get NULL or CONTINUE. Does not delete 1210 * the request. 1211 */ 1212 static ipp_t *ippSendRequest(http_t *http, ipp_t *request, char *resource) { 1213 ipp_t *response = NULL; 1214 http_status_t result; 1215 bool retry; 1216 1217 do { 1218 retry = false; 1219 result = cupsSendRequest(http, request, resource, ippLength(request)); 1220 if (result != HTTP_ERROR) { 1221 response = cupsGetResponse(http, resource); 1222 result = httpGetStatus(http); 1223 } 1224 1225 if (result == HTTP_CONTINUE && response == NULL) { 1226 // We need to retry when this happens. 1227 LOGD("ippSendRequest: (Continue with NULL response) Retry"); 1228 retry = true; 1229 } else if (result == HTTP_ERROR || result >= HTTP_BAD_REQUEST) { 1230 _cupsSetHTTPError(result); 1231 break; 1232 } 1233 1234 if (http->state != HTTP_WAITING) { 1235 httpFlush(http); 1236 } 1237 } while (retry); 1238 1239 return response; 1240 } 1241 1242 /* 1243 * Call ippDoCupsIORequest, repeating if a failure occurs based on failure conditions, and 1244 * returning the response (or NULL if it failed). 1245 * 1246 * Does not free the request, and the caller must call ippDelete to free any valid response. 1247 */ 1248 ipp_t *ipp_doCupsRequest(http_t *http, ipp_t *request, char *http_resource, char *printer_uri) { 1249 ipp_status_t ipp_status; 1250 ipp_t *response = NULL; 1251 int service_unavailable_retry_count = 0; 1252 int bad_request_retry_count = 0; 1253 int internal_error_retry_count = 0; 1254 ipp_version_state ipp_version_supported = IPP_VERSION_RESOLVED; 1255 1256 // Fail if any of these parameters are NULL 1257 if (http == NULL || request == NULL || http_resource == NULL || printer_uri == NULL) { 1258 return NULL; 1259 } 1260 1261 do { 1262 // Give up immediately if wprint is done. 1263 if (!wprintIsRunning()) return NULL; 1264 1265 // This is a no-op until we hit the error IPP_VERSION_NOT_SUPPORTED and retry. 1266 if (set_ipp_version(request, printer_uri, http, ipp_version_supported) != 0) { 1267 // We tried to find the correct IPP version by doing a series of get attribute 1268 // requests but they all failed... we give up. 1269 LOGE("ipp_doCupsRequest: set_ipp_version!=0, version not set"); 1270 break; 1271 } 1272 1273 response = ippSendRequest(http, request, http_resource); 1274 if (response == NULL) { 1275 ipp_status = cupsLastError(); 1276 if (ipp_status == IPP_INTERNAL_ERROR || ipp_status == HTTP_ERROR) { 1277 internal_error_retry_count++; 1278 if (internal_error_retry_count > IPP_INTERNAL_ERROR_MAX_RETRIES) { 1279 break; 1280 } 1281 1282 LOGE("ipp_doCupsRequest: %s %d received, retry %d of %d", 1283 printer_uri, ipp_status, internal_error_retry_count, 1284 IPP_INTERNAL_ERROR_MAX_RETRIES); 1285 continue; 1286 } else if (ipp_status == IPP_SERVICE_UNAVAILABLE) { 1287 service_unavailable_retry_count++; 1288 if (service_unavailable_retry_count > IPP_SERVICE_ERROR_MAX_RETRIES) { 1289 break; 1290 } 1291 1292 LOGE("ipp_doCupsRequest: %s IPP_SERVICE_UNAVAILABLE received, retrying %d of %d", 1293 printer_uri, service_unavailable_retry_count, 1294 IPP_SERVICE_ERROR_MAX_RETRIES); 1295 continue; 1296 } else if (ipp_status == IPP_BAD_REQUEST) { 1297 bad_request_retry_count++; 1298 if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) { 1299 break; 1300 } 1301 1302 LOGD("ipp_doCupsRequest: %s IPP_BAD_REQUEST received. retry (%d) of (%d)", 1303 printer_uri, bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); 1304 continue; 1305 } else if (ipp_status == IPP_NOT_FOUND) { 1306 LOGE("ipp_doCupsRequest: %s IPP_NOT_FOUND received.", printer_uri); 1307 break; 1308 } 1309 } else { 1310 ipp_status = cupsLastError(); 1311 if (ipp_status == IPP_BAD_REQUEST) { 1312 bad_request_retry_count++; 1313 LOGE("ipp_doCupsRequest: %s IPP_BAD_REQUEST received. retry (%d) of (%d)", 1314 printer_uri, bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); 1315 if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) { 1316 break; 1317 } 1318 1319 ippDelete(response); 1320 response = NULL; 1321 continue; 1322 } else if (ipp_status == IPP_VERSION_NOT_SUPPORTED) { 1323 ipp_version_supported = IPP_VERSION_UNSUPPORTED; 1324 ippDelete(response); 1325 response = NULL; 1326 continue; 1327 } 1328 } 1329 break; 1330 } while (1); 1331 1332 return response; 1333 }