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 2006 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 	return (0);
169 }
170 
171 int
172 uri_to_string(uri_t *uri, char *buffer, size_t buflen)
173 {
174 	if ((uri == NULL) || (buffer == NULL) || (buflen == 0) ||
175 	    (uri->scheme == NULL) ||
176 	    ((uri->password != NULL) && (uri->user == NULL)) ||
177 	    ((uri->user != NULL) && (uri->host == NULL)) ||
178 	    ((uri->port != NULL) && (uri->host == NULL)) ||
179 	    ((uri->fragment != NULL) && (uri->path == NULL)) ||
180 	    ((uri->query != NULL) && (uri->path == NULL))) {
181 		errno = EINVAL;
182 		return (-1);
183 	}
184 
185 	(void) memset(buffer, 0, buflen);
186 
187 	if (uri->scheme_part == NULL) {
188 		(void) snprintf(buffer, buflen,
189 				"%s://%s%s%s%s%s%s%s%s%s%s%s%s%s",
190 				uri->scheme,
191 				(uri->user ? uri->user : ""),
192 				(uri->password ? ":" : ""),
193 				(uri->password ? uri->password : ""),
194 				(uri->user ? "@": ""),
195 				(uri->host ? uri->host : ""),
196 				(uri->port ? ":" : ""),
197 				(uri->port ? uri->port : ""),
198 				(uri->path[0] != '/' ? "/" : ""), uri->path,
199 				(uri->fragment ? "#" : ""),
200 				(uri->fragment ? uri->fragment : ""),
201 				(uri->query ? "?" : ""),
202 				(uri->query ? uri->query : ""));
203 	} else {
204 		(void) snprintf(buffer, buflen, "%s:%s", uri->scheme,
205 				uri->scheme_part);
206 	}
207 
208 	return (0);
209 }
210 
211 void
212 uri_free(uri_t *uri)
213 {
214 	if (uri != NULL) {
215 		if (uri->scheme != NULL)
216 			free(uri->scheme);
217 		if (uri->scheme_part != NULL)
218 			free(uri->scheme_part);
219 		if (uri->user != NULL)
220 			free(uri->user);
221 		if (uri->password != NULL)
222 			free(uri->password);
223 		if (uri->host != NULL)
224 			free(uri->host);
225 		if (uri->port != NULL)
226 			free(uri->port);
227 		if (uri->path != NULL)
228 			free(uri->path);
229 		if (uri->fragment != NULL)
230 			free(uri->fragment);
231 		if (uri->query != NULL)
232 			free(uri->query);
233 		/* help me debug */
234 		if (uri->user_part != NULL)
235 			free(uri->user_part);
236 		if (uri->host_part != NULL)
237 			free(uri->host_part);
238 		if (uri->path_part != NULL)
239 			free(uri->path_part);
240 		free(uri);
241 	}
242 }
243 
244 #ifdef DEADBEEF
245 static void
246 uri_dump(FILE *fp, uri_t *uri)
247 {
248 	if (uri != NULL) {
249 		fprintf(fp, "URI:\n");
250 		if (uri->scheme != NULL)
251 			fprintf(fp, "scheme: %s\n", uri->scheme);
252 		if (uri->scheme_part != NULL)
253 			fprintf(fp, "scheme_part: %s\n", uri->scheme_part);
254 		if (uri->user != NULL)
255 			fprintf(fp, "user: %s\n", uri->user);
256 		if (uri->password != NULL)
257 			fprintf(fp, "password: %s\n", uri->password);
258 		if (uri->host != NULL)
259 			fprintf(fp, "host: %s\n", uri->host);
260 		if (uri->port != NULL)
261 			fprintf(fp, "port: %s\n", uri->port);
262 		if (uri->path != NULL)
263 			fprintf(fp, "path: %s\n", uri->path);
264 		if (uri->fragment != NULL)
265 			fprintf(fp, "fragment: %s\n", uri->fragment);
266 		if (uri->query != NULL)
267 			fprintf(fp, "query: %s\n", uri->query);
268 		/* help me debug */
269 		if (uri->user_part != NULL)
270 			fprintf(fp, "user_part: %s\n", uri->user_part);
271 		if (uri->host_part != NULL)
272 			fprintf(fp, "host_part: %s\n", uri->host_part);
273 		if (uri->path_part != NULL)
274 			fprintf(fp, "path_part: %s\n", uri->path_part);
275 		fflush(fp);
276 	}
277 }
278 
279 int
280 main(int argc, char *argv[])
281 {
282 	uri_t *u = NULL;
283 
284 	if (argc != 2) {
285 		fprintf(stderr, "Usage: %s uri\n", argv[0]);
286 		exit(1);
287 	}
288 
289 	if (uri_from_string(argv[1], &u) == 0) {
290 		char buf[BUFSIZ];
291 
292 		uri_dump(stdout, u);
293 		uri_to_string(u, buf, sizeof (buf));
294 		fprintf(stdout, "reconstituted: %s\n", buf);
295 
296 		uri_to_string(u, buf, 12);
297 		fprintf(stdout, "reconstituted(12): %s\n", buf);
298 	} else
299 		printf(" failed for %s  (%s)\n", argv[1], strerror(errno));
300 
301 	exit(0);
302 }
303 #endif /* DEADBEEF */
304