static char rcsid[] = "@(#)$Id: url.c,v 1.13 2006/07/24 18:45:00 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.13 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI>
 *      or  Kari Hurtta <elm@elmme-mailer.org>
 *****************************************************************************/

#include "def_url.h"
#include "s_me.h"

DEBUG_VAR(Debug,__FILE__,"url");

static unsigned char * s2us P_((char *str));
static unsigned char * s2us(str)
     char *str;
{
    return (unsigned char *)str;
}


static CONST unsigned char *cs2us P_((const char *str));
static CONST unsigned char *cs2us(str)
     CONST char *str;
{
    return (CONST unsigned char *)str;
}

static char *us2s P_((unsigned char *str));
static char *us2s(str) 
     unsigned char *str;
{
    return (char *)str;
}

static struct scheme BUILTIN_SCHEMES[] = {
    { "--dummy--", & NO_URL_HANDLER     },
    { "mailto",    & URL_mailto_handler },
    { "http",      & DUMMY_http_HANDLER },
    { "https",     & DUMMY_http_HANDLER },
    { "imap",      & URL_imap_handler   }
};

static struct scheme * scheme_list  = BUILTIN_SCHEMES;
static int             scheme_count = 
   sizeof (BUILTIN_SCHEMES) / sizeof (BUILTIN_SCHEMES[0]);

static struct scheme * index_to_scheme P_((int idx));
static struct scheme * index_to_scheme(idx) 
     int idx;
{
    if (idx < 0 || idx >= scheme_count)
 	panic("URL PANIC",__FILE__,__LINE__,"index_to_scheme",
	      "Bad index",0);
    return & (scheme_list[idx]);
}

int name_to_scheme_idx(name)
     const char * name;
{
    int i;

    for (i = 0; i < scheme_count; i++) {
	if (0 == strcmp(name,scheme_list[i].scheme))
	    return i;
    }

    if (BUILTIN_SCHEMES == scheme_list) {
	scheme_list = safe_malloc((scheme_count+1) * sizeof (scheme_list[0]));
	
	for (i = 0; i < scheme_count; i++)
	    scheme_list[i] = BUILTIN_SCHEMES[i];
    } else
	scheme_list = safe_realloc(scheme_list,
				   (scheme_count+1) * sizeof (scheme_list[0]));

    scheme_list[scheme_count].scheme  = safe_strdup(name);
    scheme_list[scheme_count].handler = & NO_URL_HANDLER;

    return scheme_count++;
}

/* ------------------------------------------------------------------------- */

/* RFC 1738:
   
The character "#" is unsafe and should
always be encoded because it is used in World Wide Web and in other
systems to delimit a URL from a fragment/anchor identifier that might
follow it.  The character "%" is unsafe because it is used for
encodings of other characters.

*/

/* RFC 1738:
   
If the character corresponding to an octet is
reserved in a scheme, the octet must be encoded.  The characters ";",
"/", "?", ":", "@", "=" and "&" are the characters which may be
reserved for special meaning within a scheme. No other characters may
be reserved within a scheme.

*/


static struct url * alloc_url P_((struct url_handler *handler,
				  int scheme));
static struct url * alloc_url(handler,scheme)
     struct url_handler *handler;
     int scheme;
{
    struct url * ret = safe_malloc(sizeof(*ret));

    /* bzero is defined hdrs/defs.h */
    bzero((void *)ret,sizeof (*ret));

    ret->magic  = URL_magic;

    if (URL_handler_magic != handler->magic)
	panic("URL PANIC",__FILE__,__LINE__,"alloc_url",
	      "bad handler magic number",0);

    ret->scheme    = scheme;
    ret->current_handler = handler;
    ret->host      = NULL;
    ret->path      = NULL;
    
    ret->u.dummy   = NULL;
    ret->fragment  = NULL;

    ret->current_handler->uh_init_it(ret);

    return ret;
}

static struct host_part * new_host_part P_((void));
static struct host_part * new_host_part() 
{
    struct host_part * ptr = safe_malloc(sizeof (*ptr));

    /* bzero is defined hdrs/defs.h */
    bzero((void *)ptr,sizeof (*ptr));

    ptr->user      = NULL;
    ptr->password  = NULL;
    ptr->host      = NULL;
    ptr->port      = 0;
    return ptr;
}

static void free_host_part P_((struct host_part **ptr));
static void free_host_part(ptr)
     struct host_part **ptr;
{
    if ((*ptr)->user)
	free_url_element(& ((*ptr)->user));
    if ((*ptr)->password)
	free_url_element(& ((*ptr)->password));
    if ((*ptr)->host)
	free_url_element(& ((*ptr)->host));
    (*ptr)->port = 0;

    free(*ptr);
    *ptr = NULL;
}

struct url * url_from_raw(raw,parent,header_error)
     struct string *raw; 
     struct url *parent;
     struct header_errors **header_error;
{

    int X = 0;
    int  startpos = 0;

