2 * IPP data file parsing functions.
4 * Copyright © 2007-2019 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
12 * Include necessary headers...
15 #include "ipp-private.h"
16 #include "string-private.h"
17 #include "debug-internal.h"
24 static ipp_t
*parse_collection(_ipp_file_t
*f
, _ipp_vars_t
*v
, void *user_data
);
25 static int parse_value(_ipp_file_t
*f
, _ipp_vars_t
*v
, void *user_data
, ipp_t
*ipp
, ipp_attribute_t
**attr
, int element
);
26 static void report_error(_ipp_file_t
*f
, _ipp_vars_t
*v
, void *user_data
, const char *message
, ...) _CUPS_FORMAT(4, 5);
30 * '_ippFileParse()' - Parse an IPP data file.
33 ipp_t
* /* O - IPP attributes or @code NULL@ on failure */
35 _ipp_vars_t
*v
, /* I - Variables */
36 const char *filename
, /* I - Name of file to parse */
37 void *user_data
) /* I - User data pointer */
39 _ipp_file_t f
; /* IPP data file information */
40 ipp_t
*attrs
= NULL
; /* Active IPP message */
41 ipp_attribute_t
*attr
= NULL
; /* Current attribute */
42 char token
[1024]; /* Token string */
43 ipp_t
*ignored
= NULL
; /* Ignored attributes */
46 DEBUG_printf(("_ippFileParse(v=%p, filename=\"%s\", user_data=%p)", (void *)v
, filename
, user_data
));
49 * Initialize file info...
52 memset(&f
, 0, sizeof(f
));
53 f
.filename
= filename
;
56 if ((f
.fp
= cupsFileOpen(filename
, "r")) == NULL
)
58 DEBUG_printf(("1_ippFileParse: Unable to open \"%s\": %s", filename
, strerror(errno
)));
63 * Do the callback with a NULL token to setup any initial state...
66 (*v
->tokencb
)(&f
, v
, user_data
, NULL
);
69 * Read data file, using the callback function as needed...
72 while (_ippFileReadToken(&f
, token
, sizeof(token
)))
74 if (!_cups_strcasecmp(token
, "DEFINE") || !_cups_strcasecmp(token
, "DEFINE-DEFAULT"))
76 char name
[128], /* Variable name */
77 value
[1024], /* Variable value */
78 temp
[1024]; /* Temporary string */
82 if (_ippFileReadToken(&f
, name
, sizeof(name
)) && _ippFileReadToken(&f
, temp
, sizeof(temp
)))
84 if (_cups_strcasecmp(token
, "DEFINE-DEFAULT") || !_ippVarsGet(v
, name
))
86 _ippVarsExpand(v
, value
, temp
, sizeof(value
));
87 _ippVarsSet(v
, name
, value
);
92 report_error(&f
, v
, user_data
, "Missing %s name and/or value on line %d of \"%s\".", token
, f
.linenum
, f
.filename
);
96 else if (f
.attrs
&& !_cups_strcasecmp(token
, "ATTR"))
99 * Attribute definition...
102 char syntax
[128], /* Attribute syntax (value tag) */
103 name
[128]; /* Attribute name */
104 ipp_tag_t value_tag
; /* Value tag */
108 if (!_ippFileReadToken(&f
, syntax
, sizeof(syntax
)))
110 report_error(&f
, v
, user_data
, "Missing ATTR syntax on line %d of \"%s\".", f
.linenum
, f
.filename
);
113 else if ((value_tag
= ippTagValue(syntax
)) < IPP_TAG_UNSUPPORTED_VALUE
)
115 report_error(&f
, v
, user_data
, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax
, f
.linenum
, f
.filename
);
119 if (!_ippFileReadToken(&f
, name
, sizeof(name
)) || !name
[0])
121 report_error(&f
, v
, user_data
, "Missing ATTR name on line %d of \"%s\".", f
.linenum
, f
.filename
);
125 if (!v
->attrcb
|| (*v
->attrcb
)(&f
, user_data
, name
))
128 * Add this attribute...
136 * Ignore this attribute...
145 if (value_tag
< IPP_TAG_INTEGER
)
148 * Add out-of-band attribute - no value string needed...
151 ippAddOutOfBand(attrs
, f
.group_tag
, value_tag
, name
);
156 * Add attribute with one or more values...
159 attr
= ippAddString(attrs
, f
.group_tag
, value_tag
, name
, NULL
, NULL
);
161 if (!parse_value(&f
, v
, user_data
, attrs
, &attr
, 0))
166 else if (attr
&& !_cups_strcasecmp(token
, ","))
169 * Additional value...
172 if (!parse_value(&f
, v
, user_data
, attrs
, &attr
, ippGetCount(attr
)))
184 if (!(*v
->tokencb
)(&f
, v
, user_data
, token
))
190 * Close the file and free ignored attributes, then return any attributes we
202 * '_ippFileReadToken()' - Read a token from an IPP data file.
205 int /* O - 1 on success, 0 on failure */
206 _ippFileReadToken(_ipp_file_t
*f
, /* I - File to read from */
207 char *token
, /* I - Token string buffer */
208 size_t tokensize
)/* I - Size of token string buffer */
210 int ch
, /* Character from file */
211 quote
= 0; /* Quoting character */
212 char *tokptr
= token
, /* Pointer into token buffer */
213 *tokend
= token
+ tokensize
- 1;/* End of token buffer */
217 * Skip whitespace and comments...
220 DEBUG_printf(("1_ippFileReadToken: linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
222 while ((ch
= cupsFileGetChar(f
->fp
)) != EOF
)
224 if (_cups_isspace(ch
))
233 DEBUG_printf(("1_ippFileReadToken: LF in leading whitespace, linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
242 DEBUG_puts("1_ippFileReadToken: Skipping comment in leading whitespace...");
244 while ((ch
= cupsFileGetChar(f
->fp
)) != EOF
)
253 DEBUG_printf(("1_ippFileReadToken: LF at end of comment, linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
264 DEBUG_puts("1_ippFileReadToken: EOF");
277 DEBUG_printf(("1_ippFileReadToken: LF in token, linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
283 * End of quoted text...
287 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at closing quote.", token
));
290 else if (!quote
&& _cups_isspace(ch
))
293 * End of unquoted text...
297 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before whitespace.", token
));
300 else if (!quote
&& (ch
== '\'' || ch
== '\"' || ch
== '<'))
303 * Start of quoted text or regular expression...
311 DEBUG_printf(("1_ippFileReadToken: Start of quoted string, quote=%c, pos=%ld", quote
, (long)cupsFileTell(f
->fp
)));
313 else if (!quote
&& ch
== '#')
316 * Start of comment...
319 cupsFileSeek(f
->fp
, cupsFileTell(f
->fp
) - 1);
321 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before comment.", token
));
324 else if (!quote
&& (ch
== '{' || ch
== '}' || ch
== ','))
333 * Return the preceding token first...
336 cupsFileSeek(f
->fp
, cupsFileTell(f
->fp
) - 1);
341 * Return this delimiter by itself...
344 *tokptr
++ = (char)ch
;
348 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token
));
356 * Quoted character...
359 DEBUG_printf(("1_ippFileReadToken: Quoted character at pos=%ld", (long)cupsFileTell(f
->fp
)));
361 if ((ch
= cupsFileGetChar(f
->fp
)) == EOF
)
364 DEBUG_puts("1_ippFileReadToken: EOF");
370 DEBUG_printf(("1_ippFileReadToken: quoted LF, linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
388 if (quote
!= '>' || !isspace(ch
& 255))
393 * Add to current token...
396 *tokptr
++ = (char)ch
;
405 DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token
));
412 * Get the next character...
415 ch
= cupsFileGetChar(f
->fp
);
419 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at EOF.", token
));
421 return (tokptr
> token
);
426 * 'parse_collection()' - Parse an IPP collection value.
429 static ipp_t
* /* O - Collection value or @code NULL@ on error */
431 _ipp_file_t
*f
, /* I - IPP data file */
432 _ipp_vars_t
*v
, /* I - IPP variables */
433 void *user_data
) /* I - User data pointer */
435 ipp_t
*col
= ippNew(); /* Collection value */
436 ipp_attribute_t
*attr
= NULL
; /* Current member attribute */
437 char token
[1024]; /* Token string */
441 * Parse the collection value...
444 while (_ippFileReadToken(f
, token
, sizeof(token
)))
446 if (!_cups_strcasecmp(token
, "}"))
449 * End of collection value...
454 else if (!_cups_strcasecmp(token
, "MEMBER"))
457 * Member attribute definition...
460 char syntax
[128], /* Attribute syntax (value tag) */
461 name
[128]; /* Attribute name */
462 ipp_tag_t value_tag
; /* Value tag */
466 if (!_ippFileReadToken(f
, syntax
, sizeof(syntax
)))
468 report_error(f
, v
, user_data
, "Missing ATTR syntax on line %d of \"%s\".", f
->linenum
, f
->filename
);
473 else if ((value_tag
= ippTagValue(syntax
)) < IPP_TAG_UNSUPPORTED_VALUE
)
475 report_error(f
, v
, user_data
, "Bad ATTR syntax \"%s\" on line %d of \"%s\".", syntax
, f
->linenum
, f
->filename
);
481 if (!_ippFileReadToken(f
, name
, sizeof(name
)) || !name
[0])
483 report_error(f
, v
, user_data
, "Missing ATTR name on line %d of \"%s\".", f
->linenum
, f
->filename
);
489 if (value_tag
< IPP_TAG_INTEGER
)
492 * Add out-of-band attribute - no value string needed...
495 ippAddOutOfBand(col
, IPP_TAG_ZERO
, value_tag
, name
);
500 * Add attribute with one or more values...
503 attr
= ippAddString(col
, IPP_TAG_ZERO
, value_tag
, name
, NULL
, NULL
);
505 if (!parse_value(f
, v
, user_data
, col
, &attr
, 0))
514 else if (attr
&& !_cups_strcasecmp(token
, ","))
517 * Additional value...
520 if (!parse_value(f
, v
, user_data
, col
, &attr
, ippGetCount(attr
)))
533 report_error(f
, v
, user_data
, "Unknown directive \"%s\" on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
547 * 'parse_value()' - Parse an IPP value.
550 static int /* O - 1 on success or 0 on error */
551 parse_value(_ipp_file_t
*f
, /* I - IPP data file */
552 _ipp_vars_t
*v
, /* I - IPP variables */
553 void *user_data
,/* I - User data pointer */
554 ipp_t
*ipp
, /* I - IPP message */
555 ipp_attribute_t
**attr
, /* IO - IPP attribute */
556 int element
) /* I - Element number */
558 char value
[2049], /* Value string */
559 *valueptr
, /* Pointer into value string */
560 temp
[2049], /* Temporary string */
561 *tempptr
; /* Pointer into temporary string */
562 size_t valuelen
; /* Length of value */
565 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
567 report_error(f
, v
, user_data
, "Missing value on line %d of \"%s\".", f
->linenum
, f
->filename
);
571 _ippVarsExpand(v
, value
, temp
, sizeof(value
));
573 switch (ippGetValueTag(*attr
))
575 case IPP_TAG_BOOLEAN
:
576 return (ippSetBoolean(ipp
, attr
, element
, !_cups_strcasecmp(value
, "true")));
580 case IPP_TAG_INTEGER
:
581 return (ippSetInteger(ipp
, attr
, element
, (int)strtol(value
, NULL
, 0)));
588 day
, /* Day of month */
592 utc_offset
= 0; /* Timezone offset from UTC */
593 ipp_uchar_t date
[11]; /* dateTime value */
601 time_t curtime
; /* Current time in seconds */
602 int period
= 0, /* Current period value */
603 saw_T
= 0; /* Saw time separator */
605 curtime
= time(NULL
);
607 for (valueptr
= value
+ 1; *valueptr
; valueptr
++)
609 if (isdigit(*valueptr
& 255))
611 period
= strtol(valueptr
, &valueptr
, 10);
613 if (!valueptr
|| period
< 0)
615 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
620 if (*valueptr
== 'Y')
622 curtime
+= 365 * 86400 * period
;
625 else if (*valueptr
== 'M')
628 curtime
+= 60 * period
;
630 curtime
+= 30 * 86400 * period
;
634 else if (*valueptr
== 'D')
636 curtime
+= 86400 * period
;
639 else if (*valueptr
== 'H')
641 curtime
+= 3600 * period
;
644 else if (*valueptr
== 'S')
649 else if (*valueptr
== 'T')
656 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
661 return (ippSetDate(ipp
, attr
, element
, ippTimeToDate(curtime
)));
663 else if (sscanf(value
, "%d-%d-%dT%d:%d:%d%d", &year
, &month
, &day
, &hour
, &minute
, &second
, &utc_offset
) < 6)
666 * Date/time value did not parse...
669 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
673 date
[0] = (ipp_uchar_t
)(year
>> 8);
674 date
[1] = (ipp_uchar_t
)(year
& 255);
675 date
[2] = (ipp_uchar_t
)month
;
676 date
[3] = (ipp_uchar_t
)day
;
677 date
[4] = (ipp_uchar_t
)hour
;
678 date
[5] = (ipp_uchar_t
)minute
;
679 date
[6] = (ipp_uchar_t
)second
;
683 utc_offset
= -utc_offset
;
684 date
[8] = (ipp_uchar_t
)'-';
688 date
[8] = (ipp_uchar_t
)'+';
691 date
[9] = (ipp_uchar_t
)(utc_offset
/ 100);
692 date
[10] = (ipp_uchar_t
)(utc_offset
% 100);
694 return (ippSetDate(ipp
, attr
, element
, date
));
698 case IPP_TAG_RESOLUTION
:
700 int xres
, /* X resolution */
701 yres
; /* Y resolution */
702 char *ptr
; /* Pointer into value */
704 xres
= yres
= (int)strtol(value
, (char **)&ptr
, 10);
705 if (ptr
> value
&& xres
> 0)
708 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
711 if (ptr
<= value
|| xres
<= 0 || yres
<= 0 || !ptr
|| (_cups_strcasecmp(ptr
, "dpi") && _cups_strcasecmp(ptr
, "dpc") && _cups_strcasecmp(ptr
, "dpcm") && _cups_strcasecmp(ptr
, "other")))
713 report_error(f
, v
, user_data
, "Bad resolution value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
717 if (!_cups_strcasecmp(ptr
, "dpi"))
718 return (ippSetResolution(ipp
, attr
, element
, IPP_RES_PER_INCH
, xres
, yres
));
719 else if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
720 return (ippSetResolution(ipp
, attr
, element
, IPP_RES_PER_CM
, xres
, yres
));
722 return (ippSetResolution(ipp
, attr
, element
, (ipp_res_t
)0, xres
, yres
));
728 int lower
, /* Lower value */
729 upper
; /* Upper value */
731 if (sscanf(value
, "%d-%d", &lower
, &upper
) != 2)
733 report_error(f
, v
, user_data
, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
737 return (ippSetRange(ipp
, attr
, element
, lower
, upper
));
741 case IPP_TAG_STRING
:
742 valuelen
= strlen(value
);
744 if (value
[0] == '<' && value
[strlen(value
) - 1] == '>')
748 report_error(f
, v
, user_data
, "Bad ATTR octetString value on line %d of \"%s\".", f
->linenum
, f
->filename
);
752 valueptr
= value
+ 1;
755 while (*valueptr
&& *valueptr
!= '>')
757 if (!isxdigit(valueptr
[0] & 255) || !isxdigit(valueptr
[1] & 255))
759 report_error(f
, v
, user_data
, "Bad ATTR octetString value on line %d of \"%s\".", f
->linenum
, f
->filename
);
763 if (valueptr
[0] >= '0' && valueptr
[0] <= '9')
764 *tempptr
= (char)((valueptr
[0] - '0') << 4);
766 *tempptr
= (char)((tolower(valueptr
[0]) - 'a' + 10) << 4);
768 if (valueptr
[1] >= '0' && valueptr
[1] <= '9')
769 *tempptr
|= (valueptr
[1] - '0');
771 *tempptr
|= (tolower(valueptr
[1]) - 'a' + 10);
776 return (ippSetOctetString(ipp
, attr
, element
, temp
, (int)(tempptr
- temp
)));
779 return (ippSetOctetString(ipp
, attr
, element
, value
, (int)valuelen
));
782 case IPP_TAG_TEXTLANG
:
783 case IPP_TAG_NAMELANG
:
786 case IPP_TAG_KEYWORD
:
788 case IPP_TAG_URISCHEME
:
789 case IPP_TAG_CHARSET
:
790 case IPP_TAG_LANGUAGE
:
791 case IPP_TAG_MIMETYPE
:
792 return (ippSetString(ipp
, attr
, element
, value
));
795 case IPP_TAG_BEGIN_COLLECTION
:
797 int status
; /* Add status */
798 ipp_t
*col
; /* Collection value */
800 if (strcmp(value
, "{"))
802 report_error(f
, v
, user_data
, "Bad ATTR collection value on line %d of \"%s\".", f
->linenum
, f
->filename
);
806 if ((col
= parse_collection(f
, v
, user_data
)) == NULL
)
809 status
= ippSetCollection(ipp
, attr
, element
, col
);
817 report_error(f
, v
, user_data
, "Unsupported ATTR value on line %d of \"%s\".", f
->linenum
, f
->filename
);
826 * 'report_error()' - Report an error.
831 _ipp_file_t
*f
, /* I - IPP data file */
832 _ipp_vars_t
*v
, /* I - Error callback function, if any */
833 void *user_data
, /* I - User data pointer */
834 const char *message
, /* I - Printf-style message */
835 ...) /* I - Additional arguments as needed */
837 char buffer
[8192]; /* Formatted string */
838 va_list ap
; /* Argument pointer */
841 va_start(ap
, message
);
842 vsnprintf(buffer
, sizeof(buffer
), message
, ap
);
846 (*v
->errorcb
)(f
, user_data
, buffer
);
848 fprintf(stderr
, "%s\n", buffer
);