/*----------------------------------------------------------------------------------------*/
/* uri.h                                                                                  */
/* copyright (c) innovaphone 2005                                                         */
/*                                                                                        */
/*----------------------------------------------------------------------------------------*/

#include <platform.h>
#include "../ilib/uri.h"

USING_NAMESPACE

uri_dissector::uri_dissector()
{
	memset(this, 0, sizeof(*this));
}

bool uri_dissector::is_ip(char * startPos)
{
    while (*startPos && *startPos != '/' && *startPos != ':') {            // search for other chars before port/path, as a fqdn may start with a digit ...
        if ((*startPos < '0' || *startPos > '9') && *startPos != '.') {    // doesn't seem to be an IP address
            return false;
        }
        startPos++;
    }
    return true;   // assume IP
}

bool uri_dissector::dissect_uri(char *uri)
{
	char    *start = uri, *end;
	bool    found_host = false;
	bool    found_path = false;
	bool    found_anchor = false;
	bool    found_query = false;
	bool    found_params = false;

	if (!uri || !*uri)
		return(false);

	memset(this, 0, sizeof(*this));

	if ('.' == *uri || '/' == *uri) {
		this->path = start;
		found_host = true;
		found_path = true;
	}

last_resort:
	for (;; uri++) {
		if (!*uri) {
			if (this->ip && !this->e_ip) this->e_ip = uri;
			if (this->fqdn && !this->e_fqdn) this->e_fqdn = uri;
			if (this->path && !this->e_path) this->e_path = uri;
			if (this->anchor && !this->e_anchor) this->e_anchor = uri;
			if (this->query && !this->e_query) this->e_query = uri;
			if (this->params && !this->e_params) this->e_params = uri;
			if (!this->path && !this->scheme) {
				this->path = uri = start;
				goto last_resort;
			}
			break;
		}

		if (!found_query && '?' == *uri) {
			found_query = true;
			if (this->path && !this->e_path) this->e_path = uri;
			if (this->fqdn && !this->e_fqdn) this->e_fqdn = uri;
			if (this->ip && !this->e_ip) this->e_ip = uri;
			if (this->anchor && !this->e_anchor) this->e_anchor = uri;
			this->query = uri + 1;
			continue;
		}

		if (!found_anchor && '#' == *uri) {
			found_anchor = true;
			if (this->path && !this->e_path) this->e_path = uri;
			if (this->file && !this->e_file) this->e_file = uri;
			if (this->fqdn && !this->e_fqdn) this->e_fqdn = uri;
			if (this->ip && !this->e_ip) this->e_ip = uri;
			if (this->params && !this->e_params) this->e_params = uri;
			this->anchor = uri + 1;
			continue;
		}

        if (!found_path && '/' == *uri) {
			found_path = true;

			if (this->ip && !this->e_ip) this->e_ip = uri;
			else if (this->fqdn && !this->e_fqdn) this->e_fqdn = uri;
			if (this->params && !this->e_params) this->e_params = uri;
			this->path = uri;
			continue;
		}

		if (!found_host && ':' == uri[0]) {
			found_host = true;
			if ('\0' == uri[1])
				return(false);

			this->scheme = start;
			this->e_scheme = uri++;

			if ('/' == *uri) {
				this->hier = uri++;
				this->e_hier = uri++;
				if ('/' == *e_hier)
					this->e_hier++;
				if ('/' == *uri) {
					/* no host, but path found
					 */
					uri--;
					continue;
				}
			}
            if ('[' == *uri) {      // start of IPV6 address
                this->ip = uri;
            }
            else if (*uri >= '0' && *uri <= '9' && is_ip(uri + 1)) {
                this->ip = uri;
            }
			else
				this->fqdn = uri;

			continue;
		}

		if (!found_params && ';' == uri[0]) {
			found_params = true;
			if (this->fqdn && !this->e_fqdn) this->e_fqdn = uri;
			if (this->ip && !this->e_ip) this->e_ip = uri;
			uri++;
			this->params = uri;

		}
	} /* for */

	if (found_host && (this->fqdn || this->ip)) {
		char *at = NULL, *e_at = NULL, *start = NULL, *end = NULL;
		if (this->fqdn && this->e_fqdn) {
			at = start = this->fqdn;
			e_at = end = this->e_fqdn;
		}
		else if (this->ip && this->e_ip) {
			at = start = this->ip;
			e_at = end = this->e_ip;
		}
		else
			return(false);

		for (; at < e_at && '@' != *at; at++) { ; }

		if (at < e_at) {
			/* Found '@'.
			   Move ptr. to host part. Seek user and password
			   */
			at++;
			if (at < e_at) {
                if (*at >= '0' && *at <= '9' && is_ip(at + 1)) {
					this->ip = at;
					this->e_ip = e_at;
					this->fqdn = this->e_fqdn = NULL;
				}
				else {
					this->fqdn = at;
					this->e_fqdn = e_at;
					this->ip = this->e_ip = NULL;
				}
				at--;

				for (this->usr = this->e_usr = start; this->e_usr <= at && '@' != *this->e_usr; this->e_usr++) {
					if (':' == *this->e_usr) {
						this->pwd = this->e_usr + 1;
						break;
					}
				}

				if (this->pwd) {
					for (this->e_pwd = this->pwd; this->e_pwd <= at && '@' != *this->e_pwd; this->e_pwd++) { ; }

					if ('@' != *this->e_pwd) {
						this->pwd = this->e_pwd = NULL;
					}
				}
			}
		}
	}

	if (this->path) {
		/* split path into path and file
		 */
		end = uri;

		for (; uri >= start; uri--) {
			if ('/' == *uri) {
				this->file = uri + 1;

				if (this->anchor && !this->query)
					this->e_file = this->anchor - 1;
				else if (this->query)
					this->e_file = this->query - 1;
				else
					this->e_file = end;

				this->e_path = uri + 1;
				break;
			}

			if (uri == this->path) {
				this->file = uri;

				if (this->anchor && !this->query)
					this->e_file = this->anchor - 1;
				else if (this->query)
					this->e_file = this->query - 1;
				else
					this->e_file = end;

				this->path = this->e_path = NULL;
				break;
			}
		}
	}

	if (this->fqdn || this->ip) {
		/* Split host into host:port
			*/
		char *e = this->e_ip ? this->e_ip : this->e_fqdn;
		char *p = e;
		char *s = this->ip ? this->ip : this->fqdn;
		for (; p >= s && ':' != *p && ']' != *p; p--) { ; }
		if (p > s && ':' == *p && '0' <= p[1] && '9' >= p[1]) {
			this->port = &p[1];
			this->e_port = e;
			if (this->e_fqdn)
				this->e_fqdn = p;
			if (this->e_ip)
				this->e_ip = p;
		}
	}
	return(true);
}

