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