1 /*
2  * Option encoding routines for CUPS.
3  *
4  * Copyright © 2007-2019 by Apple Inc.
5  * Copyright © 1997-2007 by Easy Software Products.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
8  * information.
9  */
10 
11 /*
12  * Include necessary headers...
13  */
14 
15 #include "cups-private.h"
16 #include "debug-internal.h"
17 
18 
19 /*
20  * Local list of option names, the value tags they should use, and the list of
21  * supported operations...
22  *
23  * **** THIS LIST MUST BE SORTED BY ATTRIBUTE NAME ****
24  */
25 
26 static const ipp_op_t ipp_job_creation[] =
27 {
28   IPP_OP_PRINT_JOB,
29   IPP_OP_PRINT_URI,
30   IPP_OP_VALIDATE_JOB,
31   IPP_OP_CREATE_JOB,
32   IPP_OP_HOLD_JOB,
33   IPP_OP_SET_JOB_ATTRIBUTES,
34   IPP_OP_CUPS_NONE
35 };
36 
37 static const ipp_op_t ipp_doc_creation[] =
38 {
39   IPP_OP_PRINT_JOB,
40   IPP_OP_PRINT_URI,
41   IPP_OP_SEND_DOCUMENT,
42   IPP_OP_SEND_URI,
43   IPP_OP_SET_JOB_ATTRIBUTES,
44   IPP_OP_SET_DOCUMENT_ATTRIBUTES,
45   IPP_OP_CUPS_NONE
46 };
47 
48 static const ipp_op_t ipp_sub_creation[] =
49 {
50   IPP_OP_PRINT_JOB,
51   IPP_OP_PRINT_URI,
52   IPP_OP_CREATE_JOB,
53   IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS,
54   IPP_OP_CREATE_JOB_SUBSCRIPTIONS,
55   IPP_OP_CUPS_NONE
56 };
57 
58 static const ipp_op_t ipp_all_print[] =
59 {
60   IPP_OP_PRINT_JOB,
61   IPP_OP_PRINT_URI,
62   IPP_OP_VALIDATE_JOB,
63   IPP_OP_CREATE_JOB,
64   IPP_OP_SEND_DOCUMENT,
65   IPP_OP_SEND_URI,
66   IPP_OP_CUPS_NONE
67 };
68 
69 static const ipp_op_t ipp_set_printer[] =
70 {
71   IPP_OP_SET_PRINTER_ATTRIBUTES,
72   IPP_OP_CUPS_ADD_MODIFY_PRINTER,
73   IPP_OP_CUPS_ADD_MODIFY_CLASS,
74   IPP_OP_CUPS_NONE
75 };
76 
77 static const ipp_op_t cups_schemes[] =
78 {
79   IPP_OP_CUPS_GET_DEVICES,
80   IPP_OP_CUPS_GET_PPDS,
81   IPP_OP_CUPS_NONE
82 };
83 
84 static const ipp_op_t cups_get_ppds[] =
85 {
86   IPP_OP_CUPS_GET_PPDS,
87   IPP_OP_CUPS_NONE
88 };
89 
90 static const ipp_op_t cups_ppd_name[] =
91 {
92   IPP_OP_CUPS_ADD_MODIFY_PRINTER,
93   IPP_OP_CUPS_GET_PPD,
94   IPP_OP_CUPS_NONE
95 };
96 
97 static const _ipp_option_t ipp_options[] =
98 {
99   { 1, "auth-info",		IPP_TAG_TEXT,		IPP_TAG_JOB },
100   { 1, "auth-info-default",	IPP_TAG_TEXT,		IPP_TAG_PRINTER },
101   { 1, "auth-info-required",	IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
102   { 0, "blackplot",		IPP_TAG_BOOLEAN,	IPP_TAG_JOB },
103   { 0, "blackplot-default",	IPP_TAG_BOOLEAN,	IPP_TAG_PRINTER },
104   { 0, "brightness",		IPP_TAG_INTEGER,	IPP_TAG_JOB },
105   { 0, "brightness-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
106   { 0, "columns",		IPP_TAG_INTEGER,	IPP_TAG_JOB },
107   { 0, "columns-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
108   { 0, "compression",		IPP_TAG_KEYWORD,	IPP_TAG_OPERATION,
109 							IPP_TAG_ZERO,
110 							ipp_doc_creation },
111   { 0, "copies",		IPP_TAG_INTEGER,	IPP_TAG_JOB,
112 							IPP_TAG_DOCUMENT },
113   { 0, "copies-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
114   { 0, "date-time-at-completed",IPP_TAG_DATE,		IPP_TAG_ZERO }, /* never send as option */
115   { 0, "date-time-at-creation",	IPP_TAG_DATE,		IPP_TAG_ZERO }, /* never send as option */
116   { 0, "date-time-at-processing",IPP_TAG_DATE,		IPP_TAG_ZERO }, /* never send as option */
117   { 0, "device-uri",		IPP_TAG_URI,		IPP_TAG_PRINTER },
118   { 1, "document-copies",	IPP_TAG_RANGE,		IPP_TAG_JOB,
119 							IPP_TAG_DOCUMENT,
120 							ipp_doc_creation },
121   { 0, "document-format",	IPP_TAG_MIMETYPE,	IPP_TAG_OPERATION,
122 							IPP_TAG_ZERO,
123 							ipp_doc_creation },
124   { 0, "document-format-default", IPP_TAG_MIMETYPE,	IPP_TAG_PRINTER },
125   { 1, "document-numbers",	IPP_TAG_RANGE,		IPP_TAG_JOB,
126 							IPP_TAG_DOCUMENT,
127 							ipp_all_print },
128   { 1, "exclude-schemes",	IPP_TAG_NAME,		IPP_TAG_OPERATION,
129 							IPP_TAG_ZERO,
130 							cups_schemes },
131   { 1, "finishings",		IPP_TAG_ENUM,		IPP_TAG_JOB,
132 							IPP_TAG_DOCUMENT },
133   { 1, "finishings-col",	IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB,
134 							IPP_TAG_DOCUMENT },
135   { 1, "finishings-col-default", IPP_TAG_BEGIN_COLLECTION, IPP_TAG_PRINTER },
136   { 1, "finishings-default",	IPP_TAG_ENUM,		IPP_TAG_PRINTER },
137   { 0, "fit-to-page",		IPP_TAG_BOOLEAN,	IPP_TAG_JOB,
138 							IPP_TAG_DOCUMENT },
139   { 0, "fit-to-page-default",	IPP_TAG_BOOLEAN,	IPP_TAG_PRINTER },
140   { 0, "fitplot",		IPP_TAG_BOOLEAN,	IPP_TAG_JOB },
141   { 0, "fitplot-default",	IPP_TAG_BOOLEAN,	IPP_TAG_PRINTER },
142   { 0, "gamma",			IPP_TAG_INTEGER,	IPP_TAG_JOB },
143   { 0, "gamma-default",		IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
144   { 0, "hue",			IPP_TAG_INTEGER,	IPP_TAG_JOB },
145   { 0, "hue-default",		IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
146   { 1, "include-schemes",	IPP_TAG_NAME,		IPP_TAG_OPERATION,
147 							IPP_TAG_ZERO,
148 							cups_schemes },
149   { 0, "ipp-attribute-fidelity", IPP_TAG_BOOLEAN,	IPP_TAG_OPERATION },
150   { 0, "job-account-id",        IPP_TAG_NAME,           IPP_TAG_JOB },
151   { 0, "job-account-id-default",IPP_TAG_NAME,           IPP_TAG_PRINTER },
152   { 0, "job-accounting-user-id", IPP_TAG_NAME,          IPP_TAG_JOB },
153   { 0, "job-accounting-user-id-default", IPP_TAG_NAME,  IPP_TAG_PRINTER },
154   { 0, "job-authorization-uri",	IPP_TAG_URI,		IPP_TAG_OPERATION },
155   { 0, "job-cancel-after",	IPP_TAG_INTEGER,	IPP_TAG_JOB },
156   { 0, "job-cancel-after-default", IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
157   { 0, "job-hold-until",	IPP_TAG_KEYWORD,	IPP_TAG_JOB },
158   { 0, "job-hold-until-default", IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
159   { 0, "job-id",		IPP_TAG_INTEGER,	IPP_TAG_ZERO }, /* never send as option */
160   { 0, "job-impressions",	IPP_TAG_INTEGER,	IPP_TAG_OPERATION },
161   { 0, "job-impressions-completed", IPP_TAG_INTEGER,	IPP_TAG_ZERO }, /* never send as option */
162   { 0, "job-k-limit",		IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
163   { 0, "job-k-octets",		IPP_TAG_INTEGER,	IPP_TAG_OPERATION },
164   { 0, "job-k-octets-completed",IPP_TAG_INTEGER,	IPP_TAG_ZERO }, /* never send as option */
165   { 0, "job-media-sheets",	IPP_TAG_INTEGER,	IPP_TAG_OPERATION },
166   { 0, "job-media-sheets-completed", IPP_TAG_INTEGER,	IPP_TAG_ZERO }, /* never send as option */
167   { 0, "job-name",		IPP_TAG_NAME,		IPP_TAG_OPERATION,
168 							IPP_TAG_JOB },
169   { 0, "job-page-limit",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
170   { 0, "job-pages",		IPP_TAG_INTEGER,	IPP_TAG_OPERATION },
171   { 0, "job-pages-completed",	IPP_TAG_INTEGER,	IPP_TAG_ZERO }, /* never send as option */
172   { 0, "job-password",          IPP_TAG_STRING,         IPP_TAG_OPERATION,
173 							IPP_TAG_ZERO,
174 							ipp_job_creation },
175   { 0, "job-password-encryption", IPP_TAG_KEYWORD,      IPP_TAG_OPERATION,
176 							IPP_TAG_ZERO,
177 							ipp_job_creation },
178   { 0, "job-priority",		IPP_TAG_INTEGER,	IPP_TAG_JOB },
179   { 0, "job-priority-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
180   { 0, "job-quota-period",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
181   { 1, "job-sheets",		IPP_TAG_NAME,		IPP_TAG_JOB },
182   { 1, "job-sheets-default",	IPP_TAG_NAME,		IPP_TAG_PRINTER },
183   { 0, "job-state",		IPP_TAG_ENUM,		IPP_TAG_ZERO }, /* never send as option */
184   { 0, "job-state-message",	IPP_TAG_TEXT,		IPP_TAG_ZERO }, /* never send as option */
185   { 0, "job-state-reasons",	IPP_TAG_KEYWORD,	IPP_TAG_ZERO }, /* never send as option */
186   { 0, "job-uuid",		IPP_TAG_URI,		IPP_TAG_JOB },
187   { 0, "landscape",		IPP_TAG_BOOLEAN,	IPP_TAG_JOB },
188   { 1, "marker-change-time",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
189   { 1, "marker-colors",		IPP_TAG_NAME,		IPP_TAG_PRINTER },
190   { 1, "marker-high-levels",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
191   { 1, "marker-levels",		IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
192   { 1, "marker-low-levels",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
193   { 0, "marker-message",	IPP_TAG_TEXT,		IPP_TAG_PRINTER },
194   { 1, "marker-names",		IPP_TAG_NAME,		IPP_TAG_PRINTER },
195   { 1, "marker-types",		IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
196   { 1, "media",			IPP_TAG_KEYWORD,	IPP_TAG_JOB,
197 							IPP_TAG_DOCUMENT },
198   { 0, "media-bottom-margin",	IPP_TAG_INTEGER,	IPP_TAG_JOB,
199 							IPP_TAG_DOCUMENT },
200   { 0, "media-col",		IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB,
201 							IPP_TAG_DOCUMENT },
202   { 0, "media-col-default",	IPP_TAG_BEGIN_COLLECTION, IPP_TAG_PRINTER },
203   { 0, "media-color",		IPP_TAG_KEYWORD,	IPP_TAG_JOB,
204 							IPP_TAG_DOCUMENT },
205   { 1, "media-default",		IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
206   { 0, "media-key",		IPP_TAG_KEYWORD,	IPP_TAG_JOB,
207 							IPP_TAG_DOCUMENT },
208   { 0, "media-left-margin",	IPP_TAG_INTEGER,	IPP_TAG_JOB,
209 							IPP_TAG_DOCUMENT },
210   { 0, "media-right-margin",	IPP_TAG_INTEGER,	IPP_TAG_JOB,
211 							IPP_TAG_DOCUMENT },
212   { 0, "media-size",		IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB,
213 							IPP_TAG_DOCUMENT },
214   { 0, "media-size-name",	IPP_TAG_KEYWORD,	IPP_TAG_JOB,
215 							IPP_TAG_DOCUMENT },
216   { 0, "media-source",		IPP_TAG_KEYWORD,	IPP_TAG_JOB,
217 							IPP_TAG_DOCUMENT },
218   { 0, "media-top-margin",	IPP_TAG_INTEGER,	IPP_TAG_JOB,
219 							IPP_TAG_DOCUMENT },
220   { 0, "media-type",		IPP_TAG_KEYWORD,	IPP_TAG_JOB,
221 							IPP_TAG_DOCUMENT },
222   { 0, "mirror",		IPP_TAG_BOOLEAN,	IPP_TAG_JOB },
223   { 0, "mirror-default",	IPP_TAG_BOOLEAN,	IPP_TAG_PRINTER },
224   { 0, "multiple-document-handling", IPP_TAG_KEYWORD,	IPP_TAG_JOB,
225 							IPP_TAG_DOCUMENT },
226   { 0, "multiple-document-handling-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER },
227   { 0, "natural-scaling",	IPP_TAG_INTEGER,	IPP_TAG_JOB },
228   { 0, "natural-scaling-default", IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
229   { 0, "notify-charset",	IPP_TAG_CHARSET,	IPP_TAG_SUBSCRIPTION },
230   { 1, "notify-events",		IPP_TAG_KEYWORD,	IPP_TAG_SUBSCRIPTION },
231   { 1, "notify-events-default",	IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
232   { 0, "notify-lease-duration",	IPP_TAG_INTEGER,	IPP_TAG_SUBSCRIPTION },
233   { 0, "notify-lease-duration-default", IPP_TAG_INTEGER, IPP_TAG_PRINTER },
234   { 0, "notify-natural-language", IPP_TAG_LANGUAGE,	IPP_TAG_SUBSCRIPTION },
235   { 0, "notify-pull-method",	IPP_TAG_KEYWORD,	IPP_TAG_SUBSCRIPTION },
236   { 0, "notify-recipient-uri",	IPP_TAG_URI,		IPP_TAG_SUBSCRIPTION },
237   { 0, "notify-time-interval",	IPP_TAG_INTEGER,	IPP_TAG_SUBSCRIPTION },
238   { 0, "notify-user-data",	IPP_TAG_STRING,		IPP_TAG_SUBSCRIPTION },
239   { 0, "number-up",		IPP_TAG_INTEGER,	IPP_TAG_JOB,
240 							IPP_TAG_DOCUMENT },
241   { 0, "number-up-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
242   { 0, "number-up-layout",	IPP_TAG_KEYWORD,	IPP_TAG_JOB,
243 							IPP_TAG_DOCUMENT },
244   { 0, "number-up-layout-default", IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
245   { 0, "orientation-requested",	IPP_TAG_ENUM,		IPP_TAG_JOB,
246 							IPP_TAG_DOCUMENT },
247   { 0, "orientation-requested-default", IPP_TAG_ENUM,	IPP_TAG_PRINTER },
248   { 0, "output-bin",		IPP_TAG_KEYWORD,	IPP_TAG_JOB,
249 							IPP_TAG_DOCUMENT },
250   { 0, "output-bin-default",	IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
251   { 1, "overrides",		IPP_TAG_BEGIN_COLLECTION, IPP_TAG_JOB,
252 							IPP_TAG_DOCUMENT },
253   { 0, "page-bottom",		IPP_TAG_INTEGER,	IPP_TAG_JOB },
254   { 0, "page-bottom-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
255   { 0, "page-delivery",		IPP_TAG_KEYWORD,	IPP_TAG_JOB,
256 							IPP_TAG_DOCUMENT },
257   { 0, "page-delivery-default",	IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
258   { 0, "page-left",		IPP_TAG_INTEGER,	IPP_TAG_JOB },
259   { 0, "page-left-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
260   { 1, "page-ranges",		IPP_TAG_RANGE,		IPP_TAG_JOB,
261 							IPP_TAG_DOCUMENT },
262   { 0, "page-right",		IPP_TAG_INTEGER,	IPP_TAG_JOB },
263   { 0, "page-right-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
264   { 0, "page-top",		IPP_TAG_INTEGER,	IPP_TAG_JOB },
265   { 0, "page-top-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
266   { 1, "pages",			IPP_TAG_RANGE,		IPP_TAG_JOB,
267 							IPP_TAG_DOCUMENT },
268   { 0, "penwidth",		IPP_TAG_INTEGER,	IPP_TAG_JOB },
269   { 0, "penwidth-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
270   { 0, "port-monitor",		IPP_TAG_NAME,		IPP_TAG_PRINTER },
271   { 0, "ppd-device-id",		IPP_TAG_TEXT,		IPP_TAG_OPERATION,
272 							IPP_TAG_ZERO,
273 							cups_get_ppds },
274   { 0, "ppd-make",		IPP_TAG_TEXT,		IPP_TAG_OPERATION,
275 							IPP_TAG_ZERO,
276 							cups_get_ppds },
277   { 0, "ppd-make-and-model",	IPP_TAG_TEXT,		IPP_TAG_OPERATION,
278 							IPP_TAG_ZERO,
279 							cups_get_ppds },
280   { 0, "ppd-model-number",	IPP_TAG_INTEGER,	IPP_TAG_OPERATION,
281 							IPP_TAG_ZERO,
282 							cups_get_ppds },
283   { 0, "ppd-name",		IPP_TAG_NAME,		IPP_TAG_OPERATION,
284 							IPP_TAG_ZERO,
285 							cups_ppd_name },
286   { 0, "ppd-natural-language",	IPP_TAG_LANGUAGE,	IPP_TAG_OPERATION,
287 							IPP_TAG_ZERO,
288 							cups_get_ppds },
289   { 0, "ppd-product",		IPP_TAG_TEXT,		IPP_TAG_OPERATION,
290 							IPP_TAG_ZERO,
291 							cups_get_ppds },
292   { 0, "ppd-psversion",		IPP_TAG_TEXT,		IPP_TAG_OPERATION,
293 							IPP_TAG_ZERO,
294 							cups_get_ppds },
295   { 0, "ppd-type",		IPP_TAG_KEYWORD,	IPP_TAG_OPERATION,
296 							IPP_TAG_ZERO,
297 							cups_get_ppds },
298   { 0, "ppi",			IPP_TAG_INTEGER,	IPP_TAG_JOB },
299   { 0, "ppi-default",		IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
300   { 0, "prettyprint",		IPP_TAG_BOOLEAN,	IPP_TAG_JOB },
301   { 0, "prettyprint-default",	IPP_TAG_BOOLEAN,	IPP_TAG_PRINTER },
302   { 0, "print-color-mode",	IPP_TAG_KEYWORD,	IPP_TAG_JOB,
303 							IPP_TAG_DOCUMENT },
304   { 0, "print-color-mode-default", IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
305   { 0, "print-content-optimize", IPP_TAG_KEYWORD,	IPP_TAG_JOB,
306 							IPP_TAG_DOCUMENT },
307   { 0, "print-content-optimize-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER },
308   { 0, "print-quality",		IPP_TAG_ENUM,		IPP_TAG_JOB,
309 							IPP_TAG_DOCUMENT },
310   { 0, "print-quality-default",	IPP_TAG_ENUM,		IPP_TAG_PRINTER },
311   { 0, "print-rendering-intent", IPP_TAG_KEYWORD,	IPP_TAG_JOB,
312 							IPP_TAG_DOCUMENT },
313   { 0, "print-rendering-intent-default", IPP_TAG_KEYWORD, IPP_TAG_PRINTER },
314   { 0, "print-scaling",		IPP_TAG_KEYWORD,	IPP_TAG_JOB,
315 							IPP_TAG_DOCUMENT },
316   { 0, "print-scaling-default",	IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
317   { 1, "printer-alert",		IPP_TAG_STRING,		IPP_TAG_PRINTER },
318   { 1, "printer-alert-description", IPP_TAG_TEXT,	IPP_TAG_PRINTER },
319   { 1, "printer-commands",	IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
320   { 0, "printer-error-policy",	IPP_TAG_NAME,		IPP_TAG_PRINTER },
321   { 1, "printer-finisher",	IPP_TAG_STRING,		IPP_TAG_PRINTER },
322   { 1, "printer-finisher-description", IPP_TAG_TEXT,	IPP_TAG_PRINTER },
323   { 1, "printer-finisher-supplies", IPP_TAG_STRING,	IPP_TAG_PRINTER },
324   { 1, "printer-finisher-supplies-description", IPP_TAG_TEXT, IPP_TAG_PRINTER },
325   { 0, "printer-geo-location",	IPP_TAG_URI,		IPP_TAG_PRINTER },
326   { 0, "printer-info",		IPP_TAG_TEXT,		IPP_TAG_PRINTER },
327   { 1, "printer-input-tray",	IPP_TAG_STRING,		IPP_TAG_PRINTER },
328   { 0, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN,	IPP_TAG_PRINTER },
329   { 0, "printer-is-shared",	IPP_TAG_BOOLEAN,	IPP_TAG_PRINTER },
330   { 0, "printer-is-temporary",	IPP_TAG_BOOLEAN,	IPP_TAG_PRINTER },
331   { 0, "printer-location",	IPP_TAG_TEXT,		IPP_TAG_PRINTER },
332   { 0, "printer-make-and-model", IPP_TAG_TEXT,		IPP_TAG_PRINTER },
333   { 0, "printer-more-info",	IPP_TAG_URI,		IPP_TAG_PRINTER },
334   { 0, "printer-op-policy",	IPP_TAG_NAME,		IPP_TAG_PRINTER },
335   { 1, "printer-output-tray",	IPP_TAG_STRING,		IPP_TAG_PRINTER },
336   { 0, "printer-resolution",	IPP_TAG_RESOLUTION,	IPP_TAG_JOB,
337 							IPP_TAG_DOCUMENT },
338   { 0, "printer-resolution-default", IPP_TAG_RESOLUTION, IPP_TAG_PRINTER },
339   { 0, "printer-state",		IPP_TAG_ENUM,		IPP_TAG_PRINTER },
340   { 0, "printer-state-change-time", IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
341   { 1, "printer-state-reasons",	IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
342   { 1, "printer-supply",	IPP_TAG_STRING,		IPP_TAG_PRINTER },
343   { 1, "printer-supply-description", IPP_TAG_TEXT,	IPP_TAG_PRINTER },
344   { 0, "printer-type",		IPP_TAG_ENUM,		IPP_TAG_PRINTER },
345   { 0, "printer-uri",		IPP_TAG_URI,		IPP_TAG_OPERATION },
346   { 1, "printer-uri-supported",	IPP_TAG_URI,		IPP_TAG_PRINTER },
347   { 0, "queued-job-count",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
348   { 0, "raw",			IPP_TAG_MIMETYPE,	IPP_TAG_OPERATION },
349   { 1, "requested-attributes",	IPP_TAG_NAME,		IPP_TAG_OPERATION },
350   { 1, "requesting-user-name-allowed", IPP_TAG_NAME,	IPP_TAG_PRINTER },
351   { 1, "requesting-user-name-denied", IPP_TAG_NAME,	IPP_TAG_PRINTER },
352   { 0, "resolution",		IPP_TAG_RESOLUTION,	IPP_TAG_JOB },
353   { 0, "resolution-default",	IPP_TAG_RESOLUTION,	IPP_TAG_PRINTER },
354   { 0, "saturation",		IPP_TAG_INTEGER,	IPP_TAG_JOB },
355   { 0, "saturation-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
356   { 0, "scaling",		IPP_TAG_INTEGER,	IPP_TAG_JOB },
357   { 0, "scaling-default",	IPP_TAG_INTEGER,	IPP_TAG_PRINTER },
358   { 0, "sides",			IPP_TAG_KEYWORD,	IPP_TAG_JOB,
359 							IPP_TAG_DOCUMENT },
360   { 0, "sides-default",		IPP_TAG_KEYWORD,	IPP_TAG_PRINTER },
361   { 0, "time-at-completed",	IPP_TAG_INTEGER,	IPP_TAG_ZERO }, /* never send as option */
362   { 0, "time-at-creation",	IPP_TAG_INTEGER,	IPP_TAG_ZERO }, /* never send as option */
363   { 0, "time-at-processing",	IPP_TAG_INTEGER,	IPP_TAG_ZERO }, /* never send as option */
364   { 0, "wrap",			IPP_TAG_BOOLEAN,	IPP_TAG_JOB },
365   { 0, "wrap-default",		IPP_TAG_BOOLEAN,	IPP_TAG_PRINTER },
366   { 0, "x-dimension",		IPP_TAG_INTEGER,	IPP_TAG_JOB,
367 							IPP_TAG_DOCUMENT },
368   { 0, "y-dimension",		IPP_TAG_INTEGER,	IPP_TAG_JOB,
369 							IPP_TAG_DOCUMENT }
370 };
371 
372 
373 /*
374  * Local functions...
375  */
376 
377 static int	compare_ipp_options(_ipp_option_t *a, _ipp_option_t *b);
378 
379 
380 /*
381  * '_cupsEncodeOption()' - Encode a single option as an IPP attribute.
382  */
383 
384 ipp_attribute_t *			/* O - New attribute or @code NULL@ on error */
_cupsEncodeOption(ipp_t * ipp,ipp_tag_t group_tag,_ipp_option_t * map,const char * name,const char * value)385 _cupsEncodeOption(
386     ipp_t         *ipp,			/* I - IPP request/response/collection */
387     ipp_tag_t     group_tag,		/* I - Group tag */
388     _ipp_option_t *map,			/* I - Option mapping, if any */
389     const char    *name,		/* I - Attribute name */
390     const char    *value)		/* I - Value */
391 {
392   int			i,		/* Looping var */
393 			count;		/* Number of values */
394   char			*s,		/* Pointer into option value */
395 			*val,		/* Pointer to option value */
396 			*copy,		/* Copy of option value */
397 			*sep,		/* Option separator */
398 			quote;		/* Quote character */
399   ipp_attribute_t	*attr;		/* IPP attribute */
400   ipp_tag_t		value_tag;	/* IPP value tag */
401   ipp_t			*collection;	/* Collection value */
402   int			num_cols;	/* Number of collection values */
403   cups_option_t		*cols;		/* Collection values */
404 
405 
406   DEBUG_printf(("_cupsEncodeOption(ipp=%p(%s), group=%s, map=%p, name=\"%s\", value=\"%s\")", (void *)ipp, ipp ? ippOpString(ippGetOperation(ipp)) : "", ippTagString(group_tag), (void *)map, name, value));
407 
408  /*
409   * Figure out the attribute syntax for encoding...
410   */
411 
412   if (!map)
413     map = _ippFindOption(name);
414 
415   if (map)
416     value_tag = map->value_tag;
417   else if (!_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "false"))
418     value_tag = IPP_TAG_BOOLEAN;
419   else if (value[0] == '{')
420     value_tag = IPP_TAG_BEGIN_COLLECTION;
421   else
422     value_tag = IPP_TAG_NAME;
423 
424  /*
425   * Count the number of values...
426   */
427 
428   if (map && map->multivalue)
429   {
430     for (count = 1, sep = (char *)value, quote = 0; *sep; sep ++)
431     {
432       if (*sep == quote)
433 	quote = 0;
434       else if (!quote && (*sep == '\'' || *sep == '\"'))
435       {
436        /*
437 	* Skip quoted option value...
438 	*/
439 
440 	quote = *sep;
441       }
442       else if (*sep == ',' && !quote)
443 	count ++;
444       else if (*sep == '\\' && sep[1])
445 	sep ++;
446     }
447   }
448   else
449     count = 1;
450 
451   DEBUG_printf(("2_cupsEncodeOption: value_tag=%s, count=%d", ippTagString(value_tag), count));
452 
453  /*
454   * Allocate memory for the attribute values...
455   */
456 
457   if ((attr = ippAddStrings(ipp, group_tag, value_tag, name, count, NULL, NULL)) == NULL)
458   {
459    /*
460     * Ran out of memory!
461     */
462 
463     DEBUG_puts("1_cupsEncodeOption: Ran out of memory for attributes.");
464     return (NULL);
465   }
466 
467   if (count > 1)
468   {
469    /*
470     * Make a copy of the value we can fiddle with...
471     */
472 
473     if ((copy = strdup(value)) == NULL)
474     {
475      /*
476       * Ran out of memory!
477       */
478 
479       DEBUG_puts("1_cupsEncodeOption: Ran out of memory for value copy.");
480       ippDeleteAttribute(ipp, attr);
481       return (NULL);
482     }
483 
484     val = copy;
485   }
486   else
487   {
488    /*
489     * Since we have a single value, use the value directly...
490     */
491 
492     val  = (char *)value;
493     copy = NULL;
494   }
495 
496  /*
497   * Scan the value string for values...
498   */
499 
500   for (i = 0, sep = val; i < count; val = sep, i ++)
501   {
502    /*
503     * Find the end of this value and mark it if needed...
504     */
505 
506     if (count > 1)
507     {
508       for (quote = 0; *sep; sep ++)
509       {
510 	if (*sep == quote)
511 	{
512 	 /*
513 	  * Finish quoted value...
514 	  */
515 
516 	  quote = 0;
517 	}
518 	else if (!quote && (*sep == '\'' || *sep == '\"'))
519 	{
520 	 /*
521 	  * Handle quoted option value...
522 	  */
523 
524 	  quote = *sep;
525 	}
526 	else if (*sep == ',')
527 	  break;
528 	else if (*sep == '\\' && sep[1])
529 	{
530 	 /*
531 	  * Skip quoted character...
532 	  */
533 
534 	  memmove(sep, sep + 1, strlen(sep));
535 	}
536       }
537 
538       if (*sep == ',')
539 	*sep++ = '\0';
540     }
541 
542    /*
543     * Copy the option value(s) over as needed by the type...
544     */
545 
546     switch (attr->value_tag)
547     {
548       case IPP_TAG_INTEGER :
549       case IPP_TAG_ENUM :
550 	 /*
551 	  * Integer/enumeration value...
552 	  */
553 
554 	  ippSetInteger(ipp, &attr, i, (int)strtol(val, &s, 10));
555 	  break;
556 
557       case IPP_TAG_BOOLEAN :
558 	  if (!_cups_strcasecmp(val, "true") || !_cups_strcasecmp(val, "on") || !_cups_strcasecmp(val, "yes"))
559 	  {
560 	   /*
561 	    * Boolean value - true...
562 	    */
563 
564             ippSetBoolean(ipp, &attr, i, 1);
565 	  }
566 	  else
567 	  {
568 	   /*
569 	    * Boolean value - false...
570 	    */
571 
572             ippSetBoolean(ipp, &attr, i, 0);
573 	  }
574 	  break;
575 
576       case IPP_TAG_RANGE :
577           {
578 	   /*
579 	    * Range...
580 	    */
581 
582 	    int lower, upper;		/* Lower and upper ranges... */
583 
584 	    if (*val == '-')
585 	    {
586 	      lower = 1;
587 	      s     = val;
588 	    }
589 	    else
590 	      lower = (int)strtol(val, &s, 10);
591 
592 	    if (*s == '-')
593 	    {
594 	      if (s[1])
595 		upper = (int)strtol(s + 1, NULL, 10);
596 	      else
597 		upper = 2147483647;
598 	    }
599 	    else
600 	      upper = lower;
601 
602             ippSetRange(ipp, &attr, i, lower, upper);
603 	  }
604 	  break;
605 
606       case IPP_TAG_RESOLUTION :
607           {
608 	   /*
609 	    * Resolution...
610 	    */
611 	    int		xres, yres;	/* Resolution values */
612 	    ipp_res_t	units;		/* Resolution units */
613 
614 	    xres = (int)strtol(val, &s, 10);
615 
616 	    if (*s == 'x')
617 	      yres = (int)strtol(s + 1, &s, 10);
618 	    else
619 	      yres = xres;
620 
621 	    if (!_cups_strcasecmp(s, "dpc") || !_cups_strcasecmp(s, "dpcm"))
622 	      units = IPP_RES_PER_CM;
623 	    else
624 	      units = IPP_RES_PER_INCH;
625 
626 	    ippSetResolution(ipp, &attr, i, units, xres, yres);
627           }
628 	  break;
629 
630       case IPP_TAG_STRING :
631 	 /*
632 	  * octetString
633 	  */
634 
635           ippSetOctetString(ipp, &attr, i, val, (int)strlen(val));
636 	  break;
637 
638       case IPP_TAG_BEGIN_COLLECTION :
639 	 /*
640 	  * Collection value
641 	  */
642 
643 	  num_cols = cupsParseOptions(val, 0, &cols);
644 	  if ((collection = ippNew()) == NULL)
645 	  {
646 	    cupsFreeOptions(num_cols, cols);
647 
648 	    if (copy)
649 	      free(copy);
650 
651 	    ippDeleteAttribute(ipp, attr);
652 	    return (NULL);
653 	  }
654 
655 	  ippSetCollection(ipp, &attr, i, collection);
656 	  cupsEncodeOptions2(collection, num_cols, cols, IPP_TAG_JOB);
657 	  cupsFreeOptions(num_cols, cols);
658 	  break;
659 
660       default :
661 	  ippSetString(ipp, &attr, i, val);
662 	  break;
663     }
664   }
665 
666   if (copy)
667     free(copy);
668 
669   return (attr);
670 }
671 
672 
673 /*
674  * 'cupsEncodeOption()' - Encode a single option into an IPP attribute.
675  *
676  * @since CUPS 2.3/macOS 10.14@
677  */
678 
679 ipp_attribute_t	*			/* O - New attribute or @code NULL@ on error */
cupsEncodeOption(ipp_t * ipp,ipp_tag_t group_tag,const char * name,const char * value)680 cupsEncodeOption(ipp_t      *ipp,	/* I - IPP request/response */
681                  ipp_tag_t  group_tag,	/* I - Attribute group */
682                  const char *name,	/* I - Option name */
683                  const char *value)	/* I - Option string value */
684 {
685   return (_cupsEncodeOption(ipp, group_tag, _ippFindOption(name), name, value));
686 }
687 
688 
689 /*
690  * 'cupsEncodeOptions()' - Encode printer options into IPP attributes.
691  *
692  * This function adds operation, job, and then subscription attributes,
693  * in that order. Use the @link cupsEncodeOptions2@ function to add attributes
694  * for a single group.
695  */
696 
697 void
cupsEncodeOptions(ipp_t * ipp,int num_options,cups_option_t * options)698 cupsEncodeOptions(ipp_t         *ipp,		/* I - IPP request/response */
699         	  int           num_options,	/* I - Number of options */
700 		  cups_option_t *options)	/* I - Options */
701 {
702   DEBUG_printf(("cupsEncodeOptions(%p, %d, %p)", (void *)ipp, num_options, (void *)options));
703 
704  /*
705   * Add the options in the proper groups & order...
706   */
707 
708   cupsEncodeOptions2(ipp, num_options, options, IPP_TAG_OPERATION);
709   cupsEncodeOptions2(ipp, num_options, options, IPP_TAG_JOB);
710   cupsEncodeOptions2(ipp, num_options, options, IPP_TAG_SUBSCRIPTION);
711 }
712 
713 
714 /*
715  * 'cupsEncodeOptions2()' - Encode printer options into IPP attributes for a group.
716  *
717  * This function only adds attributes for a single group. Call this
718  * function multiple times for each group, or use @link cupsEncodeOptions@
719  * to add the standard groups.
720  *
721  * @since CUPS 1.2/macOS 10.5@
722  */
723 
724 void
cupsEncodeOptions2(ipp_t * ipp,int num_options,cups_option_t * options,ipp_tag_t group_tag)725 cupsEncodeOptions2(
726     ipp_t         *ipp,			/* I - IPP request/response */
727     int           num_options,		/* I - Number of options */
728     cups_option_t *options,		/* I - Options */
729     ipp_tag_t     group_tag)		/* I - Group to encode */
730 {
731   int			i;		/* Looping var */
732   char			*val;		/* Pointer to option value */
733   cups_option_t		*option;	/* Current option */
734   ipp_op_t		op;		/* Operation for this request */
735   const ipp_op_t	*ops;		/* List of allowed operations */
736 
737 
738   DEBUG_printf(("cupsEncodeOptions2(ipp=%p(%s), num_options=%d, options=%p, group_tag=%x)", (void *)ipp, ipp ? ippOpString(ippGetOperation(ipp)) : "", num_options, (void *)options, group_tag));
739 
740  /*
741   * Range check input...
742   */
743 
744   if (!ipp || num_options < 1 || !options)
745     return;
746 
747  /*
748   * Do special handling for the document-format/raw options...
749   */
750 
751   op = ippGetOperation(ipp);
752 
753   if (group_tag == IPP_TAG_OPERATION && (op == IPP_OP_PRINT_JOB || op == IPP_OP_PRINT_URI || op == IPP_OP_SEND_DOCUMENT || op == IPP_OP_SEND_URI))
754   {
755    /*
756     * Handle the document format stuff first...
757     */
758 
759     if ((val = (char *)cupsGetOption("document-format", num_options, options)) != NULL)
760       ippAddString(ipp, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, val);
761     else if (cupsGetOption("raw", num_options, options))
762       ippAddString(ipp, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, "application/vnd.cups-raw");
763     else
764       ippAddString(ipp, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, "application/octet-stream");
765   }
766 
767  /*
768   * Then loop through the options...
769   */
770 
771   for (i = num_options, option = options; i > 0; i --, option ++)
772   {
773     _ipp_option_t	*match;		/* Matching attribute */
774 
775    /*
776     * Skip document format options that are handled above...
777     */
778 
779     if (!_cups_strcasecmp(option->name, "raw") || !_cups_strcasecmp(option->name, "document-format") || !option->name[0])
780       continue;
781 
782    /*
783     * Figure out the proper value and group tags for this option...
784     */
785 
786     if ((match = _ippFindOption(option->name)) != NULL)
787     {
788       if (match->group_tag != group_tag && match->alt_group_tag != group_tag)
789         continue;
790 
791       if (match->operations)
792         ops = match->operations;
793       else if (group_tag == IPP_TAG_JOB)
794         ops = ipp_job_creation;
795       else if (group_tag == IPP_TAG_DOCUMENT)
796         ops = ipp_doc_creation;
797       else if (group_tag == IPP_TAG_SUBSCRIPTION)
798         ops = ipp_sub_creation;
799       else if (group_tag == IPP_TAG_PRINTER)
800         ops = ipp_set_printer;
801       else
802       {
803 	DEBUG_printf(("2cupsEncodeOptions2: Skipping \"%s\".", option->name));
804         continue;
805       }
806     }
807     else
808     {
809       int	namelen;		/* Length of name */
810 
811       namelen = (int)strlen(option->name);
812 
813       if (namelen < 10 || (strcmp(option->name + namelen - 8, "-default") && strcmp(option->name + namelen - 10, "-supported")))
814       {
815 	if (group_tag != IPP_TAG_JOB && group_tag != IPP_TAG_DOCUMENT)
816 	{
817 	  DEBUG_printf(("2cupsEncodeOptions2: Skipping \"%s\".", option->name));
818           continue;
819         }
820       }
821       else if (group_tag != IPP_TAG_PRINTER)
822       {
823 	DEBUG_printf(("2cupsEncodeOptions2: Skipping \"%s\".", option->name));
824         continue;
825       }
826 
827       if (group_tag == IPP_TAG_JOB)
828         ops = ipp_job_creation;
829       else if (group_tag == IPP_TAG_DOCUMENT)
830         ops = ipp_doc_creation;
831       else
832         ops = ipp_set_printer;
833     }
834 
835    /*
836     * Verify that we send this attribute for this operation...
837     */
838 
839     while (*ops != IPP_OP_CUPS_NONE)
840       if (op == *ops)
841         break;
842       else
843         ops ++;
844 
845     if (*ops == IPP_OP_CUPS_NONE && op != IPP_OP_CUPS_NONE)
846     {
847       DEBUG_printf(("2cupsEncodeOptions2: Skipping \"%s\".", option->name));
848       continue;
849     }
850 
851     _cupsEncodeOption(ipp, group_tag, match, option->name, option->value);
852   }
853 }
854 
855 
856 #ifdef DEBUG
857 /*
858  * '_ippCheckOptions()' - Validate that the option array is sorted properly.
859  */
860 
861 const char *				/* O - First out-of-order option or NULL */
_ippCheckOptions(void)862 _ippCheckOptions(void)
863 {
864   int	i;				/* Looping var */
865 
866 
867   for (i = 0; i < (int)(sizeof(ipp_options) / sizeof(ipp_options[0]) - 1); i ++)
868     if (strcmp(ipp_options[i].name, ipp_options[i + 1].name) >= 0)
869       return (ipp_options[i + 1].name);
870 
871   return (NULL);
872 }
873 #endif /* DEBUG */
874 
875 
876 /*
877  * '_ippFindOption()' - Find the attribute information for an option.
878  */
879 
880 _ipp_option_t *				/* O - Attribute information */
_ippFindOption(const char * name)881 _ippFindOption(const char *name)	/* I - Option/attribute name */
882 {
883   _ipp_option_t	key;			/* Search key */
884 
885 
886  /*
887   * Lookup the proper value and group tags for this option...
888   */
889 
890   key.name = name;
891 
892   return ((_ipp_option_t *)bsearch(&key, ipp_options,
893                                    sizeof(ipp_options) / sizeof(ipp_options[0]),
894 				   sizeof(ipp_options[0]),
895 				   (int (*)(const void *, const void *))
896 				       compare_ipp_options));
897 }
898 
899 
900 /*
901  * 'compare_ipp_options()' - Compare two IPP options.
902  */
903 
904 static int				/* O - Result of comparison */
compare_ipp_options(_ipp_option_t * a,_ipp_option_t * b)905 compare_ipp_options(_ipp_option_t *a,	/* I - First option */
906                     _ipp_option_t *b)	/* I - Second option */
907 {
908   return (strcmp(a->name, b->name));
909 }
910