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 */
33static status_t determine_ipp_version(char *, http_t *);
34
35/*
36 * Tests IPP versions and sets it to the latest working version
37 */
38static 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 */
43static 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 */
48static 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 */
57struct 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
84typedef struct {
85    double Lower;
86    double Upper;
87} media_dimension_mm_t;
88
89static const char *__request_ipp_version[] = {"ipp-versions-supported"};
90
91static int __ipp_version_major = 2;
92static int __ipp_version_minor = 0;
93
94status_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
118static 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
136static 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
227ipp_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
279void 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
395static 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
484void 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
576void 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
604const 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
614int 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 */
636static 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 */
660static 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
740void 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
903static 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
921void 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 */
932static 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
947void 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
1302typedef struct {
1303    const char *uri;
1304    int valid;
1305} parsed_uri_t;
1306
1307static 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
1354void 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
1368void 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
1405http_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 */
1434static 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 */
1470ipp_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}