1 /* media_params.c
2  * Routines for parsing media type parameters as per RFC 822 and RFC 2045
3  * Copyright 2004, Anders Broman.
4  * Copyright 2004, Olivier Biot.
5  *
6  * Refer to the AUTHORS file or the AUTHORS section in the man page
7  * for contacting the author(s) of this file.
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * SPDX-License-Identifier: GPL-2.0-or-later
14  */
15 
16 #include "config.h"
17 
18 #include <string.h>
19 #include <glib.h>
20 
21 #include <epan/media_params.h>
22 
23 static const char *
ws_get_next_media_type_parameter(const char * pos,gsize * retnamelen,const char ** retvalue,gsize * retvaluelen,const char ** nextp)24 ws_get_next_media_type_parameter(const char *pos, gsize *retnamelen,
25                                  const char **retvalue, gsize *retvaluelen,
26                                  const char **nextp)
27 {
28     const char *p, *namep, *valuep;
29     char c;
30 
31     p = pos;
32     while ((c = *p) != '\0' && g_ascii_isspace(c))
33         p++; /* Skip white space */
34 
35     if (c == '\0') {
36         /* No more parameters left */
37         return NULL;
38     }
39 
40     namep = p;
41 
42     /* Look for a '\0' (end of string), '=' (end of parameter name,
43        beginning of parameter value), or ';' (end of parameter). */
44     while ((c = *p) != '\0' && c != '=' && c != ';')
45         p++;
46     *retnamelen = (gsize) (p - namep);
47     if (c == '\0') {
48         /* End of string, so end of parameter, no parameter value */
49         if (retvalue != NULL)
50             *retvalue = NULL;
51         if (retvaluelen != NULL)
52             *retvaluelen = 0;
53         *nextp = p;
54         return namep;
55     }
56     if (c == ';') {
57         /* End of parameter, no parameter value */
58         if (retvalue != NULL)
59             *retvalue = NULL;
60         if (retvaluelen != NULL)
61             *retvaluelen = 0;
62         *nextp = p + 1;
63         return namep;
64     }
65     /* The parameter has a value.  Skip the '=' */
66     p++;
67     valuep = p;
68     if (retvalue != NULL)
69         *retvalue = valuep;
70     /* Is the value a quoted string? */
71     if (*p == '"') {
72         /* Yes. Skip the opening quote, and scan forward looking for
73            a non-escaped closing quote. */
74         p++;
75         for (;;) {
76             c = *p;
77             if (c == '\0') {
78                 /* End-of-string.  We're done.
79                    (XXX - this is an error.) */
80                 if (retvaluelen != NULL) {
81                     *retvaluelen = (gsize) (p - valuep);
82                 }
83                 *nextp = p;
84                 return namep;
85             }
86             if (c == '"') {
87                 /* Closing quote.  Skip it; we're done with
88                    the quoted-string. */
89                 p++;
90                 break;
91             }
92             if (c == '\\') {
93                 /* Backslash; this escapes the next character
94                    (quoted-pair). Skip the backslash, and make
95                    sure there *is* a next character. */
96                 p++;
97                 if (*p == '\0') {
98                     /* Nothing left; we're done.
99                        (XXX - this is an error.) */
100                     break;
101                 }
102             }
103             /* Skip the character we just processed. */
104             p++;
105         }
106         /* Now scan forward looking for a '\0' (end of string)
107            or ';' (end of parameter), in case there's any
108             extra cruft after the quoted-string. */
109         while ((c = *p) != '\0' && c != ';')
110            p++;
111     } else {
112         /* No.  Just scan forward looking for a '\0' (end
113            of string) or ';' (end of parameter). */
114         while ((c = *p) != '\0' && c != ';')
115             p++;
116     }
117     if (c == '\0') {
118         /* End of string, so end of parameter */
119         if (retvaluelen != NULL) {
120             *retvaluelen = (gsize) (p - valuep);
121         }
122         *nextp = p;
123         return namep;
124     }
125     /* End of parameter; point past the terminating ';' */
126     if (retvaluelen != NULL) {
127         *retvaluelen = (gsize) (p - valuep);
128     }
129     *nextp = p + 1;
130     return namep;
131 }
132 
133 char *
ws_find_media_type_parameter(wmem_allocator_t * scope,const char * parameters,const char * key)134 ws_find_media_type_parameter(wmem_allocator_t *scope, const char *parameters, const char *key)
135 {
136     const char *p, *name, *value;
137     char c;
138     gsize keylen, namelen, valuelen;
139     char *valuestr, *vp;
140 
141     if (parameters == NULL || key == NULL)
142         /* we won't be able to find anything */
143         return NULL;
144 
145     keylen = (gsize) strlen(key);
146     if (keylen == 0) {
147         /* There's no parameter name to searh for */
148         return NULL;
149     }
150     p = parameters;
151     if (*p == '\0') {
152         /* There are no parameters in which to search */
153         return NULL;
154     }
155 
156     do {
157         /* Get the next parameter. */
158         name = ws_get_next_media_type_parameter(p, &namelen, &value,
159                                                 &valuelen, &p);
160         if (name == NULL) {
161             /* No more parameters - not found. */
162             return NULL;
163         }
164 
165         /* Is it the parameter we're looking for? */
166         if (namelen == keylen && g_ascii_strncasecmp(name, key, keylen) == 0) {
167             /* Yes. */
168             break;
169         }
170     } while (*p);
171 
172     if (value == NULL) {
173         /* The parameter doesn't have a value. */
174         return NULL;
175     }
176 
177     /* We found the parameter with that name; now extract the value. */
178     valuestr = (char *)wmem_alloc(scope, valuelen + 1);
179     vp = valuestr;
180     p = value;
181     /* Is the value a quoted string? */
182     if (*p == '"') {
183         /* Yes. Skip the opening quote, and scan forward looking for
184            a non-escaped closing quote, copying characters. */
185         p++;
186         for (;;) {
187             c = *p;
188             if (c == '\0') {
189                 /* End-of-string.  We're done.
190                    (XXX - this is an error.) */
191                 *vp = '\0';
192                 return valuestr;
193             }
194             if (c == '"') {
195                 /* Closing quote.  Skip it; we're done with
196                    the quoted-string. */
197                 p++;
198                 break;
199             }
200             if (c == '\\') {
201                 /* Backslash; this escapes the next character
202                    (quoted-pair). Skip the backslash, and make
203                    sure there *is* a next character. */
204                 p++;
205                 if (*p == '\0') {
206                     /* Nothing left; we're done.
207                        (XXX - this is an error.) */
208                     break;
209                 }
210             }
211             /* Copy the character. */
212             *vp++ = *p++;
213         }
214     } else {
215         /* No.  Just scan forward until we see a '\0' (end of
216            string or a non-token character, copying characters. */
217         while ((c = *p) != '\0' && g_ascii_isgraph(c) && c != '(' &&
218                 c != ')' && c != '<' && c != '>' && c != '@' &&
219                 c != ',' && c != ';' && c != ':' && c != '\\' &&
220                 c != '"' && c != '/' && c != '[' && c != ']' &&
221                 c != '?' && c != '=' && c != '{' && c != '}') {
222             *vp++ = c;
223             p++;
224         }
225     }
226     *vp = '\0';
227     return valuestr;
228 }
229