/* * Copyright (C) 2016 The Android Open Source Project * Copyright (C) 2016 Mopria Alliance, Inc. * Copyright (C) 2013 Hewlett-Packard Development Company, L.P. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "lib_wprint.h" #include "cups.h" #include "http-private.h" #include "ipphelper.h" #include "wprint_debug.h" #include "ipp_print.h" #include "../plugins/media.h" #define TAG "ipphelper" /* * Get the IPP version of the given printer */ static status_t determine_ipp_version(char *, http_t *); /* * Tests IPP versions and sets it to the latest working version */ static status_t test_and_set_ipp_version(char *, http_t *, int, int); /* * Parses supported IPP versions from the IPP response and copies them into ippVersions */ static void parse_IPPVersions(ipp_t *response, ipp_version_supported_t *ippVersions); /* * Parses printer URIs from the IPP response and copies them into capabilities */ static void parse_printerUris(ipp_t *response, printer_capabilities_t *capabilities); /* * Known media sizes. * * A note on rounding: In some cases the Android-specified width (in mils) is rounded down. * This causes artifacts in libjpeg-turbo when rendering to the correct width, so in these * cases we override with a rounded-up value. */ struct MediaSizeTableElement SupportedMediaSizes[SUPPORTED_MEDIA_SIZE_COUNT] = { { US_LETTER, "LETTER", 8500, 11000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_letter_8.5x11in" }, { US_LEGAL, "LEGAL", 8500, 14000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_legal_8.5x14in" }, { LEDGER, "LEDGER", 11000, 17000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_ledger_11x17in" }, { INDEX_CARD_5X7, "5X7", 5000, 7000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_5x7_5x7in" }, // Android system uses width of 11690 { ISO_A3, "A3", 11694, 16540, 297, 420, "iso_a3_297x420mm" }, // Android system uses width of 8267 { ISO_A4, "A4", 8268, 11692, 210, 297, "iso_a4_210x297mm" }, { ISO_A5, "A5", 5830, 8270, 148, 210, "iso_a5_148x210mm" }, // Android system uses width of 10118 { JIS_B4, "JIS B4", 10119, 14331, 257, 364, "jis_b4_257x364mm" }, // Android system uses width of 7165 { JIS_B5, "JIS B5", 7167, 10118, 182, 257, "jis_b5_182x257mm" }, { US_GOVERNMENT_LETTER, "8x10", 8000, 10000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_govt-letter_8x10in" }, { INDEX_CARD_4X6, "4x6", 4000, 6000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_index-4x6_4x6in" }, { JPN_HAGAKI_PC, "JPOST", 3940, 5830, 100, 148, "jpn_hagaki_100x148mm" }, { PHOTO_89X119, "89X119", 3504, 4685, 89, 119, "om_dsc-photo_89x119mm" }, { CARD_54X86, "54X86", 2126, 3386, 54, 86, "om_card_54x86mm" }, { OE_PHOTO_L, "L", 3500, 5000, UNKNOWN_VALUE, UNKNOWN_VALUE, "oe_photo-l_3.5x5in" } }; typedef struct { double Lower; double Upper; } media_dimension_mm_t; static const char *__request_ipp_version[] = {"ipp-versions-supported"}; static int __ipp_version_major = 2; static int __ipp_version_minor = 0; status_t set_ipp_version(ipp_t *op_to_set, char *printer_uri, http_t *http, ipp_version_state use_existing_version) { LOGD("set_ipp_version(): Enter %d", use_existing_version); if (op_to_set == NULL) { return ERROR; } switch (use_existing_version) { case NEW_REQUEST_SEQUENCE: __ipp_version_major = 2; __ipp_version_minor = 0; break; case IPP_VERSION_RESOLVED: break; case IPP_VERSION_UNSUPPORTED: if (determine_ipp_version(printer_uri, http) != 0) { return ERROR; } break; } ippSetVersion(op_to_set, __ipp_version_major, __ipp_version_minor); LOGD("set_ipp_version(): Done"); return OK; } static status_t determine_ipp_version(char *printer_uri, http_t *http) { LOGD("determine_ipp_version(): Enter printer_uri = %s", printer_uri); if (http == NULL) { LOGE("determine_ipp_version(): http is NULL cannot continue"); return ERROR; } if ((test_and_set_ipp_version(printer_uri, http, 1, 1) == OK) || (test_and_set_ipp_version(printer_uri, http, 1, 0) == OK) || (test_and_set_ipp_version(printer_uri, http, 2, 0) == OK)) { LOGD("successfully set ipp version."); } else { LOGD("could not get ipp version using any known ipp version."); return ERROR; } return OK; } static status_t test_and_set_ipp_version(char *printer_uri, http_t *http, int major, int minor) { status_t return_value = ERROR; int service_unavailable_retry_count = 0; int bad_request_retry_count = 0; ipp_t *request = NULL; ipp_t *response; ipp_version_supported_t ippVersions; char http_resource[1024]; int op = IPP_GET_PRINTER_ATTRIBUTES; LOGD("test_and_set_ipp_version(): Enter %d - %d", major, minor); memset(&ippVersions, 0, sizeof(ipp_version_supported_t)); getResourceFromURI(printer_uri, http_resource, 1024); do { request = ippNewRequest(op); ippSetVersion(request, major, minor); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", sizeof(__request_ipp_version) / sizeof(__request_ipp_version[0]), NULL, __request_ipp_version); if ((response = cupsDoRequest(http, request, http_resource)) == NULL) { ipp_status_t ipp_status = cupsLastError(); LOGD("test_and_set_ipp_version: response is null: ipp_status %d %s", ipp_status, ippErrorString(ipp_status)); if (ipp_status == IPP_INTERNAL_ERROR) { LOGE("test_and_set_ipp_version: 1280 received, bailing..."); break; } else if ((ipp_status == IPP_SERVICE_UNAVAILABLE) && (service_unavailable_retry_count < IPP_SERVICE_ERROR_MAX_RETRIES)) { LOGE("test_and_set_ipp_version: 1282 received, retrying %d of %d", service_unavailable_retry_count, IPP_SERVICE_ERROR_MAX_RETRIES); service_unavailable_retry_count++; continue; } else if (ipp_status == IPP_BAD_REQUEST) { LOGE("test_and_set_ipp_version: IPP_Status of IPP_BAD_REQUEST " "received. retry (%d) of (%d)", bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) { break; } bad_request_retry_count++; continue; } else if (ipp_status == IPP_NOT_FOUND) { LOGE("test_and_set_ipp_version: IPP_Status of IPP_NOT_FOUND received"); break; } return_value = ERROR; } else { ipp_status_t ipp_status = cupsLastError(); LOGD("ipp CUPS last ERROR: %d, %s", ipp_status, ippErrorString(ipp_status)); if (ipp_status == IPP_BAD_REQUEST) { LOGD("IPP_Status of IPP_BAD_REQUEST received. retry (%d) of (%d)", bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) { break; } bad_request_retry_count++; ippDelete(response); continue; } parse_IPPVersions(response, &ippVersions); if (ippVersions.supportsIpp20) { __ipp_version_major = 2; __ipp_version_minor = 0; return_value = OK; LOGD("test_and_set_ipp_version(): ipp version set to %d,%d", __ipp_version_major, __ipp_version_minor); } else if (ippVersions.supportsIpp11) { __ipp_version_major = 1; __ipp_version_minor = 1; return_value = OK; LOGD("test_and_set_ipp_version(): ipp version set to %d,%d", __ipp_version_major, __ipp_version_minor); } else if (ippVersions.supportsIpp10) { __ipp_version_major = 1; __ipp_version_minor = 0; return_value = OK; LOGD("test_and_set_ipp_version(): ipp version set to %d,%d", __ipp_version_major, __ipp_version_minor); } else { LOGD("test_and_set_ipp_version: ipp version not found"); return_value = ERROR; } } if (response != NULL) ippDelete(response); break; } while (1); return return_value; } ipp_status_t get_PrinterState(http_t *http, char *printer_uri, printer_state_dyn_t *printer_state_dyn, ipp_pstate_t *printer_state) { LOGD("get_PrinterState(): Enter"); // Requested printer attributes static const char *pattrs[] = {"printer-make-and-model", "printer-state", "printer-state-message", "printer-state-reasons"}; ipp_t *request = NULL; ipp_t *response = NULL; ipp_status_t ipp_status = IPP_OK; int op = IPP_GET_PRINTER_ATTRIBUTES; char http_resource[1024]; getResourceFromURI(printer_uri, http_resource, 1024); if (printer_state_dyn == NULL) { LOGE("get_PrinterState(): printer_state_dyn is null"); return ipp_status; } if (printer_state) { *printer_state = IPP_PRINTER_STOPPED; } else { LOGE("get_PrinterState(): printer_state is null"); } request = ippNewRequest(op); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]), NULL, pattrs); if ((response = ipp_doCupsRequest(http, request, http_resource, printer_uri)) == NULL) { ipp_status = cupsLastError(); LOGE("get_PrinterState(): response is null: ipp_status %d", ipp_status); printer_state_dyn->printer_status = PRINT_STATUS_UNABLE_TO_CONNECT; printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT; } else { ipp_status = cupsLastError(); LOGD("ipp CUPS last ERROR: %d, %s", ipp_status, ippErrorString(ipp_status)); get_PrinterStateReason(response, printer_state, printer_state_dyn); LOGD("get_PrinterState(): printer_state_dyn->printer_status: %d", printer_state_dyn->printer_status); } LOGD("get_PrinterState(): exit http->fd %d, ipp_status %d, printer_state %d", http->fd, ipp_status, printer_state_dyn->printer_status); ippDelete(request); ippDelete(response); return ipp_status; } void get_PrinterStateReason(ipp_t *response, ipp_pstate_t *printer_state, printer_state_dyn_t *printer_state_dyn) { LOGD("get_PrinterStateReason(): Enter"); ipp_attribute_t *attrptr; int reason_idx = 0; int idx = 0; ipp_pstate_t printer_ippstate = IPP_PRINTER_IDLE; if ((attrptr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) == NULL) { LOGE("get_PrinterStateReason printer-state null"); printer_state_dyn->printer_status = PRINT_STATUS_UNABLE_TO_CONNECT; printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT; } else { printer_ippstate = (ipp_pstate_t) ippGetInteger(attrptr, 0); *printer_state = printer_ippstate; LOGD("get_PrinterStateReason printer-state: %d", printer_ippstate); // set the printer_status; they may be modified based on the status reasons below. switch (printer_ippstate) { case IPP_PRINTER_IDLE: printer_state_dyn->printer_status = PRINT_STATUS_IDLE; break; case IPP_PRINTER_PROCESSING: printer_state_dyn->printer_status = PRINT_STATUS_PRINTING; break; case IPP_PRINTER_STOPPED: printer_state_dyn->printer_status = PRINT_STATUS_SVC_REQUEST; break; } } if ((attrptr = ippFindAttribute(response, "printer-state-reasons", IPP_TAG_KEYWORD)) == NULL) { LOGE(" get_PrinterStateReason printer-state reason null"); printer_state_dyn->printer_status = PRINT_STATUS_UNABLE_TO_CONNECT; printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT; } else { for (idx = 0; idx < ippGetCount(attrptr); idx++) { // Per RFC2911 any of these can have -error, -warning, or -report appended to end LOGD("get_PrinterStateReason printer-state-reason: %s", ippGetString(attrptr, idx, NULL)); if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_NONE, strlen(IPP_PRNT_STATE_NONE)) == 0) { switch (printer_ippstate) { case IPP_PRINTER_IDLE: printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_IDLE; break; case IPP_PRINTER_PROCESSING: printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_PRINTING; break; case IPP_PRINTER_STOPPED: // should this be PRINT_STATUS_SVC_REQUEST printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_UNKNOWN; break; } } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_SPOOL_FULL, strlen(IPP_PRNT_STATE_SPOOL_FULL)) == 0) { switch (printer_ippstate) { case IPP_PRINTER_IDLE: printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_UNKNOWN; break; case IPP_PRINTER_PROCESSING: printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_PRINTING; break; case IPP_PRINTER_STOPPED: // should this be PRINT_STATUS_SVC_REQUEST printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_UNKNOWN; break; } } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_MARKER_SUPPLY_LOW, strlen(IPP_PRNT_STATE_MARKER_SUPPLY_LOW)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_LOW_ON_INK; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_TONER_LOW, strlen(IPP_PRNT_STATE_TONER_LOW)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_LOW_ON_TONER; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_OTHER_WARN, strlen(IPP_PRNT_STATE_OTHER_WARN)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_UNKNOWN; } else { // check blocking cases if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_MEDIA_NEEDED, strlen(IPP_PRNT_STATE_MEDIA_NEEDED)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_OUT_OF_PAPER; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_MEDIA_EMPTY, strlen(IPP_PRNT_STATE_MEDIA_EMPTY)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_OUT_OF_PAPER; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_TONER_EMPTY, strlen(IPP_PRNT_STATE_TONER_EMPTY)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_OUT_OF_TONER; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_MARKER_SUPPLY_EMPTY, strlen(IPP_PRNT_STATE_MARKER_SUPPLY_EMPTY)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_OUT_OF_INK; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_DOOR_OPEN, strlen(IPP_PRNT_STATE_DOOR_OPEN)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_DOOR_OPEN; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_COVER_OPEN, strlen(IPP_PRNT_STATE_COVER_OPEN)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_DOOR_OPEN; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_MEDIA_JAM, strlen(IPP_PRNT_STATE_MEDIA_JAM)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_JAMMED; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_SHUTDOWN, strlen(IPP_PRNT_SHUTDOWN)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_SHUTTING_DOWN; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_STATE_OTHER_ERR, strlen(IPP_PRNT_STATE_OTHER_ERR)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_SVC_REQUEST; } else if (strncmp(ippGetString(attrptr, idx, NULL), IPP_PRNT_PAUSED, strlen(IPP_PRNT_PAUSED)) == 0) { printer_state_dyn->printer_reasons[reason_idx++] = PRINT_STATUS_UNKNOWN; } } } // end of reasons loop } } static void print_col(ipp_t *col) { int i; ipp_attribute_t *attr; LOGD("{"); for (attr = ippFirstAttribute(col); attr; attr = ippNextAttribute(col)) { switch (ippGetValueTag(attr)) { case IPP_TAG_INTEGER: case IPP_TAG_ENUM: for (i = 0; i < ippGetCount(attr); i++) { LOGD(" %s(%s%s)= %d ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), ippGetInteger(attr, i)); } break; case IPP_TAG_BOOLEAN: for (i = 0; i < ippGetCount(attr); i++) { if (ippGetBoolean(attr, i)) { LOGD(" %s(%s%s)= true ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); } else { LOGD(" %s(%s%s)= false ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); } } break; case IPP_TAG_NOVALUE: LOGD(" %s(%s%s)= novalue", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); break; case IPP_TAG_RANGE: for (i = 0; i < ippGetCount(attr); i++) { int lower, upper; lower = ippGetRange(attr, i, &upper); LOGD(" %s(%s%s)= %d-%d ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), lower, upper); } break; case IPP_TAG_RESOLUTION: for (i = 0; i < ippGetCount(attr); i++) { ipp_res_t units; int xres, yres; xres = ippGetResolution(attr, i, &yres, &units); LOGD(" %s(%s%s)= %dx%d%s ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpc"); } break; case IPP_TAG_STRING: case IPP_TAG_TEXT: case IPP_TAG_NAME: case IPP_TAG_KEYWORD: case IPP_TAG_CHARSET: case IPP_TAG_URI: case IPP_TAG_MIMETYPE: case IPP_TAG_LANGUAGE: for (i = 0; i < ippGetCount(attr); i++) { LOGD(" %s(%s%s)= \"%s\" ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), ippGetString(attr, i, NULL)); } break; case IPP_TAG_TEXTLANG: case IPP_TAG_NAMELANG: for (i = 0; i < ippGetCount(attr); i++) { const char *charset; const char *text; text = ippGetString(attr, i, &charset); LOGD(" %s(%s%s)= \"%s\",%s ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), text, charset); } break; case IPP_TAG_BEGIN_COLLECTION: for (i = 0; i < ippGetCount(attr); i++) { print_col(ippGetCollection(attr, i)); } break; default: break; } } LOGD("}"); } void print_attr(ipp_attribute_t *attr) { int i; if (ippGetName(attr) == NULL) { return; } switch (ippGetValueTag(attr)) { case IPP_TAG_INTEGER: case IPP_TAG_ENUM: for (i = 0; i < ippGetCount(attr); i++) { LOGD("%s (%s%s) = %d ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), ippGetInteger(attr, i)); } break; case IPP_TAG_BOOLEAN: for (i = 0; i < ippGetCount(attr); i++) { if (ippGetBoolean(attr, i)) { LOGD("%s (%s%s) = true ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); } else { LOGD("%s (%s%s) = false ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); } } break; case IPP_TAG_NOVALUE: LOGD("%s (%s%s) = novalue", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); break; case IPP_TAG_RANGE: for (i = 0; i < ippGetCount(attr); i++) { int lower, upper; lower = ippGetRange(attr, i, &upper); LOGD("%s (%s%s) = %d-%d ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), lower, upper); } break; case IPP_TAG_RESOLUTION: for (i = 0; i < ippGetCount(attr); i++) { ipp_res_t units; int xres, yres; xres = ippGetResolution(attr, i, &yres, &units); LOGD("%s (%s%s) = %dx%d%s ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), xres, yres, units == IPP_RES_PER_INCH ? "dpi" : "dpc"); } break; case IPP_TAG_STRING: case IPP_TAG_TEXT: case IPP_TAG_NAME: case IPP_TAG_KEYWORD: case IPP_TAG_CHARSET: case IPP_TAG_URI: case IPP_TAG_MIMETYPE: case IPP_TAG_LANGUAGE: for (i = 0; i < ippGetCount(attr); i++) { LOGD("%s (%s%s) = \"%s\" ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), ippGetString(attr, i, NULL)); } break; case IPP_TAG_TEXTLANG: case IPP_TAG_NAMELANG: for (i = 0; i < ippGetCount(attr); i++) { const char *charset; const char *text; text = ippGetString(attr, i, &charset); LOGD("%s (%s%s) = \"%s\",%s ", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), text, charset); } break; case IPP_TAG_BEGIN_COLLECTION: for (i = 0; i < ippGetCount(attr); i++) { LOGD("%s (%s%s): IPP_TAG_BEGIN_COLLECTION", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); print_col(ippGetCollection(attr, i)); } LOGD("%s (%s%s): IPP_TAG_END_COLLECTION", ippGetName(attr), ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr))); break; default: break; } } void parse_IPPVersions(ipp_t *response, ipp_version_supported_t *ippVersions) { int i; ipp_attribute_t *attrptr; char ipp10[] = "1.0"; char ipp11[] = "1.1"; char ipp20[] = "2.0"; LOGD(" Entered IPPVersions"); if (ippVersions != NULL) { memset(ippVersions, 0, sizeof(ipp_version_supported_t)); LOGD(" in get_supportedIPPVersions"); attrptr = ippFindAttribute(response, "ipp-versions-supported", IPP_TAG_KEYWORD); if (attrptr != NULL) { LOGD(" in get_supportedIPPVersions: %d", ippGetCount(attrptr)); for (i = 0; i < ippGetCount(attrptr); i++) { if (strcmp(ipp10, ippGetString(attrptr, i, NULL)) == 0) { ippVersions->supportsIpp10 = 1; } else if (strcmp(ipp11, ippGetString(attrptr, i, NULL)) == 0) { ippVersions->supportsIpp11 = 1; } else if (strcmp(ipp20, ippGetString(attrptr, i, NULL)) == 0) { ippVersions->supportsIpp20 = 1; } else { LOGD("found another ipp version. %s", ippGetString(attrptr, i, NULL)); } } } } } const char *mapDFMediaToIPPKeyword(media_size_t media_size) { int i; for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { if (SupportedMediaSizes[i].media_size == (media_size_t) media_size) { return (SupportedMediaSizes[i].PWGName); } } return (SupportedMediaSizes[0].PWGName); } int ipp_find_media_size(const char *ipp_media_keyword, media_size_t *media_size) { int i; LOGD("ipp_find_media_size entry is %s", ipp_media_keyword); for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { if (strcmp(SupportedMediaSizes[i].PWGName, ipp_media_keyword) == 0) { LOGD(" mediaArraySize: match string %s PT_size: %d", SupportedMediaSizes[i].PWGName, SupportedMediaSizes[i].media_size); break; } } if (i < SUPPORTED_MEDIA_SIZE_COUNT) { *media_size = SupportedMediaSizes[i].media_size; return i; } else { return -1; } return -1; } /* * Return a freshly allocated string copied from another string */ static char *substring(const char *string, int position, int length) { char *pointer; int c; pointer = malloc(length + 1); if (pointer == NULL) { exit(EXIT_FAILURE); } for (c = 0; c < position - 1; c++) { string++; } for (c = 0; c < length; c++) { *(pointer + c) = *string; string++; } *(pointer + c) = '\0'; return pointer; } /* * Parse and IPP media size and return dimensions in millimeters. * Format is region_name_#x# or format is custom_(min\max)_#x# */ static int getMediaDimensions_mm(const char *mediaSize, media_dimension_mm_t *media_dimensions) { char *tempMediaSize = NULL; char *um; char *buf = NULL; // custom char *dim = NULL; // min char *upper = NULL; // 8.5 const char t[2] = "_"; const char x[2] = "x"; char in[3] = "in"; double inch_to_mm = 25.4; tempMediaSize = malloc(strlen(mediaSize) + 1); if (tempMediaSize == NULL) { exit(EXIT_FAILURE); } strcpy(tempMediaSize, mediaSize); LOGD("getMediaDimensions_mm Start media:%s", tempMediaSize); um = substring(mediaSize, strlen(tempMediaSize) - 1, 2); LOGD("getMediaDimensions um=%s", um); // fill in buf with what we need to work with buf = strtok(tempMediaSize, t); // custom while (buf != NULL) { if (dim != NULL) { free(dim); } dim = malloc(strlen(buf) + 1); if (dim == NULL) { exit(EXIT_FAILURE); } strcpy(dim, buf); buf = strtok(NULL, t); } if (dim != NULL) { LOGD("getMediaDimensions part2=%s", dim); media_dimensions->Lower = atof(strtok(dim, x)); LOGD("getMediaDimensions lower=%g", media_dimensions->Lower); upper = strtok(NULL, x); if (upper != NULL) { // Finally the upper bound char *tempStr = substring(upper, 1, strlen(upper) - 2); media_dimensions->Upper = atof(tempStr); free(tempStr); tempStr = substring(upper, 1, strlen(upper) - 2); LOGD("getMediaDimensions part4=%s", tempStr); free(tempStr); LOGD("getMediaDimensions upper=%g", media_dimensions->Upper); // finally make sure we are in mm if (strcmp(um, in) == 0) { LOGD("getMediaDimensions part5"); media_dimensions->Lower = media_dimensions->Lower * inch_to_mm; media_dimensions->Upper = media_dimensions->Upper * inch_to_mm; } if (media_dimensions->Lower > 0 && media_dimensions->Upper > 0) { double dTemp = 0; if (media_dimensions->Lower > media_dimensions->Upper) { dTemp = media_dimensions->Lower; media_dimensions->Lower = media_dimensions->Upper; media_dimensions->Upper = dTemp; } LOGD("getMediaDimensions final lower=%g", media_dimensions->Lower); LOGD("getMediaDimensions final upper=%g", media_dimensions->Upper); free(tempMediaSize); free(dim); free(um); return 1; } } free(dim); } free(um); free(tempMediaSize); return 0; } void parse_getMediaSupported(ipp_t *response, media_supported_t *media_supported, printer_capabilities_t *capabilities) { int i; ipp_attribute_t *attrptr; int sizes_idx = 0; char custom_min[] = "custom_min"; char custom_max[] = "custom_max"; bool apply_custom_min_max = true; int iterate = 0; char *optout_manufacture_list[7] = {"Brother", "Epson", "Fuji Xerox", "Konica Minolta", "Kyocera", "Canon", "UTAX_TA"}; int manufacturerlist_size = (sizeof(optout_manufacture_list) / sizeof(*optout_manufacture_list)); media_dimension_mm_t custom_min_dim; media_dimension_mm_t custom_max_dim; int rCustomMin = 0; int rCustomMax = 0; char *tempMediaSize = NULL; char manufacturername_from_model[256]; LOGD(" Entered getMediaSupported"); media_size_t media_sizeTemp; int idx = 0; // First check printer-device-id for manufacturer exception if ((attrptr = ippFindAttribute(response, "printer-device-id", IPP_TAG_TEXT)) != NULL) { strlcpy(capabilities->make, ippGetString(attrptr, 0, NULL), sizeof(capabilities->make)); LOGD("manufacturer_from_deviceid: %s", capabilities->make); for (iterate = 0; iterate < manufacturerlist_size; iterate++) { if (strcasestr(capabilities->make, optout_manufacture_list[iterate]) != NULL) { LOGD("printer device id cmp: %s", strcasestr(capabilities->make, optout_manufacture_list[iterate])); apply_custom_min_max = false; } } } // Second check printer-make-and-model for manufacturer exception if (apply_custom_min_max) { // Get manufacturer name using printer-make-and-model attrptr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT); if (attrptr != NULL) { strlcpy(manufacturername_from_model, ippGetString(attrptr, 0, NULL), sizeof(manufacturername_from_model)); LOGD("manufacturer_from_make_model: %s", manufacturername_from_model); for (iterate = 0; iterate < manufacturerlist_size; iterate++) { if (strcasestr(manufacturername_from_model, optout_manufacture_list[iterate]) != NULL) { LOGD("printer make model cmp: %s", strcasestr(manufacturername_from_model, optout_manufacture_list[iterate])); apply_custom_min_max = false; } } } } if ((attrptr = ippFindAttribute(response, "media-supported", IPP_TAG_KEYWORD)) != NULL) { LOGD("media-supported found; number of values %d", ippGetCount(attrptr)); for (i = 0; i < ippGetCount(attrptr); i++) { idx = ipp_find_media_size(ippGetString(attrptr, i, NULL), &media_sizeTemp); LOGD(" Temp - i: %d idx %d keyword: %s PT_size %d", i, idx, ippGetString( attrptr, i, NULL), media_sizeTemp); // Modified since anytime the find media size returned 0 it could either mean // NOT found or na_letter. if (idx >= 0) { media_supported->media_size[sizes_idx] = media_sizeTemp; media_supported->idxKeywordTranTable[sizes_idx] = idx; sizes_idx++; } if (idx == -1) { // it might be a custom range if (tempMediaSize != NULL) { free(tempMediaSize); } tempMediaSize = malloc(strlen(ippGetString(attrptr, i, NULL)) + 1); if (tempMediaSize == NULL) { exit(EXIT_FAILURE); } strcpy(tempMediaSize, ippGetString(attrptr, i, NULL)); if (strstr(tempMediaSize, custom_min) != NULL) { rCustomMin = getMediaDimensions_mm(tempMediaSize, &custom_min_dim); } if (strstr(tempMediaSize, custom_max) != NULL) { rCustomMax = getMediaDimensions_mm(tempMediaSize, &custom_max_dim); } } } // if value equal to particular manufacture condition goes here if (apply_custom_min_max) { LOGD("*****apply custom range for this manufacture"); // Now add in custom sizes if there is a custom min max range media_dimension_mm_t media_dim; int rMediaDim = 0; LOGD("rCustomMin:%d rCustomMax:%d", rCustomMin, rCustomMax); if (rCustomMin > 0 && rCustomMax > 0) { // we have custom support LOGD("CustomRange minLower:%g minUpper:%g maxLower:%g maxUpper:%g", custom_min_dim.Lower, custom_min_dim.Upper, custom_max_dim.Lower, custom_max_dim.Upper); media_dim.Lower = 0; media_dim.Upper = 0; for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { int found; int j; found = 0; int sizes_idx_start = sizes_idx - 1; for (j = 0; j < sizes_idx_start; j++) { if (i == media_supported->idxKeywordTranTable[j]) { found = 1; break; } } if (found == 0) { // check custom if (tempMediaSize != NULL) { free(tempMediaSize); } tempMediaSize = malloc(strlen(SupportedMediaSizes[i].PWGName) + 1); if (tempMediaSize == NULL) { exit(EXIT_FAILURE); } strcpy(tempMediaSize, SupportedMediaSizes[i].PWGName); LOGD("NOT FOUND CHECKING CUSTOM:%s", tempMediaSize); rMediaDim = getMediaDimensions_mm(tempMediaSize, &media_dim); if (rMediaDim > 0) { LOGD("CustomRange minLower:%g minUpper:%g maxLower:%g maxUpper:%g" "lower:%g upper:%g", custom_min_dim.Lower, custom_min_dim.Upper, custom_max_dim.Lower, custom_max_dim.Upper, media_dim.Lower, media_dim.Upper); if (media_dim.Lower >= custom_min_dim.Lower && media_dim.Lower <= custom_max_dim.Lower && media_dim.Upper >= custom_min_dim.Upper && media_dim.Upper <= custom_max_dim.Upper) { LOGD("Add Media Size %s!!", tempMediaSize); media_supported->media_size[sizes_idx] = SupportedMediaSizes[i].media_size; media_supported->idxKeywordTranTable[sizes_idx] = i; sizes_idx++; } } } } if (tempMediaSize != NULL) { free(tempMediaSize); tempMediaSize = NULL; } } } else { LOGD("*****Dont apply custom range for this manufacture"); } } else { LOGD("media-supported not found"); } if (tempMediaSize) { free(tempMediaSize); } } static void get_supportedPrinterResolutions(ipp_attribute_t *attrptr, printer_capabilities_t *capabilities) { int idx = 0; int i; for (i = 0; i < ippGetCount(attrptr); i++) { ipp_res_t units; int xres, yres; xres = ippGetResolution(attrptr, i, &yres, &units); if (units == IPP_RES_PER_INCH) { if ((idx < MAX_RESOLUTIONS_SUPPORTED) && (xres == yres)) { capabilities->supportedResolutions[idx] = xres; idx++; } } } capabilities->numSupportedResolutions = idx; } void getResourceFromURI(const char *uri, char *resource, int resourcelen) { char scheme[1024]; char username[1024]; char host[1024]; int port; httpSeparateURI(0, uri, scheme, 1024, username, 1024, host, 1024, &port, resource, resourcelen); } /* * Add a new media type to a printer's collection of supported media types */ static void addMediaType(printer_capabilities_t *capabilities, media_type_t mediaType) { int index; for (index = 0; index < capabilities->numSupportedMediaTypes; index++) { // Skip if already present if (capabilities->supportedMediaTypes[index] == mediaType) return; } // Add if not found and not too many if (capabilities->numSupportedMediaTypes < MAX_MEDIA_TYPES_SUPPORTED) { capabilities->supportedMediaTypes[capabilities->numSupportedMediaTypes++] = mediaType; } else { LOGI("Hit MAX_MEDIA_TYPES_SUPPORTED while adding %d", mediaType); } } void parse_printerAttributes(ipp_t *response, printer_capabilities_t *capabilities) { int i, j; ipp_attribute_t *attrptr; LOGD("Entered parse_printerAttributes"); media_supported_t media_supported; for (i = 0; i <= PAGE_STATUS_MAX - 1; i++) { media_supported.media_size[i] = 0; } parse_getMediaSupported(response, &media_supported, capabilities); parse_printerUris(response, capabilities); LOGD("Media Supported: "); int idx = 0; capabilities->numSupportedMediaTypes = 0; for (i = 0; i <= PAGE_STATUS_MAX - 1; i++) { if (media_supported.media_size[i] != 0) { capabilities->supportedMediaSizes[capabilities->numSupportedMediaSizes++] = media_supported.media_size[i]; idx = media_supported.idxKeywordTranTable[i]; LOGD(" i %d, \tPT_Size: %d \tidx %d \tKeyword: %s", i, media_supported.media_size[i], idx, SupportedMediaSizes[idx].PWGName); } } LOGD(""); if ((attrptr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL) { LOGD("printer-name: %s", ippGetString(attrptr, 0, NULL)); strlcpy(capabilities->name, ippGetString(attrptr, 0, NULL), sizeof(capabilities->name)); } if ((attrptr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT)) != NULL) { strlcpy(capabilities->make, ippGetString(attrptr, 0, NULL), sizeof(capabilities->make)); } if ((attrptr = ippFindAttribute(response, "printer-uuid", IPP_TAG_URI)) != NULL) { strlcpy(capabilities->uuid, ippGetString(attrptr, 0, NULL), sizeof(capabilities->uuid)); } if ((attrptr = ippFindAttribute(response, "printer-location", IPP_TAG_TEXT)) != NULL) { strlcpy(capabilities->location, ippGetString(attrptr, 0, NULL), sizeof(capabilities->location)); } if ((attrptr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL) { strlcpy(capabilities->mediaDefault, ippGetString(attrptr, 0, NULL), sizeof(capabilities->mediaDefault)); } if ((attrptr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN)) != NULL) { if (ippGetBoolean(attrptr, 0)) { capabilities->color = 1; } } if ((attrptr = ippFindAttribute(response, "copies-supported", IPP_TAG_RANGE)) != NULL) { int upper = 0; for (i = 0; i < ippGetCount(attrptr); i++) { ippGetRange(attrptr, i, &upper); } if (upper > 1) { capabilities->canCopy = 1; } } if ((attrptr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { if (strcmp("color", ippGetString(attrptr, i, NULL)) == 0) { capabilities->color = 1; } } } char imagePCLm[] = "application/PCLm"; char imagePWG[] = "image/pwg-raster"; char imagePDF[] = "image/pdf"; char applicationPDF[] = "application/pdf"; if ((attrptr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { if (strcmp(imagePDF, ippGetString(attrptr, i, NULL)) == 0) { capabilities->canPrintPDF = 1; } else if (strcmp(applicationPDF, ippGetString(attrptr, i, NULL)) == 0) { capabilities->canPrintPDF = 1; } else if (strcmp(imagePCLm, ippGetString(attrptr, i, NULL)) == 0) { capabilities->canPrintPCLm = 1; } else if (strcmp(applicationPDF, ippGetString(attrptr, i, NULL)) == 0) { capabilities->canPrintPDF = 1; } else if (strcmp(imagePWG, ippGetString(attrptr, i, NULL)) == 0) { capabilities->canPrintPWG = 1; } } } if ((attrptr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { if (strcmp(IPP_SIDES_TWO_SIDED_SHORT_EDGE, ippGetString(attrptr, i, NULL)) == 0) { capabilities->duplex = 1; } else if (strcmp(IPP_SIDES_TWO_SIDED_LONG_EDGE, ippGetString(attrptr, i, NULL)) == 0) { capabilities->duplex = 1; } } } // Look up supported media types capabilities->numSupportedMediaTypes = 0; if (((attrptr = ippFindAttribute(response, "media-type-supported", IPP_TAG_KEYWORD)) != NULL) || ((attrptr = ippFindAttribute(response, "media-type-supported", IPP_TAG_NAME)) != NULL)) { for (i = 0; i < ippGetCount(attrptr); i++) { if (strcasestr(ippGetString(attrptr, i, NULL), "photographic-glossy")) { addMediaType(capabilities, MEDIA_PHOTO_GLOSSY); } else if (strcasestr(ippGetString(attrptr, i, NULL), "photo")) { addMediaType(capabilities, MEDIA_PHOTO); } else if (strcasestr(ippGetString(attrptr, i, NULL), "stationery")) { addMediaType(capabilities, MEDIA_PLAIN); } } } if (capabilities->numSupportedMediaTypes == 0) { // If no recognized media types were found, fall back to all 3 just in case addMediaType(capabilities, MEDIA_PLAIN); addMediaType(capabilities, MEDIA_PHOTO); addMediaType(capabilities, MEDIA_PHOTO_GLOSSY); } capabilities->numSupportedResolutions = 0; // only appears that SMM supports the pclm-source-resolution-supported attribute // if that is not present, use the printer-resolution-supported attribute to determine // if 300DPI is supported if ((attrptr = ippFindAttribute(response, "pclm-source-resolution-supported", IPP_TAG_RESOLUTION)) != NULL) { get_supportedPrinterResolutions(attrptr, capabilities); } else if ((attrptr = ippFindAttribute(response, "printer-resolution-supported", IPP_TAG_RESOLUTION)) != NULL) { get_supportedPrinterResolutions(attrptr, capabilities); } char ipp10[] = "1.0"; char ipp11[] = "1.1"; char ipp20[] = "2.0"; if ((attrptr = ippFindAttribute(response, "ipp-versions-supported", IPP_TAG_KEYWORD)) != NULL) { unsigned char supportsIpp20 = 0; unsigned char supportsIpp11 = 0; unsigned char supportsIpp10 = 0; for (i = 0; i < ippGetCount(attrptr); i++) { if (strcmp(ipp10, ippGetString(attrptr, i, NULL)) == 0) { supportsIpp10 = 1; } else if (strcmp(ipp11, ippGetString(attrptr, i, NULL)) == 0) { supportsIpp11 = 1; } else if (strcmp(ipp20, ippGetString(attrptr, i, NULL)) == 0) { supportsIpp20 = 1; } else { LOGD("found another ipp version. %s", ippGetString(attrptr, i, NULL)); } if (supportsIpp20) { capabilities->ippVersionMajor = 2; capabilities->ippVersionMinor = 0; } else if (supportsIpp11) { capabilities->ippVersionMajor = 1; capabilities->ippVersionMinor = 1; } else if (supportsIpp10) { capabilities->ippVersionMajor = 1; capabilities->ippVersionMinor = 0; } else { // default to 1.0 capabilities->ippVersionMajor = 1; capabilities->ippVersionMinor = 0; } } } char epcl10[] = "1.0"; if ((attrptr = ippFindAttribute(response, "epcl-version-supported", IPP_TAG_KEYWORD)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { LOGD("setting epcl_ipp_version (KEYWORD) %s", ippGetString(attrptr, i, NULL)); // substring match because different devices implemented spec differently if (strstr(ippGetString(attrptr, i, NULL), epcl10) != NULL) { LOGD("setting epcl_ipp_version = 1"); capabilities->ePclIppVersion = 1; } } } if ((attrptr = ippFindAttribute(response, "epcl-version-supported", IPP_TAG_TEXT)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { LOGD("setting epcl_ipp_verion (TEXT) %s", ippGetString(attrptr, i, NULL)); // substring match because different devices implemented spec differently if (strstr(ippGetString(attrptr, i, NULL), epcl10) != NULL) { LOGD("setting epcl_ipp_verion = 1"); capabilities->ePclIppVersion = 1; } } } if ((attrptr = ippFindAttribute(response, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { LOGD("Gathering margins supported"); ipp_t *collection = ippGetCollection(attrptr, i); for (j = 0, attrptr = ippFirstAttribute(collection); (j < 4) && (attrptr != NULL); attrptr = ippNextAttribute(collection)) { if (strcmp("media-top-margin", ippGetName(attrptr)) == 0) { capabilities->printerTopMargin = ippGetInteger(attrptr, 0); } else if (strcmp("media-bottom-margin", ippGetName(attrptr)) == 0) { capabilities->printerBottomMargin = ippGetInteger(attrptr, 0); } else if (strcmp("media-left-margin", ippGetName(attrptr)) == 0) { capabilities->printerLeftMargin = ippGetInteger(attrptr, 0); } else if (strcmp("media-right-margin", ippGetName(attrptr)) == 0) { capabilities->printerRightMargin = ippGetInteger(attrptr, 0); } } } } if ((attrptr = ippFindAttribute(response, "media-size-name", IPP_TAG_KEYWORD)) != NULL) { capabilities->isMediaSizeNameSupported = true; } else { capabilities->isMediaSizeNameSupported = false; } // is strip length supported? if so, stored in capabilities if ((attrptr = ippFindAttribute(response, "pclm-strip-height-preferred", IPP_TAG_INTEGER)) != NULL) { LOGD("pclm-strip-height-preferred=%d", ippGetInteger(attrptr, 0)); // if the strip height is 0, the device wants us to send the entire page in one band // (according to ePCL spec). Since our code doesn't currently support generating an entire // page in one band, set the strip height to the default value every device *should* support // also, for some reason our code crashes when it attempts to generate strips at 512 or // above. Therefore, limiting the upper bound strip height to 256 if (ippGetInteger(attrptr, 0) == 0 || ippGetInteger(attrptr, 0) > 256) { capabilities->stripHeight = STRIPE_HEIGHT; } else { capabilities->stripHeight = ippGetInteger(attrptr, 0); } } else { capabilities->stripHeight = STRIPE_HEIGHT; } // what is the preferred compression method - jpeg, flate, rle if ((attrptr = ippFindAttribute(response, "pclm-compression-method-preferred", IPP_TAG_KEYWORD)) != NULL) { LOGD("pclm-compression-method-preferred=%s", ippGetString(attrptr, 0, NULL)); } // is device able to rotate back page for duplex jobs? if ((attrptr = ippFindAttribute(response, "pclm-raster-back-side", IPP_TAG_KEYWORD)) != NULL) { LOGD("pclm-raster-back-side=%s", ippGetString(attrptr, 0, NULL)); if (strcmp(ippGetString(attrptr, 0, NULL), "rotated") == 0) { capabilities->canRotateDuplexBackPage = 0; LOGD("Device cannot rotate back page for duplex jobs."); } else { capabilities->canRotateDuplexBackPage = 1; } } // look for full-bleed supported by looking for 0 on all margins bool topsupported = false, bottomsupported = false, rightsupported = false, leftsupported = false; if ((attrptr = ippFindAttribute(response, "media-top-margin-supported", IPP_TAG_INTEGER)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { if (ippGetInteger(attrptr, i) == 0) { LOGD("Top Margin Supported"); topsupported = true; break; } } } if ((attrptr = ippFindAttribute(response, "media-bottom-margin-supported", IPP_TAG_INTEGER)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { if (ippGetInteger(attrptr, i) == 0) { LOGD("Bottom Margin Supported"); bottomsupported = true; break; } } } if ((attrptr = ippFindAttribute(response, "media-right-margin-supported", IPP_TAG_INTEGER)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { if (ippGetInteger(attrptr, i) == 0) { LOGD("Right Margin Supported"); rightsupported = true; break; } } } if ((attrptr = ippFindAttribute(response, "media-left-margin-supported", IPP_TAG_INTEGER)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { if (ippGetInteger(attrptr, i) == 0) { LOGD("Left Margin Supported"); leftsupported = true; break; } } } if (topsupported && bottomsupported && rightsupported && leftsupported) { LOGD("full-bleed is supported"); capabilities->borderless = 1; } else { LOGD("full-bleed is NOT supported"); } if ((attrptr = ippFindAttribute(response, "printer-device-id", IPP_TAG_TEXT)) != NULL) { if (strstr(ippGetString(attrptr, 0, NULL), "PCL3GUI") != NULL) { capabilities->inkjet = 1; } } else if (capabilities->borderless == 1) { capabilities->inkjet = 1; } // determine if device prints pages face-down capabilities->faceDownTray = 1; if ((attrptr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_KEYWORD)) != NULL) { if (strstr(ippGetString(attrptr, 0, NULL), "face-up") != NULL) { capabilities->faceDownTray = 0; } } // Determine supported document format details if ((attrptr = ippFindAttribute(response, "document-format-details-supported", IPP_TAG_KEYWORD)) != NULL) { for (i = 0; i < ippGetCount(attrptr); i++) { if (strcmp("document-source-application-name", ippGetString(attrptr, i, NULL)) == 0) { capabilities->docSourceAppName = 1; } else if ( strcmp("document-source-application-version", ippGetString(attrptr, i, NULL)) == 0) { capabilities->docSourceAppVersion = 1; } else if (strcmp("document-source-os-name", ippGetString(attrptr, i, NULL)) == 0) { capabilities->docSourceOsName = 1; } else if (strcmp("document-source-os-version", ippGetString(attrptr, i, NULL)) == 0) { capabilities->docSourceOsVersion = 1; } } } debuglist_printerCapabilities(capabilities); } // Used in parse_printerUris #define MAX_URIS 10 typedef struct { const char *uri; int valid; } parsed_uri_t; static void parse_printerUris(ipp_t *response, printer_capabilities_t *capabilities) { ipp_attribute_t *attrptr; int i; parsed_uri_t uris[MAX_URIS] = {0}; if ((attrptr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL) { for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) { uris[i].uri = ippGetString(attrptr, i, NULL); uris[i].valid = true; } } // If security or authentication is required (non-"none") at any URI, mark it invalid if ((attrptr = ippFindAttribute(response, "uri-security-supported", IPP_TAG_KEYWORD)) != NULL) { for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) { if (strcmp("none", ippGetString(attrptr, i, NULL)) != 0) { LOGD("parse_printerUris %s invalid because sec=%s", uris[i].uri, ippGetString(attrptr, i, NULL)); uris[i].valid = false; } } } if ((attrptr = ippFindAttribute(response, "uri-authentication-supported", IPP_TAG_KEYWORD)) != NULL) { for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) { // Allow "none" and "requesting-user-name" only if (strcmp("none", ippGetString(attrptr, i, NULL)) != 0 && strcmp("requesting-user-name", ippGetString(attrptr, i, NULL)) != 0) { LOGD("parse_printerUris %s invalid because auth=%s", uris[i].uri, ippGetString(attrptr, i, NULL)); uris[i].valid = false; } } } // Find a valid URI and copy it into place. for (i = 0; i < MAX_URIS; i++) { if (uris[i].valid) { LOGD("parse_printerUris found %s", uris[i].uri); strlcpy(capabilities->printerUri, uris[i].uri, sizeof(capabilities->printerUri)); break; } } } void debuglist_printerCapabilities(printer_capabilities_t *capabilities) { LOGD("printer make: %s", capabilities->make); LOGD("printer default media: %s", capabilities->mediaDefault); LOGD("canPrintPDF: %d", capabilities->canPrintPDF); LOGD("duplex: %d", capabilities->duplex); LOGD("canRotateDuplexBackPage: %d", capabilities->canRotateDuplexBackPage); LOGD("color: %d", capabilities->color); LOGD("canCopy: %d", capabilities->canCopy); LOGD("ippVersionMajor: %d", capabilities->ippVersionMajor); LOGD("ippVersionMinor: %d", capabilities->ippVersionMinor); LOGD("strip height: %d", capabilities->stripHeight); LOGD("faceDownTray: %d", capabilities->faceDownTray); } void debuglist_printerStatus(printer_state_dyn_t *printer_state_dyn) { const char *decoded = "unknown"; if (printer_state_dyn->printer_status == PRINT_STATUS_INITIALIZING) { decoded = "Initializing"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_SHUTTING_DOWN) { decoded = "Shutting Down"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_UNABLE_TO_CONNECT) { decoded = "Unable To Connect"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_UNKNOWN) { decoded = "Unknown"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_OFFLINE) { decoded = "Offline"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_IDLE) { decoded = "Idle"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_PRINTING) { decoded = "Printing"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_OUT_OF_PAPER) { decoded = "Out Of Paper"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_OUT_OF_INK) { decoded = "Out Of Ink"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_JAMMED) { decoded = "Jammed"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_DOOR_OPEN) { decoded = "Door Open"; } else if (printer_state_dyn->printer_status == PRINT_STATUS_SVC_REQUEST) { decoded = "Service Request"; } LOGD("printer status: %d (%s)", printer_state_dyn->printer_status, decoded); int idx = 0; for (idx = 0; idx < (PRINT_STATUS_MAX_STATE + 1); idx++) { if (PRINT_STATUS_MAX_STATE != printer_state_dyn->printer_reasons[idx]) { LOGD("printer_reasons (%d): %d", idx, printer_state_dyn->printer_reasons[idx]); } } } http_t *ipp_cups_connect(const wprint_connect_info_t *connect_info, char *printer_uri, unsigned int uriLength) { const char *uri_path; http_t *curl_http = NULL; if ((connect_info->uri_path == NULL) || (strlen(connect_info->uri_path) == 0)) { uri_path = DEFAULT_IPP_URI_RESOURCE; } else { uri_path = connect_info->uri_path; } int ippPortNumber = ((connect_info->port_num == IPP_PORT) ? ippPort() : connect_info->port_num); curl_http = httpConnect(connect_info->printer_addr, ippPortNumber); httpSetTimeout(curl_http, DEFAULT_IPP_TIMEOUT, NULL, 0); httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, uriLength, connect_info->uri_scheme, NULL, connect_info->printer_addr, ippPortNumber, uri_path); if (curl_http == NULL) { LOGD("ipp_cups_connect failed addr=%s port=%d", connect_info->printer_addr, ippPortNumber); } return curl_http; } /* * Send a request using cupsSendRequest(). Loop if we get NULL or CONTINUE. Does not delete * the request. */ static ipp_t *ippSendRequest(http_t *http, ipp_t *request, char *resource) { ipp_t *response = NULL; http_status_t result; bool retry; do { retry = false; result = cupsSendRequest(http, request, resource, ippLength(request)); if (result != HTTP_ERROR) { response = cupsGetResponse(http, resource); result = httpGetStatus(http); } if (result == HTTP_CONTINUE && response == NULL) { // We need to retry when this happens. LOGD("ippSendRequest: (Continue with NULL response) Retry"); retry = true; } else if (result == HTTP_ERROR || result >= HTTP_BAD_REQUEST) { _cupsSetHTTPError(result); break; } if (http->state != HTTP_WAITING) { httpFlush(http); } } while (retry); return response; } /* * Call ippDoCupsIORequest, repeating if a failure occurs based on failure conditions, and * returning the response (or NULL if it failed). * * Does not free the request, and the caller must call ippDelete to free any valid response. */ ipp_t *ipp_doCupsRequest(http_t *http, ipp_t *request, char *http_resource, char *printer_uri) { ipp_status_t ipp_status; ipp_t *response = NULL; int service_unavailable_retry_count = 0; int bad_request_retry_count = 0; int internal_error_retry_count = 0; ipp_version_state ipp_version_supported = IPP_VERSION_RESOLVED; // Fail if any of these parameters are NULL if (http == NULL || request == NULL || http_resource == NULL || printer_uri == NULL) { return NULL; } do { // Give up immediately if wprint is done. if (!wprintIsRunning()) return NULL; // This is a no-op until we hit the error IPP_VERSION_NOT_SUPPORTED and retry. if (set_ipp_version(request, printer_uri, http, ipp_version_supported) != 0) { // We tried to find the correct IPP version by doing a series of get attribute // requests but they all failed... we give up. LOGE("ipp_doCupsRequest: set_ipp_version!=0, version not set"); break; } response = ippSendRequest(http, request, http_resource); if (response == NULL) { ipp_status = cupsLastError(); if (ipp_status == IPP_INTERNAL_ERROR || ipp_status == HTTP_ERROR) { internal_error_retry_count++; if (internal_error_retry_count > IPP_INTERNAL_ERROR_MAX_RETRIES) { break; } LOGE("ipp_doCupsRequest: %s %d received, retry %d of %d", printer_uri, ipp_status, internal_error_retry_count, IPP_INTERNAL_ERROR_MAX_RETRIES); continue; } else if (ipp_status == IPP_SERVICE_UNAVAILABLE) { service_unavailable_retry_count++; if (service_unavailable_retry_count > IPP_SERVICE_ERROR_MAX_RETRIES) { break; } LOGE("ipp_doCupsRequest: %s IPP_SERVICE_UNAVAILABLE received, retrying %d of %d", printer_uri, service_unavailable_retry_count, IPP_SERVICE_ERROR_MAX_RETRIES); continue; } else if (ipp_status == IPP_BAD_REQUEST) { bad_request_retry_count++; if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) { break; } LOGD("ipp_doCupsRequest: %s IPP_BAD_REQUEST received. retry (%d) of (%d)", printer_uri, bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); continue; } else if (ipp_status == IPP_NOT_FOUND) { LOGE("ipp_doCupsRequest: %s IPP_NOT_FOUND received.", printer_uri); break; } } else { ipp_status = cupsLastError(); if (ipp_status == IPP_BAD_REQUEST) { bad_request_retry_count++; LOGE("ipp_doCupsRequest: %s IPP_BAD_REQUEST received. retry (%d) of (%d)", printer_uri, bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) { break; } ippDelete(response); response = NULL; continue; } else if (ipp_status == IPP_VERSION_NOT_SUPPORTED) { ipp_version_supported = IPP_VERSION_UNSUPPORTED; ippDelete(response); response = NULL; continue; } } break; } while (1); return response; }