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