    int len = string_len(raw);
    int scheme_found = 0;
    charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);

    char * buffer = NULL;
    int scheme_idx = -1;
    struct scheme * sch = NULL;
    int L;

    struct host_part * host_part = NULL;
    struct url_path  * path_part = NULL;
    struct url *ret = NULL;

    int no_relative = 0;
    int abs_path = 0;

    uint16 ch = 0;

    if (len < 1) {
	DPRINT(Debug,7,(&Debug,"url_from_raw: empty url\n"));
	return NULL;
    }

    if (!ascii_ptr)
	panic("URL_PANIC",__FILE__,__LINE__,"url_from_raw",
	      "US-ASCII not found",0);

    /* RFC 1808:

       However,
       there is enough uniformity in the use of URLs to allow a parser to
       resolve relative URLs based upon a single, generic-RL syntax.  This
       generic-RL syntax consists of six components:
       
       <scheme>://<net_loc>/<path>;<params>?<query>#<fragment>

       each of which, except <scheme>, may be absent from a particular URL.
    */


    /* This code assumes that code charset is ASCII */
    buffer = safe_malloc(len);
        
    /* check for scheme */
    for (; X < len; X++) {
	ch = give_unicode_from_string(raw,X);

	if (0x003A /* : */ == ch) {
	    scheme_found = X;     /* NOTE: scheme_found is not set 
				           if ':' found from first position (0)
				  */
	    break;
	}

	/* RFC 1738:

	Scheme names consist of a sequence of characters. The lower case
	letters "a"--"z", digits, and the characters plus ("+"), period
	("."), and hyphen ("-") are allowed. For resiliency, programs
	interpreting URLs should treat upper case letters as equivalent to
	lower case in scheme names (e.g., allow "HTTP" as well as "http").

	*/

	/* valid characters */

	if (0x0061 /* a */ <= ch && ch <= 0x007A /* z */  ||
	    0x0030 /* 0 */ <= ch && ch <= 0x0039 /* 9 */  ||
	    0x002B /* + */ == ch ||
	    0x002E /* . */ == ch ||
	    0x002D /* - */ == ch)
	    buffer[X] = ch;
	else if (0x0041 /* A */ <= ch && ch <= 0x005A /* Z */)
	    buffer[X] = ch + 0x0061 /* a */ - 0x0041 /* A */;
	else {
	    DPRINT(Debug,7,(&Debug,
			    "url_from_raw: [%d] ch=%04x not valid for scheme = raw=%S\n",
			    X,ch,raw));
	    break;
	}
    }


    if (scheme_found) {

	if (X != scheme_found)
	    panic("URL PANIC",__FILE__,__LINE__,"url_from_raw",
		  "Bad X",0);

	DPRINT(Debug,7,(&Debug,
			"url_from_raw: scheme found -- absolute URL\n"));

	/* This code assumes that code charset is ASCII */
	buffer[scheme_found] = '\0';

	DPRINT(Debug,11,(&Debug,
			"url_from_raw: raw scheme=%s\n",buffer));

	scheme_idx = name_to_scheme_idx(buffer);
	
	/* Skip ':' */	
	startpos = ++X;
	ch = 0;
	
	no_relative = 1;

    } else {
	X  = 0;   /* Reset scanner */
	ch = 0;

	if (!parent) {
	    DPRINT(Debug,2,(&Debug,
			    "url_from_raw: Relative URL (no scheme) without base URL: %S",
			    raw));

	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet,
					 MeRelativeURL,
					 "Relative URL (no scheme) without base URL: %S"),
			  raw);
	    goto failure;
	}
	
	scheme_idx = parent->scheme;
    }

    if (X < len)
	ch = give_unicode_from_string(raw,X);


    sch = index_to_scheme(scheme_idx);

    if (URL_handler_magic != sch->handler->magic) 
	panic("URL PANIC",__FILE__,__LINE__,"url_from_raw",
	      "Bad scheme magic number",0);

    if (0 != (sch->handler->flag & URLFLAG_common)) {

	if (X+2 < len &&
	    0x002F /* / */ == give_unicode_from_string(raw,X) &&
	    0x002F /* / */ == give_unicode_from_string(raw,X+1)
	    ) {
	    
	    struct string * raw_user = NULL;
	    struct string * raw_password = NULL;
	    struct string * raw_host = NULL;
	    struct string * port = NULL;
	    long            port_number = -1;

	    int             failure0 = 0;
	    int             was_at = 0;
	    
	    DPRINT(Debug,7,(&Debug,
			    "url_from_raw: Common internet scheme syntax found\n"));
	    X += 2;
	    ch = 0;
	    
	    startpos = X;
	    
	    /* 1) Possible USER part */
	    
	    
	    for (; X < len; X++, ch = 0) {
		ch = give_unicode_from_string(raw,X);
		
		if (0x0023 /* # */ == ch)
		    break;			     /* Anchor	-- do not allow */
		
		if (0x003B /* ; */ == ch ||	     /* part delimiters -- do not allow */
		    0x003F /* ? */ == ch)
		    break;
		
		if (0x002F /* / */ == ch)
		    break;			     /* URL specific part */
		
		if (0x0040 /* @ */ == ch)
		    break;			     /* username/password seen */
		
		if (0x003A /* : */ == ch) 
		    break;			     /* password indicator */
		
	    }
	    
	    if (X < len) {
		DPRINT(Debug,11,(&Debug,
				 "url_from_raw: username part ended with 0x%04x pos %d\n",
				 ch,X));
	    }

	    was_at = (0x0040 /* @ */ == ch);

	    /* If : found that may be also port on
	       http://host:port/
	    */

	    if (0x003A /* : */ == ch) {
		int X1;

		for (X1 = X+1; X1 < len; X1++) {
		    uint16 ch1 = give_unicode_from_string(raw,X1); 

		    if (0x0023 /* # */ == ch1)
			break;			     /* Anchor	-- do not allow */
		
		    if (0x003B /* ; */ == ch1 ||	     /* part delimiters -- do not allow */
			0x003F /* ? */ == ch1)
			break;
		    
		    if (0x002F /* / */ == ch1)
			break;			     /* URL specific part */
		    
		    if (0x0040 /* @ */ == ch1) {
			was_at = 1;
			break;			     /* username/password seen */
		    }
		    
		    if (0x003A /* : */ == ch1) 
			break;			     /* do not allow  */
		    
		}
	    }
	            

	    if (was_at) {
		
		int L = X - startpos;
		/* First part is username */
		
		DPRINT(Debug,7,(&Debug,
				"url_from_raw: %d - %d (len=%d) username found\n",
				X,startpos,L));
		
		raw_user = clip_from_string(raw,&startpos,L);
		
		if (startpos != X)
		    panic("URL PANIC",__FILE__,__LINE__,"url_from_raw",
			  "Clipping Error (user -part)",0);
		
		DPRINT(Debug,11,(&Debug,
				 "url_from_raw: raw user=%S\n",raw_user));
	    }

	    if (was_at &&
		0x003A /* : */ == ch) {  
		/* Find  password */

		startpos = ++X;
		ch = 0;

		for (; X < len; X++, ch = 0) {
		    ch = give_unicode_from_string(raw,X);
		
		    if (0x0023 /* # */ == ch)
			break;			 /* Anchor -- do not allow */
		
		    if (0x003B /* ; */ == ch ||	 /* part delimiters -- do not allow */
			0x003F /* ? */ == ch)
			break;
		
		    if (0x002F /* / */ == ch)
			break;			 /* URL specific part -- do not allow */
		
		    if (0x0040 /* @ */ == ch)
			break;			 /* username/password seen -- OK */
		
		    if (0x003A /* : */ == ch) 
			break;			 /* password indicator -- do not allow */
		}

		if (X < len) {
		    DPRINT(Debug,11,(&Debug,
				     "url_from_raw: password part ended with 0x%04x pos %d\n",
				     ch,X));
		}

		if (0x0040 /* @ */ != ch) {
		    DPRINT(Debug,2,(&Debug,
				    "url_from_raw: Failed to parse password from URL: %S\n",
				    raw));
		    process_header_error(header_error,
					 CATGETS(elm_msg_cat, MeSet,
						 MeFailedPasswordURL,
						 "Failed to parse password from URL: %S"),
					 raw);
		    goto common_failure;
		}

		/* second part is password */
		L = X - startpos;


		DPRINT(Debug,7,(&Debug,
				"url_from_raw: %d - %d (len=%d) password found\n",
				X,startpos,L));
	    
		raw_password = clip_from_string(raw,&startpos,L);
	    
		if (startpos != X)
		    panic("URL PANIC",__FILE__,__LINE__,"url_from_raw",
			  "Clipping Error (password -part)",0);

		DPRINT(Debug,50,(&Debug,
				 "url_from_raw: raw password=%S\n",raw_password));

	    }


	    /* Need read next part for hostname */
	    if (0x0040 /* @ */ == ch) {

		startpos = ++X;
		ch = 0;

		for (; X < len; X++, ch = 0) {
		    ch = give_unicode_from_string(raw,X);
		
		    if (0x0023 /* # */ == ch)
			break;			 /* fragment -- OK */
		
		    if (0x003B /* ; */ == ch ||	 /* part delimiters -- do not allow */
			0x003F /* ? */ == ch)
			break;
		
		    if (0x002F /* / */ == ch)
			break;			 /* URL specific part -- OK */
		
		    if (0x0040 /* @ */ == ch)
			break;			 /* -- second @ -character -- do not allow */
		
		    if (0x003A /* : */ == ch) 
			break;			 /* port indicator -- OK */
		}

		if (X < len) {
		    DPRINT(Debug,11,(&Debug,
				     "url_from_raw: hostname part ended with 0x%04x pos %d\n",
				     ch,X));
		}


	    }

	    /* End of URL is OK on here */

	    if (X < len &&
		0x0023 /* # */ != ch &&
		0x002F /* / */ != ch &&
		0x003A /* : */ != ch) {
		DPRINT(Debug,2,(&Debug,
				"url_from_raw: Failed to parse hostname from URL: %S\n",
				raw));
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeFailedHostnameURL,
					     "Failed to parse hostname from URL: %S"),
				     raw);
		goto common_failure;
	    }


	    /* third part is hostname */
	    L = X - startpos;
	
	
	    DPRINT(Debug,7,(&Debug,
			    "url_from_raw: %d - %d (len=%d) hostname found\n",
			    X,startpos,L));


	    raw_host = clip_from_string(raw,&startpos,L);
	
	    if (startpos != X)
		panic("URL PANIC",__FILE__,__LINE__,"url_from_raw",
		      "Clipping Error (host -part)",0);

	    DPRINT(Debug,11,(&Debug,
			     "url_from_raw: raw host=%S\n",raw_host));


	    if (0x003A /* : */ == ch) {
		int L;
		int port_found = 0;
		/* Find port */
		int badpos;

		startpos = ++X;
		ch = 0;

		for (; X < len; X++, ch = 0) {
		    ch = give_unicode_from_string(raw,X);
				
		    if (0x0023 /* # */ == ch)
			break;                   /* fragment -- OK     */

		    if (0x002F /* / */ == ch)
			break;			 /* URL specific part -- OK */
		
		    /* Valid characters are numbers */
		    if (0x0030 /* 0 */ <= ch && ch <= 0x0039 /* 9 */)
			port_found = 1;
		    else {
			DPRINT(Debug,7,(&Debug,
					"url_from_raw: [%d] ch=%04x not valid for port = raw=%S\n",
					X,ch,raw));
			break;
		    }


		}

		if (X < len) {
		    DPRINT(Debug,11,(&Debug,
				     "url_from_raw: port part ended with 0x%04x pos %d\n",
				     ch,X));
		}

		/* End of URL is OK on here */

		if (X < len &&
		    0x0023 /* # */ != ch &&
		    0x002F /* / */ != ch ||
		    !port_found) {
		    DPRINT(Debug,2,(&Debug,
				    "url_from_raw: Failed to parse port from URL: %S\n",
				    raw));
		    process_header_error(header_error,
					 CATGETS(elm_msg_cat, MeSet,
						 MeFailedPortURL,
						 "Failed to parse port from URL: %S"),
					 raw);
		    goto common_failure;
		}

		/* fourth part is port */
		L = X - startpos;
	
	
		DPRINT(Debug,7,(&Debug,
				"url_from_raw: %d - %d (len=%d) port found\n",
				X,startpos,L));


		port = clip_from_string(raw,&startpos,L);
	
		if (startpos != X)
		    panic("URL PANIC",__FILE__,__LINE__,"url_from_raw",
			  "Clipping Error (host -part)",0);
	    
		DPRINT(Debug,11,(&Debug,
				 "url_from_raw: port=%S\n",port));
			    
		port_number = string_to_long(port,&badpos);
		if (port_number > 0xFFFF || badpos != -1) {
		    DPRINT(Debug,2,(&Debug,
				    "url_from_raw: Failed to parse port from URL: %S\n",
				    raw));
		    process_header_error(header_error,
					 CATGETS(elm_msg_cat, MeSet,
						 MeFailedPortURL,
						 "Failed to parse port from URL: %S"),
					 raw);
		    goto common_failure;
		}		
	       
	    }
	    
	    if (raw_user && 
		0 == (sch->handler->flag & URLFLAG_username)) {
		DPRINT(Debug,2,(&Debug,
				"url_from_raw: Username not allowed on %s scheme, but is given on URL: %S\n",
				sch->scheme,
				raw));

		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeUsernameGivedUrl,
					     "Username not allowed on %s scheme, but is given on URL: %S"),
				     sch->scheme,
				     raw);
	    }	    


	    if (raw_password && 
		0 == (sch->handler->flag & URLFLAG_password)) {
		DPRINT(Debug,2,(&Debug,
				"url_from_raw: Password not allowed on %s scheme, but is given on URL\n",
				sch->scheme));
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MePasswordGivedUrl,
					     "Password not allowed on %s scheme, but is given on URL"),
				     sch->scheme);
	    }	    


	    if (port && 
		0 == (sch->handler->flag & URLFLAG_port)) {
		DPRINT(Debug,2,(&Debug,
				"url_from_raw: Port not allowed on %s scheme, but is given on URL: %S\n",
				sch->scheme,
				raw));
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MePortGivedUrl,
					     "Port not allowed on %s scheme, but is given on URL: %S"),
				     sch->scheme,
				     raw);
	    }	    


	    host_part = new_host_part();

	    if (raw_user)
		host_part->user = element_from_raw(raw_user);
	    if (raw_password)
		host_part->password = element_from_raw(raw_password);
	    if (raw_host)
		host_part->host = element_from_raw(raw_host);
	    if (-1 != port_number)
		host_part->port = port_number;

	    no_relative = 1;


	    if (0) {
	    common_failure:
		failure0 = 1;

		DPRINT(Debug,7,(&Debug,
				"url_from_raw: Failed to parse common internet scheme syntax\n"));
	    }

	    if (raw_user)
		free_string(& raw_user);
	    if (raw_password)
		free_string(& raw_password);
	    
	    if (raw_host)
		free_string(& raw_host);
	    
	    if (port)
		free_string(& port);
		
	    if (failure0)
		goto failure;
	    

	} else if (no_relative) {
	    DPRINT(Debug,2,(&Debug,
			    "url_from_raw: Host part is missing from URL: %S",
			    raw));
	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet,
					 MeURLnoHostPart,
					 "Host part is missing from URL: %S"),
				 raw);
	    
	    goto failure;

	} else if (!parent) {
	    DPRINT(Debug,2,(&Debug,
			    "url_from_raw: Relative URL (no hostname) without base URL: %S",
			    raw));
	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet,
					 MeRelativeURL1,
					 "Relative URL (no hostname) without base URL: %S"),
				 raw);
	    goto failure;
	    
	} else {   /* Handle relative URL */
			    	    	   	    
	    if (!parent->host) {
		DPRINT(Debug,2,(&Debug,
				"url_from_raw: Relative URL (no hostname) without hostname on base URL: %S",
				raw));

		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeRelativeURL2,
					     "Relative URL (no hostname) without hostname on base URL: %S"),
				     raw);
		goto failure;

	    }

	    host_part = new_host_part();

	    if (0 == (sch->handler->flag & URLFLAG_username)) {
		if (parent->host->user)
		    host_part->user = dup_url_element(parent->host->user);
	    }

	    if (0 == (sch->handler->flag & URLFLAG_password)) {
		if (parent->host->password)
		    host_part->password = dup_url_element(parent->host->
							  password);
	    }

	    if (parent->host->host)
		host_part->host = dup_url_element(parent->host->host);
	    
	    if (0 == (sch->handler->flag & URLFLAG_port) &&
		parent->scheme == scheme_idx)
		host_part->port = parent->host->port;
	    
	}
    }


    /* Read next part for path or url specific part */

    if (0x0023 /* # */ != ch &&
	0x003B /* ; */ != ch &&
	0x003F /* ? */ != ch &&
	X < len) {

	struct string * raw_path = NULL;
	int failure1 = 0;

	startpos = X;

	if (X < len &&
	    0x002F /* / */ == ch) {

	    DPRINT(Debug,11,(&Debug,
			     "url_from_raw: Absolute path -- starting with / \n"));
	    abs_path = 1;
	}

	for (; X < len; X++, ch = 0) {
	    ch = give_unicode_from_string(raw,X);
	    
	    if (0x0023 /* # */ == ch)
		break;			 /* fragment -- OK */
		
	    if (0x003B /* ; */ == ch ||	 /* part delimiters -- OK */
		0x003F /* ? */ == ch)
		break;
		
	}

	if (X < len) {
	    DPRINT(Debug,11,(&Debug,
			     "url_from_raw: path part ended with 0x%04x pos %d\n",
			     ch,X));
	}

	
	/* fifth part is path */
	L = X - startpos;

	DPRINT(Debug,7,(&Debug,
			"url_from_raw: %d - %d (len=%d) path (or scheme specific part) found\n",
			X,startpos,L));


	raw_path = clip_from_string(raw,&startpos,L);
	
	if (startpos != X)
	    panic("URL PANIC",__FILE__,__LINE__,"url_from_raw",
		  "Clipping Error (path -part)",0);
	    
	DPRINT(Debug,11,(&Debug,
			 "url_from_raw: raw path=%S\n",raw_path));

	if (0 != (sch->handler->flag & URLFLAG_path)) {
	    
	    if (abs_path)
		path_part = raw_to_url_path(raw_path);
	    else if (no_relative) {
		DPRINT(Debug,2,(&Debug,
				"url_from_raw: Path part is relative on URL: %S",
				raw));
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeURLRelPathpart,
					     "Path part is relative on URL: %S"),
				     raw);

		goto path_failure;

	    } else if (!parent) {
		DPRINT(Debug,2,(&Debug,
				"url_from_raw: Relative URL (relative path) without base URL: %S",
				raw));
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeRelativeURL3,
					     "Relative URL (relative path) without base URL: %S"),
				     raw);
		goto path_failure;
	    } else {
		struct url_path * Z;

		if (!parent->path) {
		    DPRINT(Debug,2,(&Debug,
				    "url_from_raw: Relative URL (relative path) without path on base URL: %S",
				    raw));
		    process_header_error(header_error,
					 CATGETS(elm_msg_cat, MeSet,
						 MeRelativeURL4,
						 "Relative URL (relative path) without path on base URL: %S"),
					 raw);
		    goto path_failure;
		}

		Z = raw_to_url_path(raw_path);
		
		path_part = join_url_path(parent->path,Z);

		free_url_path(&Z);

		if (!path_part)
		    goto path_failure;
	    }	           	      
	} else {
	    int r;

	    ret = alloc_url(sch->handler,scheme_idx);

	    r = sch->handler->uh_parse_it_not_path(ret,raw_path,header_error);

	    if (!r)
		goto path_failure;
	}

	no_relative = 1;

	if (0) {
	path_failure:
	    failure1 = 1;

	    DPRINT(Debug,7,(&Debug,
			    "url_from_raw: Failed to parse path syntax\n"));
	}


	if (raw_path)
	    free_string(&raw_path);

	if (failure1)
	    goto failure;

    } else {
	/* No path */

	if (0 != (sch->handler->flag & URLFLAG_path)) {

	    if (no_relative) {
		DPRINT(Debug,2,(&Debug,
				"url_from_raw: Path part is missing from URL: %S",
				raw));
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeURLnoPathPart,
					     "Path part is missing from URL: %S"),
				     raw);

		goto failure;
	    } else if (!parent) {
		DPRINT(Debug,2,(&Debug,
				"url_from_raw: Relative URL (no path) without base URL: %S",
				raw));
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeRelativeURL5,
					     "Relative URL (no path) without base URL: %S"),
				     raw);
		goto failure;
	    
	    } else {   /* Handle relative URL */

		if (!parent->path) {
		    DPRINT(Debug,2,(&Debug,
				    "url_from_raw: Relative URL (no path) without path on base URL: %S",
				    raw));
		    process_header_error(header_error,
					 CATGETS(elm_msg_cat, MeSet,
						 MeRelativeURL6,
						 "Relative URL (no path) without path on base URL: %S"),
					 raw);
		    goto failure;
		    
		}
		
		path_part = dup_url_path(parent->path);
	
	    }
	} else {
	    
	    if (! no_relative &&
		parent &&
		sch->handler == parent->current_handler &&
		parent->scheme == scheme_idx) {

		int r;

		ret = alloc_url(sch->handler,scheme_idx);

		r = sch->handler->uh_dup_it_not_path(ret,parent);

		if (!r)
		    goto failure;
	    }
	}
    }

    if (!ret)
	ret = alloc_url(sch->handler,scheme_idx);


    ret->host   = host_part;
    ret->path   = path_part;

    /* Read next part for params */

    if (X < len &&
	0x003B /* ; */ == ch) {

	struct string * raw_params = NULL;
	int failure2 = 0;
	int r;

	startpos = ++X;
	ch = 0;


	for (; X < len; X++, ch = 0) {
	    ch = give_unicode_from_string(raw,X);
	    
	    if (0x0023 /* # */ == ch)
		break;			 /* fragment -- OK */
		
	    if (0x003F /* ? */ == ch)	 /* part delimiter -- OK */
		
		break;
		
	}
	
	if (X < len) {
	    DPRINT(Debug,11,(&Debug,
			     "url_from_raw: params part ended with 0x%04x pos %d\n",
			     ch,X));
	}


	/* sixth part is params */
	L = X - startpos;

	DPRINT(Debug,7,(&Debug,
			"url_from_raw: %d - %d (len=%d) params found\n",
			X,startpos,L));


	raw_params = clip_from_string(raw,&startpos,L);
	
	if (startpos != X)
	    panic("URL PANIC",__FILE__,__LINE__,"url_from_raw",
		  "Clipping Error (params -part)",0);
	    
	DPRINT(Debug,11,(&Debug,
			 "url_from_raw: raw params=%S\n",raw_params));

	if (0 == (sch->handler->flag & URLFLAG_params)) {

	    DPRINT(Debug,2,(&Debug,
			    "url_from_raw: Params not allowed on %s scheme, but is given on URL: %S",
			    sch->scheme,
			    raw));
	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet,
					 MeParamsGivedUrl,
					 "Params not allowed on %s scheme, but is given on URL: %S"),
				 sch->scheme,
				 raw);
	}	    

	r = sch->handler->uh_parse_it_params(ret,raw_params,header_error);
	
	if (!r) {
	    failure2 = 1;

	    DPRINT(Debug,7,(&Debug,
			    "url_from_raw: Failed to parse params syntax\n"));
	}

	if (raw_params)
	    free_string(&raw_params);

	if (failure2)
	    goto failure;

	no_relative = 1;
    } else {
	/* No params */

	if (! no_relative &&
	    parent &&
	    sch->handler == parent->current_handler &&
	    parent->scheme == scheme_idx &&
	    0 != (sch->handler->flag & URLFLAG_params)) {
	    
	    int r;
	    
	    r = sch->handler->uh_dup_it_params(ret,parent);
	    
	    if (!r)
		goto failure;
	}       
    }

    /* Read next part for query */

    if (X < len &&
	0x003F /* ? */ == ch) {
	struct string * raw_query = NULL;
	int failure3 = 0;
	int r;

	startpos = ++X;
	ch = 0;


	for (; X < len; X++, ch = 0) {
	    ch = give_unicode_from_string(raw,X);
	    
	    if (0x0023 /* # */ == ch)
		break;			 /* fragment -- OK */
	}
	
	if (X < len) {
	    DPRINT(Debug,11,(&Debug,
			     "url_from_raw: query part ended with 0x%04x pos %d\n",
			     ch,X));
	}


	/* seventh part is params */
	L = X - startpos;

	DPRINT(Debug,7,(&Debug,
			"url_from_raw: %d - %d (len=%d) query found\n",
			X,startpos,L));


	raw_query = clip_from_string(raw,&startpos,L);
	
	if (startpos != X)
	    panic("URL PANIC",__FILE__,__LINE__,"url_from_raw",
		  "Clipping Error (query -part)",0);
	    
	DPRINT(Debug,11,(&Debug,
			 "url_from_raw: raw query=%S\n",raw_query));

	if (0 == (sch->handler->flag & URLFLAG_query)) {
	    DPRINT(Debug,2,(&Debug,
			    "url_from_raw: Query not allowed on %s scheme, but is given on URL: %S",
			    sch->scheme,
			    raw));
	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet,
					 MeQueryGivedUrl,
					 "Query not allowed on %s scheme, but is given on URL: %S"),
				 sch->scheme,
				 raw);
	}	    

	r = sch->handler->uh_parse_it_query(ret,raw_query,header_error);
	
	if (!r) {
	    failure3 = 1;

	    DPRINT(Debug,7,(&Debug,
			    "url_from_raw: Failed to parse query syntax\n"));
	}

	if (raw_query)
	    free_string(&raw_query);

	if (failure3)
	    goto failure;

	no_relative = 1;
    } else {
	/* No query */

	if (! no_relative &&
	    parent &&
	    sch->handler == parent->current_handler &&
	    parent->scheme == scheme_idx &&
	    0 != (sch->handler->flag & URLFLAG_query)) {
	    
	    int r;

	    r = sch->handler->uh_dup_it_query(ret,parent);
	    
	    if (!r)
		goto failure;
	}       
    }


    /* Read next part for fragment */

    if (X < len &&
	0x0023 /* # */ == ch) {
	struct string * raw_fragment = NULL;
	int failure4 = 0;
	int r;

	startpos = ++X;
	ch = 0;
#if 0
	for (; X < len; X++, ch = 0) {
	    ch = give_unicode_from_string(raw,X);

	    /* Dummy! */
	}
#else
	X = len;
#endif

	/* eighth part is fragment */
	L = X - startpos;

	DPRINT(Debug,7,(&Debug,
			"url_from_raw: %d - %d (len=%d) fragment found\n",
			X,startpos,L));


	raw_fragment = clip_from_string(raw,&startpos,L);
	
	if (startpos != X)
	    panic("URL PANIC",__FILE__,__LINE__,"url_from_raw",
		  "Clipping Error (fragment -part)",0);
	
	DPRINT(Debug,11,(&Debug,
			 "url_from_raw: raw fragment=%S\n",raw_fragment));


	ret->fragment = element_from_raw(raw_fragment);
	
	free_string(&raw_fragment);

    } else {

	if (! no_relative &&
	    parent &&
	    parent->fragment)
	    ret->fragment = dup_url_element(parent->fragment);

    }

    if (X < len) {
	DPRINT(Debug,2,(&Debug,
			"url_from_raw: Failed to parse scheme %s URL: %S\n",
			sch->scheme,
			raw));
	process_header_error(header_error,			     
			     CATGETS(elm_msg_cat, MeSet,
				     MeGeneralFailedUrl,
				     "Failed to parse scheme %s URL: %S"),
			     sch->scheme,
			     raw);
	
	goto failure;	    
    }

    if (buffer)
	free(buffer);

    return ret;

 failure:
    if (buffer)
	free(buffer);

    if (host_part)
	free_host_part(& host_part);

    if (path_part)
	free_url_path(& (path_part));

    if (ret)
	free_url(&ret);

    return NULL;
}

