1 /*
2  * Copyright (C) 1997-2004, Michael Jennings
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies of the Software, its documentation and marketing & publicity
13  * materials, and acknowledgment shall be given in the documentation, materials
14  * and software packages that this Software was used.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 static const char __attribute__((unused)) cvs_ident[] = "$Id: url.c,v 1.20 2004/07/23 21:38:39 mej Exp $";
25 
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29 
30 #include <libast_internal.h>
31 
32 /* *INDENT-OFF* */
33 static SPIF_CONST_TYPE(class) u_class = {
34     SPIF_DECL_CLASSNAME(url),
35     (spif_func_t) spif_url_new,
36     (spif_func_t) spif_url_init,
37     (spif_func_t) spif_url_done,
38     (spif_func_t) spif_url_del,
39     (spif_func_t) spif_url_show,
40     (spif_func_t) spif_url_comp,
41     (spif_func_t) spif_url_dup,
42     (spif_func_t) spif_url_type
43 };
44 SPIF_TYPE(class) SPIF_CLASS_VAR(url) = &u_class;
45 /* *INDENT-ON* */
46 
47 static spif_bool_t spif_url_parse(spif_url_t);
48 
49 spif_url_t
spif_url_new(void)50 spif_url_new(void)
51 {
52     spif_url_t self;
53 
54     self = SPIF_ALLOC(url);
55     if (!spif_url_init(self)) {
56         SPIF_DEALLOC(self);
57         self = SPIF_NULL_TYPE(url);
58     }
59     return self;
60 }
61 
62 spif_url_t
spif_url_new_from_str(spif_str_t other)63 spif_url_new_from_str(spif_str_t other)
64 {
65     spif_url_t self;
66 
67     self = SPIF_ALLOC(url);
68     if (!spif_url_init_from_str(self, other)) {
69         SPIF_DEALLOC(self);
70         self = SPIF_NULL_TYPE(url);
71     }
72     return self;
73 }
74 
75 spif_url_t
spif_url_new_from_ptr(spif_charptr_t other)76 spif_url_new_from_ptr(spif_charptr_t other)
77 {
78     spif_url_t self;
79 
80     self = SPIF_ALLOC(url);
81     if (!spif_url_init_from_ptr(self, other)) {
82         SPIF_DEALLOC(self);
83         self = SPIF_NULL_TYPE(url);
84     }
85     return self;
86 }
87 
88 spif_bool_t
spif_url_init(spif_url_t self)89 spif_url_init(spif_url_t self)
90 {
91     ASSERT_RVAL(!SPIF_URL_ISNULL(self), FALSE);
92     if (!spif_str_init(SPIF_STR(self))) {
93         return FALSE;
94     }
95     spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS_VAR(url));
96     self->proto = SPIF_NULL_TYPE(str);
97     self->user = SPIF_NULL_TYPE(str);
98     self->passwd = SPIF_NULL_TYPE(str);
99     self->host = SPIF_NULL_TYPE(str);
100     self->port = SPIF_NULL_TYPE(str);
101     self->path = SPIF_NULL_TYPE(str);
102     self->query = SPIF_NULL_TYPE(str);
103     return TRUE;
104 }
105 
106 spif_bool_t
spif_url_init_from_str(spif_url_t self,spif_str_t other)107 spif_url_init_from_str(spif_url_t self, spif_str_t other)
108 {
109     ASSERT_RVAL(!SPIF_URL_ISNULL(self), FALSE);
110     if (!spif_str_init_from_ptr(SPIF_STR(self), SPIF_STR_STR(other))) {
111         return FALSE;
112     }
113     spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS_VAR(url));
114     self->proto = SPIF_NULL_TYPE(str);
115     self->user = SPIF_NULL_TYPE(str);
116     self->passwd = SPIF_NULL_TYPE(str);
117     self->host = SPIF_NULL_TYPE(str);
118     self->port = SPIF_NULL_TYPE(str);
119     self->path = SPIF_NULL_TYPE(str);
120     self->query = SPIF_NULL_TYPE(str);
121     spif_url_parse(self);
122     return TRUE;
123 }
124 
125 spif_bool_t
spif_url_init_from_ptr(spif_url_t self,spif_charptr_t other)126 spif_url_init_from_ptr(spif_url_t self, spif_charptr_t other)
127 {
128     ASSERT_RVAL(!SPIF_URL_ISNULL(self), FALSE);
129     if (!spif_str_init_from_ptr(SPIF_STR(self), other)) {
130         return FALSE;
131     }
132     spif_obj_set_class(SPIF_OBJ(self), SPIF_CLASS_VAR(url));
133     self->proto = SPIF_NULL_TYPE(str);
134     self->user = SPIF_NULL_TYPE(str);
135     self->passwd = SPIF_NULL_TYPE(str);
136     self->host = SPIF_NULL_TYPE(str);
137     self->port = SPIF_NULL_TYPE(str);
138     self->path = SPIF_NULL_TYPE(str);
139     self->query = SPIF_NULL_TYPE(str);
140     spif_url_parse(self);
141     return TRUE;
142 }
143 
144 spif_bool_t
spif_url_done(spif_url_t self)145 spif_url_done(spif_url_t self)
146 {
147     ASSERT_RVAL(!SPIF_URL_ISNULL(self), FALSE);
148     if (!SPIF_STR_ISNULL(self->proto)) {
149         spif_str_del(self->proto);
150         self->proto = SPIF_NULL_TYPE(str);
151     }
152     if (!SPIF_STR_ISNULL(self->user)) {
153         spif_str_del(self->user);
154         self->user = SPIF_NULL_TYPE(str);
155     }
156     if (!SPIF_STR_ISNULL(self->passwd)) {
157         spif_str_del(self->passwd);
158         self->passwd = SPIF_NULL_TYPE(str);
159     }
160     if (!SPIF_STR_ISNULL(self->host)) {
161         spif_str_del(self->host);
162         self->host = SPIF_NULL_TYPE(str);
163     }
164     if (!SPIF_STR_ISNULL(self->port)) {
165         spif_str_del(self->port);
166         self->port = SPIF_NULL_TYPE(str);
167     }
168     if (!SPIF_STR_ISNULL(self->path)) {
169         spif_str_del(self->path);
170         self->path = SPIF_NULL_TYPE(str);
171     }
172     if (!SPIF_STR_ISNULL(self->query)) {
173         spif_str_del(self->query);
174         self->query = SPIF_NULL_TYPE(str);
175     }
176     spif_str_done(SPIF_STR(self));
177     return TRUE;
178 }
179 
180 spif_bool_t
spif_url_del(spif_url_t self)181 spif_url_del(spif_url_t self)
182 {
183     ASSERT_RVAL(!SPIF_URL_ISNULL(self), FALSE);
184     spif_url_done(self);
185     SPIF_DEALLOC(self);
186     return TRUE;
187 }
188 
189 spif_str_t
spif_url_show(spif_url_t self,spif_charptr_t name,spif_str_t buff,size_t indent)190 spif_url_show(spif_url_t self, spif_charptr_t name, spif_str_t buff, size_t indent)
191 {
192     spif_char_t tmp[4096];
193 
194     if (SPIF_URL_ISNULL(self)) {
195         SPIF_OBJ_SHOW_NULL(url, name, buff, indent, tmp);
196         return buff;
197     }
198 
199     memset(tmp, ' ', indent);
200     snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent,
201              "(spif_url_t) %s:  %10p {\n",
202              name, SPIF_CAST(ptr) self);
203     if (SPIF_STR_ISNULL(buff)) {
204         buff = spif_str_new_from_ptr(tmp);
205     } else {
206         spif_str_append_from_ptr(buff, tmp);
207     }
208 
209     buff = spif_str_show(self->proto, SPIF_CHARPTR("proto"), buff, indent + 2);
210     buff = spif_str_show(self->user, SPIF_CHARPTR("user"), buff, indent + 2);
211     buff = spif_str_show(self->passwd, SPIF_CHARPTR("passwd"), buff, indent + 2);
212     buff = spif_str_show(self->host, SPIF_CHARPTR("host"), buff, indent + 2);
213     buff = spif_str_show(self->port, SPIF_CHARPTR("port"), buff, indent + 2);
214     buff = spif_str_show(self->path, SPIF_CHARPTR("path"), buff, indent + 2);
215     buff = spif_str_show(self->query, SPIF_CHARPTR("query"), buff, indent + 2);
216 
217     snprintf(SPIF_CHARPTR_C(tmp) + indent, sizeof(tmp) - indent, "}\n");
218     spif_str_append_from_ptr(buff, tmp);
219     return buff;
220 }
221 
222 spif_cmp_t
spif_url_comp(spif_url_t self,spif_url_t other)223 spif_url_comp(spif_url_t self, spif_url_t other)
224 {
225     SPIF_OBJ_COMP_CHECK_NULL(self, other);
226     return spif_str_comp(SPIF_STR(self), SPIF_STR(other));
227 }
228 
229 spif_url_t
spif_url_dup(spif_url_t self)230 spif_url_dup(spif_url_t self)
231 {
232     spif_url_t tmp;
233 
234     ASSERT_RVAL(!SPIF_URL_ISNULL(self), SPIF_NULL_TYPE(url));
235     tmp = spif_url_new_from_str(SPIF_STR(self));
236     return tmp;
237 }
238 
239 spif_classname_t
spif_url_type(spif_url_t self)240 spif_url_type(spif_url_t self)
241 {
242     ASSERT_RVAL(!SPIF_URL_ISNULL(self), SPIF_NULL_TYPE(classname));
243     return SPIF_OBJ_CLASSNAME(self);
244 }
245 
SPIF_DEFINE_PROPERTY_FUNC(url,str,proto)246 SPIF_DEFINE_PROPERTY_FUNC(url, str, proto)
247 SPIF_DEFINE_PROPERTY_FUNC(url, str, user)
248 SPIF_DEFINE_PROPERTY_FUNC(url, str, passwd)
249 SPIF_DEFINE_PROPERTY_FUNC(url, str, host)
250 SPIF_DEFINE_PROPERTY_FUNC(url, str, port)
251 SPIF_DEFINE_PROPERTY_FUNC(url, str, path)
252 SPIF_DEFINE_PROPERTY_FUNC(url, str, query)
253 
254 static spif_bool_t
255 spif_url_parse(spif_url_t self)
256 {
257     spif_charptr_t s = SPIF_STR_STR(SPIF_STR(self));
258     spif_charptr_t pstr, pend, ptmp;
259 
260     ASSERT_RVAL(!SPIF_URL_ISNULL(self), FALSE);
261     pstr = s;
262 
263     /* Check for "proto:" at the beginning. */
264     pend = SPIF_CHARPTR(strchr(SPIF_CHARPTR_C(s), ':'));
265     if (pend != NULL) {
266         for (; pstr < pend; pstr++) {
267             if (!isalnum(*pstr)) {
268                 break;
269             }
270         }
271         if (pstr == pend) {
272             /* Got one. */
273             self->proto = spif_str_new_from_buff(s, pend - s);
274             pstr++;
275         } else {
276             /* Nope, reset. */
277             pstr = s;
278         }
279     }
280 
281     if ((*pstr == '/') && (pstr[1] == '/')) {
282         pstr += 2;
283     }
284 
285     /* Knock out the path and query if they're there. */
286     pend = SPIF_CHARPTR(strchr(SPIF_CHARPTR_C(pstr), '/'));
287     if (pend != NULL) {
288         spif_charptr_t tmp = SPIF_CHARPTR(strchr(SPIF_CHARPTR_C(pend), '?'));
289 
290         if (tmp != NULL) {
291             self->query = spif_str_new_from_ptr(tmp + 1);
292             self->path = spif_str_new_from_buff(pend, tmp - pend);
293         } else {
294           self->path = spif_str_new_from_ptr(pend);
295         }
296     } else if ((pend = SPIF_CHARPTR(strchr(SPIF_CHARPTR_C(pstr), '?'))) != NULL) {
297         self->query = spif_str_new_from_ptr(pend + 1);
298     } else {
299         for (pend = pstr; *pend; pend++);
300     }
301     /* At this point, pend *must* point to the end of the user/pass/host/port part. */
302 
303     /* Check for an @ sign, which would mean we have auth info. */
304     ptmp = SPIF_CHARPTR(strchr(SPIF_CHARPTR_C(pstr), '@'));
305     if ((ptmp != NULL) && (ptmp < pend)) {
306         spif_charptr_t tmp = SPIF_CHARPTR(strchr(SPIF_CHARPTR_C(pstr), ':'));
307 
308         if ((tmp != NULL) && (tmp < ptmp)) {
309             /* Both username and password. */
310             self->user = spif_str_new_from_buff(pstr, tmp - pstr);
311             self->passwd = spif_str_new_from_buff((tmp + 1), ptmp - tmp - 1);
312         } else {
313             self->user = spif_str_new_from_buff(pstr, ptmp - pstr);
314         }
315         pstr = ptmp + 1;
316     }
317 
318     /* All that remains now between pstr and pend is host and maybe port. */
319     ptmp = SPIF_CHARPTR(strchr(SPIF_CHARPTR_C(pstr), ':'));
320     if ((ptmp != NULL) && (ptmp < pend)) {
321         self->host = spif_str_new_from_buff(pstr, ptmp - pstr);
322         self->port = spif_str_new_from_buff((ptmp + 1), pend - ptmp - 1);
323     } else if (pstr != pend) {
324         self->host = spif_str_new_from_buff(pstr, pend - pstr);
325     }
326 
327     /* If we have a proto but no port, see if we can resolve the port using the proto. */
328     if (SPIF_STR_ISNULL(self->port) && !SPIF_STR_ISNULL(self->proto)) {
329         spif_protoinfo_t proto;
330         spif_servinfo_t serv;
331 
332         proto = getprotobyname(SPIF_CHARPTR_C(SPIF_STR_STR(self->proto)));
333         if (proto == NULL) {
334             /* If it's not a protocol, it's probably a service. */
335             serv = getservbyname(SPIF_CHARPTR_C(SPIF_STR_STR(self->proto)), "tcp");
336             if (serv == NULL) {
337                 serv = getservbyname(SPIF_CHARPTR_C(SPIF_STR_STR(self->proto)), "udp");
338             }
339             if (serv != NULL) {
340                 proto = getprotobyname(serv->s_proto);
341                 REQUIRE_RVAL(proto != NULL, FALSE);
342             }
343         }
344         if (proto != NULL) {
345             spif_char_t buff[32];
346 
347             snprintf(SPIF_CHARPTR_C(buff), sizeof(buff), "%d", ntohs(serv->s_port));
348             self->port = spif_str_new_from_ptr(buff);
349         }
350     }
351 
352     return TRUE;
353 }
354 
355 spif_bool_t
spif_url_unparse(spif_url_t self)356 spif_url_unparse(spif_url_t self)
357 {
358     ASSERT_RVAL(!SPIF_URL_ISNULL(self), FALSE);
359     spif_str_done(SPIF_STR(self));
360     spif_str_init_from_ptr(SPIF_STR(self), SPIF_CHARPTR(""));
361 
362     /* First, proto followed by a colon. */
363     if (!SPIF_STR_ISNULL(self->proto)) {
364         spif_str_append(SPIF_STR(self), self->proto);
365         spif_str_append_char(SPIF_STR(self), ':');
366     }
367 
368     /* If we have a port but no host, make it localhost. */
369     if (!SPIF_STR_ISNULL(self->port) && SPIF_STR_ISNULL(self->host)) {
370         self->host = spif_str_new_from_ptr(SPIF_CHARPTR("localhost"));
371     }
372 
373     /* We need the // if we have a hostname. */
374     if (!SPIF_STR_ISNULL(self->host)) {
375         spif_str_append_from_ptr(SPIF_STR(self), SPIF_CHARPTR("//"));
376     }
377 
378     if (!SPIF_STR_ISNULL(self->user)) {
379         spif_str_append(SPIF_STR(self), self->user);
380         if (!SPIF_STR_ISNULL(self->passwd)) {
381             spif_str_append_char(SPIF_STR(self), ':');
382             spif_str_append(SPIF_STR(self), self->passwd);
383         }
384         spif_str_append_char(SPIF_STR(self), '@');
385     }
386 
387     if (!SPIF_STR_ISNULL(self->host)) {
388         spif_str_append(SPIF_STR(self), self->host);
389         if (!SPIF_STR_ISNULL(self->port)) {
390             spif_str_append_char(SPIF_STR(self), ':');
391             spif_str_append(SPIF_STR(self), self->port);
392         }
393     }
394 
395     if (!SPIF_STR_ISNULL(self->path)) {
396         spif_str_append(SPIF_STR(self), self->path);
397     }
398 
399     if (!SPIF_STR_ISNULL(self->query)) {
400         spif_str_append_char(SPIF_STR(self), '?');
401         spif_str_append(SPIF_STR(self), self->query);
402     }
403     return TRUE;
404 }
405