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