struct url * new_url(scheme)
     const char *scheme;
{
    struct url *ret = NULL;

    int scheme_idx = -1;
    struct scheme * sch = NULL;


    scheme_idx = name_to_scheme_idx(scheme);
    sch = index_to_scheme(scheme_idx);

    ret = alloc_url(sch->handler,scheme_idx);

    return ret;
}

void free_url(url)
     struct url **url;
{
    if (URL_magic != (*url)->magic)
	panic("URL PANIC",__FILE__,__LINE__,"free_url",
	      "Bad magic number",0);

    
    if ((*url)->host)
	free_host_part(& ((*url)->host));

    if ((*url)->path)
	free_url_path(& ((*url)->path));

    if ((*url)->fragment)
	free_url_element(& ((*url)->fragment));

    if (URL_handler_magic != (*url)->current_handler->magic)
	panic("URL PANIC",__FILE__,__LINE__,"free_url",
	      "Bad handler magic number",0);

    (*url)->current_handler->uh_free_it(*url);
	
    (*url)->magic = 0;   /* Invalidate */
    free(*url);
    *url = NULL;
}

enum url_type get_url_type(url)
     CONST struct url *url;
{
    if (URL_magic != url->magic)
	panic("URL PANIC",__FILE__,__LINE__,"get_url_type",
	      "Bad magic number",0);

    if (URL_handler_magic != url->current_handler->magic)
	panic("URL PANIC",__FILE__,__LINE__,"get_url_type",
	      "Bad handler magic number",0);

    return url->current_handler->uh_get_it_type(url);
}