bool uri_dissector::path_to_argv(char *buf, int sz, int &argc, char * argv[])
{
	char        *p, *e, *n, *b = buf;
	int         largc = argc;

	if (argc <= 0) return(false);
	argc = 0;

	p = this->path, e = this->e_path, n = this->path;
	if (p) {
		if ('/' == *p) { p++; n++; }
		if (e - p >= sz) return (false);

		memcpy(b, p, e - p);

		for (; (p < e); p++) {
			if ('/' == *p) {
				if (argc >= largc) return(false);
				argv[argc++] = b;
				b += p - n;
				*b++ = '\0';
				n = p + 1;
			}
		}
	}
	return(true);
}

bool uri_dissector::querystring_to_argv(char *buf, int sz, int &argc, char * argv[])
{
	char        *p, *e, *n, *b = buf;
	int         largc = argc;

	if (argc <= 0) return(false);
	argc = 0;

	p = this->query, e = this->e_query, n = this->query;
	if (p) {
		if (e - p >= sz) return (false);

		memcpy(b, p, e - p);

		for (; (p < e); p++) {
			if ('=' == *p) {
				if (argc >= largc) return(false);
				argv[argc++] = b;
				b += p - n;
				*b++ = '\0';
				n = p + 1;
				for (; p < e && '&' != *p; p++) {}
				if (argc >= largc) return(false);
				argv[argc++] = b;
				b += p - n;
				*b++ = '\0';
				n = p + 1;
			}
		}
	}
	return(true);
}

