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