enum url_type get_url_type_default(url)
     CONST struct url *url;
{
    if (URL_magic != url->magic)
	panic("URL PANIC",__FILE__,__LINE__,"get_url_type_default",
	      "Bad magic number",0);

    if (URL_handler_magic != url->current_handler->magic)
	panic("URL PANIC",__FILE__,__LINE__,"get_url_type_default",
	      "Bad handler magic number",0);

    return url->current_handler->url_type;
}



int set_mailing_headers_from_url(headers,url,mailer_info)
     struct mailing_headers *headers;
     CONST struct url *url;
     struct mailer_info  *mailer_info;
{
    if (URL_magic != url->magic)
	panic("URL PANIC",__FILE__,__LINE__,"set_mailing_headers_from_url",
	      "Bad magic number",0);

    if (URL_handler_magic != url->current_handler->magic)
	panic("URL PANIC",__FILE__,__LINE__,"set_mailing_headers_from_url",
	      "Bad handler magic number",0);

    return url->current_handler->uh_set_mailing_headers_from_it(headers,
								url,
								mailer_info);
}

struct text_block * give_text_body_from_url(url,quote_l,errors)
     CONST struct url *url;
     int quote_l;
     int *errors;
{
    CONST struct string     *body = NULL;
    struct text_block *ret  = NULL;
    int ERR = 0;

    if (URL_magic != url->magic)
	panic("URL PANIC",__FILE__,__LINE__,"set_mailing_headers_from_url",
	      "Bad magic number",0);
    
    if (URL_handler_magic != url->current_handler->magic)
	panic("URL PANIC",__FILE__,__LINE__,"set_mailing_headers_from_url",
	      "Bad handler magic number",0);
    

    body = url->current_handler->uh_get_body_string_from_it(url,&ERR);

    if (errors)
	*errors = ERR;

    if (!body)
	return NULL;

    ret = block_from_string(body,quote_l);
    
    return ret;
}

