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
== '\"'))
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
)));
391 * Add to current token...
394 *tokptr
++ = (char)ch
;
403 DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token
));
409 * Get the next character...
412 ch
= cupsFileGetChar(f
->fp
);
416 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at EOF.", token
));
418 return (tokptr
> token
);
423 * 'parse_collection()' - Parse an IPP collection value.
426 static ipp_t
* /* O - Collection value or @code NULL@ on error */
428 _ipp_file_t
*f
, /* I - IPP data file */
429 _ipp_vars_t
*v
, /* I - IPP variables */
430 void *user_data
) /* I - User data pointer */
432 ipp_t
*col
= ippNew(); /* Collection value */
433 ipp_attribute_t
*attr
= NULL
; /* Current member attribute */
434 char token
[1024]; /* Token string */
438 * Parse the collection value...
441 while (_ippFileReadToken(f
, token
, sizeof(token
)))
443 if (!_cups_strcasecmp(token
, "}"))
446 * End of collection value...
451 else if (!_cups_strcasecmp(token
, "MEMBER"))
454 * Member attribute definition...
457 char syntax
[128], /* Attribute syntax (value tag) */
458 name
[128]; /* Attribute name */
459 ipp_tag_t value_tag
; /* Value tag */
463 if (!_ippFileReadToken(f
, syntax
, sizeof(syntax
)))
465 report_error(f
, v
, user_data
, "Missing MEMBER syntax on line %d of \"%s\".", f
->linenum
, f
->filename
);
470 else if ((value_tag
= ippTagValue(syntax
)) < IPP_TAG_UNSUPPORTED_VALUE
)
472 report_error(f
, v
, user_data
, "Bad MEMBER syntax \"%s\" on line %d of \"%s\".", syntax
, f
->linenum
, f
->filename
);
478 if (!_ippFileReadToken(f
, name
, sizeof(name
)) || !name
[0])
480 report_error(f
, v
, user_data
, "Missing MEMBER name on line %d of \"%s\".", f
->linenum
, f
->filename
);
486 if (value_tag
< IPP_TAG_INTEGER
)
489 * Add out-of-band attribute - no value string needed...
492 ippAddOutOfBand(col
, IPP_TAG_ZERO
, value_tag
, name
);
497 * Add attribute with one or more values...
500 attr
= ippAddString(col
, IPP_TAG_ZERO
, value_tag
, name
, NULL
, NULL
);
502 if (!parse_value(f
, v
, user_data
, col
, &attr
, 0))
511 else if (attr
&& !_cups_strcasecmp(token
, ","))
514 * Additional value...
517 if (!parse_value(f
, v
, user_data
, col
, &attr
, ippGetCount(attr
)))
530 report_error(f
, v
, user_data
, "Unknown directive \"%s\" on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
544 * 'parse_value()' - Parse an IPP value.
547 static int /* O - 1 on success or 0 on error */
548 parse_value(_ipp_file_t
*f
, /* I - IPP data file */
549 _ipp_vars_t
*v
, /* I - IPP variables */
550 void *user_data
,/* I - User data pointer */
551 ipp_t
*ipp
, /* I - IPP message */
552 ipp_attribute_t
**attr
, /* IO - IPP attribute */
553 int element
) /* I - Element number */
555 char value
[2049], /* Value string */
556 *valueptr
, /* Pointer into value string */
557 temp
[2049], /* Temporary string */
558 *tempptr
; /* Pointer into temporary string */
559 size_t valuelen
; /* Length of value */
562 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
564 report_error(f
, v
, user_data
, "Missing value on line %d of \"%s\".", f
->linenum
, f
->filename
);
568 _ippVarsExpand(v
, value
, temp
, sizeof(value
));
570 switch (ippGetValueTag(*attr
))
572 case IPP_TAG_BOOLEAN
:
573 return (ippSetBoolean(ipp
, attr
, element
, !_cups_strcasecmp(value
, "true")));
577 case IPP_TAG_INTEGER
:
578 return (ippSetInteger(ipp
, attr
, element
, (int)strtol(value
, NULL
, 0)));
585 day
, /* Day of month */
589 utc_offset
= 0; /* Timezone offset from UTC */
590 ipp_uchar_t date
[11]; /* dateTime value */
598 time_t curtime
; /* Current time in seconds */
599 int period
= 0, /* Current period value */
600 saw_T
= 0; /* Saw time separator */
602 curtime
= time(NULL
);
604 for (valueptr
= value
+ 1; *valueptr
; valueptr
++)
606 if (isdigit(*valueptr
& 255))
608 period
= (int)strtol(valueptr
, &valueptr
, 10);
610 if (!valueptr
|| period
< 0)
612 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
617 if (*valueptr
== 'Y')
619 curtime
+= 365 * 86400 * period
;
622 else if (*valueptr
== 'M')
625 curtime
+= 60 * period
;
627 curtime
+= 30 * 86400 * period
;
631 else if (*valueptr
== 'D')
633 curtime
+= 86400 * period
;
636 else if (*valueptr
== 'H')
638 curtime
+= 3600 * period
;
641 else if (*valueptr
== 'S')
646 else if (*valueptr
== 'T')
653 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
658 return (ippSetDate(ipp
, attr
, element
, ippTimeToDate(curtime
)));
660 else if (sscanf(value
, "%d-%d-%dT%d:%d:%d%d", &year
, &month
, &day
, &hour
, &minute
, &second
, &utc_offset
) < 6)
663 * Date/time value did not parse...
666 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
670 date
[0] = (ipp_uchar_t
)(year
>> 8);
671 date
[1] = (ipp_uchar_t
)(year
& 255);
672 date
[2] = (ipp_uchar_t
)month
;
673 date
[3] = (ipp_uchar_t
)day
;
674 date
[4] = (ipp_uchar_t
)hour
;
675 date
[5] = (ipp_uchar_t
)minute
;
676 date
[6] = (ipp_uchar_t
)second
;
680 utc_offset
= -utc_offset
;
681 date
[8] = (ipp_uchar_t
)'-';
685 date
[8] = (ipp_uchar_t
)'+';
688 date
[9] = (ipp_uchar_t
)(utc_offset
/ 100);
689 date
[10] = (ipp_uchar_t
)(utc_offset
% 100);
691 return (ippSetDate(ipp
, attr
, element
, date
));
695 case IPP_TAG_RESOLUTION
:
697 int xres
, /* X resolution */
698 yres
; /* Y resolution */
699 char *ptr
; /* Pointer into value */
701 xres
= yres
= (int)strtol(value
, (char **)&ptr
, 10);
702 if (ptr
> value
&& xres
> 0)
705 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
708 if (ptr
<= value
|| xres
<= 0 || yres
<= 0 || !ptr
|| (_cups_strcasecmp(ptr
, "dpi") && _cups_strcasecmp(ptr
, "dpc") && _cups_strcasecmp(ptr
, "dpcm") && _cups_strcasecmp(ptr
, "other")))
710 report_error(f
, v
, user_data
, "Bad resolution value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
714 if (!_cups_strcasecmp(ptr
, "dpi"))
715 return (ippSetResolution(ipp
, attr
, element
, IPP_RES_PER_INCH
, xres
, yres
));
716 else if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
717 return (ippSetResolution(ipp
, attr
, element
, IPP_RES_PER_CM
, xres
, yres
));
719 return (ippSetResolution(ipp
, attr
, element
, (ipp_res_t
)0, xres
, yres
));
725 int lower
, /* Lower value */
726 upper
; /* Upper value */
728 if (sscanf(value
, "%d-%d", &lower
, &upper
) != 2)
730 report_error(f
, v
, user_data
, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
734 return (ippSetRange(ipp
, attr
, element
, lower
, upper
));
738 case IPP_TAG_STRING
:
739 valuelen
= strlen(value
);
741 if (value
[0] == '<' && value
[strlen(value
) - 1] == '>')
745 report_error(f
, v
, user_data
, "Bad octetString value on line %d of \"%s\".", f
->linenum
, f
->filename
);
749 valueptr
= value
+ 1;
752 while (*valueptr
&& *valueptr
!= '>')
754 if (!isxdigit(valueptr
[0] & 255) || !isxdigit(valueptr
[1] & 255))
756 report_error(f
, v
, user_data
, "Bad octetString value on line %d of \"%s\".", f
->linenum
, f
->filename
);
760 if (valueptr
[0] >= '0' && valueptr
[0] <= '9')
761 *tempptr
= (char)((valueptr
[0] - '0') << 4);
763 *tempptr
= (char)((tolower(valueptr
[0]) - 'a' + 10) << 4);
765 if (valueptr
[1] >= '0' && valueptr
[1] <= '9')
766 *tempptr
|= (valueptr
[1] - '0');
768 *tempptr
|= (tolower(valueptr
[1]) - 'a' + 10);
773 return (ippSetOctetString(ipp
, attr
, element
, temp
, (int)(tempptr
- temp
)));
776 return (ippSetOctetString(ipp
, attr
, element
, value
, (int)valuelen
));
779 case IPP_TAG_TEXTLANG
:
780 case IPP_TAG_NAMELANG
:
783 case IPP_TAG_KEYWORD
:
785 case IPP_TAG_URISCHEME
:
786 case IPP_TAG_CHARSET
:
787 case IPP_TAG_LANGUAGE
:
788 case IPP_TAG_MIMETYPE
:
789 return (ippSetString(ipp
, attr
, element
, value
));
792 case IPP_TAG_BEGIN_COLLECTION
:
794 int status
; /* Add status */
795 ipp_t
*col
; /* Collection value */
797 if (strcmp(value
, "{"))
799 report_error(f
, v
, user_data
, "Bad collection value on line %d of \"%s\".", f
->linenum
, f
->filename
);
803 if ((col
= parse_collection(f
, v
, user_data
)) == NULL
)
806 status
= ippSetCollection(ipp
, attr
, element
, col
);
814 report_error(f
, v
, user_data
, "Unsupported value on line %d of \"%s\".", f
->linenum
, f
->filename
);
823 * 'report_error()' - Report an error.
828 _ipp_file_t
*f
, /* I - IPP data file */
829 _ipp_vars_t
*v
, /* I - Error callback function, if any */
830 void *user_data
, /* I - User data pointer */
831 const char *message
, /* I - Printf-style message */
832 ...) /* I - Additional arguments as needed */
834 char buffer
[8192]; /* Formatted string */
835 va_list ap
; /* Argument pointer */
838 va_start(ap
, message
);
839 vsnprintf(buffer
, sizeof(buffer
), message
, ap
);
843 (*v
->errorcb
)(f
, user_data
, buffer
);
845 fprintf(stderr
, "%s\n", buffer
);