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