#ifdef REMOTE_MBX
struct folder_browser * get_browser_from_host_url(url,method,verify_remote)
     const struct url *url;
     const struct browser_url_method *method;
     verify_remote_url_f    * verify_remote;
{
    
    struct folder_browser * browser = NULL;

    CONST struct string  * user      = NULL;
    CONST struct string  * host1     = NULL;
    CONST struct string  * password  = NULL;

    char * host = NULL;
    uint16 port = 0;
    uint16 defport = 0;
    int    anon = 0;


    if (URL_magic != url->magic)
	panic("URL PANIC",__FILE__,__LINE__,"get_browser_from_host_url",
	      "Bad magic number",0);
    
    if (! url->host) {

	DPRINT(Debug,11,(&Debug,
			 "get_browser_from_host_url: No host part on url\n"));
	return NULL;
    }

    /* FIXME

       Non-ascii hostnames (ie. IDN) is not currently
       supported ....
    */
    
    if (url->host->host) {
	struct string * host2;

	host1 = parsed_from_element(url->host->host,NULL);
	if (!host1) {

	    DPRINT(Debug,11,(&Debug,
			     "get_browser_from_host_url: Bad host on imap url\n"));
	    
	    goto fail;

	}

	if (!  can_ascii_string(host1)) 
	    lib_error(CATGETS(elm_msg_cat, MeSet,
			      MeNonAsciiHostnameUrl,
			      "Non-ascii hostanames on url are not supported: %S"),
		      host1);

	/* NOTE: Original string is kept, if it can not converted
	         to US-ASCII -- then this produces garbage, but
		 user is warned
	*/
	host2 = ascify_string(host1);

	host = us2s(stream_from_string(host2,0,NULL));

	free_string(&host2);
    }

    if (url->host->user) {
	user = parsed_from_element(url->host->user,NULL);

	if (!user) {
	    DPRINT(Debug,11,(&Debug,
			     "get_browser_from_host_url: Bad user on url\n"));
	    
	    goto fail;	    
	}
    }

    if (url->host->password) {
	password = parsed_from_element(url->host->password,NULL);

	if (!user) {
	    DPRINT(Debug,11,(&Debug,
			     "get_browser_from_host_url: Bad password on url\n"));
	    
	    goto fail;	    
	}
    }

    port    = url->host->port;
    defport = browser_URL_default_port(method);

    if (user) {
	if (!verify_remote(url,user,host1,NULL,port,defport)) {
	    DPRINT(Debug,11,(&Debug,
			     "get_browser_from_host_url: Remote URL canceled\n"));
	    goto fail;
	}

	browser =  browser_connect_URL(method,
				       user,
				       password,
				       host,port);
    } else {
	struct string * anon_passwd = NULL;
	struct string * anon = format_string(FRM("anonymous"));

	if (password)
	    anon_passwd = dup_string(password);
	
	if (!verify_remote(url,anon,host1,&anon_passwd,port,defport)) {
	    DPRINT(Debug,11,(&Debug,
			     "get_browser_from_host_url: Remote URL canceled\n"));

	    free_string(&anon);
	    if (anon_passwd)
		free_string(&anon_passwd);

	    goto fail;
	}

	browser =  browser_connect_URL(method,
				       anon,
				       anon_passwd,
				       host,port);
	
	free_string(&anon);
	if (anon_passwd)
	    free_string(&anon_passwd);
	
    }

    if (!browser) {
 	DPRINT(Debug,11,(&Debug,
			 "get_browser_from_host_url: browser_connect_URL failed\n"));
    }

 fail:
    
    if (host)
	free(host);

    return browser;
}

