]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/ipp-file.c
Update copyrights and license text on files that were missed.
[thirdparty/cups.git] / cups / ipp-file.c
CommitLineData
fd96ad89
MS
1/*
2 * IPP data file parsing functions.
3 *
4 * Copyright © 2007-2018 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 "ipp-private.h"
16#include "string-private.h"
17#include "debug-private.h"
18
19
20/*
21 * Local functions...
22 */
23
a166e933
MS
24static ipp_t *parse_collection(_ipp_file_t *f, _ipp_vars_t *v, void *user_data);
25static int parse_value(_ipp_file_t *f, _ipp_vars_t *v, void *user_data, ipp_t *ipp, ipp_attribute_t **attr, int element);
26static void report_error(_ipp_file_t *f, _ipp_vars_t *v, void *user_data, const char *message, ...) __attribute((__format__ (__printf__, 4, 5)));
fd96ad89
MS
27
28
29/*
30 * '_ippFileParse()' - Parse an IPP data file.
31 */
32
33ipp_t * /* O - IPP attributes or @code NULL@ on failure */
34_ippFileParse(
35 _ipp_vars_t *v, /* I - Variables */
36 const char *filename, /* I - Name of file to parse */
fd96ad89
MS
37 void *user_data) /* I - User data pointer */
38{
39 _ipp_file_t f; /* IPP data file information */
a166e933 40 ipp_t *attrs = NULL; /* Active IPP message */
fd96ad89
MS
41 ipp_attribute_t *attr = NULL; /* Current attribute */
42 char token[1024]; /* Token string */
a166e933 43 ipp_t *ignored = NULL; /* Ignored attributes */
fd96ad89
MS
44
45
a166e933 46 DEBUG_printf(("_ippFileParse(v=%p, filename=\"%s\", user_data=%p)", (void *)v, filename, user_data));
fd96ad89
MS
47
48 /*
49 * Initialize file info...
50 */
51
52 memset(&f, 0, sizeof(f));
53 f.filename = filename;
54 f.linenum = 1;
55
56 if ((f.fp = cupsFileOpen(filename, "r")) == NULL)
57 {
58 DEBUG_printf(("1_ippFileParse: Unable to open \"%s\": %s", filename, strerror(errno)));
59 return (0);
60 }
61
62 /*
63 * Do the callback with a NULL token to setup any initial state...
64 */
65
a166e933 66 (*v->tokencb)(&f, v, user_data, NULL);
fd96ad89
MS
67
68 /*
69 * Read data file, using the callback function as needed...
70 */
71
72 while (_ippFileReadToken(&f, token, sizeof(token)))
73 {
74 if (!_cups_strcasecmp(token, "DEFINE") || !_cups_strcasecmp(token, "DEFINE-DEFAULT"))
75 {
76 char name[128], /* Variable name */
77 value[1024], /* Variable value */
78 temp[1024]; /* Temporary string */
79
80 attr = NULL;
81
82 if (_ippFileReadToken(&f, name, sizeof(name)) && _ippFileReadToken(&f, temp, sizeof(temp)))
83 {
84 _ippVarsExpand(v, value, temp, sizeof(value));
85 _ippVarsSet(v, name, value);
86 }
87 else
88 {
a166e933 89 report_error(&f, v, user_data, "Missing %s name and/or value on line %d of \"%s\".", token, f.linenum, f.filename);
fd96ad89
MS
90 break;
91 }
92 }
93 else if (f.attrs && !_cups_strcasecmp(token, "ATTR"))
94 {
95 /*
96 * Attribute definition...
97 */
98
99 char syntax[128], /* Attribute syntax (value tag) */
100 name[128]; /* Attribute name */
101 ipp_tag_t value_tag; /* Value tag */
102
103 attr = NULL;
104
105 if (!_ippFileReadToken(&f, syntax, sizeof(syntax)))
106 {
a166e933 107 report_error(&f, v, user_data, "Missing ATTR syntax on line %d of \"%s\".", f.linenum, f.filename);
fd96ad89
MS
108 break;
109 }
110 else if ((value_tag = ippTagValue(syntax)) < IPP_TAG_UNSUPPORTED_VALUE)
111 {
a166e933 112 report_error(&f, v, user_data, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax, f.linenum, f.filename);
fd96ad89
MS
113 break;
114 }
115
116 if (!_ippFileReadToken(&f, name, sizeof(name)) || !name[0])
117 {
a166e933 118 report_error(&f, v, user_data, "Missing ATTR name on line %d of \"%s\".", f.linenum, f.filename);
fd96ad89
MS
119 break;
120 }
121
a166e933
MS
122 if (!v->attrcb || (*v->attrcb)(&f, user_data, name))
123 {
124 /*
125 * Add this attribute...
126 */
127
128 attrs = f.attrs;
129 }
130 else
131 {
132 /*
133 * Ignore this attribute...
134 */
135
136 if (!ignored)
137 ignored = ippNew();
138
139 attrs = ignored;
140 }
141
fd96ad89
MS
142 if (value_tag < IPP_TAG_INTEGER)
143 {
144 /*
145 * Add out-of-band attribute - no value string needed...
146 */
147
a166e933 148 ippAddOutOfBand(attrs, f.group_tag, value_tag, name);
fd96ad89
MS
149 }
150 else
151 {
152 /*
153 * Add attribute with one or more values...
154 */
155
a166e933 156 attr = ippAddString(attrs, f.group_tag, value_tag, name, NULL, NULL);
fd96ad89 157
a166e933 158 if (!parse_value(&f, v, user_data, attrs, &attr, 0))
fd96ad89 159 break;
fd96ad89
MS
160 }
161
162 }
163 else if (attr && !_cups_strcasecmp(token, ","))
164 {
165 /*
166 * Additional value...
167 */
168
a166e933 169 if (!parse_value(&f, v, user_data, attrs, &attr, ippGetCount(attr)))
fd96ad89 170 break;
fd96ad89
MS
171 }
172 else
173 {
174 /*
175 * Something else...
176 */
177
a166e933
MS
178 attr = NULL;
179 attrs = NULL;
fd96ad89 180
a166e933 181 if (!(*v->tokencb)(&f, v, user_data, token))
fd96ad89
MS
182 break;
183 }
184 }
185
186 /*
a166e933
MS
187 * Close the file and free ignored attributes, then return any attributes we
188 * kept...
fd96ad89
MS
189 */
190
191 cupsFileClose(f.fp);
a166e933 192 ippDelete(ignored);
fd96ad89
MS
193
194 return (f.attrs);
195}
196
197
198/*
199 * '_ippFileReadToken()' - Read a token from an IPP data file.
200 */
201
202int /* O - 1 on success, 0 on failure */
203_ippFileReadToken(_ipp_file_t *f, /* I - File to read from */
204 char *token, /* I - Token string buffer */
205 size_t tokensize)/* I - Size of token string buffer */
206{
207 int ch, /* Character from file */
208 quote = 0; /* Quoting character */
209 char *tokptr = token, /* Pointer into token buffer */
210 *tokend = token + tokensize - 1;/* End of token buffer */
211
212
213 /*
214 * Skip whitespace and comments...
215 */
216
217 while ((ch = cupsFileGetChar(f->fp)) != EOF)
218 {
219 if (_cups_isspace(ch))
220 {
221 /*
222 * Whitespace...
223 */
224
225 if (ch == '\n')
226 f->linenum ++;
227 }
228 else if (ch == '#')
229 {
230 /*
231 * Comment...
232 */
233
234 while ((ch = cupsFileGetChar(f->fp)) != EOF)
235 {
236 if (ch == '\n')
237 break;
238 }
239
240 if (ch == '\n')
241 f->linenum ++;
242 else
243 break;
244 }
245 else
246 break;
247 }
248
249 if (ch == EOF)
250 {
251 DEBUG_puts("1_ippFileReadToken: EOF");
252 return (0);
253 }
254
255 /*
256 * Read a token...
257 */
258
259 while (ch != EOF)
260 {
8e2e09c1
MS
261 if (ch == '\n')
262 f->linenum ++;
263
fd96ad89
MS
264 if (ch == quote)
265 {
266 /*
267 * End of quoted text...
268 */
269
270 *tokptr = '\0';
271 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token));
272 return (1);
273 }
274 else if (!quote && _cups_isspace(ch))
275 {
276 /*
277 * End of unquoted text...
278 */
279
280 *tokptr = '\0';
281 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token));
282 return (1);
283 }
284 else if (!quote && (ch == '\'' || ch == '\"'))
285 {
286 /*
287 * Start of quoted text or regular expression...
288 */
289
290 quote = ch;
291 }
292 else if (!quote && ch == '#')
293 {
294 /*
295 * Start of comment...
296 */
297
298 cupsFileSeek(f->fp, cupsFileTell(f->fp) - 1);
299 *tokptr = '\0';
300 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token));
301 return (1);
302 }
303 else if (!quote && (ch == '{' || ch == '}' || ch == ','))
304 {
305 /*
306 * Delimiter...
307 */
308
309 if (tokptr > token)
310 {
311 /*
312 * Return the preceding token first...
313 */
314
315 cupsFileSeek(f->fp, cupsFileTell(f->fp) - 1);
316 }
317 else
318 {
319 /*
320 * Return this delimiter by itself...
321 */
322
323 *tokptr++ = (char)ch;
324 }
325
326 *tokptr = '\0';
327 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token));
328 return (1);
329 }
330 else
331 {
332 if (ch == '\\')
333 {
334 /*
335 * Quoted character...
336 */
337
338 if ((ch = cupsFileGetChar(f->fp)) == EOF)
339 {
340 *token = '\0';
341 DEBUG_puts("1_ippFileReadToken: EOF");
342 return (0);
343 }
8e2e09c1
MS
344 else if (ch == '\n')
345 f->linenum ++;
fd96ad89
MS
346 }
347
348 if (tokptr < tokend)
349 {
350 /*
351 * Add to current token...
352 */
353
354 *tokptr++ = (char)ch;
355 }
356 else
357 {
358 /*
359 * Token too long...
360 */
361
362 *tokptr = '\0';
363 DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token));
364 return (0);
365 }
366 }
367
368 /*
369 * Get the next character...
370 */
371
372 ch = cupsFileGetChar(f->fp);
373 }
374
375 *tokptr = '\0';
376 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token));
377
378 return (tokptr > token);
379}
380
381
382/*
383 * 'parse_collection()' - Parse an IPP collection value.
384 */
385
386static ipp_t * /* O - Collection value or @code NULL@ on error */
387parse_collection(
388 _ipp_file_t *f, /* I - IPP data file */
389 _ipp_vars_t *v, /* I - IPP variables */
fd96ad89
MS
390 void *user_data) /* I - User data pointer */
391{
392 ipp_t *col = ippNew(); /* Collection value */
393 ipp_attribute_t *attr = NULL; /* Current member attribute */
394 char token[1024]; /* Token string */
395
396
397 /*
398 * Parse the collection value...
399 */
400
401 while (_ippFileReadToken(f, token, sizeof(token)))
402 {
403 if (!_cups_strcasecmp(token, "}"))
404 {
405 /*
406 * End of collection value...
407 */
408
409 break;
410 }
411 else if (!_cups_strcasecmp(token, "MEMBER"))
412 {
413 /*
414 * Member attribute definition...
415 */
416
417 char syntax[128], /* Attribute syntax (value tag) */
418 name[128]; /* Attribute name */
419 ipp_tag_t value_tag; /* Value tag */
420
421 attr = NULL;
422
423 if (!_ippFileReadToken(f, syntax, sizeof(syntax)))
424 {
a166e933 425 report_error(f, v, user_data, "Missing ATTR syntax on line %d of \"%s\".", f->linenum, f->filename);
fd96ad89
MS
426 ippDelete(col);
427 col = NULL;
428 break;
429 }
430 else if ((value_tag = ippTagValue(syntax)) < IPP_TAG_UNSUPPORTED_VALUE)
431 {
a166e933 432 report_error(f, v, user_data, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax, f->linenum, f->filename);
fd96ad89
MS
433 ippDelete(col);
434 col = NULL;
435 break;
436 }
437
438 if (!_ippFileReadToken(f, name, sizeof(name)) || !name[0])
439 {
a166e933 440 report_error(f, v, user_data, "Missing ATTR name on line %d of \"%s\".", f->linenum, f->filename);
fd96ad89
MS
441 ippDelete(col);
442 col = NULL;
443 break;
444 }
445
446 if (value_tag < IPP_TAG_INTEGER)
447 {
448 /*
449 * Add out-of-band attribute - no value string needed...
450 */
451
452 ippAddOutOfBand(col, IPP_TAG_ZERO, value_tag, name);
453 }
454 else
455 {
456 /*
457 * Add attribute with one or more values...
458 */
459
460 attr = ippAddString(col, IPP_TAG_ZERO, value_tag, name, NULL, NULL);
461
a166e933 462 if (!parse_value(f, v, user_data, col, &attr, 0))
fd96ad89
MS
463 {
464 ippDelete(col);
465 col = NULL;
466 break;
467 }
468 }
469
470 }
471 else if (attr && !_cups_strcasecmp(token, ","))
472 {
473 /*
474 * Additional value...
475 */
476
a166e933 477 if (!parse_value(f, v, user_data, col, &attr, ippGetCount(attr)))
fd96ad89
MS
478 {
479 ippDelete(col);
480 col = NULL;
481 break;
482 }
483 }
484 else
485 {
486 /*
487 * Something else...
488 */
489
a166e933 490 report_error(f, v, user_data, "Unknown directive \"%s\" on line %d of \"%s\".", token, f->linenum, f->filename);
fd96ad89
MS
491 ippDelete(col);
492 col = NULL;
493 attr = NULL;
494 break;
495
496 }
497 }
498
499 return (col);
500}
501
502
503/*
504 * 'parse_value()' - Parse an IPP value.
505 */
506
507static int /* O - 1 on success or 0 on error */
508parse_value(_ipp_file_t *f, /* I - IPP data file */
509 _ipp_vars_t *v, /* I - IPP variables */
fd96ad89
MS
510 void *user_data,/* I - User data pointer */
511 ipp_t *ipp, /* I - IPP message */
512 ipp_attribute_t **attr, /* IO - IPP attribute */
513 int element) /* I - Element number */
514{
515 char value[1024], /* Value string */
516 temp[1024]; /* Temporary string */
517
518
519 if (!_ippFileReadToken(f, temp, sizeof(temp)))
520 {
a166e933 521 report_error(f, v, user_data, "Missing value on line %d of \"%s\".", f->linenum, f->filename);
fd96ad89
MS
522 return (0);
523 }
524
525 _ippVarsExpand(v, value, temp, sizeof(value));
526
527 switch (ippGetValueTag(*attr))
528 {
529 case IPP_TAG_BOOLEAN :
530 return (ippSetBoolean(ipp, attr, element, !_cups_strcasecmp(value, "true")));
531 break;
532
533 case IPP_TAG_ENUM :
534 case IPP_TAG_INTEGER :
564cd57d 535 return (ippSetInteger(ipp, attr, element, (int)strtol(value, NULL, 0)));
fd96ad89
MS
536 break;
537
06e1afc3
MS
538 case IPP_TAG_DATE :
539 {
540 int year, /* Year */
541 month, /* Month */
542 day, /* Day of month */
543 hour, /* Hour */
544 minute, /* Minute */
545 second, /* Second */
546 utc_offset = 0; /* Timezone offset from UTC */
547 ipp_uchar_t date[11]; /* dateTime value */
548
f17549fb 549 if (sscanf(value, "%d-%d-%dT%d:%d:%d%d", &year, &month, &day, &hour, &minute, &second, &utc_offset) < 6)
06e1afc3
MS
550 {
551 report_error(f, v, user_data, "Bad dateTime value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
552 return (0);
553 }
554
555 date[0] = (ipp_uchar_t)(year >> 8);
556 date[1] = (ipp_uchar_t)(year & 255);
557 date[2] = (ipp_uchar_t)month;
558 date[3] = (ipp_uchar_t)day;
559 date[4] = (ipp_uchar_t)hour;
560 date[5] = (ipp_uchar_t)minute;
561 date[6] = (ipp_uchar_t)second;
562 date[7] = 0;
563 if (utc_offset < 0)
564 {
565 utc_offset = -utc_offset;
566 date[8] = (ipp_uchar_t)'-';
567 }
568 else
569 {
570 date[8] = (ipp_uchar_t)'+';
571 }
572
573 date[9] = (ipp_uchar_t)(utc_offset / 100);
574 date[10] = (ipp_uchar_t)(utc_offset % 100);
575
576 return (ippSetDate(ipp, attr, element, date));
577 }
578 break;
579
fd96ad89
MS
580 case IPP_TAG_RESOLUTION :
581 {
582 int xres, /* X resolution */
583 yres; /* Y resolution */
584 char *ptr; /* Pointer into value */
585
586 xres = yres = (int)strtol(value, (char **)&ptr, 10);
587 if (ptr > value && xres > 0)
588 {
589 if (*ptr == 'x')
590 yres = (int)strtol(ptr + 1, (char **)&ptr, 10);
591 }
592
593 if (ptr <= value || xres <= 0 || yres <= 0 || !ptr || (_cups_strcasecmp(ptr, "dpi") && _cups_strcasecmp(ptr, "dpc") && _cups_strcasecmp(ptr, "dpcm") && _cups_strcasecmp(ptr, "other")))
594 {
a166e933 595 report_error(f, v, user_data, "Bad resolution value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
fd96ad89
MS
596 return (0);
597 }
598
599 if (!_cups_strcasecmp(ptr, "dpi"))
600 return (ippSetResolution(ipp, attr, element, IPP_RES_PER_INCH, xres, yres));
601 else if (!_cups_strcasecmp(ptr, "dpc") || !_cups_strcasecmp(ptr, "dpcm"))
602 return (ippSetResolution(ipp, attr, element, IPP_RES_PER_CM, xres, yres));
603 else
604 return (ippSetResolution(ipp, attr, element, (ipp_res_t)0, xres, yres));
605 }
606 break;
607
608 case IPP_TAG_RANGE :
609 {
610 int lower, /* Lower value */
611 upper; /* Upper value */
612
613 if (sscanf(value, "%d-%d", &lower, &upper) != 2)
614 {
a166e933 615 report_error(f, v, user_data, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", value, f->linenum, f->filename);
fd96ad89
MS
616 return (0);
617 }
618
619 return (ippSetRange(ipp, attr, element, lower, upper));
620 }
621 break;
622
623 case IPP_TAG_STRING :
624 return (ippSetOctetString(ipp, attr, element, value, (int)strlen(value)));
625 break;
626
627 case IPP_TAG_TEXTLANG :
628 case IPP_TAG_NAMELANG :
629 case IPP_TAG_TEXT :
630 case IPP_TAG_NAME :
631 case IPP_TAG_KEYWORD :
632 case IPP_TAG_URI :
633 case IPP_TAG_URISCHEME :
634 case IPP_TAG_CHARSET :
635 case IPP_TAG_LANGUAGE :
636 case IPP_TAG_MIMETYPE :
637 return (ippSetString(ipp, attr, element, value));
638 break;
639
640 case IPP_TAG_BEGIN_COLLECTION :
641 {
642 int status; /* Add status */
643 ipp_t *col; /* Collection value */
644
645 if (strcmp(value, "{"))
646 {
a166e933 647 report_error(f, v, user_data, "Bad ATTR collection value on line %d of \"%s\".", f->linenum, f->filename);
fd96ad89
MS
648 return (0);
649 }
650
a166e933 651 if ((col = parse_collection(f, v, user_data)) == NULL)
fd96ad89
MS
652 return (0);
653
654 status = ippSetCollection(ipp, attr, element, col);
655 ippDelete(col);
656
657 return (status);
658 }
659 break;
660
661 default :
a166e933 662 report_error(f, v, user_data, "Unsupported ATTR value on line %d of \"%s\".", f->linenum, f->filename);
fd96ad89
MS
663 return (0);
664 }
665
666 return (1);
667}
668
669
670/*
671 * 'report_error()' - Report an error.
672 */
673
674static void
675report_error(
a166e933
MS
676 _ipp_file_t *f, /* I - IPP data file */
677 _ipp_vars_t *v, /* I - Error callback function, if any */
678 void *user_data, /* I - User data pointer */
679 const char *message, /* I - Printf-style message */
fd96ad89
MS
680 ...) /* I - Additional arguments as needed */
681{
682 char buffer[8192]; /* Formatted string */
683 va_list ap; /* Argument pointer */
684
685
686 va_start(ap, message);
687 vsnprintf(buffer, sizeof(buffer), message, ap);
688 va_end(ap);
689
a166e933
MS
690 if (v->errorcb)
691 (*v->errorcb)(f, user_data, buffer);
fd96ad89
MS
692 else
693 fprintf(stderr, "%s\n", buffer);
694}