| /* |
| * 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" |
| #define IPP_JOB_UNKNOWN ((ipp_jstate_t)(-1)) |
| |
| const char *resource_extensions_arr[] = { |
| DEFAULT_IPP_URI_RESOURCE, "/" |
| }; |
| |
| /* |
| * 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", |
| 215900, 279400 }, |
| { US_LEGAL, "LEGAL", 8500, 14000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_legal_8.5x14in", |
| 215900, 355600 }, |
| { LEDGER, "LEDGER", 11000, 17000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_ledger_11x17in", |
| 279400, 431800 }, |
| { INDEX_CARD_5X7, "5X7", 5000, 7000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_5x7_5x7in", |
| 127000, 177800 }, |
| |
| // Android system uses width of 11690 |
| { ISO_A3, "A3", 11694, 16540, 297, 420, "iso_a3_297x420mm", |
| 297000, 420000 }, |
| |
| // Android system uses width of 8267 |
| { ISO_A4, "A4", 8268, 11692, 210, 297, "iso_a4_210x297mm", |
| 210000, 297000 }, |
| { ISO_A5, "A5", 5830, 8270, 148, 210, "iso_a5_148x210mm", |
| 148000, 210000 }, |
| |
| // Android system uses width of 10118 |
| { JIS_B4, "JIS B4", 10119, 14331, 257, 364, "jis_b4_257x364mm", |
| 257000, 364000 }, |
| |
| // Android system uses width of 7165 |
| { JIS_B5, "JIS B5", 7167, 10118, 182, 257, "jis_b5_182x257mm", |
| 182000, 257000 }, |
| { US_GOVERNMENT_LETTER, "8x10", 8000, 10000, UNKNOWN_VALUE, UNKNOWN_VALUE, |
| "na_govt-letter_8x10in",203200, 254000 }, |
| { INDEX_CARD_4X6, "4x6", 4000, 6000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_index-4x6_4x6in", |
| 101600, 152400 }, |
| { JPN_HAGAKI_PC, "JPOST", 3940, 5830, 100, 148, "jpn_hagaki_100x148mm", |
| 100000, 148000 }, |
| { PHOTO_89X119, "89X119", 3504, 4685, 89, 119, "om_dsc-photo_89x119mm", |
| 89000, 119000 }, |
| { CARD_54X86, "54X86", 2126, 3386, 54, 86, "om_card_54x86mm", |
| 54000, 86000 }, |
| { OE_PHOTO_L, "L", 3500, 5000, UNKNOWN_VALUE, UNKNOWN_VALUE, "oe_photo-l_3.5x5in", |
| 88900, 127000 }, |
| |
| // Large formats |
| { ISO_AND_JIS_A0, "A0", 33110, 46810, 841, 1189, "iso_a0_841x1189mm", |
| 841000, 1189000}, |
| { ISO_AND_JIS_A1, "A1", 23390, 33110, 594, 841, "iso_a1_594x841mm", |
| 594000, 841000}, |
| { ISO_AND_JIS_A2, "A2", 16540, 23390, 420, 594, "iso_a2_420x594mm", |
| 420000, 594000}, |
| { ARCH_A, "9X12", 9000, 12000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_arch-a_9x12in", |
| 228600, 304800}, |
| { ARCH_B, "12X18", 12000, 18000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_arch-b_12x18in", |
| 304800, 457200}, |
| { ARCH_C, "18x24", 18000, 24000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_arch-c_18x24in", |
| 457200, 609600}, |
| { ARCH_D, "24x36", 24000, 36000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_arch-d_24x36in", |
| 609600, 914400}, |
| { ARCH_E, "36x48", 36000, 48000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_arch-e_36x48in", |
| 914400, 1219200}, |
| { ARCH_E1, "30x42", 30000, 42000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_wide-format_30x42in", |
| 762000, 1066800}, |
| { C_SIZE, "AnsiC", 17000, 22000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_c_17x22in", |
| 431800, 558800}, |
| { D_SIZE, "AnsiD", 22000, 34000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_d_22x34in", |
| 558800, 863600}, |
| { E_SIZE, "AnsiE", 34000, 44000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_e_34x44in", |
| 863600, 1117600}, |
| { F_SIZE, "AnsiF", 28000, 40000, UNKNOWN_VALUE, UNKNOWN_VALUE, "asme_f_28x40in", |
| 711200, 1016000}, |
| { SUPER_B, "SuperB", 13000, 19000, UNKNOWN_VALUE, UNKNOWN_VALUE, "na_super-b_13x19in", |
| 330200, 482600} |
| }; |
| |
| 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 |
| } |
| } |
| |
| void set_jobStateDyn(ipp_t *response, |
| ipp_jstate_t *job_state, |
| job_state_dyn_t *job_state_dyn) { |
| ipp_attribute_t *attr; |
| if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) == NULL) { |
| LOGE(" job-state null"); |
| } else { |
| *job_state = (ipp_jstate_t) ippGetInteger(attr, 0); |
| } |
| |
| switch (*job_state) { |
| case IPP_JOB_PENDING: |
| job_state_dyn->job_state = IPP_JOB_STATE_PENDING; |
| break; |
| case IPP_JOB_HELD: |
| job_state_dyn->job_state = IPP_JOB_STATE_PENDING_HELD; |
| break; |
| case IPP_JOB_PROCESSING: |
| job_state_dyn->job_state = IPP_JOB_STATE_PROCESSING; |
| break; |
| case IPP_JOB_STOPPED: |
| job_state_dyn->job_state = IPP_JOB_STATE_PROCESSING_STOPPED; |
| break; |
| case IPP_JOB_CANCELED: |
| job_state_dyn->job_state = IPP_JOB_STATE_CANCELED; |
| break; |
| case IPP_JOB_ABORTED: |
| job_state_dyn->job_state = IPP_JOB_STATE_ABORTED; |
| break; |
| case IPP_JOB_COMPLETED: |
| job_state_dyn->job_state = IPP_JOB_STATE_COMPLETED; |
| break; |
| default: |
| if (*job_state == IPP_JOB_UNKNOWN) { |
| job_state_dyn->job_state = IPP_JOB_STATE_UNABLE_TO_CONNECT; |
| } |
| break; |
| } |
| } |
| |
| void parse_jobStateReasons(ipp_t *response, |
| job_state_dyn_t *job_state_dyn) { |
| ipp_attribute_t *attr; |
| if ((attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD)) == NULL) { |
| job_state_dyn->job_state = IPP_JOB_STATE_UNABLE_TO_CONNECT; |
| job_state_dyn->job_state_reasons[0] = IPP_JOB_STATE_REASON_UNABLE_TO_CONNECT; |
| } else { |
| int reasons_idx = 0; |
| for (int i = 0; i < ippGetCount(attr); i++) { |
| const char *text = ippGetString(attr, i, NULL); |
| LOGD("get_JobStatus: ipp job-state-reason(%d) : %s", i, text); |
| if (strcmp(text, "job-canceled-by-user") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_JOB_CANCELED_BY_USER; |
| } else if (strcmp(text, "job-canceled-at-device") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_JOB_CANCELED_AT_DEVICE; |
| } else if (strcmp(text, "aborted-by-system") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_ABORTED_BY_SYSTEM; |
| } else if (strcmp(text, "unsupported-compression") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_UNSUPPORTED_COMPRESSION; |
| } else if (strcmp(text, "compression-error") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_COMPRESSION_ERROR; |
| } else if (strcmp(text, "unsupported-document-format") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_UNSUPPORTED_DOCUMENT_FORMAT; |
| } else if (strcmp(text, "document-format-error") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_DOCUMENT_FORMAT_ERROR; |
| } else if (strcmp(text, "service-off-line") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_SERVICE_OFFLINE; |
| } else if (strcmp(text, "document-password-error") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_DOCUMENT_PASSWORD_ERROR; |
| } else if (strcmp(text, "document-permission-error") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_DOCUMENT_PERMISSION_ERROR; |
| } else if (strcmp(text, "document-security-error") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_DOCUMENT_SECURITY_ERROR; |
| } else if (strcmp(text, "document-unprintable-error") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_DOCUMENT_UNPRINTABLE_ERROR; |
| } else if (strcmp(text, "document-access-error") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_DOCUMENT_ACCESS_ERROR; |
| } else if (strcmp(text, "submission-interrupted") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_SUBMISSION_INTERRUPTED; |
| } else if (strcmp(text, "account-authorization-failed") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_AUTHORIZATION_FAILED; |
| } else if (strcmp(text, "account-closed") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_ACCOUNT_CLOSED; |
| } else if (strcmp(text, "account-info-needed") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_ACCOUNT_INFO_NEEDED; |
| } else if (strcmp(text, "account-limit-reached") == 0) { |
| job_state_dyn->job_state_reasons[reasons_idx++] = |
| IPP_JOB_STATE_REASON_ACCOUNT_LIMIT_REACHED; |
| } |
| } |
| } |
| } |
| |
| ipp_status_t get_JobStatus(http_t *http, |
| char *printer_uri, /* I - URI buffer */ |
| int job_id, |
| job_state_dyn_t *job_state_dyn, |
| ipp_jstate_t *job_state, |
| const char *requesting_user) { |
| |
| LOGD("get_JobStatus(): Enter"); |
| static const char *const jattrs[] = |
| { /* Job attributes we want */ |
| "job-id", |
| "job-printer-uri", |
| "job-name", |
| "job-state", |
| "job-state-reasons" |
| }; |
| int service_unavailable_retry_count = 0; |
| int bad_request_retry_count = 0; |
| int op = IPP_GET_JOB_ATTRIBUTES; |
| ipp_t *request; /* IPP request object */ |
| ipp_t *response; /* IPP response object */ |
| ipp_attribute_t *attr; /* Current IPP attribute */ |
| ipp_attribute_t *attrptr; /* Attribute pointer */ |
| ipp_status_t ipp_status = IPP_OK; /* Status of IPP request */ |
| ipp_version_state ipp_version_supported = IPP_VERSION_RESOLVED; |
| char http_resource[1024]; |
| getResourceFromURI(printer_uri, http_resource, 1024); |
| |
| if (job_state_dyn == NULL) { |
| LOGE("get_JobStatus(): ERROR: get_JobStatus entry: job_state_dyn is null"); |
| return ipp_status; |
| } |
| |
| if (job_state != NULL) { |
| *job_state = IPP_JOB_UNKNOWN; |
| } else { |
| LOGE(" get_JobStatus: job_state is null"); |
| // return error... |
| return ipp_status; |
| } |
| |
| LOGD(" get_JobStatus IPP_GET_JOB_ATTRIBUTES http->fd %d", http->fd); |
| do { |
| ipp_status = IPP_OK; // reset ipp_status |
| |
| request = ippNewRequest(op); |
| if (set_ipp_version(request, printer_uri, http, ipp_version_supported) != 0) { |
| LOGE("get_JobStatus(): set_ipp_version!=0, version not set"); |
| ipp_status = IPP_VERSION_NOT_SUPPORTED; |
| ippDelete(request); |
| break; |
| } |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", |
| NULL, requesting_user); |
| ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", |
| sizeof(jattrs) / sizeof(jattrs[0]), NULL, jattrs); |
| |
| for (attrptr = ippFirstAttribute(request); attrptr; attrptr = ippNextAttribute(request)) { |
| print_attr(attrptr); |
| } |
| |
| if ((response = cupsDoRequest(http, request, http_resource)) == NULL) { |
| job_state_dyn->job_state = IPP_JOB_STATE_UNABLE_TO_CONNECT; |
| job_state_dyn->job_state_reasons[0] = IPP_JOB_STATE_REASON_UNABLE_TO_CONNECT; |
| ipp_status = cupsLastError(); |
| LOGE(" get_JobStatus: response is null: ipp_status %d", ipp_status); |
| if (ipp_status == IPP_INTERNAL_ERROR) { |
| LOGE("get_JobStatus: 1280 received, bailing..."); |
| break; |
| } |
| if (ipp_status == IPP_SERVICE_UNAVAILABLE && |
| (service_unavailable_retry_count < IPP_SERVICE_ERROR_MAX_RETRIES)) { |
| LOGE("1282 received, retrying %d of %d", service_unavailable_retry_count, |
| IPP_SERVICE_ERROR_MAX_RETRIES); |
| service_unavailable_retry_count++; |
| continue; |
| } |
| if (ipp_status == IPP_BAD_REQUEST) { |
| LOGE("IPP_Status of IPP_BAD_REQUEST received. retry (%d) of (%d)", |
| bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); |
| bad_request_retry_count++; |
| continue; |
| } |
| if (ipp_status == IPP_NOT_FOUND) { |
| LOGE("IPP_Status of IPP_NOT_FOUND received. Switching resource path."); |
| if (tryNextResourceExtension(printer_uri)) { |
| getResourceFromURI(printer_uri, http_resource, 1024); |
| continue; |
| } else { |
| LOGE("No more resource paths to try"); |
| break; |
| } |
| } |
| } else { |
| ipp_status = cupsLastError(); |
| |
| LOGD("ipp CUPS last ERROR: %d, %s", ipp_status, ippErrorString(ipp_status)); |
| if (ipp_status == IPP_BAD_REQUEST) { |
| LOGE("IPP_Status of IPP_BAD_REQUEST received. retry (%d) of (%d)", |
| bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES); |
| bad_request_retry_count++; |
| ippDelete(response); |
| continue; |
| } |
| if (ipp_status == IPP_VERSION_NOT_SUPPORTED) { |
| ipp_version_supported = IPP_VERSION_UNSUPPORTED; |
| ippDelete(response); |
| continue; |
| } |
| LOGD(" get_JobStatus: response!=null: ipp_status %d", ipp_status); |
| for (attrptr = ippFirstAttribute(response); |
| attrptr; |
| attrptr = ippNextAttribute(response)) |
| print_attr(attrptr); |
| } |
| |
| set_jobStateDyn(response, job_state, job_state_dyn); |
| parse_jobStateReasons(response, job_state_dyn); |
| |
| if (response != NULL) ippDelete(response); |
| |
| break; |
| } while (bad_request_retry_count < IPP_BAD_REQUEST_MAX_RETRIES && |
| service_unavailable_retry_count < IPP_SERVICE_ERROR_MAX_RETRIES); |
| |
| LOGD(" get_JobStatus exit ipp_status %d, job_state %d", ipp_status, *job_state); |
| |
| return ipp_status; |
| } |
| |
| 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; |
| } |
| |
| static int getMediaSizeFromTag(const char *media_size_tag, media_size_t *media_size) { |
| int i; |
| LOGD("find media size from tag %s", media_size_tag); |
| for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { |
| if (strcmp(SupportedMediaSizes[i].PWGName, media_size_tag) == 0) { |
| LOGD("Media size match: pwg name %s", SupportedMediaSizes[i].PWGName); |
| *media_size = SupportedMediaSizes[i].media_size; |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| static int getMediaSizeNameFromDimens( |
| unsigned int mediaColReadyWidth, |
| unsigned int mediaColReadyHeight, |
| media_size_t *media_size) { |
| int i; |
| for (i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { |
| if (SupportedMediaSizes[i].WidthInMicrometers / 10 == mediaColReadyWidth |
| && SupportedMediaSizes[i].HeightInMicrometers / 10 == mediaColReadyHeight) { |
| *media_size = SupportedMediaSizes[i].media_size; |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| static void addMediaIfNotDuplicate( |
| int idx, |
| int *sizes_idx, |
| media_supported_t *media_supported, |
| media_size_t media_size) { |
| if (idx >= 0) { |
| // Check if we've already added this media size to the supported list |
| bool isDuplicate = false; |
| for (int j = 0; j < (*sizes_idx); j++) { |
| if (media_supported->idxKeywordTranTable[j] == idx) { |
| isDuplicate = true; |
| break; |
| } |
| } |
| // Only add if it hasn't been added yet |
| if (!isDuplicate) { |
| media_supported->media_size[(*sizes_idx)] = media_size; |
| media_supported->idxKeywordTranTable[(*sizes_idx)] = idx; |
| (*sizes_idx)++; |
| } |
| } |
| } |
| |
| static void addRollSupportedSizes( |
| unsigned int width, |
| unsigned int minHeight, |
| unsigned int maxHeight, |
| media_supported_t *media_supported, |
| int *sizesIdx) { |
| // If a supported media size fits on the roll size, add it to the list |
| for (int i = 0; i < SUPPORTED_MEDIA_SIZE_COUNT; i++) { |
| if(SupportedMediaSizes[i].WidthInMicrometers / 10 <= width |
| && SupportedMediaSizes[i].HeightInMicrometers / 10 >= minHeight |
| && SupportedMediaSizes[i].HeightInMicrometers / 10 <= maxHeight) { |
| addMediaIfNotDuplicate(i, sizesIdx, media_supported, SupportedMediaSizes[i].media_size); |
| } |
| } |
| } |
| |
| void parse_getMediaSupported( |
| ipp_t *response, |
| media_supported_t *media_supported, |
| printer_capabilities_t *capabilities) { |
| int i; |
| int sizes_idx = 0; |
| LOGD(" Entered getMediaSupported"); |
| |
| media_size_t media_sizeTemp; |
| int idx = 0; |
| |
| // Check for media-col-ready first |
| ipp_attribute_t *attrptr; |
| if((attrptr = |
| ippFindAttribute(response, "media-col-ready", IPP_TAG_BEGIN_COLLECTION)) != NULL) { |
| LOGD("media-col-ready found"); |
| for (i = 0; i < ippGetCount(attrptr); i++) { |
| ipp_t *collection = ippGetCollection(attrptr, i); |
| ipp_attribute_t *attrptr2; |
| media_ready_set_t mediaReadySet = {}; |
| int minHeight = 0, maxHeight = 0; |
| for (attrptr2 = ippFirstAttribute(collection); |
| (attrptr2 != NULL); |
| attrptr2 = ippNextAttribute(collection)) { |
| if (strcmp("media-size", ippGetName(attrptr2)) == 0) { |
| ipp_t *collection_sec = ippGetCollection(attrptr2, 0); |
| ipp_attribute_t *attrptr3; |
| for (attrptr3 = ippFirstAttribute(collection_sec); |
| (attrptr3 != NULL); |
| attrptr3 = ippNextAttribute(collection_sec)) { |
| if (strcmp("x-dimension", ippGetName(attrptr3)) == 0) { |
| mediaReadySet.x_dimension = ippGetInteger(attrptr3, 0); |
| } else if (strcmp("y-dimension", ippGetName(attrptr3)) == 0) { |
| if (ippGetValueTag(attrptr3) == IPP_TAG_RANGE) { |
| minHeight = ippGetRange(attrptr3, 0, &maxHeight); |
| mediaReadySet.y_dimension = minHeight; |
| } else if (ippGetValueTag(attrptr3) == IPP_TAG_INTEGER) { |
| mediaReadySet.y_dimension = ippGetInteger(attrptr3, 0); |
| } |
| } |
| } |
| } else if (strcmp("media-source", ippGetName(attrptr2)) == 0) { |
| memset(mediaReadySet.media_tray_tag, 0, MAX_STRING + 1); |
| strncpy(mediaReadySet.media_tray_tag, |
| ippGetString(attrptr2, 0, NULL), MAX_STRING); |
| } |
| } |
| if (minHeight > 0 && maxHeight > 0 |
| && strstr(mediaReadySet.media_tray_tag, "roll") != NULL) { |
| // If the source is a roll, add supported sizes that would fit on the roll |
| addRollSupportedSizes(mediaReadySet.x_dimension, minHeight, maxHeight, |
| media_supported, &sizes_idx); |
| } else { |
| // Get the media size name from x and y dimensions |
| idx = getMediaSizeNameFromDimens(mediaReadySet.x_dimension, |
| mediaReadySet.y_dimension, |
| &media_sizeTemp); |
| addMediaIfNotDuplicate(idx, &sizes_idx, media_supported, media_sizeTemp); |
| } |
| } |
| } |
| // Check media-ready if no supported media was found in media-col-ready |
| if (sizes_idx == 0 |
| && (attrptr = ippFindAttribute(response, "media-ready", IPP_TAG_KEYWORD)) != NULL) { |
| LOGD("media-ready found"); |
| for (i = 0; i < ippGetCount(attrptr); i++) { |
| char media_size_tag[MAX_STRING + 1]; |
| memset(media_size_tag, 0, MAX_STRING + 1); |
| strncpy(media_size_tag, |
| ippGetString(attrptr, i, NULL), MAX_STRING); |
| idx = getMediaSizeFromTag(media_size_tag, &media_sizeTemp); |
| addMediaIfNotDuplicate(idx, &sizes_idx, media_supported, media_sizeTemp); |
| } |
| } |
| |
| // Set media ready size as default if we found any |
| if (sizes_idx > 0) { |
| strlcpy(capabilities->mediaDefault, mapDFMediaToIPPKeyword(media_supported->media_size[0]), |
| sizeof(capabilities->mediaDefault)); |
| } |
| |
| // Append media-supported. media is de-duplicated later in java |
| 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); |
| |
| // 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 (sizes_idx == 0) { |
| LOGD("No supported media found"); |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| if ((attrptr = ippFindAttribute(response, "printer-dns-sd-name", IPP_TAG_NAME)) != NULL) { |
| strlcpy(capabilities->name, ippGetString(attrptr, 0, NULL), sizeof(capabilities->name)); |
| } |
| |
| if (!capabilities->name[0]) { |
| if ((attrptr = ippFindAttribute(response, "printer-info", IPP_TAG_TEXT)) != NULL) { |
| strlcpy(capabilities->name, ippGetString(attrptr, 0, NULL), sizeof(capabilities->name)); |
| } |
| } |
| |
| if (!capabilities->name[0]) { |
| if ((attrptr = ippFindAttribute(response, "printer-name", IPP_TAG_TEXT)) != 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 |
| && strlen(capabilities->mediaDefault) <= 0) { |
| 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; |
| } |
| } |
| } |
| if ((attrptr = ippFindAttribute(response, "print-quality-supported", IPP_TAG_ENUM)) != |
| NULL) { |
| for (i = 0; i < ippGetCount(attrptr) && capabilities->numSupportedQuality |
| < MAX_QUALITY_SUPPORTED; i++) { |
| LOGD("print-quality-supported: %d", ippGetInteger(attrptr, i)); |
| capabilities->supportedQuality[capabilities->numSupportedQuality++] = |
| ippGetInteger(attrptr, i); |
| } |
| } |
| |
| 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); |
| } else if (strcasestr(ippGetString(attrptr, i, NULL), "auto")) { |
| addMediaType(capabilities, MEDIA_AUTO); |
| } |
| } |
| } |
| |
| 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? (assume PCLM and PWG are similar) |
| capabilities->canRotateDuplexBackPage = 0; |
| if ((attrptr = ippFindAttribute(response, "pclm-raster-back-side", IPP_TAG_KEYWORD)) == NULL) { |
| attrptr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD); |
| } |
| if (attrptr != NULL && strcmp(ippGetString(attrptr, 0, NULL), "rotated") != 0) { |
| LOGD("Device can rotate back page for duplex jobs."); |
| 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; |
| } |
| } |
| if ((attrptr = ippFindAttribute(response, "printer-output-tray", IPP_TAG_STRING)) != NULL) { |
| for (i = 0; i < ippGetCount(attrptr); i++) { |
| int length = 0; |
| const char *tray_str = ippGetOctetString(attrptr, i, &length); |
| if (strstr(tray_str, "faceUp") != 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 authentication is required by any URI, mark it invalid |
| 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++) { |
| // Copy if the URI is valid and we haven't yet discovered ipps |
| if (uris[i].valid && strncmp(capabilities->printerUri, "ipps://", 7) != 0) { |
| LOGD("parse_printerUris found %s", uris[i].uri); |
| strlcpy(capabilities->printerUri, uris[i].uri, sizeof(capabilities->printerUri)); |
| } |
| } |
| } |
| |
| 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]); |
| } |
| } |
| } |
| |
| /* |
| * Handle server certificate information. |
| */ |
| static int ipp_server_cert_cb(http_t *http, void *tls, cups_array_t *certs, void *user_data) { |
| wprint_connect_info_t *connect_info = (wprint_connect_info_t *)user_data; |
| int error = 0; |
| if (connect_info->validate_certificate) { |
| http_credential_t *credential = cupsArrayFirst(certs); |
| if (credential) { |
| LOGD("ipp_server_cert_cb: validate_certificate (len=%zu)", credential->datalen); |
| error = connect_info->validate_certificate(connect_info, credential->data, |
| credential->datalen); |
| } |
| } |
| return error; |
| } |
| |
| 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; |
| |
| cupsSetServerCertCB(ipp_server_cert_cb, (void *)connect_info); |
| |
| 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); |
| |
| if (strstr(connect_info->uri_scheme,IPPS_PREFIX) != NULL) { |
| curl_http = httpConnect2(connect_info->printer_addr, ippPortNumber, NULL, AF_UNSPEC, |
| HTTP_ENCRYPTION_ALWAYS, 1, HTTP_TIMEOUT_MILLIS, NULL); |
| |
| // If ALWAYS doesn't work, fall back to REQUIRED |
| if (curl_http == NULL) { |
| curl_http = httpConnect2(connect_info->printer_addr, ippPortNumber, NULL, AF_UNSPEC, |
| HTTP_ENCRYPTION_REQUIRED, 1, HTTP_TIMEOUT_MILLIS, NULL); |
| } |
| } else { |
| curl_http = httpConnect2(connect_info->printer_addr, ippPortNumber, NULL, AF_UNSPEC, |
| HTTP_ENCRYPTION_IF_REQUESTED, 1, HTTP_TIMEOUT_MILLIS, NULL); |
| } |
| |
| httpSetTimeout(curl_http, (double)connect_info->timeout / 1000, NULL, 0); |
| httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, uriLength, connect_info->uri_scheme, NULL, |
| connect_info->printer_addr, ippPortNumber, "%s", uri_path); |
| |
| if (curl_http == NULL) { |
| LOGD("ipp_cups_connect failed addr=%s port=%d", connect_info->printer_addr, ippPortNumber); |
| } |
| |
| cupsSetServerCertCB(NULL, NULL); |
| 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) { |
| 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; |
| } |
| |
| int getJobId(http_t *http, |
| char *http_resource, |
| char *printer_uri, /* I - URI buffer */ |
| job_state_dyn_t *job_state_dyn, |
| const char *requesting_user) { |
| int job_id = -1; |
| // Requested print job attributes |
| static const char *jattrs[] = {"job-id"}; |
| ipp_t *request = NULL; /* IPP request object */ |
| ipp_t *response = NULL; /* IPP response object */ |
| |
| request = ippNewRequest(IPP_GET_JOBS); |
| |
| if (request != NULL) { |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); |
| ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", |
| NULL, requesting_user); |
| ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", |
| sizeof(jattrs) / sizeof(jattrs[0]), NULL, jattrs); |
| |
| if ((response = ipp_doCupsRequest(http, request, http_resource, printer_uri)) == NULL) { |
| job_state_dyn->job_state = IPP_JOB_STATE_UNABLE_TO_CONNECT; |
| job_state_dyn->job_state_reasons[0] = IPP_JOB_STATE_REASON_UNABLE_TO_CONNECT; |
| } else { |
| ipp_attribute_t *attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER); |
| if (attr != NULL) job_id = ippGetInteger(attr, 0); |
| } |
| } |
| |
| if (request != NULL) ippDelete(request); |
| if (response != NULL) ippDelete(response); |
| |
| LOGD("getJobId() returning job-id: %d", job_id); |
| return job_id; |
| } |
| |
| int tryNextResourceExtension(char *printer_uri) { |
| char scheme[1024]; |
| char username[1024]; |
| char host[1024]; |
| char resource[1024]; |
| int port; |
| |
| httpSeparateURI(0, printer_uri, scheme, 1024, username, 1024, host, 1024, |
| &port, resource, 1024); |
| |
| int index; |
| for (index = 0; index < ARRAY_SIZE(resource_extensions_arr); index++) { |
| if (strcmp(resource_extensions_arr[index], resource) == 0) { |
| break; |
| } |
| } |
| if (index >= (ARRAY_SIZE(resource_extensions_arr) - 1)) { |
| return 0; |
| } else { |
| httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, 1024, scheme, NULL, |
| host, port, "%s", resource_extensions_arr[index + 1]); |
| LOGD("next resource %s", printer_uri); |
| return 1; |
| } |
| } |