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