int browser_select_url_path(browser,url,method)
     struct folder_browser *browser;
     CONST struct url *url;
     CONST struct browser_url_method *method;
{
    int len,i;
    struct string * elems;
    CONST struct url_path_elem *path_elem;
    CONST struct string  ** decoded_vector = NULL;
    int dlen;
    int ret = 0;

    if (URL_magic != url->magic)
	panic("URL PANIC",__FILE__,__LINE__,"browser_select_url_path",
	      "Bad magic number",0);
    
    if (! url->path) {

	DPRINT(Debug,11,(&Debug,
			 "get_browser_from_host_url: No path part on url\n"));
	return 0;
    }

    len = url_path_len(url->path);

    if (len < 1) {
	DPRINT(Debug,11,(&Debug,
			 "get_browser_from_host_url: No path elems\n"));
	return 0;
    }



   
    /* On absolute urls first elems (0) have
       -> elem == NULL and
       -> trailing_slash = 1
       this correspond / on beginning of URL 
    */


    if (! (path_elem = get_url_path_element(url->path,0)) ||
	path_elem->elem) {
	DPRINT(Debug,11,(&Debug,
			 "get_browser_from_host_url: Not absolute\n"));

	goto fail;
    }

    /* All elemnts 0 .. len-2  must have 
       -> trailing_slash = 1
    */
    for (i = 0; i < len-1; i++) {
	if (! (path_elem = get_url_path_element(url->path,i)) ||
	    ! path_elem->trailing_slash) {

	    DPRINT(Debug,11,(&Debug,
			     "get_browser_from_host_url: path elem [%d] Missing /\n",
			     i));
	    
	    goto fail;
	}
    }

    /* Last element most be
       -> trailing_slash = 0
    */
    
    if (! (path_elem = get_url_path_element(url->path,len-1)) ||
	path_elem->trailing_slash) {

	DPRINT(Debug,11,(&Debug,
			 "get_browser_from_host_url: must not be directory URL, path last elem [%d] have ending /\n",
			 len-1));
	goto fail;
    }


    dlen = len-1;

    if (dlen < 1) 
	panic("URL PANIC",__FILE__,__LINE__,
	      "get_browser_from_host_url",
	      "Bad dlen",0);

    decoded_vector = safe_malloc(dlen * sizeof (decoded_vector[0]));

    for (i = 0; i < dlen; i++) {

	if (i+1 >= len)
	    panic("URL PANIC",__FILE__,__LINE__,
		  "get_browser_from_host_url",
		  "Bad index",0);
	
	path_elem = get_url_path_element(url->path,i+1);
	if (!path_elem)
	    panic("URL PANIC",__FILE__,__LINE__,
		  "get_browser_from_host_url",
		  "failed to get path elem",0);

	decoded_vector[i] = parsed_from_element(path_elem->elem,NULL);
	
	if (! decoded_vector[i]) {
	    
	    DPRINT(Debug,11,(&Debug,
			     "get_browser_from_host_url: Failed to decode %d\n",
			     i+1));
	    goto fail;
	}
	
	
    }

    /* This is always absolute path -- from root as defined for URL */
    if (! select_item_from_URL(method,browser,
			       dlen,
			       decoded_vector)) {
	DPRINT(Debug,11,(&Debug,
			 "get_browser_from_host_url: failed to select url (dlen=%d)\n",
			 dlen));
       
	goto fail;
    }

    ret = 1;

 fail:
    if (decoded_vector)
     free(decoded_vector);
    
    DPRINT(Debug,11,(&Debug,
		     "get_browser_from_host_url=%d\n",ret));
    

    return ret;
}
#endif

