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...
308 DEBUG_printf(("1_ippFileReadToken: Start of quoted string, quote=%c, pos=%ld", quote
, (long)cupsFileTell(f
->fp
)));
310 else if (!quote
&& ch
== '#')
313 * Start of comment...
316 cupsFileSeek(f
->fp
, cupsFileTell(f
->fp
) - 1);
318 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" before comment.", token
));
321 else if (!quote
&& (ch
== '{' || ch
== '}' || ch
== ','))
330 * Return the preceding token first...
333 cupsFileSeek(f
->fp
, cupsFileTell(f
->fp
) - 1);
338 * Return this delimiter by itself...
341 *tokptr
++ = (char)ch
;
345 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\".", token
));
353 * Quoted character...
356 DEBUG_printf(("1_ippFileReadToken: Quoted character at pos=%ld", (long)cupsFileTell(f
->fp
)));
358 if ((ch
= cupsFileGetChar(f
->fp
)) == EOF
)
361 DEBUG_puts("1_ippFileReadToken: EOF");
367 DEBUG_printf(("1_ippFileReadToken: quoted LF, linenum=%d, pos=%ld", f
->linenum
, (long)cupsFileTell(f
->fp
)));
388 * Add to current token...
391 *tokptr
++ = (char)ch
;
400 DEBUG_printf(("1_ippFileReadToken: Too long: \"%s\".", token
));
406 * Get the next character...
409 ch
= cupsFileGetChar(f
->fp
);
413 DEBUG_printf(("1_ippFileReadToken: Returning \"%s\" at EOF.", token
));
415 return (tokptr
> token
);
420 * 'parse_collection()' - Parse an IPP collection value.
423 static ipp_t
* /* O - Collection value or @code NULL@ on error */
425 _ipp_file_t
*f
, /* I - IPP data file */
426 _ipp_vars_t
*v
, /* I - IPP variables */
427 void *user_data
) /* I - User data pointer */
429 ipp_t
*col
= ippNew(); /* Collection value */
430 ipp_attribute_t
*attr
= NULL
; /* Current member attribute */
431 char token
[1024]; /* Token string */
435 * Parse the collection value...
438 while (_ippFileReadToken(f
, token
, sizeof(token
)))
440 if (!_cups_strcasecmp(token
, "}"))
443 * End of collection value...
448 else if (!_cups_strcasecmp(token
, "MEMBER"))
451 * Member attribute definition...
454 char syntax
[128], /* Attribute syntax (value tag) */
455 name
[128]; /* Attribute name */
456 ipp_tag_t value_tag
; /* Value tag */
460 if (!_ippFileReadToken(f
, syntax
, sizeof(syntax
)))
462 report_error(f
, v
, user_data
, "Missing MEMBER syntax on line %d of \"%s\".", f
->linenum
, f
->filename
);
467 else if ((value_tag
= ippTagValue(syntax
)) < IPP_TAG_UNSUPPORTED_VALUE
)
469 report_error(f
, v
, user_data
, "Bad MEMBER syntax \"%s\" on line %d of \"%s\".", syntax
, f
->linenum
, f
->filename
);
475 if (!_ippFileReadToken(f
, name
, sizeof(name
)) || !name
[0])
477 report_error(f
, v
, user_data
, "Missing MEMBER name on line %d of \"%s\".", f
->linenum
, f
->filename
);
483 if (value_tag
< IPP_TAG_INTEGER
)
486 * Add out-of-band attribute - no value string needed...
489 ippAddOutOfBand(col
, IPP_TAG_ZERO
, value_tag
, name
);
494 * Add attribute with one or more values...
497 attr
= ippAddString(col
, IPP_TAG_ZERO
, value_tag
, name
, NULL
, NULL
);
499 if (!parse_value(f
, v
, user_data
, col
, &attr
, 0))
508 else if (attr
&& !_cups_strcasecmp(token
, ","))
511 * Additional value...
514 if (!parse_value(f
, v
, user_data
, col
, &attr
, ippGetCount(attr
)))
527 report_error(f
, v
, user_data
, "Unknown directive \"%s\" on line %d of \"%s\".", token
, f
->linenum
, f
->filename
);
541 * 'parse_value()' - Parse an IPP value.
544 static int /* O - 1 on success or 0 on error */
545 parse_value(_ipp_file_t
*f
, /* I - IPP data file */
546 _ipp_vars_t
*v
, /* I - IPP variables */
547 void *user_data
,/* I - User data pointer */
548 ipp_t
*ipp
, /* I - IPP message */
549 ipp_attribute_t
**attr
, /* IO - IPP attribute */
550 int element
) /* I - Element number */
552 char value
[2049], /* Value string */
553 *valueptr
, /* Pointer into value string */
554 temp
[2049], /* Temporary string */
555 *tempptr
; /* Pointer into temporary string */
556 size_t valuelen
; /* Length of value */
559 if (!_ippFileReadToken(f
, temp
, sizeof(temp
)))
561 report_error(f
, v
, user_data
, "Missing value on line %d of \"%s\".", f
->linenum
, f
->filename
);
565 _ippVarsExpand(v
, value
, temp
, sizeof(value
));
567 switch (ippGetValueTag(*attr
))
569 case IPP_TAG_BOOLEAN
:
570 return (ippSetBoolean(ipp
, attr
, element
, !_cups_strcasecmp(value
, "true")));
573 case IPP_TAG_INTEGER
:
574 return (ippSetInteger(ipp
, attr
, element
, (int)strtol(value
, NULL
, 0)));
580 day
, /* Day of month */
584 utc_offset
= 0; /* Timezone offset from UTC */
585 ipp_uchar_t date
[11]; /* dateTime value */
593 time_t curtime
; /* Current time in seconds */
594 int period
= 0, /* Current period value */
595 saw_T
= 0; /* Saw time separator */
597 curtime
= time(NULL
);
599 for (valueptr
= value
+ 1; *valueptr
; valueptr
++)
601 if (isdigit(*valueptr
& 255))
603 period
= (int)strtol(valueptr
, &valueptr
, 10);
605 if (!valueptr
|| period
< 0)
607 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
612 if (*valueptr
== 'Y')
614 curtime
+= 365 * 86400 * period
;
617 else if (*valueptr
== 'M')
620 curtime
+= 60 * period
;
622 curtime
+= 30 * 86400 * period
;
626 else if (*valueptr
== 'D')
628 curtime
+= 86400 * period
;
631 else if (*valueptr
== 'H')
633 curtime
+= 3600 * period
;
636 else if (*valueptr
== 'S')
641 else if (*valueptr
== 'T')
648 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
653 return (ippSetDate(ipp
, attr
, element
, ippTimeToDate(curtime
)));
655 else if (sscanf(value
, "%d-%d-%dT%d:%d:%d%d", &year
, &month
, &day
, &hour
, &minute
, &second
, &utc_offset
) < 6)
658 * Date/time value did not parse...
661 report_error(f
, v
, user_data
, "Bad dateTime value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
665 date
[0] = (ipp_uchar_t
)(year
>> 8);
666 date
[1] = (ipp_uchar_t
)(year
& 255);
667 date
[2] = (ipp_uchar_t
)month
;
668 date
[3] = (ipp_uchar_t
)day
;
669 date
[4] = (ipp_uchar_t
)hour
;
670 date
[5] = (ipp_uchar_t
)minute
;
671 date
[6] = (ipp_uchar_t
)second
;
675 utc_offset
= -utc_offset
;
676 date
[8] = (ipp_uchar_t
)'-';
680 date
[8] = (ipp_uchar_t
)'+';
683 date
[9] = (ipp_uchar_t
)(utc_offset
/ 100);
684 date
[10] = (ipp_uchar_t
)(utc_offset
% 100);
686 return (ippSetDate(ipp
, attr
, element
, date
));
689 case IPP_TAG_RESOLUTION
:
691 int xres
, /* X resolution */
692 yres
; /* Y resolution */
693 char *ptr
; /* Pointer into value */
695 xres
= yres
= (int)strtol(value
, (char **)&ptr
, 10);
696 if (ptr
> value
&& xres
> 0)
699 yres
= (int)strtol(ptr
+ 1, (char **)&ptr
, 10);
702 if (ptr
<= value
|| xres
<= 0 || yres
<= 0 || !ptr
|| (_cups_strcasecmp(ptr
, "dpi") && _cups_strcasecmp(ptr
, "dpc") && _cups_strcasecmp(ptr
, "dpcm") && _cups_strcasecmp(ptr
, "other")))
704 report_error(f
, v
, user_data
, "Bad resolution value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
708 if (!_cups_strcasecmp(ptr
, "dpi"))
709 return (ippSetResolution(ipp
, attr
, element
, IPP_RES_PER_INCH
, xres
, yres
));
710 else if (!_cups_strcasecmp(ptr
, "dpc") || !_cups_strcasecmp(ptr
, "dpcm"))
711 return (ippSetResolution(ipp
, attr
, element
, IPP_RES_PER_CM
, xres
, yres
));
713 return (ippSetResolution(ipp
, attr
, element
, (ipp_res_t
)0, xres
, yres
));
718 int lower
, /* Lower value */
719 upper
; /* Upper value */
721 if (sscanf(value
, "%d-%d", &lower
, &upper
) != 2)
723 report_error(f
, v
, user_data
, "Bad rangeOfInteger value \"%s\" on line %d of \"%s\".", value
, f
->linenum
, f
->filename
);
727 return (ippSetRange(ipp
, attr
, element
, lower
, upper
));
730 case IPP_TAG_STRING
:
731 valuelen
= strlen(value
);
733 if (value
[0] == '<' && value
[strlen(value
) - 1] == '>')
737 report_error(f
, v
, user_data
, "Bad octetString value on line %d of \"%s\".", f
->linenum
, f
->filename
);
741 valueptr
= value
+ 1;
744 while (*valueptr
&& *valueptr
!= '>')
746 if (!isxdigit(valueptr
[0] & 255) || !isxdigit(valueptr
[1] & 255))
748 report_error(f
, v
, user_data
, "Bad octetString value on line %d of \"%s\".", f
->linenum
, f
->filename
);
752 if (valueptr
[0] >= '0' && valueptr
[0] <= '9')
753 *tempptr
= (char)((valueptr
[0] - '0') << 4);
755 *tempptr
= (char)((tolower(valueptr
[0]) - 'a' + 10) << 4);
757 if (valueptr
[1] >= '0' && valueptr
[1] <= '9')
758 *tempptr
|= (valueptr
[1] - '0');
760 *tempptr
|= (tolower(valueptr
[1]) - 'a' + 10);
765 return (ippSetOctetString(ipp
, attr
, element
, temp
, (int)(tempptr
- temp
)));
768 return (ippSetOctetString(ipp
, attr
, element
, value
, (int)valuelen
));
770 case IPP_TAG_TEXTLANG
:
771 case IPP_TAG_NAMELANG
:
774 case IPP_TAG_KEYWORD
:
776 case IPP_TAG_URISCHEME
:
777 case IPP_TAG_CHARSET
:
778 case IPP_TAG_LANGUAGE
:
779 case IPP_TAG_MIMETYPE
:
780 return (ippSetString(ipp
, attr
, element
, value
));
782 case IPP_TAG_BEGIN_COLLECTION
:
784 int status
; /* Add status */
785 ipp_t
*col
; /* Collection value */
787 if (strcmp(value
, "{"))
789 report_error(f
, v
, user_data
, "Bad collection value on line %d of \"%s\".", f
->linenum
, f
->filename
);
793 if ((col
= parse_collection(f
, v
, user_data
)) == NULL
)
796 status
= ippSetCollection(ipp
, attr
, element
, col
);
803 report_error(f
, v
, user_data
, "Unsupported value on line %d of \"%s\".", f
->linenum
, f
->filename
);
810 * 'report_error()' - Report an error.
815 _ipp_file_t
*f
, /* I - IPP data file */
816 _ipp_vars_t
*v
, /* I - Error callback function, if any */
817 void *user_data
, /* I - User data pointer */
818 const char *message
, /* I - Printf-style message */
819 ...) /* I - Additional arguments as needed */
821 char buffer
[8192]; /* Formatted string */
822 va_list ap
; /* Argument pointer */
825 va_start(ap
, message
);
826 vsnprintf(buffer
, sizeof(buffer
), message
, ap
);
830 (*v
->errorcb
)(f
, user_data
, buffer
);
832 fprintf(stderr
, "%s\n", buffer
);