char* uri_dissector::compose(char *buf, dword len_buf, dword flags/*=COMPOSE_ALL*/)
{
	char *addr = ip ? ip : fqdn, *e_addr = ip ? e_ip : e_fqdn;
	unsigned n;

	len_buf--;
	if (flags&COMPOSE_SCHEME) {
		n = (unsigned)(e_scheme - scheme);
		if (n > len_buf) n = len_buf;
		memcpy(buf, scheme, n);
		buf += n; len_buf -= n;
	}
	if (flags&COMPOSE_HIER) {
		if ((flags&COMPOSE_SCHEME) && (len_buf != 0)) {
			*(buf++) = ':'; len_buf--;
		}
		n = (unsigned)(e_hier - hier);
		if (n > len_buf) n = len_buf;
		memcpy(buf, hier, n);
		buf += n; len_buf -= n;
	}
	if (flags&COMPOSE_USR) {
		n = (unsigned)(e_usr - usr);
		if (n > len_buf) n = len_buf;
		memcpy(buf, usr, n);
		buf += n; len_buf -= n;
	}
	if (flags&COMPOSE_PWD) {
		if (flags&COMPOSE_USR && this->pwd && (len_buf != 0)) {
			*(buf++) = ':'; len_buf--;
		}
		n = (unsigned)(e_pwd - pwd);
		if (n > len_buf) n = len_buf;
		memcpy(buf, pwd, n);
		buf += n; len_buf -= n;
	}
	if (flags&COMPOSE_HOST) {
		if ((((flags&COMPOSE_USR) && this->usr) || ((flags&COMPOSE_PWD) && this->pwd)) && addr && (len_buf != 0)) {
			*(buf++) = '@'; len_buf--;
		}
		n = (unsigned)(e_addr - addr);
		if (n > len_buf) n = len_buf;
		memcpy(buf, addr, n);
		buf += n; len_buf -= n;
	}
	if (flags&COMPOSE_PORT) {
		if (flags&COMPOSE_HOST && port && (len_buf != 0)) {
			*(buf++) = ':'; len_buf--;
		}
		n = (unsigned)(e_port - port);
		if (n > len_buf) n = len_buf;
		memcpy(buf, port, n);
		buf += n; len_buf -= n;
	}
	if (flags&COMPOSE_PARAMS) {
		if (flags&COMPOSE_HOST && this->params && (len_buf != 0)) {
			*(buf++) = ';'; len_buf--;
		}
		n = (unsigned)(this->e_params - this->params);
		if (n > len_buf) n = len_buf;
		memcpy(buf, this->params, n);
		buf += n; len_buf -= n;
	}
	if (flags&COMPOSE_PATH) {
		n = (unsigned)(e_path - path);
		if (n > len_buf) n = len_buf;
		memcpy(buf, path, n);
		buf += n; len_buf -= n;
	}
	if (flags&COMPOSE_FILE) {
		n = (unsigned)(e_file - file);
		if (n > len_buf) n = len_buf;
		memcpy(buf, file, n);
		buf += n; len_buf -= n;
	}
	if (flags&COMPOSE_ANCHOR) {
		if (flags&COMPOSE_FILE && this->anchor && (len_buf != 0)) {
			*(buf++) = '#'; len_buf--;
		}
		n = (unsigned)(e_anchor - anchor);
		if (n > len_buf) n = len_buf;
		memcpy(buf, anchor, n);
		buf += n; len_buf -= n;
	}
	if (flags&COMPOSE_QUERY) {
		if (flags&COMPOSE_FILE && this->query && (len_buf != 0)) {
			*(buf++) = '?'; len_buf--;
		}
		n = (unsigned)(e_query - query);
		if (n > len_buf) n = len_buf;
		memcpy(buf, query, n);
		buf += n; len_buf -= n;
	}
	*buf = '\0';
	return(buf);
}
