1*2264Sjacobs /*
2*2264Sjacobs  * CDDL HEADER START
3*2264Sjacobs  *
4*2264Sjacobs  * The contents of this file are subject to the terms of the
5*2264Sjacobs  * Common Development and Distribution License (the "License").
6*2264Sjacobs  * You may not use this file except in compliance with the License.
7*2264Sjacobs  *
8*2264Sjacobs  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*2264Sjacobs  * or http://www.opensolaris.org/os/licensing.
10*2264Sjacobs  * See the License for the specific language governing permissions
11*2264Sjacobs  * and limitations under the License.
12*2264Sjacobs  *
13*2264Sjacobs  * When distributing Covered Code, include this CDDL HEADER in each
14*2264Sjacobs  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*2264Sjacobs  * If applicable, add the following below this CDDL HEADER, with the
16*2264Sjacobs  * fields enclosed by brackets "[]" replaced with your own identifying
17*2264Sjacobs  * information: Portions Copyright [yyyy] [name of copyright owner]
18*2264Sjacobs  *
19*2264Sjacobs  * CDDL HEADER END
20*2264Sjacobs  */
21*2264Sjacobs 
22*2264Sjacobs /*
23*2264Sjacobs  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*2264Sjacobs  * Use is subject to license terms.
25*2264Sjacobs  *
26*2264Sjacobs  */
27*2264Sjacobs 
28*2264Sjacobs /* $Id: uri.c 146 2006-03-24 00:26:54Z njacobs $ */
29*2264Sjacobs 
30*2264Sjacobs #pragma ident	"%Z%%M%	%I%	%E% SMI"
31*2264Sjacobs 
32*2264Sjacobs /*LINTLIBRARY*/
33*2264Sjacobs 
34*2264Sjacobs #include <stdio.h>
35*2264Sjacobs #include <stdlib.h>
36*2264Sjacobs #include <unistd.h>
37*2264Sjacobs #include <string.h>
38*2264Sjacobs #include <sys/types.h>
39*2264Sjacobs #include <errno.h>
40*2264Sjacobs #include "uri.h"
41*2264Sjacobs 
42*2264Sjacobs static char *
43*2264Sjacobs strndup(char *string, size_t length)
44*2264Sjacobs {
45*2264Sjacobs 	char *result = NULL;
46*2264Sjacobs 
47*2264Sjacobs 	if (length > 0) {
48*2264Sjacobs 		length++;
49*2264Sjacobs 
50*2264Sjacobs 
51*2264Sjacobs 		if ((result = calloc(1, length)) != NULL)
52*2264Sjacobs 			(void) strlcat(result, string, length);
53*2264Sjacobs 	}
54*2264Sjacobs 
55*2264Sjacobs 	return (result);
56*2264Sjacobs }
57*2264Sjacobs 
58*2264Sjacobs 
59*2264Sjacobs /*
60*2264Sjacobs  * This will handle the following forms:
61*2264Sjacobs  *	scheme:scheme_data
62*2264Sjacobs  *	scheme://[[user[:password]@]host[:port]]/path[[#fragment]|[?query]]
63*2264Sjacobs  */
64*2264Sjacobs int
65*2264Sjacobs uri_from_string(char *string, uri_t **uri)
66*2264Sjacobs {
67*2264Sjacobs 	char *ptr;
68*2264Sjacobs 	uri_t *u;
69*2264Sjacobs 
70*2264Sjacobs 	if ((string == NULL) || (uri == NULL)) {
71*2264Sjacobs 		errno = EINVAL;
72*2264Sjacobs 		return (-1);
73*2264Sjacobs 	}
74*2264Sjacobs 
75*2264Sjacobs 	/* find the scheme:scheme_part split */
76*2264Sjacobs 	if ((ptr = strchr(string, ':')) == NULL) {
77*2264Sjacobs 		errno = EINVAL;
78*2264Sjacobs 		return (-1);
79*2264Sjacobs 	}
80*2264Sjacobs 
81*2264Sjacobs 	if ((*uri = u = calloc(1, sizeof (*u))) == NULL)
82*2264Sjacobs 		return (-1);
83*2264Sjacobs 
84*2264Sjacobs 	u->scheme = strndup(string, ptr - string);
85*2264Sjacobs 
86*2264Sjacobs 	if ((ptr[1] == '/') && (ptr[2] == '/')) {
87*2264Sjacobs 		/*
88*2264Sjacobs 		 * CSTYLED
89*2264Sjacobs 		 * scheme://[host_part]/[path_part]
90*2264Sjacobs 		 */
91*2264Sjacobs 		char *end = NULL, *user = NULL, *host = NULL, *path = NULL;
92*2264Sjacobs 
93*2264Sjacobs 		string = ptr + 3; /* skip the :// */
94*2264Sjacobs 
95*2264Sjacobs 		if ((path = end = strchr(string, '/')) == NULL)
96*2264Sjacobs 			for (end = string; *end != '\0'; end++);
97*2264Sjacobs 
98*2264Sjacobs 		u->host_part = strndup(string, end - string);
99*2264Sjacobs 
100*2264Sjacobs 		for (host = string; host < end; host ++)
101*2264Sjacobs 			if (*host == '@') {
102*2264Sjacobs 				/* string to host is the user part */
103*2264Sjacobs 				u->user_part = strndup(string, host-string);
104*2264Sjacobs 				/* host+1 to end is the host part */
105*2264Sjacobs 				u->host_part = strndup(host + 1,
106*2264Sjacobs 							end - (host+1));
107*2264Sjacobs 				user = string;
108*2264Sjacobs 				host++;
109*2264Sjacobs 				break;
110*2264Sjacobs 			}
111*2264Sjacobs 
112*2264Sjacobs 		if (user != NULL) {
113*2264Sjacobs 			char *password  = NULL;
114*2264Sjacobs 
115*2264Sjacobs 			for (password = user; (password < host - 1); password++)
116*2264Sjacobs 				if (*password == ':') {
117*2264Sjacobs 					u->password = strndup(password + 1,
118*2264Sjacobs 							host - password - 2);
119*2264Sjacobs 					break;
120*2264Sjacobs 				}
121*2264Sjacobs 			u->user = strndup(user, password - user);
122*2264Sjacobs 		} else
123*2264Sjacobs 			host = string;
124*2264Sjacobs 
125*2264Sjacobs 		if (host != NULL) {
126*2264Sjacobs 			char *port  = NULL;
127*2264Sjacobs 
128*2264Sjacobs 			for (port = host; (port < path); port++)
129*2264Sjacobs 				if ((*port == ':') || (*port == '/'))
130*2264Sjacobs 					break;
131*2264Sjacobs 
132*2264Sjacobs 			if (port < path) {
133*2264Sjacobs 				u->port = strndup(port + 1, path - port - 1);
134*2264Sjacobs 			}
135*2264Sjacobs 
136*2264Sjacobs 			u->host = strndup(host, port - host);
137*2264Sjacobs 		}
138*2264Sjacobs 
139*2264Sjacobs 		if (path != NULL) {
140*2264Sjacobs 			char *name = strrchr(path, '/');
141*2264Sjacobs 
142*2264Sjacobs 			u->path_part = strdup(path);
143*2264Sjacobs 
144*2264Sjacobs 			if (name != NULL) {
145*2264Sjacobs 				char *query, *fragment;
146*2264Sjacobs 
147*2264Sjacobs 				query = strrchr(name, '?');
148*2264Sjacobs 				if ((query != NULL) && (*query != '\0')) {
149*2264Sjacobs 					u->query = strdup(query + 1);
150*2264Sjacobs 					end = query;
151*2264Sjacobs 				} else
152*2264Sjacobs 					for (end = path; *end != '\0'; end++);
153*2264Sjacobs 
154*2264Sjacobs 				fragment = strrchr(name, '#');
155*2264Sjacobs 				if ((fragment != NULL) && (*fragment != '\0')) {
156*2264Sjacobs 					u->fragment = strndup(fragment + 1,
157*2264Sjacobs 							end - fragment - 1);
158*2264Sjacobs 					end = fragment;
159*2264Sjacobs 				}
160*2264Sjacobs 
161*2264Sjacobs 				u->path = strndup(path, end - path);
162*2264Sjacobs 			}
163*2264Sjacobs 		}
164*2264Sjacobs 	} else {	/* scheme:scheme_part */
165*2264Sjacobs 		u->scheme_part = strdup(&ptr[1]);
166*2264Sjacobs 	}
167*2264Sjacobs 
168*2264Sjacobs 	return (0);
169*2264Sjacobs }
170*2264Sjacobs 
171*2264Sjacobs int
172*2264Sjacobs uri_to_string(uri_t *uri, char *buffer, size_t buflen)
173*2264Sjacobs {
174*2264Sjacobs 	if ((uri == NULL) || (buffer == NULL) || (buflen == 0) ||
175*2264Sjacobs 	    (uri->scheme == NULL) ||
176*2264Sjacobs 	    ((uri->password != NULL) && (uri->user == NULL)) ||
177*2264Sjacobs 	    ((uri->user != NULL) && (uri->host == NULL)) ||
178*2264Sjacobs 	    ((uri->port != NULL) && (uri->host == NULL)) ||
179*2264Sjacobs 	    ((uri->fragment != NULL) && (uri->path == NULL)) ||
180*2264Sjacobs 	    ((uri->query != NULL) && (uri->path == NULL))) {
181*2264Sjacobs 		errno = EINVAL;
182*2264Sjacobs 		return (-1);
183*2264Sjacobs 	}
184*2264Sjacobs 
185*2264Sjacobs 	(void) memset(buffer, 0, buflen);
186*2264Sjacobs 
187*2264Sjacobs 	if (uri->scheme_part == NULL) {
188*2264Sjacobs 		(void) snprintf(buffer, buflen,
189*2264Sjacobs 				"%s://%s%s%s%s%s%s%s%s%s%s%s%s%s",
190*2264Sjacobs 				uri->scheme,
191*2264Sjacobs 				(uri->user ? uri->user : ""),
192*2264Sjacobs 				(uri->password ? ":" : ""),
193*2264Sjacobs 				(uri->password ? uri->password : ""),
194*2264Sjacobs 				(uri->user ? "@": ""),
195*2264Sjacobs 				(uri->host ? uri->host : ""),
196*2264Sjacobs 				(uri->port ? ":" : ""),
197*2264Sjacobs 				(uri->port ? uri->port : ""),
198*2264Sjacobs 				(uri->path[0] != '/' ? "/" : ""), uri->path,
199*2264Sjacobs 				(uri->fragment ? "#" : ""),
200*2264Sjacobs 				(uri->fragment ? uri->fragment : ""),
201*2264Sjacobs 				(uri->query ? "?" : ""),
202*2264Sjacobs 				(uri->query ? uri->query : ""));
203*2264Sjacobs 	} else {
204*2264Sjacobs 		(void) snprintf(buffer, buflen, "%s:%s", uri->scheme,
205*2264Sjacobs 				uri->scheme_part);
206*2264Sjacobs 	}
207*2264Sjacobs 
208*2264Sjacobs 	return (0);
209*2264Sjacobs }
210*2264Sjacobs 
211*2264Sjacobs void
212*2264Sjacobs uri_free(uri_t *uri)
213*2264Sjacobs {
214*2264Sjacobs 	if (uri != NULL) {
215*2264Sjacobs 		if (uri->scheme != NULL)
216*2264Sjacobs 			free(uri->scheme);
217*2264Sjacobs 		if (uri->scheme_part != NULL)
218*2264Sjacobs 			free(uri->scheme_part);
219*2264Sjacobs 		if (uri->user != NULL)
220*2264Sjacobs 			free(uri->user);
221*2264Sjacobs 		if (uri->password != NULL)
222*2264Sjacobs 			free(uri->password);
223*2264Sjacobs 		if (uri->host != NULL)
224*2264Sjacobs 			free(uri->host);
225*2264Sjacobs 		if (uri->port != NULL)
226*2264Sjacobs 			free(uri->port);
227*2264Sjacobs 		if (uri->path != NULL)
228*2264Sjacobs 			free(uri->path);
229*2264Sjacobs 		if (uri->fragment != NULL)
230*2264Sjacobs 			free(uri->fragment);
231*2264Sjacobs 		if (uri->query != NULL)
232*2264Sjacobs 			free(uri->query);
233*2264Sjacobs 		/* help me debug */
234*2264Sjacobs 		if (uri->user_part != NULL)
235*2264Sjacobs 			free(uri->user_part);
236*2264Sjacobs 		if (uri->host_part != NULL)
237*2264Sjacobs 			free(uri->host_part);
238*2264Sjacobs 		if (uri->path_part != NULL)
239*2264Sjacobs 			free(uri->path_part);
240*2264Sjacobs 		free(uri);
241*2264Sjacobs 	}
242*2264Sjacobs }
243*2264Sjacobs 
244*2264Sjacobs #ifdef DEADBEEF
245*2264Sjacobs static void
246*2264Sjacobs uri_dump(FILE *fp, uri_t *uri)
247*2264Sjacobs {
248*2264Sjacobs 	if (uri != NULL) {
249*2264Sjacobs 		fprintf(fp, "URI:\n");
250*2264Sjacobs 		if (uri->scheme != NULL)
251*2264Sjacobs 			fprintf(fp, "scheme: %s\n", uri->scheme);
252*2264Sjacobs 		if (uri->scheme_part != NULL)
253*2264Sjacobs 			fprintf(fp, "scheme_part: %s\n", uri->scheme_part);
254*2264Sjacobs 		if (uri->user != NULL)
255*2264Sjacobs 			fprintf(fp, "user: %s\n", uri->user);
256*2264Sjacobs 		if (uri->password != NULL)
257*2264Sjacobs 			fprintf(fp, "password: %s\n", uri->password);
258*2264Sjacobs 		if (uri->host != NULL)
259*2264Sjacobs 			fprintf(fp, "host: %s\n", uri->host);
260*2264Sjacobs 		if (uri->port != NULL)
261*2264Sjacobs 			fprintf(fp, "port: %s\n", uri->port);
262*2264Sjacobs 		if (uri->path != NULL)
263*2264Sjacobs 			fprintf(fp, "path: %s\n", uri->path);
264*2264Sjacobs 		if (uri->fragment != NULL)
265*2264Sjacobs 			fprintf(fp, "fragment: %s\n", uri->fragment);
266*2264Sjacobs 		if (uri->query != NULL)
267*2264Sjacobs 			fprintf(fp, "query: %s\n", uri->query);
268*2264Sjacobs 		/* help me debug */
269*2264Sjacobs 		if (uri->user_part != NULL)
270*2264Sjacobs 			fprintf(fp, "user_part: %s\n", uri->user_part);
271*2264Sjacobs 		if (uri->host_part != NULL)
272*2264Sjacobs 			fprintf(fp, "host_part: %s\n", uri->host_part);
273*2264Sjacobs 		if (uri->path_part != NULL)
274*2264Sjacobs 			fprintf(fp, "path_part: %s\n", uri->path_part);
275*2264Sjacobs 		fflush(fp);
276*2264Sjacobs 	}
277*2264Sjacobs }
278*2264Sjacobs 
279*2264Sjacobs int
280*2264Sjacobs main(int argc, char *argv[])
281*2264Sjacobs {
282*2264Sjacobs 	uri_t *u = NULL;
283*2264Sjacobs 
284*2264Sjacobs 	if (argc != 2) {
285*2264Sjacobs 		fprintf(stderr, "Usage: %s uri\n", argv[0]);
286*2264Sjacobs 		exit(1);
287*2264Sjacobs 	}
288*2264Sjacobs 
289*2264Sjacobs 	if (uri_from_string(argv[1], &u) == 0) {
290*2264Sjacobs 		char buf[BUFSIZ];
291*2264Sjacobs 
292*2264Sjacobs 		uri_dump(stdout, u);
293*2264Sjacobs 		uri_to_string(u, buf, sizeof (buf));
294*2264Sjacobs 		fprintf(stdout, "reconstituted: %s\n", buf);
295*2264Sjacobs 
296*2264Sjacobs 		uri_to_string(u, buf, 12);
297*2264Sjacobs 		fprintf(stdout, "reconstituted(12): %s\n", buf);
298*2264Sjacobs 	} else
299*2264Sjacobs 		printf(" failed for %s  (%s)\n", argv[1], strerror(errno));
300*2264Sjacobs 
301*2264Sjacobs 	exit(0);
302*2264Sjacobs }
303*2264Sjacobs #endif /* DEADBEEF */
304