struct folder_info * get_folder_from_url(url,remote_url)
     const struct url *url;
     verify_remote_url_f *remote_url;
{
    struct folder_info *folder;

    if (URL_magic != url->magic)
	panic("URL PANIC",__FILE__,__LINE__,"get_folder_from_url",
	      "Bad magic number",0);

    if (URL_handler_magic != url->current_handler->magic)
	panic("URL PANIC",__FILE__,__LINE__,"get_folder_from_url",
	      "Bad handler magic number",0);

    folder = url->current_handler->uh_get_folder_from_it(url,remote_url);

    return folder;
}

/* This gives aboslute URL even when original URL was relative */
struct string * raw_from_url(url)
     CONST struct url *url;
{
    struct string * ret = NULL;
    struct scheme * sch;
    charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);

    struct string * Sa = NULL;
    int r;

    if (!ascii_ptr)
	panic("CHARSET PANIC",__FILE__,__LINE__,"parsed_to_raw",
	      "US-ASCII not found",0);

    if (URL_magic != url->magic)
	panic("URL PANIC",__FILE__,__LINE__,"raw_from_url",
	      "Bad magic number",0);

    if (-1 == url->scheme) {
	DPRINT(Debug,7,(&Debug,"raw_from_url: Invalid scheme\n"));

	return NULL;
    }

    sch = index_to_scheme(url->scheme);

    ret = new_string(ascii_ptr);

    add_ascii_to_string(ret,cs2us(sch->scheme));   /* Add scheme name */
    add_ascii_to_string(ret,s2us(":"));
    
    if (URL_handler_magic != url->current_handler->magic)
	panic("URL PANIC",__FILE__,__LINE__,"parsed_to_raw",
	      "Bad handler magic number",0);

    if (url->host) {
	CONST struct string * R;

	add_ascii_to_string(ret,s2us("//"));

	if (url->host->user) {
	    CONST struct string * R = raw_from_element(url->host->user);
	    if (!R) {
		DPRINT(Debug,7,(&Debug,"raw_from_url: Failed to get username\n"));
		goto failure;
	    }
	    append_string(&ret,R);

	    if (url->host->password) {
		CONST struct string * R = 
		    raw_from_element(url->host->password);
		if (!R) {
		    DPRINT(Debug,7,(&Debug,"raw_from_url: Failed to get password\n"));
		    goto failure;
		}
		add_ascii_to_string(ret,s2us(":"));
		append_string(&ret,R);
	    }

	    add_ascii_to_string(ret,s2us("@"));
	}

	R = raw_from_element(url->host->host);
	if (!R) {
	    DPRINT(Debug,7,(&Debug,"raw_from_url: Failed to get hostname\n"));
	    goto failure;
	}
	append_string(&ret,R);

	if (url->host->port) {
	    struct string * S = format_string(FRM("%d"),
					      url->host->port);
	    add_ascii_to_string(ret,s2us(":"));
	    append_string(&ret,S);
	    free_string(&S);
	}

    } 

    if (url->path) {
	/* Path includes starting / -character */

	struct string * S = url_path_to_raw(url->path);

	if (!S) {
	    DPRINT(Debug,7,(&Debug,"raw_from_url: Failed to get path\n"));
	    goto failure;

	}

	append_string(&ret,S);
	free_string(&S);

    } else if (0 == (sch->handler->flag & URLFLAG_path)) {

	struct string * S = sch->handler->uh_not_path_to_raw_it(url);

	if (!S) {
	    DPRINT(Debug,7,(&Debug,"raw_from_url: Failed to get not-path\n"));
	    goto failure;

	}

	append_string(&ret,S);
	free_string(&S);
    }


    r = sch->handler->uh_params_to_raw_it(url,&Sa);
    if (Sa) {
	add_ascii_to_string(ret,s2us(";"));

	append_string(&ret,Sa);
	free_string(&Sa);
    }
    if (!r && 0 != (sch->handler->flag & URLFLAG_params)) {
	DPRINT(Debug,7,(&Debug,"raw_from_url: Failed to get params\n"));
	goto failure;
    }


    r = sch->handler->uh_query_to_raw_it(url,&Sa);
    if (Sa) {
	add_ascii_to_string(ret,s2us("?"));

	append_string(&ret,Sa);
	free_string(&Sa);
    }
    if (!r && 0 != (sch->handler->flag & URLFLAG_query)) {
	DPRINT(Debug,7,(&Debug,"raw_from_url: Failed to get query\n"));
	goto failure;
    }

    if (url->fragment) {
	CONST struct string * S = 
	    raw_from_element(url->fragment);

	if (!S) {
	    DPRINT(Debug,7,(&Debug,"raw_from_url: Failed to get fragment\n"));
	    goto failure;
	}

	add_ascii_to_string(ret,s2us("#"));
	append_string(&ret,S);
    }

    return ret;

 failure:
    free_string(&ret);
    return NULL;    
}


/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1