12264Sjacobs /*
22264Sjacobs  * CDDL HEADER START
32264Sjacobs  *
42264Sjacobs  * The contents of this file are subject to the terms of the
52264Sjacobs  * Common Development and Distribution License (the "License").
62264Sjacobs  * You may not use this file except in compliance with the License.
72264Sjacobs  *
82264Sjacobs  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92264Sjacobs  * or http://www.opensolaris.org/os/licensing.
102264Sjacobs  * See the License for the specific language governing permissions
112264Sjacobs  * and limitations under the License.
122264Sjacobs  *
132264Sjacobs  * When distributing Covered Code, include this CDDL HEADER in each
142264Sjacobs  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152264Sjacobs  * If applicable, add the following below this CDDL HEADER, with the
162264Sjacobs  * fields enclosed by brackets "[]" replaced with your own identifying
172264Sjacobs  * information: Portions Copyright [yyyy] [name of copyright owner]
182264Sjacobs  *
192264Sjacobs  * CDDL HEADER END
202264Sjacobs  */
212264Sjacobs 
222264Sjacobs /*
23*7253Sjacobs  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
242264Sjacobs  * Use is subject to license terms.
252264Sjacobs  *
262264Sjacobs  */
272264Sjacobs 
282264Sjacobs /* $Id: uri.c 146 2006-03-24 00:26:54Z njacobs $ */
292264Sjacobs 
302264Sjacobs #pragma ident	"%Z%%M%	%I%	%E% SMI"
312264Sjacobs 
322264Sjacobs /*LINTLIBRARY*/
332264Sjacobs 
342264Sjacobs #include <stdio.h>
352264Sjacobs #include <stdlib.h>
362264Sjacobs #include <unistd.h>
372264Sjacobs #include <string.h>
382264Sjacobs #include <sys/types.h>
392264Sjacobs #include <errno.h>
402264Sjacobs #include "uri.h"
412264Sjacobs 
422264Sjacobs static char *
432264Sjacobs strndup(char *string, size_t length)
442264Sjacobs {
452264Sjacobs 	char *result = NULL;
462264Sjacobs 
472264Sjacobs 	if (length > 0) {
482264Sjacobs 		length++;
492264Sjacobs 
502264Sjacobs 
512264Sjacobs 		if ((result = calloc(1, length)) != NULL)
522264Sjacobs 			(void) strlcat(result, string, length);
532264Sjacobs 	}
542264Sjacobs 
552264Sjacobs 	return (result);
562264Sjacobs }
572264Sjacobs 
582264Sjacobs 
592264Sjacobs /*
602264Sjacobs  * This will handle the following forms:
612264Sjacobs  *	scheme:scheme_data
622264Sjacobs  *	scheme://[[user[:password]@]host[:port]]/path[[#fragment]|[?query]]
632264Sjacobs  */
642264Sjacobs int
652264Sjacobs uri_from_string(char *string, uri_t **uri)
662264Sjacobs {
672264Sjacobs 	char *ptr;
682264Sjacobs 	uri_t *u;
692264Sjacobs 
702264Sjacobs 	if ((string == NULL) || (uri == NULL)) {
712264Sjacobs 		errno = EINVAL;
722264Sjacobs 		return (-1);
732264Sjacobs 	}
742264Sjacobs 
752264Sjacobs 	/* find the scheme:scheme_part split */
762264Sjacobs 	if ((ptr = strchr(string, ':')) == NULL) {
772264Sjacobs 		errno = EINVAL;
782264Sjacobs 		return (-1);
792264Sjacobs 	}
802264Sjacobs 
812264Sjacobs 	if ((*uri = u = calloc(1, sizeof (*u))) == NULL)
822264Sjacobs 		return (-1);
832264Sjacobs 
842264Sjacobs 	u->scheme = strndup(string, ptr - string);
852264Sjacobs 
862264Sjacobs 	if ((ptr[1] == '/') && (ptr[2] == '/')) {
872264Sjacobs 		/*
882264Sjacobs 		 * CSTYLED
892264Sjacobs 		 * scheme://[host_part]/[path_part]
902264Sjacobs 		 */
912264Sjacobs 		char *end = NULL, *user = NULL, *host = NULL, *path = NULL;
922264Sjacobs 
932264Sjacobs 		string = ptr + 3; /* skip the :// */
942264Sjacobs 
952264Sjacobs 		if ((path = end = strchr(string, '/')) == NULL)
962264Sjacobs 			for (end = string; *end != '\0'; end++);
972264Sjacobs 
982264Sjacobs 		u->host_part = strndup(string, end - string);
992264Sjacobs 
1002264Sjacobs 		for (host = string; host < end; host ++)
1012264Sjacobs 			if (*host == '@') {
1022264Sjacobs 				/* string to host is the user part */
1032264Sjacobs 				u->user_part = strndup(string, host-string);
1042264Sjacobs 				/* host+1 to end is the host part */
1052264Sjacobs 				u->host_part = strndup(host + 1,
1062264Sjacobs 							end - (host+1));
1072264Sjacobs 				user = string;
1082264Sjacobs 				host++;
1092264Sjacobs 				break;
1102264Sjacobs 			}
1112264Sjacobs 
1122264Sjacobs 		if (user != NULL) {
1132264Sjacobs 			char *password  = NULL;
1142264Sjacobs 
1152264Sjacobs 			for (password = user; (password < host - 1); password++)
1162264Sjacobs 				if (*password == ':') {
1172264Sjacobs 					u->password = strndup(password + 1,
1182264Sjacobs 							host - password - 2);
1192264Sjacobs 					break;
1202264Sjacobs 				}
1212264Sjacobs 			u->user = strndup(user, password - user);
1222264Sjacobs 		} else
1232264Sjacobs 			host = string;
1242264Sjacobs 
1252264Sjacobs 		if (host != NULL) {
1262264Sjacobs 			char *port  = NULL;
1272264Sjacobs 
1282264Sjacobs 			for (port = host; (port < path); port++)
1292264Sjacobs 				if ((*port == ':') || (*port == '/'))
1302264Sjacobs 					break;
1312264Sjacobs 
1322264Sjacobs 			if (port < path) {
1332264Sjacobs 				u->port = strndup(port + 1, path - port - 1);
1342264Sjacobs 			}
1352264Sjacobs 
1362264Sjacobs 			u->host = strndup(host, port - host);
1372264Sjacobs 		}
1382264Sjacobs 
1392264Sjacobs 		if (path != NULL) {
1402264Sjacobs 			char *name = strrchr(path, '/');
1412264Sjacobs 
1422264Sjacobs 			u->path_part = strdup(path);
1432264Sjacobs 
1442264Sjacobs 			if (name != NULL) {
1452264Sjacobs 				char *query, *fragment;
1462264Sjacobs 
1472264Sjacobs 				query = strrchr(name, '?');
1482264Sjacobs 				if ((query != NULL) && (*query != '\0')) {
1492264Sjacobs 					u->query = strdup(query + 1);
1502264Sjacobs 					end = query;
1512264Sjacobs 				} else
1522264Sjacobs 					for (end = path; *end != '\0'; end++);
1532264Sjacobs 
1542264Sjacobs 				fragment = strrchr(name, '#');
1552264Sjacobs 				if ((fragment != NULL) && (*fragment != '\0')) {
1562264Sjacobs 					u->fragment = strndup(fragment + 1,
1572264Sjacobs 							end - fragment - 1);
1582264Sjacobs 					end = fragment;
1592264Sjacobs 				}
1602264Sjacobs 
1612264Sjacobs 				u->path = strndup(path, end - path);
1622264Sjacobs 			}
1632264Sjacobs 		}
1642264Sjacobs 	} else {	/* scheme:scheme_part */
1652264Sjacobs 		u->scheme_part = strdup(&ptr[1]);
1662264Sjacobs 	}
1672264Sjacobs 
168*7253Sjacobs 	if ((u->host_part == NULL) && (u->path_part == NULL) &&
169*7253Sjacobs 	    (u->scheme_part == NULL)) {
170*7253Sjacobs 		errno = EINVAL;
171*7253Sjacobs 		uri_free(u);
172*7253Sjacobs 		*uri = NULL;
173*7253Sjacobs 		return (-1);
174*7253Sjacobs 	}
175*7253Sjacobs 
1762264Sjacobs 	return (0);
1772264Sjacobs }
1782264Sjacobs 
1792264Sjacobs int
1802264Sjacobs uri_to_string(uri_t *uri, char *buffer, size_t buflen)
1812264Sjacobs {
1822264Sjacobs 	if ((uri == NULL) || (buffer == NULL) || (buflen == 0) ||
1832264Sjacobs 	    (uri->scheme == NULL) ||
1842264Sjacobs 	    ((uri->password != NULL) && (uri->user == NULL)) ||
1852264Sjacobs 	    ((uri->user != NULL) && (uri->host == NULL)) ||
1862264Sjacobs 	    ((uri->port != NULL) && (uri->host == NULL)) ||
1872264Sjacobs 	    ((uri->fragment != NULL) && (uri->path == NULL)) ||
1882264Sjacobs 	    ((uri->query != NULL) && (uri->path == NULL))) {
1892264Sjacobs 		errno = EINVAL;
1902264Sjacobs 		return (-1);
1912264Sjacobs 	}
1922264Sjacobs 
1932264Sjacobs 	(void) memset(buffer, 0, buflen);
1942264Sjacobs 
1952264Sjacobs 	if (uri->scheme_part == NULL) {
1962264Sjacobs 		(void) snprintf(buffer, buflen,
1972264Sjacobs 				"%s://%s%s%s%s%s%s%s%s%s%s%s%s%s",
1982264Sjacobs 				uri->scheme,
1992264Sjacobs 				(uri->user ? uri->user : ""),
2002264Sjacobs 				(uri->password ? ":" : ""),
2012264Sjacobs 				(uri->password ? uri->password : ""),
2022264Sjacobs 				(uri->user ? "@": ""),
2032264Sjacobs 				(uri->host ? uri->host : ""),
2042264Sjacobs 				(uri->port ? ":" : ""),
2052264Sjacobs 				(uri->port ? uri->port : ""),
2062264Sjacobs 				(uri->path[0] != '/' ? "/" : ""), uri->path,
2072264Sjacobs 				(uri->fragment ? "#" : ""),
2082264Sjacobs 				(uri->fragment ? uri->fragment : ""),
2092264Sjacobs 				(uri->query ? "?" : ""),
2102264Sjacobs 				(uri->query ? uri->query : ""));
2112264Sjacobs 	} else {
2122264Sjacobs 		(void) snprintf(buffer, buflen, "%s:%s", uri->scheme,
2132264Sjacobs 				uri->scheme_part);
2142264Sjacobs 	}
2152264Sjacobs 
2162264Sjacobs 	return (0);
2172264Sjacobs }
2182264Sjacobs 
2192264Sjacobs void
2202264Sjacobs uri_free(uri_t *uri)
2212264Sjacobs {
2222264Sjacobs 	if (uri != NULL) {
2232264Sjacobs 		if (uri->scheme != NULL)
2242264Sjacobs 			free(uri->scheme);
2252264Sjacobs 		if (uri->scheme_part != NULL)
2262264Sjacobs 			free(uri->scheme_part);
2272264Sjacobs 		if (uri->user != NULL)
2282264Sjacobs 			free(uri->user);
2292264Sjacobs 		if (uri->password != NULL)
2302264Sjacobs 			free(uri->password);
2312264Sjacobs 		if (uri->host != NULL)
2322264Sjacobs 			free(uri->host);
2332264Sjacobs 		if (uri->port != NULL)
2342264Sjacobs 			free(uri->port);
2352264Sjacobs 		if (uri->path != NULL)
2362264Sjacobs 			free(uri->path);
2372264Sjacobs 		if (uri->fragment != NULL)
2382264Sjacobs 			free(uri->fragment);
2392264Sjacobs 		if (uri->query != NULL)
2402264Sjacobs 			free(uri->query);
2412264Sjacobs 		/* help me debug */
2422264Sjacobs 		if (uri->user_part != NULL)
2432264Sjacobs 			free(uri->user_part);
2442264Sjacobs 		if (uri->host_part != NULL)
2452264Sjacobs 			free(uri->host_part);
2462264Sjacobs 		if (uri->path_part != NULL)
2472264Sjacobs 			free(uri->path_part);
2482264Sjacobs 		free(uri);
2492264Sjacobs 	}
2502264Sjacobs }
2512264Sjacobs 
2522264Sjacobs #ifdef DEADBEEF
2532264Sjacobs static void
2542264Sjacobs uri_dump(FILE *fp, uri_t *uri)
2552264Sjacobs {
2562264Sjacobs 	if (uri != NULL) {
2572264Sjacobs 		fprintf(fp, "URI:\n");
2582264Sjacobs 		if (uri->scheme != NULL)
2592264Sjacobs 			fprintf(fp, "scheme: %s\n", uri->scheme);
2602264Sjacobs 		if (uri->scheme_part != NULL)
2612264Sjacobs 			fprintf(fp, "scheme_part: %s\n", uri->scheme_part);
2622264Sjacobs 		if (uri->user != NULL)
2632264Sjacobs 			fprintf(fp, "user: %s\n", uri->user);
2642264Sjacobs 		if (uri->password != NULL)
2652264Sjacobs 			fprintf(fp, "password: %s\n", uri->password);
2662264Sjacobs 		if (uri->host != NULL)
2672264Sjacobs 			fprintf(fp, "host: %s\n", uri->host);
2682264Sjacobs 		if (uri->port != NULL)
2692264Sjacobs 			fprintf(fp, "port: %s\n", uri->port);
2702264Sjacobs 		if (uri->path != NULL)
2712264Sjacobs 			fprintf(fp, "path: %s\n", uri->path);
2722264Sjacobs 		if (uri->fragment != NULL)
2732264Sjacobs 			fprintf(fp, "fragment: %s\n", uri->fragment);
2742264Sjacobs 		if (uri->query != NULL)
2752264Sjacobs 			fprintf(fp, "query: %s\n", uri->query);
2762264Sjacobs 		/* help me debug */
2772264Sjacobs 		if (uri->user_part != NULL)
2782264Sjacobs 			fprintf(fp, "user_part: %s\n", uri->user_part);
2792264Sjacobs 		if (uri->host_part != NULL)
2802264Sjacobs 			fprintf(fp, "host_part: %s\n", uri->host_part);
2812264Sjacobs 		if (uri->path_part != NULL)
2822264Sjacobs 			fprintf(fp, "path_part: %s\n", uri->path_part);
2832264Sjacobs 		fflush(fp);
2842264Sjacobs 	}
2852264Sjacobs }
2862264Sjacobs 
2872264Sjacobs int
2882264Sjacobs main(int argc, char *argv[])
2892264Sjacobs {
2902264Sjacobs 	uri_t *u = NULL;
2912264Sjacobs 
2922264Sjacobs 	if (argc != 2) {
2932264Sjacobs 		fprintf(stderr, "Usage: %s uri\n", argv[0]);
2942264Sjacobs 		exit(1);
2952264Sjacobs 	}
2962264Sjacobs 
2972264Sjacobs 	if (uri_from_string(argv[1], &u) == 0) {
2982264Sjacobs 		char buf[BUFSIZ];
2992264Sjacobs 
3002264Sjacobs 		uri_dump(stdout, u);
3012264Sjacobs 		uri_to_string(u, buf, sizeof (buf));
3022264Sjacobs 		fprintf(stdout, "reconstituted: %s\n", buf);
3032264Sjacobs 
3042264Sjacobs 		uri_to_string(u, buf, 12);
3052264Sjacobs 		fprintf(stdout, "reconstituted(12): %s\n", buf);
3062264Sjacobs 	} else
3072264Sjacobs 		printf(" failed for %s  (%s)\n", argv[1], strerror(errno));
3082264Sjacobs 
3092264Sjacobs 	exit(0);
3102264Sjacobs }
3112264Sjacobs #endif /* DEADBEEF */
312