• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
set_ipp_version(ipp_t * op_to_set,char * printer_uri,http_t * http,ipp_version_state use_existing_version)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 
determine_ipp_version(char * printer_uri,http_t * http)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 
test_and_set_ipp_version(char * printer_uri,http_t * http,int major,int minor)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 
get_PrinterState(http_t * http,char * printer_uri,printer_state_dyn_t * printer_state_dyn,ipp_pstate_t * printer_state)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 
get_PrinterStateReason(ipp_t * response,ipp_pstate_t * printer_state,printer_state_dyn_t * printer_state_dyn)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 
print_col(ipp_t * col)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 
print_attr(ipp_attribute_t * attr)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 
parse_IPPVersions(ipp_t * response,ipp_version_supported_t * ippVersions)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 
mapDFMediaToIPPKeyword(media_size_t media_size)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 
ipp_find_media_size(const char * ipp_media_keyword,media_size_t * media_size)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  */
substring(const char * string,int position,int length)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  */
getMediaDimensions_mm(const char * mediaSize,media_dimension_mm_t * media_dimensions)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 
parse_getMediaSupported(ipp_t * response,media_supported_t * media_supported,printer_capabilities_t * capabilities)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 
get_supportedPrinterResolutions(ipp_attribute_t * attrptr,printer_capabilities_t * capabilities)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 
getResourceFromURI(const char * uri,char * resource,int resourcelen)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  */
addMediaType(printer_capabilities_t * capabilities,media_type_t mediaType)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 
parse_printerAttributes(ipp_t * response,printer_capabilities_t * capabilities)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 
974     if ((attrptr = ippFindAttribute(response, "printer-dns-sd-name", IPP_TAG_NAME)) != NULL) {
975         strlcpy(capabilities->name, ippGetString(attrptr, 0, NULL), sizeof(capabilities->name));
976     }
977 
978     if (!capabilities->name[0]) {
979         if ((attrptr = ippFindAttribute(response, "printer-info", IPP_TAG_TEXT)) != NULL) {
980             strlcpy(capabilities->name, ippGetString(attrptr, 0, NULL), sizeof(capabilities->name));
981         }
982     }
983 
984     if (!capabilities->name[0]) {
985         if ((attrptr = ippFindAttribute(response, "printer-name", IPP_TAG_TEXT)) != NULL) {
986             strlcpy(capabilities->name, ippGetString(attrptr, 0, NULL), sizeof(capabilities->name));
987         }
988     }
989 
990     if ((attrptr = ippFindAttribute(response, "printer-make-and-model", IPP_TAG_TEXT)) != NULL) {
991         strlcpy(capabilities->make, ippGetString(attrptr, 0, NULL), sizeof(capabilities->make));
992     }
993 
994     if ((attrptr = ippFindAttribute(response, "printer-uuid", IPP_TAG_URI)) != NULL) {
995         strlcpy(capabilities->uuid, ippGetString(attrptr, 0, NULL), sizeof(capabilities->uuid));
996     }
997 
998     if ((attrptr = ippFindAttribute(response, "printer-location", IPP_TAG_TEXT)) != NULL) {
999         strlcpy(capabilities->location, ippGetString(attrptr, 0, NULL),
1000                 sizeof(capabilities->location));
1001     }
1002 
1003     if ((attrptr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL) {
1004         strlcpy(capabilities->mediaDefault, ippGetString(attrptr, 0, NULL),
1005                 sizeof(capabilities->mediaDefault));
1006     }
1007 
1008     if ((attrptr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN)) != NULL) {
1009         if (ippGetBoolean(attrptr, 0)) {
1010             capabilities->color = 1;
1011         }
1012     }
1013     if ((attrptr = ippFindAttribute(response, "copies-supported", IPP_TAG_RANGE)) != NULL) {
1014         int upper = 0;
1015         for (i = 0; i < ippGetCount(attrptr); i++) {
1016             ippGetRange(attrptr, i, &upper);
1017         }
1018         if (upper > 1) {
1019             capabilities->canCopy = 1;
1020         }
1021     }
1022     if ((attrptr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) !=
1023             NULL) {
1024         for (i = 0; i < ippGetCount(attrptr); i++) {
1025             if (strcmp("color", ippGetString(attrptr, i, NULL)) == 0) {
1026                 capabilities->color = 1;
1027             }
1028         }
1029     }
1030 
1031     char imagePCLm[] = "application/PCLm";
1032     char imagePWG[] = "image/pwg-raster";
1033     char imagePDF[] = "image/pdf";
1034     char applicationPDF[] = "application/pdf";
1035 
1036     if ((attrptr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE))
1037             != NULL) {
1038         for (i = 0; i < ippGetCount(attrptr); i++) {
1039             if (strcmp(imagePDF, ippGetString(attrptr, i, NULL)) == 0) {
1040                 capabilities->canPrintPDF = 1;
1041             } else if (strcmp(applicationPDF, ippGetString(attrptr, i, NULL)) == 0) {
1042                 capabilities->canPrintPDF = 1;
1043             } else if (strcmp(imagePCLm, ippGetString(attrptr, i, NULL)) == 0) {
1044                 capabilities->canPrintPCLm = 1;
1045             } else if (strcmp(applicationPDF, ippGetString(attrptr, i, NULL)) == 0) {
1046                 capabilities->canPrintPDF = 1;
1047             } else if (strcmp(imagePWG, ippGetString(attrptr, i, NULL)) == 0) {
1048                 capabilities->canPrintPWG = 1;
1049             }
1050         }
1051     }
1052 
1053     if ((attrptr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL) {
1054         for (i = 0; i < ippGetCount(attrptr); i++) {
1055             if (strcmp(IPP_SIDES_TWO_SIDED_SHORT_EDGE, ippGetString(attrptr, i, NULL)) == 0) {
1056                 capabilities->duplex = 1;
1057             } else if (strcmp(IPP_SIDES_TWO_SIDED_LONG_EDGE, ippGetString(attrptr, i, NULL)) == 0) {
1058                 capabilities->duplex = 1;
1059             }
1060         }
1061     }
1062 
1063     // Look up supported media types
1064     capabilities->numSupportedMediaTypes = 0;
1065     if (((attrptr = ippFindAttribute(response, "media-type-supported", IPP_TAG_KEYWORD)) != NULL)
1066             || ((attrptr = ippFindAttribute(response, "media-type-supported", IPP_TAG_NAME))
1067                     != NULL)) {
1068         for (i = 0; i < ippGetCount(attrptr); i++) {
1069             if (strcasestr(ippGetString(attrptr, i, NULL), "photographic-glossy")) {
1070                 addMediaType(capabilities, MEDIA_PHOTO_GLOSSY);
1071             } else if (strcasestr(ippGetString(attrptr, i, NULL), "photo")) {
1072                 addMediaType(capabilities, MEDIA_PHOTO);
1073             } else if (strcasestr(ippGetString(attrptr, i, NULL), "stationery")) {
1074                 addMediaType(capabilities, MEDIA_PLAIN);
1075             }
1076         }
1077     }
1078 
1079     if (capabilities->numSupportedMediaTypes == 0) {
1080         // If no recognized media types were found, fall back to all 3 just in case
1081         addMediaType(capabilities, MEDIA_PLAIN);
1082         addMediaType(capabilities, MEDIA_PHOTO);
1083         addMediaType(capabilities, MEDIA_PHOTO_GLOSSY);
1084     }
1085 
1086     capabilities->numSupportedResolutions = 0;
1087     // only appears that SMM supports the pclm-source-resolution-supported attribute
1088     // if that is not present, use the printer-resolution-supported attribute to determine
1089     // if 300DPI is supported
1090     if ((attrptr = ippFindAttribute(response, "pclm-source-resolution-supported",
1091             IPP_TAG_RESOLUTION)) != NULL) {
1092         get_supportedPrinterResolutions(attrptr, capabilities);
1093     } else if ((attrptr = ippFindAttribute(response, "printer-resolution-supported",
1094             IPP_TAG_RESOLUTION)) != NULL) {
1095         get_supportedPrinterResolutions(attrptr, capabilities);
1096     }
1097 
1098     char ipp10[] = "1.0";
1099     char ipp11[] = "1.1";
1100     char ipp20[] = "2.0";
1101 
1102     if ((attrptr = ippFindAttribute(response, "ipp-versions-supported", IPP_TAG_KEYWORD)) != NULL) {
1103         unsigned char supportsIpp20 = 0;
1104         unsigned char supportsIpp11 = 0;
1105         unsigned char supportsIpp10 = 0;
1106 
1107         for (i = 0; i < ippGetCount(attrptr); i++) {
1108             if (strcmp(ipp10, ippGetString(attrptr, i, NULL)) == 0) {
1109                 supportsIpp10 = 1;
1110             } else if (strcmp(ipp11, ippGetString(attrptr, i, NULL)) == 0) {
1111                 supportsIpp11 = 1;
1112             } else if (strcmp(ipp20, ippGetString(attrptr, i, NULL)) == 0) {
1113                 supportsIpp20 = 1;
1114             } else {
1115                 LOGD("found another ipp version. %s", ippGetString(attrptr, i, NULL));
1116             }
1117             if (supportsIpp20) {
1118                 capabilities->ippVersionMajor = 2;
1119                 capabilities->ippVersionMinor = 0;
1120             } else if (supportsIpp11) {
1121                 capabilities->ippVersionMajor = 1;
1122                 capabilities->ippVersionMinor = 1;
1123             } else if (supportsIpp10) {
1124                 capabilities->ippVersionMajor = 1;
1125                 capabilities->ippVersionMinor = 0;
1126             } else {
1127                 // default to 1.0
1128                 capabilities->ippVersionMajor = 1;
1129                 capabilities->ippVersionMinor = 0;
1130             }
1131         }
1132     }
1133 
1134     char epcl10[] = "1.0";
1135     if ((attrptr = ippFindAttribute(response, "epcl-version-supported", IPP_TAG_KEYWORD)) != NULL) {
1136         for (i = 0; i < ippGetCount(attrptr); i++) {
1137             LOGD("setting epcl_ipp_version (KEYWORD) %s", ippGetString(attrptr, i, NULL));
1138 
1139             // substring match because different devices implemented spec differently
1140             if (strstr(ippGetString(attrptr, i, NULL), epcl10) != NULL) {
1141                 LOGD("setting epcl_ipp_version = 1");
1142                 capabilities->ePclIppVersion = 1;
1143             }
1144         }
1145     }
1146 
1147     if ((attrptr = ippFindAttribute(response, "epcl-version-supported", IPP_TAG_TEXT)) != NULL) {
1148         for (i = 0; i < ippGetCount(attrptr); i++) {
1149             LOGD("setting epcl_ipp_verion (TEXT) %s", ippGetString(attrptr, i, NULL));
1150 
1151             // substring match because different devices implemented spec differently
1152             if (strstr(ippGetString(attrptr, i, NULL), epcl10) != NULL) {
1153                 LOGD("setting epcl_ipp_verion = 1");
1154                 capabilities->ePclIppVersion = 1;
1155             }
1156         }
1157     }
1158 
1159     if ((attrptr = ippFindAttribute(response, "media-col-default", IPP_TAG_BEGIN_COLLECTION)) !=
1160             NULL) {
1161         for (i = 0; i < ippGetCount(attrptr); i++) {
1162             LOGD("Gathering margins supported");
1163 
1164             ipp_t *collection = ippGetCollection(attrptr, i);
1165 
1166             for (j = 0, attrptr = ippFirstAttribute(collection);
1167                     (j < 4) && (attrptr != NULL); attrptr = ippNextAttribute(collection)) {
1168                 if (strcmp("media-top-margin", ippGetName(attrptr)) == 0) {
1169                     capabilities->printerTopMargin = ippGetInteger(attrptr, 0);
1170                 } else if (strcmp("media-bottom-margin", ippGetName(attrptr)) == 0) {
1171                     capabilities->printerBottomMargin = ippGetInteger(attrptr, 0);
1172                 } else if (strcmp("media-left-margin", ippGetName(attrptr)) == 0) {
1173                     capabilities->printerLeftMargin = ippGetInteger(attrptr, 0);
1174                 } else if (strcmp("media-right-margin", ippGetName(attrptr)) == 0) {
1175                     capabilities->printerRightMargin = ippGetInteger(attrptr, 0);
1176                 }
1177             }
1178         }
1179     }
1180 
1181     if ((attrptr = ippFindAttribute(response, "media-size-name", IPP_TAG_KEYWORD)) != NULL) {
1182         capabilities->isMediaSizeNameSupported = true;
1183     } else {
1184         capabilities->isMediaSizeNameSupported = false;
1185     }
1186 
1187     // is strip length supported? if so, stored in capabilities
1188     if ((attrptr = ippFindAttribute(response, "pclm-strip-height-preferred",
1189             IPP_TAG_INTEGER)) != NULL) {
1190         LOGD("pclm-strip-height-preferred=%d", ippGetInteger(attrptr, 0));
1191 
1192         // if the strip height is 0, the device wants us to send the entire page in one band
1193         // (according to ePCL spec). Since our code doesn't currently support generating an entire
1194         // page in one band, set the strip height to the default value every device *should* support
1195         // also, for some reason our code crashes when it attempts to generate strips at 512 or
1196         // above. Therefore, limiting the upper bound strip height to 256
1197         if (ippGetInteger(attrptr, 0) == 0 || ippGetInteger(attrptr, 0) > 256) {
1198             capabilities->stripHeight = STRIPE_HEIGHT;
1199         } else {
1200             capabilities->stripHeight = ippGetInteger(attrptr, 0);
1201         }
1202     } else {
1203         capabilities->stripHeight = STRIPE_HEIGHT;
1204     }
1205 
1206     // what is the preferred compression method - jpeg, flate, rle
1207     if ((attrptr = ippFindAttribute(response, "pclm-compression-method-preferred",
1208             IPP_TAG_KEYWORD)) != NULL) {
1209         LOGD("pclm-compression-method-preferred=%s", ippGetString(attrptr, 0, NULL));
1210     }
1211 
1212     // is device able to rotate back page for duplex jobs?
1213     if ((attrptr = ippFindAttribute(response, "pclm-raster-back-side", IPP_TAG_KEYWORD)) != NULL) {
1214         LOGD("pclm-raster-back-side=%s", ippGetString(attrptr, 0, NULL));
1215         if (strcmp(ippGetString(attrptr, 0, NULL), "rotated") == 0) {
1216             capabilities->canRotateDuplexBackPage = 0;
1217             LOGD("Device cannot rotate back page for duplex jobs.");
1218         } else {
1219             capabilities->canRotateDuplexBackPage = 1;
1220         }
1221     }
1222 
1223     // look for full-bleed supported by looking for 0 on all margins
1224     bool topsupported = false, bottomsupported = false, rightsupported = false,
1225             leftsupported = false;
1226     if ((attrptr = ippFindAttribute(response, "media-top-margin-supported", IPP_TAG_INTEGER)) !=
1227             NULL) {
1228         for (i = 0; i < ippGetCount(attrptr); i++) {
1229             if (ippGetInteger(attrptr, i) == 0) {
1230                 LOGD("Top Margin Supported");
1231                 topsupported = true;
1232                 break;
1233             }
1234         }
1235     }
1236     if ((attrptr = ippFindAttribute(response, "media-bottom-margin-supported", IPP_TAG_INTEGER)) !=
1237             NULL) {
1238         for (i = 0; i < ippGetCount(attrptr); i++) {
1239             if (ippGetInteger(attrptr, i) == 0) {
1240                 LOGD("Bottom Margin Supported");
1241                 bottomsupported = true;
1242                 break;
1243             }
1244         }
1245     }
1246     if ((attrptr = ippFindAttribute(response, "media-right-margin-supported", IPP_TAG_INTEGER)) !=
1247             NULL) {
1248         for (i = 0; i < ippGetCount(attrptr); i++) {
1249             if (ippGetInteger(attrptr, i) == 0) {
1250                 LOGD("Right Margin Supported");
1251                 rightsupported = true;
1252                 break;
1253             }
1254         }
1255     }
1256     if ((attrptr = ippFindAttribute(response, "media-left-margin-supported", IPP_TAG_INTEGER)) !=
1257             NULL) {
1258         for (i = 0; i < ippGetCount(attrptr); i++) {
1259             if (ippGetInteger(attrptr, i) == 0) {
1260                 LOGD("Left Margin Supported");
1261                 leftsupported = true;
1262                 break;
1263             }
1264         }
1265     }
1266 
1267     if (topsupported && bottomsupported && rightsupported && leftsupported) {
1268         LOGD("full-bleed is supported");
1269         capabilities->borderless = 1;
1270     } else {
1271         LOGD("full-bleed is NOT supported");
1272     }
1273 
1274     if ((attrptr = ippFindAttribute(response, "printer-device-id", IPP_TAG_TEXT)) != NULL) {
1275         if (strstr(ippGetString(attrptr, 0, NULL), "PCL3GUI") != NULL) {
1276             capabilities->inkjet = 1;
1277         }
1278     } else if (capabilities->borderless == 1) {
1279         capabilities->inkjet = 1;
1280     }
1281 
1282     // determine if device prints pages face-down
1283     capabilities->faceDownTray = 1;
1284     if ((attrptr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_KEYWORD)) != NULL) {
1285         if (strstr(ippGetString(attrptr, 0, NULL), "face-up") != NULL) {
1286             capabilities->faceDownTray = 0;
1287         }
1288     }
1289 
1290     // Determine supported document format details
1291     if ((attrptr = ippFindAttribute(response, "document-format-details-supported",
1292             IPP_TAG_KEYWORD)) != NULL) {
1293         for (i = 0; i < ippGetCount(attrptr); i++) {
1294             if (strcmp("document-source-application-name", ippGetString(attrptr, i, NULL)) == 0) {
1295                 capabilities->docSourceAppName = 1;
1296             } else if (
1297                     strcmp("document-source-application-version", ippGetString(attrptr, i, NULL)) ==
1298                             0) {
1299                 capabilities->docSourceAppVersion = 1;
1300             } else if (strcmp("document-source-os-name", ippGetString(attrptr, i, NULL)) == 0) {
1301                 capabilities->docSourceOsName = 1;
1302             } else if (strcmp("document-source-os-version", ippGetString(attrptr, i, NULL)) == 0) {
1303                 capabilities->docSourceOsVersion = 1;
1304             }
1305         }
1306     }
1307     debuglist_printerCapabilities(capabilities);
1308 }
1309 
1310 // Used in parse_printerUris
1311 #define MAX_URIS 10
1312 typedef struct {
1313     const char *uri;
1314     int valid;
1315 } parsed_uri_t;
1316 
parse_printerUris(ipp_t * response,printer_capabilities_t * capabilities)1317 static void parse_printerUris(ipp_t *response, printer_capabilities_t *capabilities) {
1318     ipp_attribute_t *attrptr;
1319     int i;
1320     parsed_uri_t uris[MAX_URIS] = {0};
1321 
1322     if ((attrptr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL) {
1323         for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) {
1324             uris[i].uri = ippGetString(attrptr, i, NULL);
1325             uris[i].valid = true;
1326         }
1327     }
1328 
1329     // If security or authentication is required (non-"none") at any URI, mark it invalid
1330 
1331     if ((attrptr = ippFindAttribute(response, "uri-security-supported", IPP_TAG_KEYWORD)) != NULL) {
1332         for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) {
1333             if (strcmp("none", ippGetString(attrptr, i, NULL)) != 0) {
1334                 LOGD("parse_printerUris %s invalid because sec=%s", uris[i].uri,
1335                         ippGetString(attrptr, i, NULL));
1336                 uris[i].valid = false;
1337             }
1338         }
1339     }
1340 
1341     if ((attrptr = ippFindAttribute(response, "uri-authentication-supported", IPP_TAG_KEYWORD))
1342             != NULL) {
1343         for (i = 0; i < MIN(ippGetCount(attrptr), MAX_URIS); i++) {
1344             // Allow "none" and "requesting-user-name" only
1345             if (strcmp("none", ippGetString(attrptr, i, NULL)) != 0 &&
1346                     strcmp("requesting-user-name", ippGetString(attrptr, i, NULL)) != 0) {
1347                 LOGD("parse_printerUris %s invalid because auth=%s", uris[i].uri,
1348                         ippGetString(attrptr, i, NULL));
1349                 uris[i].valid = false;
1350             }
1351         }
1352     }
1353 
1354     // Find a valid URI and copy it into place.
1355     for (i = 0; i < MAX_URIS; i++) {
1356         if (uris[i].valid) {
1357             LOGD("parse_printerUris found %s", uris[i].uri);
1358             strlcpy(capabilities->printerUri, uris[i].uri, sizeof(capabilities->printerUri));
1359             break;
1360         }
1361     }
1362 }
1363 
debuglist_printerCapabilities(printer_capabilities_t * capabilities)1364 void debuglist_printerCapabilities(printer_capabilities_t *capabilities) {
1365     LOGD("printer make: %s", capabilities->make);
1366     LOGD("printer default media: %s", capabilities->mediaDefault);
1367     LOGD("canPrintPDF: %d", capabilities->canPrintPDF);
1368     LOGD("duplex: %d", capabilities->duplex);
1369     LOGD("canRotateDuplexBackPage: %d", capabilities->canRotateDuplexBackPage);
1370     LOGD("color: %d", capabilities->color);
1371     LOGD("canCopy: %d", capabilities->canCopy);
1372     LOGD("ippVersionMajor: %d", capabilities->ippVersionMajor);
1373     LOGD("ippVersionMinor: %d", capabilities->ippVersionMinor);
1374     LOGD("strip height: %d", capabilities->stripHeight);
1375     LOGD("faceDownTray: %d", capabilities->faceDownTray);
1376 }
1377 
debuglist_printerStatus(printer_state_dyn_t * printer_state_dyn)1378 void debuglist_printerStatus(printer_state_dyn_t *printer_state_dyn) {
1379     const char *decoded = "unknown";
1380     if (printer_state_dyn->printer_status == PRINT_STATUS_INITIALIZING) {
1381         decoded = "Initializing";
1382     } else if (printer_state_dyn->printer_status == PRINT_STATUS_SHUTTING_DOWN) {
1383         decoded = "Shutting Down";
1384     } else if (printer_state_dyn->printer_status == PRINT_STATUS_UNABLE_TO_CONNECT) {
1385         decoded = "Unable To Connect";
1386     } else if (printer_state_dyn->printer_status == PRINT_STATUS_UNKNOWN) {
1387         decoded = "Unknown";
1388     } else if (printer_state_dyn->printer_status == PRINT_STATUS_OFFLINE) {
1389         decoded = "Offline";
1390     } else if (printer_state_dyn->printer_status == PRINT_STATUS_IDLE) {
1391         decoded = "Idle";
1392     } else if (printer_state_dyn->printer_status == PRINT_STATUS_PRINTING) {
1393         decoded = "Printing";
1394     } else if (printer_state_dyn->printer_status == PRINT_STATUS_OUT_OF_PAPER) {
1395         decoded = "Out Of Paper";
1396     } else if (printer_state_dyn->printer_status == PRINT_STATUS_OUT_OF_INK) {
1397         decoded = "Out Of Ink";
1398     } else if (printer_state_dyn->printer_status == PRINT_STATUS_JAMMED) {
1399         decoded = "Jammed";
1400     } else if (printer_state_dyn->printer_status == PRINT_STATUS_DOOR_OPEN) {
1401         decoded = "Door Open";
1402     } else if (printer_state_dyn->printer_status == PRINT_STATUS_SVC_REQUEST) {
1403         decoded = "Service Request";
1404     }
1405     LOGD("printer status: %d (%s)", printer_state_dyn->printer_status, decoded);
1406 
1407     int idx = 0;
1408     for (idx = 0; idx < (PRINT_STATUS_MAX_STATE + 1); idx++) {
1409         if (PRINT_STATUS_MAX_STATE != printer_state_dyn->printer_reasons[idx]) {
1410             LOGD("printer_reasons (%d): %d", idx, printer_state_dyn->printer_reasons[idx]);
1411         }
1412     }
1413 }
1414 
ipp_cups_connect(const wprint_connect_info_t * connect_info,char * printer_uri,unsigned int uriLength)1415 http_t *ipp_cups_connect(const wprint_connect_info_t *connect_info, char *printer_uri,
1416         unsigned int uriLength) {
1417     const char *uri_path;
1418     http_t *curl_http = NULL;
1419 
1420     if ((connect_info->uri_path == NULL) || (strlen(connect_info->uri_path) == 0)) {
1421         uri_path = DEFAULT_IPP_URI_RESOURCE;
1422     } else {
1423         uri_path = connect_info->uri_path;
1424     }
1425 
1426     int ippPortNumber = ((connect_info->port_num == IPP_PORT) ? ippPort() : connect_info->port_num);
1427 
1428     if (strstr(connect_info->uri_scheme,IPPS_PREFIX) != NULL) {
1429         curl_http = httpConnectEncrypt(connect_info->printer_addr, ippPortNumber, HTTP_ENCRYPTION_ALWAYS);
1430 
1431         // If ALWAYS doesn't work, fall back to REQUIRED
1432         if (curl_http == NULL) {
1433             curl_http = httpConnectEncrypt(connect_info->printer_addr, ippPortNumber, HTTP_ENCRYPT_REQUIRED);
1434         }
1435     } else {
1436         curl_http = httpConnectEncrypt(connect_info->printer_addr, ippPortNumber, HTTP_ENCRYPTION_IF_REQUESTED);
1437     }
1438 
1439     httpSetTimeout(curl_http, (double)connect_info->timeout / 1000, NULL, 0);
1440     httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, uriLength, connect_info->uri_scheme, NULL,
1441             connect_info->printer_addr, ippPortNumber, uri_path);
1442 
1443     if (curl_http == NULL) {
1444         LOGD("ipp_cups_connect failed addr=%s port=%d", connect_info->printer_addr, ippPortNumber);
1445     }
1446     return curl_http;
1447 }
1448 
1449 /*
1450  * Send a request using cupsSendRequest(). Loop if we get NULL or CONTINUE. Does not delete
1451  * the request.
1452  */
ippSendRequest(http_t * http,ipp_t * request,char * resource)1453 static ipp_t *ippSendRequest(http_t *http, ipp_t *request, char *resource) {
1454     ipp_t *response = NULL;
1455     http_status_t result;
1456     bool retry;
1457 
1458     do {
1459         retry = false;
1460         result = cupsSendRequest(http, request, resource, ippLength(request));
1461         if (result != HTTP_ERROR) {
1462             response = cupsGetResponse(http, resource);
1463             result = httpGetStatus(http);
1464         }
1465 
1466         if (result == HTTP_CONTINUE && response == NULL) {
1467             // We need to retry when this happens.
1468             LOGD("ippSendRequest: (Continue with NULL response) Retry");
1469             retry = true;
1470         } else if (result == HTTP_ERROR || result >= HTTP_BAD_REQUEST) {
1471             _cupsSetHTTPError(result);
1472             break;
1473         }
1474 
1475         if (http->state != HTTP_WAITING) {
1476             httpFlush(http);
1477         }
1478     } while (retry);
1479 
1480     return response;
1481 }
1482 
1483 /*
1484  * Call ippDoCupsIORequest, repeating if a failure occurs based on failure conditions, and
1485  * returning the response (or NULL if it failed).
1486  *
1487  * Does not free the request, and the caller must call ippDelete to free any valid response.
1488  */
ipp_doCupsRequest(http_t * http,ipp_t * request,char * http_resource,char * printer_uri)1489 ipp_t *ipp_doCupsRequest(http_t *http, ipp_t *request, char *http_resource, char *printer_uri) {
1490     ipp_status_t ipp_status;
1491     ipp_t *response = NULL;
1492     int service_unavailable_retry_count = 0;
1493     int bad_request_retry_count = 0;
1494     int internal_error_retry_count = 0;
1495     ipp_version_state ipp_version_supported = IPP_VERSION_RESOLVED;
1496 
1497     // Fail if any of these parameters are NULL
1498     if (http == NULL || request == NULL || http_resource == NULL || printer_uri == NULL) {
1499         return NULL;
1500     }
1501 
1502     do {
1503         // Give up immediately if wprint is done.
1504         if (!wprintIsRunning()) return NULL;
1505 
1506         // This is a no-op until we hit the error IPP_VERSION_NOT_SUPPORTED and retry.
1507         if (set_ipp_version(request, printer_uri, http, ipp_version_supported) != 0) {
1508             // We tried to find the correct IPP version by doing a series of get attribute
1509             // requests but they all failed... we give up.
1510             LOGE("ipp_doCupsRequest: set_ipp_version!=0, version not set");
1511             break;
1512         }
1513 
1514         response = ippSendRequest(http, request, http_resource);
1515         if (response == NULL) {
1516             ipp_status = cupsLastError();
1517             if (ipp_status == IPP_INTERNAL_ERROR || ipp_status == HTTP_ERROR) {
1518                 internal_error_retry_count++;
1519                 if (internal_error_retry_count > IPP_INTERNAL_ERROR_MAX_RETRIES) {
1520                     break;
1521                 }
1522 
1523                 LOGE("ipp_doCupsRequest: %s %d received, retry %d of %d",
1524                         printer_uri, ipp_status, internal_error_retry_count,
1525                         IPP_INTERNAL_ERROR_MAX_RETRIES);
1526                 continue;
1527             } else if (ipp_status == IPP_SERVICE_UNAVAILABLE) {
1528                 service_unavailable_retry_count++;
1529                 if (service_unavailable_retry_count > IPP_SERVICE_ERROR_MAX_RETRIES) {
1530                     break;
1531                 }
1532 
1533                 LOGE("ipp_doCupsRequest: %s IPP_SERVICE_UNAVAILABLE received, retrying %d of %d",
1534                         printer_uri, service_unavailable_retry_count,
1535                         IPP_SERVICE_ERROR_MAX_RETRIES);
1536                 continue;
1537             } else if (ipp_status == IPP_BAD_REQUEST) {
1538                 bad_request_retry_count++;
1539                 if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) {
1540                     break;
1541                 }
1542 
1543                 LOGD("ipp_doCupsRequest: %s IPP_BAD_REQUEST received. retry (%d) of (%d)",
1544                         printer_uri, bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES);
1545                 continue;
1546             } else if (ipp_status == IPP_NOT_FOUND) {
1547                 LOGE("ipp_doCupsRequest: %s IPP_NOT_FOUND received.", printer_uri);
1548                 break;
1549             }
1550         } else {
1551             ipp_status = cupsLastError();
1552             if (ipp_status == IPP_BAD_REQUEST) {
1553                 bad_request_retry_count++;
1554                 LOGE("ipp_doCupsRequest: %s IPP_BAD_REQUEST received. retry (%d) of (%d)",
1555                         printer_uri, bad_request_retry_count, IPP_BAD_REQUEST_MAX_RETRIES);
1556                 if (bad_request_retry_count > IPP_BAD_REQUEST_MAX_RETRIES) {
1557                     break;
1558                 }
1559 
1560                 ippDelete(response);
1561                 response = NULL;
1562                 continue;
1563             } else if (ipp_status == IPP_VERSION_NOT_SUPPORTED) {
1564                 ipp_version_supported = IPP_VERSION_UNSUPPORTED;
1565                 ippDelete(response);
1566                 response = NULL;
1567                 continue;
1568             }
1569         }
1570         break;
1571     } while (1);
1572 
1573     return response;
1574 }