static char rcsid[] = "@(#)$Id: mime_param.c,v 1.18 2006/05/07 08:35:31 hurtta Exp $";

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

#include "def_melib.h"

#include "s_me.h"


DEBUG_VAR(Debug,__FILE__,"mime");


static void free_parsed P_((struct mime_param *P));
static void free_parsed(P)
     struct mime_param *P;
{
    if (P->ascii_params) {
	int i;

	for (i = 0; i < P->ascii_param_count; i++) {
	    if (P->ascii_params[i].param) {
		free(P->ascii_params[i].param);
		P->ascii_params[i].param = NULL;
	    }

	    if (P->ascii_params[i].value) {
		free(P->ascii_params[i].value);
		P->ascii_params[i].value = NULL;
	    }
	}

	free(P->ascii_params);
	P->ascii_params = NULL;
    }
    P->ascii_param_count =0;

    if (P->string_params) {
	int i;

	for (i = 0; i < P->string_param_count; i++) {
	    if (P->string_params[i].param) {
		free(P->string_params[i].param);
		P->string_params[i].param = NULL;
	    }

	    if (P->string_params[i].value) {
		free_string(& (P->string_params[i].value));
	    }
	}
	
	free(P->string_params);
	P->string_params = NULL;
    }
    P->string_param_count =0;
}

static void free_raw P_((struct mime_param *P));
static void free_raw(P)
     struct mime_param *P;
{
    if (P->params) {
	int i;
	
	for (i = 0; i < P->param_count; i++) {
	    if (P->params[i].raw_param) {
		free(P->params[i].raw_param);
		P->params[i].raw_param = NULL;
	    }

	    if (P->params[i].raw_value) {
		free(P->params[i].raw_value);
		P->params[i].raw_value = NULL;
	    }
	}

	free(P->params);
	P->params = NULL;
    }
    P->param_count =0;
}

void free_mime_param(x)
     struct mime_param **x;
{
    struct mime_param *P = *x;

    if (MIME_PARAM_magic != P->magic)
	mime_panic(__FILE__,__LINE__,"free_mime_param",
		   "Bad magic number");

    free_raw(P);
    free_parsed(P);


    free(P);
    P = NULL;

    *x = P;
}

static struct mime_param * alloc_mime_param P_((void));
static struct mime_param * alloc_mime_param()
{
    struct mime_param *P = safe_malloc(sizeof (*P));

    bzero((void *)P, sizeof (*P));

    P->magic = MIME_PARAM_magic;

    P->params      = NULL;
    P->param_count = 0;

    P->def         = NULL;


    P->ascii_params      = NULL;
    P->ascii_param_count = 0;

    P->string_params      = NULL;
    P->string_param_count = 0;

    return P;
}

struct mime_param * copy_mime_param(src)
     struct mime_param *src;
{
    struct mime_param *P = alloc_mime_param();

    P->def         = src->def;


    if (src->params) {
	int i;

	P->params = safe_malloc(src->param_count * sizeof (P->params[0]));

	for (i = 0; i < src->param_count; i++) {
	    if (src->params[i].raw_param) 
		P->params[i].raw_param = safe_strdup(src->params[i].raw_param);
	    if (src->params[i].raw_value) 
		P->params[i].raw_value = safe_strdup(src->params[i].raw_value);
	}
	P->param_count = src->param_count;	
    }


    /* These params are rebuild from raw params */

    P->ascii_params      = NULL;
    P->ascii_param_count = 0;

    P->string_params      = NULL;
    P->string_param_count = 0;

    return P;
}

static struct ascii_pair * add_ascii_pair_1 P_((struct ascii_pair **list, int *len));
static struct ascii_pair * add_ascii_pair_1(list,len)
     struct ascii_pair **list;
     int *len;
{
    struct ascii_pair * ascii_params = *list;
    int                 ascii_param_count = *len;
    struct ascii_pair * ret = NULL;

    ascii_params = safe_realloc(ascii_params,
				   (ascii_param_count+1) *
				   sizeof(ascii_params[0]));

    bzero((void *)& (ascii_params[ascii_param_count]),
	  sizeof ((ascii_params[ascii_param_count])));

    ascii_params[ascii_param_count].is_compat = 0;
    ascii_params[ascii_param_count].param     = NULL;
    ascii_params[ascii_param_count].value     = NULL;

    ret = (&(ascii_params[ascii_param_count++]));

    *list = ascii_params;
    *len  = ascii_param_count;
    
    return ret;
}

static struct ascii_pair * add_ascii_pair P_((struct mime_param *P));
static struct ascii_pair * add_ascii_pair(P)
     struct mime_param *P;
{
    return add_ascii_pair_1( & (P->ascii_params),
			     & (P->ascii_param_count) );

}

static struct string_pair * add_string_pair_1 P_((struct string_pair **list,
						  int *len));
static struct string_pair * add_string_pair_1(list,len)
     struct string_pair **list;
     int *len;
{
    struct string_pair *ret;
    struct string_pair * string_params = *list;
    int string_param_count             = *len;

    string_params = safe_realloc(string_params,
				 (string_param_count +1) *
				 sizeof(string_params[0]));

    bzero((void *)& (string_params[string_param_count]),
	  sizeof ((string_params[string_param_count])));

    string_params[string_param_count].param     = NULL;
    string_params[string_param_count].value     = NULL;

    ret = (&(string_params[string_param_count++]));
   
    *list = string_params;
    *len  = string_param_count;

    return ret;
}

static struct string_pair * add_string_pair P_((struct mime_param *P));
static struct string_pair * add_string_pair(P)
     struct mime_param *P;
{
    return add_string_pair_1 (& (P->string_params),
			      & (P->string_param_count));

}

static struct mp_pair * add_mp_pair_1 P_((struct mp_pair **list, int *len));
static struct mp_pair * add_mp_pair_1(list,len)
     struct mp_pair **list;
     int *len;
{
    struct mp_pair *params = *list;
    int param_count        = *len;
    struct mp_pair *ret = NULL;

    params = safe_realloc(params,
			     (param_count +1) * sizeof (params[0]));

    bzero ((void *) &(params[param_count]), sizeof (params[param_count]));
    
    params[param_count].raw_param = NULL;
    params[param_count].raw_value = NULL;

    ret =  (& (params[ param_count++]));

    *list = params;
    *len  = param_count;

    return ret;
}

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

static struct mp_pair * add_mp_pair P_((struct mime_param *P));
static struct mp_pair * add_mp_pair(P)
     struct mime_param *P;
{
    return add_mp_pair_1(& (P->params), &(P->param_count));
}

static int append_encoded P_((struct string *buffer,char *value));
static int append_encoded(buffer,value)
     struct string *buffer;
     char *value;
{
    unsigned char *p;

    for (p = us_str(value); *p; p++) {
	if ('\'' == *p || '"' == *p) {
	    DPRINT(Debug,9,(&Debug, 
			    "append_encoded: bad character %c, value=%s\n",
			    *p,value));

	    return 0;
	}

	if ('%' == *p) {
	    int v1, v2;
	    unsigned char v;

	    p++;
	    if (! *p) {
		DPRINT(Debug,9,(&Debug, 
				"append encoded: short %% sequence, value=%s\n",
				value));

		return 0;
	    } 
	    v1 = hex(*p);
	    

	    p++;	    
	    if (! *p) {
		DPRINT(Debug,9,(&Debug, 
				"append encoded: short %% sequence, value=%s\n",
				value));
		return 0;	    
	    }

	    v2 = hex(*p);

	    if (v1 < 0 || v2 < 0) {
		DPRINT(Debug,9,(&Debug, 
				"append encoded: bad %% sequence, value=%s\n",
				value));		
		return 0;
	    }

	    v = ( v1 << 4) | v2;

	    if (! add_streambyte_to_string(buffer,v)) {
		DPRINT(Debug,9,(&Debug, 
				"append encoded: bad character %%%02x, value=%s\n",
				v,value));

		return 0;
	    }

	} else {
	    if (! add_streambyte_to_string(buffer,(unsigned char)*p)) {
		DPRINT(Debug,9,(&Debug, 
				"append encoded: bad character %c, value=%s\n",
				*p,value));
		return 0;
	    }
	}
    }
    
    return 1;
}

static struct string * parse_encoded_first P_((char *value));
static struct string * parse_encoded_first(value)
     char *value;
{
    struct string *ret = NULL;
    int l  = strlen(value);
    char * buffer  = safe_malloc(l+1);   
    char * lang = NULL;
    char * cs   = NULL;
    charset_t cs1 = NULL;

    int    count = 0;

    char *p1,*p2;

    /* character set */
    cs = buffer;

    for (p1 = value, p2 =buffer; 
	 *p1 && '\'' != *p1 && p2 <= buffer + l;
	 p1++) {

	*p2++ = *p1;
    }
    *p2 = '\0';

    if ('\'' != *p1) {
	DPRINT(Debug,9,(&Debug, "parse_encoded_first: Missing ': %s\n",value));
	goto fail;
    }
    p1++;

    if (p2 == cs)
	cs = NULL;

    /* language */
    p2++;
    lang = p2;

    for (;
	 *p1 && '\'' != *p1 && p2 <= buffer + l;
	 p1++) {

	*p2++ = *p1;
    }
    *p2 = '\0';

    if ('\'' != *p1) {
	DPRINT(Debug,9,(&Debug, "parse_encoded_first: Missing second ': %s\n",
			value));
	goto fail;
    }
    p1++;


    if (p2 == lang)
	lang = NULL;

    if (cs) {
	DPRINT(Debug,9,(&Debug, "parse_encoded_first: cs=%s\n",cs));
	cs1 = MIME_name_to_charset(cs,CHARSET_create);
    } else
	cs1 = RAW_BUFFER;   /* charset is not relevant to parameter value! */


    if (lang) {
	DPRINT(Debug,9,(&Debug, "parse_encoded_first: lang=%s\n",lang));
	ret = new_langstring(cs1,lang);
    } else
	ret = new_string(cs1);

    if (!append_encoded(ret,p1)) {

	DPRINT(Debug,9,(&Debug, 
			"parse_encoded_first: parse error .. ignoring result\n"));
	free_string(&ret);
    }

 fail:
    free(buffer);

    return ret;
}


#define MAX_POSITIONAL   100

static void parse_raw_params P_((struct mime_param *P));
static void parse_raw_params(P)
     struct mime_param *P;
{
    struct positional_params {
	char *name;
	int  bad;

	struct loc_pos {
	    int is_new;
	    int loc;
	}   *locations ;
	int  location_count;
    }  * positional = NULL;
    int  positional_count = 0;

    free_parsed(P);

    /* Pass 1 look item 0 on parameters */

    if (P->params) {
	int i;

	for (i = 0; i < P->param_count; i++) {
	    char * name = P->params[i].raw_param;
	    char * p;
	    
	    if (!name)
		continue;
	    if (!name[0]) {
		DPRINT(Debug,9,(&Debug, 
				"parse_raw_params: %d: parameter without name\n",
				i));
		continue;
	    }
	    
	    
	    for (p = name; *p; p++) {
		if ('*' == *p) 
		    break;
	    }
	    
	    if ('\0' == *p) {
		char *x;
		
		/* compat parameter */
		
		/* Check for 8-bit */
		
		if (! P->params[i].raw_value) {
		    
		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: %d: %s: compat parameter without value\n",
				    i,name));
		    
		    
		    continue;
		}
		
		for (x = P->params[i].raw_value; *x; x++) {
		    if (0 != (*x & 0x80))
			break;
		}
		
		if ('\0' == *x) {
		    /* Ascii value -- really compat */
		    
		    struct ascii_pair * A = add_ascii_pair(P);
		    int l = x - P->params[i].raw_value;
		    
		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: *** %d: %s=%s\n",
				    i,name,P->params[i].raw_value));

		    A->is_compat = 1;
		    
		    A->param     = safe_strdup(name);
		    
		    A->value = dequote_opt(P->params[i].raw_value,l);
		    
		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: [compat] %s=%s\n",
				    A->param,
				    A->value));

		} else {
		    
		    struct string_pair * S = add_string_pair(P);
		    char * val;
		    int l;
		    
		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: %d: %s: 8-bit data on compat parameter\n",
				    i,name));
		    
		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: *** %d: %s=%s\n",
				    i,name,P->params[i].raw_value));
		    
		    if (! P->def ) {
			DPRINT(Debug,9,(&Debug, 
					"parse_raw_params: NO DEFAULT CHARSET -- parameter ignored\n"));
			continue;			
		    }

		    S->param     = safe_strdup(name);
		    
		    while (*x)
			x++;
		    l = x - P->params[i].raw_value;
		    
		    val = dequote_opt(P->params[i].raw_value,l);
		    
		    S->value = new_string2(P->def,us_str(val));
		    
		    free(val);

		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: [8-bit] %s=%S\n",
				    S->param,
				    S->value));

		}
		
		
	    } else {
		
		int is_new = 0;
		int is_partial = 0;
		int index = 0;
		int l = p - name;
		char * name1 = safe_malloc(l+1);

		strncpy(name1,name,l);
		name1[l] = '\0';
		
		p++;
		if ('\0' == *p ) {
		    /* '*' was last character on name */

		    is_new = 1;

		} else if (*p >= '0' && *p <= '9') {
		    char *end = NULL;
		    long x;
		    is_partial = 1;
		    
		    x= strtol(p,&end,10);
		    
		    if (x > MAX_POSITIONAL) {
			
			DPRINT(Debug,9,(&Debug, 
					"parse_raw_params: %d: %s: On paramater %s postion value %ld is too big (max %d)\n",
					i,name,name1,x,
					MAX_POSITIONAL));
			
			goto fail1;
		    }
		    index = x;
		    
		    if ('\0' != *end &&
			'*' != *end) {
			
			DPRINT(Debug,9,(&Debug, 
					"parse_raw_params: %d: %s: On paramater %s after position value %ld there is garbage: %s\n",
					i,name,name1,x,end));
			
			goto fail1;
			
		    }
		    
		    p = end;
		}
		
		if ('*' == *p) {
		    is_new = 1;
		    
		    p++;
		}
		
		if (*p) {
		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: %d: %s: On paramater %s there is garbage after '*': %s\n",
				    i,name,name1,p));
		    goto fail1;
		}
		
		if (is_partial) {
		    
		    int x;
		    
		    for (x = 0; x < positional_count; x++) {
			if (0 == istrcmp(name1,positional[x].name))
			    break;
		    }
		    
		    if (x >= positional_count) {
			positional = safe_realloc(positional,
						  (x+1) * sizeof(positional[0]));
			positional[x].name = name1;  name1 = NULL;
			positional[x].bad = 0;
			
			positional[x].locations = NULL;
			positional[x].location_count = 0;
			positional_count = x+1;
		    }
		    
		    if (index >= positional[x].location_count) {
			int y;
			
			positional[x].locations = safe_realloc(positional[x].locations,
							       (index+1) * 
							       sizeof(positional[x].locations[0]));
			for (y = positional[x].location_count; y <= index; y++) {
			    positional[x].locations[y].is_new = 0;
			    positional[x].locations[y].loc    = -1;
			}		    
			positional[x].location_count = index+1;
		    }
		    
		    if (index < 0) {
			DPRINT(Debug,9,(&Debug, 
					"parse_raw_params: %d: %s: On paramater %s position value %ld is negative\n",
					i,name,positional[x].name,index));
			positional[x].bad = 1;
			goto fail1;
		    }
		    
		    if (positional[x].locations[index].loc != -1) {
			DPRINT(Debug,9,(&Debug, 
					"parse_raw_params: %d: %s: On paramater %s position value %ld is duplicate\n",
					i,name,positional[x].name,index));
			positional[x].bad = 1;
			goto fail1;
			
		    }
		    
		    positional[x].locations[index].loc    = i;
		    positional[x].locations[index].is_new = is_new;
		    
		} else {
		    /* Not a partial value */
		    char *value = P->params[i].raw_value;
		    struct string * X;
		    struct string_pair * S;
		    
		    if (!is_new)
			mime_panic(__FILE__,__LINE__,"parse_raw_params",
				   "compat parameters should be handled already");
		    
		    if (! value) {
			DPRINT(Debug,9,(&Debug, 
					"parse_raw_params: %d: %s: parameter %s without value\n",
					i,name,name1));
			
			goto fail1;
		    }
		    
		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: *** %d: %s=%s\n",
				    i,name,P->params[i].raw_value));

		    X = parse_encoded_first(value);
		    
		    if (!X) {
			goto fail1;
		    }
		    
		    S = add_string_pair(P);
		    S->param     = name1;   name1 = NULL;
		    S->value     = X;		

		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: [string] %s=%S\n",
				    S->param,
				    S->value));

		}
		
	    fail1:
		if (name1)
		    free(name1);
				
	    }
	}
    }

    /* Pass 2 look positional arguments */

    if (positional) {
	int x;

	for (x = 0; x < positional_count; x++) {
	    int y;
	    int new_seen = 0;

	    for (y = 0; y < positional[x].location_count; y++) {

		if (positional[x].locations[y].loc == -1) {
		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: On paramater %s position value %d is missing\n",
				    positional[x].name,y));
		    positional[x].bad = 1;
		    break;
		}

		if (positional[x].locations[y].loc < 0 ||
		    positional[x].locations[y].loc >= P->param_count)
		    mime_panic(__FILE__,__LINE__,"parse_raw_params",
			       "Bad location index");

		new_seen |=  positional[x].locations[0].is_new;
	    }

	    if (positional[x].location_count < 1)
		mime_panic(__FILE__,__LINE__,"parse_raw_params",
			   "Inbossible location count");
		
	    if (positional[x].bad) 
		continue;
		
	    if (new_seen) {
		struct string_pair * S = NULL;

		/* Handle first parameter */

		if (positional[x].locations[0].is_new) {
		    int i = positional[x].locations[0].loc;
		    char *value = P->params[i].raw_value;
		    struct string * X;

		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: *** %d: %s=%s\n",
				    i,
				    P->params[i].raw_param,
				    P->params[i].raw_value));
		    
		    
		    X = parse_encoded_first(value);
		    
		    if (!X) {
			goto fail2;
		    }
		    		    
		    S = add_string_pair(P);
		    S->param     = safe_strdup(positional[x].name);
		    S->value     = X;		


		} else {
		    int i = positional[x].locations[0].loc;
		    char *value = P->params[i].raw_value;
		    int l = strlen(value);
		    char *val;

		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: *** %d: %s=%s\n",
				    i,
				    P->params[i].raw_param,
				    P->params[i].raw_value));

		    val = dequote_opt(value,l);
		    
		    S          = add_string_pair(P);
		    S->param   = safe_strdup(positional[x].name);		    

		    /*  FIXME   ???  Is this correct ??? */
		    S->value   = new_string2(RAW_BUFFER,us_str(val));
		    
		    free(val);

		}

		/* Handle rest of parameters */

		for (y = 1; y < positional[x].location_count; y++) {
					    
		    int i = positional[x].locations[y].loc;
		    char *value = P->params[i].raw_value;

		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: *** %d: %s=%s\n",
				    i,
				    P->params[i].raw_param,
				    P->params[i].raw_value));

		    if (positional[x].locations[x].is_new) {

			if (!append_encoded(S->value,value))
			    goto failX;


		    } else {
			char * val;
			int l1;
			int errors = 0;

			int l = strlen(value);
		   
			val = dequote_opt(value,l);
			l1 = strlen(val);

			if (!add_streambytes_to_string(S->value,l1,us_str(val),&errors)) {

			    free(val);
			    goto failX;
			}
						       
			free(val);

			DPRINT(Debug,9,(&Debug, 
					"parse_raw_params: [added %d] %s=%S\n",
					x,
					S->param,
					S->value));

		    }

		}

		DPRINT(Debug,9,(&Debug, 
				"parse_raw_params: [splitted string] %s=%S\n",
				S->param,
				S->value));


		if (0) {
		failX:

		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: %s: Failed to parse value\n",
				    S->param));

		    free_string(& (S->value));
		}
		

	    } else {

		struct ascii_pair * A = add_ascii_pair(P);

		char *p;

		A->is_compat     = 0;
		A->param         = safe_strdup(positional[x].name);		    
		A->value         = NULL;
		


		for (y = 0; y < positional[x].location_count; y++) {
					    
		    int i = positional[x].locations[y].loc;
		    char *value = P->params[i].raw_value;
		    int l = strlen(value);
		    char *val;

		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: *** %d: %s=%s\n",
				    i,
				    P->params[i].raw_param,
				    P->params[i].raw_value));

		    if (positional[x].locations[x].is_new)
			mime_panic(__FILE__,__LINE__,"parse_raw_params",
				   "Inbossible encoding");



		    val = dequote_opt(value,l);
		    
		    A->value = strmcat(A->value,val);

		    free(val);
		}

		DPRINT(Debug,9,(&Debug, 
				"parse_raw_params: [splitted ascii] %s=%s\n",
				A->param,
				A->value));


		for (p = A->value; *p; p++) {
		    if (0 != (*p & 0x80))
			break;
		}

		if (*p) {
		    struct string_pair * S;
		    
		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: %s: 8-bit data on parameter\n",
				    A->param));
		    
		    if (! P->def ) {
			DPRINT(Debug,9,(&Debug, 
					"parse_raw_params: NO DEFAULT CHARSET -- parameter ignored\n"));

			/* Destroy non-ascii value */
			free(A->value); A->value = NULL;

			continue;			
		    }

		    S = add_string_pair(P);

		    S->param     = safe_strdup(A->param);
		    S->value = new_string2(P->def,us_str(A->value));

		    /* Destroy non-ascii value */
		    free(A->value); A->value = NULL;

		    DPRINT(Debug,9,(&Debug, 
				    "parse_raw_params: [fixup string] %s=%S\n",
				    S->param,
				    S->value));

		}
		
	    }
	 
	fail2:  ;
	    
	}
    }

    /* Cleanup */

    if (positional) {
	int i;

	for (i = 0; i < positional_count; i++) {
	    if (positional[i].locations) {
		free(positional[i].locations);
		positional[i].locations = NULL;
	    }
	    positional[i].location_count = 0;
	}
	free(positional);
    }
}


struct mime_param * parse_mime_param(headername,value,def,header_error)
     CONST char *headername;
     CONST char *value;
     charset_t def;
     struct header_errors **header_error;
{
    CONST char *ptr;


    struct mime_param *P = alloc_mime_param();

    DPRINT(Debug,9,(&Debug,"parse_mime_param: headername=%s value=%s\n",
		    headername ? headername : "<not given>",
		    value));
    
    P->def         = def;

    /* It is assumed that comments are already removed
       with rfc822_reap_comments()
    */

    for (ptr = value; *ptr; ) {
	CONST char * start;
	CONST char * last;
	int len;
	struct mp_pair * mp = NULL;

	while (*ptr && isascii(*ptr) && isspace(*ptr))
	    ptr++;

	if (!*ptr)
	    break ;

	start = ptr;
	last  = NULL;

	/* Get param name */
       	
	while (*ptr) {

	    if (isascii(*ptr) && isspace(*ptr)) {
		if (!last)
		    last = ptr;
	    } else if ('=' == *ptr) {
		if (!last)
		    last = ptr;
		break;
	    } else if ('"' == *ptr) {
		if (!last)
		    last = ptr;
		break;		
	    } else if (';' == *ptr) {
		
		if (!last)
		    last = ptr;
		break;		
	    } else if (last) {

		break;
	    }

	    ptr++;
	}

	if (!last)
	    last = ptr;

	while (*ptr && isascii(*ptr) && isspace(*ptr))
	    ptr++;

	if ('=' == *ptr)
	    ptr++;
	else {
	    if (headername)
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeMissingEqual,
					     "PARSE ERROR: Missing = on %s header"),
				     headername);
	}

	if (last == start) {
	    if (headername)
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeMissingName,
					     "PARSE ERROR: Missing name of param on %s header"),
				     headername);
	    
	}
	
	len = last-start;

	mp = add_mp_pair(P);

	mp->raw_param = safe_malloc(len+1);

	strncpy(mp->raw_param,start,len);
	mp->raw_param[len] = '\0';

	DPRINT(Debug,9,(&Debug,"parse_mime_param: [%d] param=%s\n",
			P->param_count-1,mp->raw_param));


	while (*ptr && isascii(*ptr) && isspace(*ptr))
	    ptr++;
	
	start = ptr;
	last  = NULL;

	if ('"' == *ptr) {
	    
	    ptr++;
	    while (*ptr && '"' != *ptr) {

		if ('\\' == *ptr) {
		    ptr++;

		    if (!*ptr) {
			if (headername)
			    process_header_error(header_error,
						 CATGETS(elm_msg_cat, MeSet,
							 MeTrailingBackslashHeader,
							 "PARSE ERROR: Trailing \\ on %s header"),
						 headername);
			
			break;	      
		    }
		}
		ptr++;
	    }

	    if ('"' != *ptr) {
		if (headername)
		    process_header_error(header_error,
					 CATGETS(elm_msg_cat, MeSet,
						 MeMissingQuote,
						 "PARSE ERROR: Missing ending \" on %s header"),
					 headername);
	    } else
		ptr++;


	} else {

	    while (*ptr) {
		if (isascii(*ptr) && isspace(*ptr)) {
		    if (!last)
			last = ptr;
		} else if ('=' == *ptr) {
		    if (headername)
			process_header_error(header_error,
					     CATGETS(elm_msg_cat, MeSet,
						     MeEqualOnValue,
						     "PARSE ERROR: Missing ; or = on param value on %s header"),
					     headername);
		} else if ('"' == *ptr) {
		    if (!last)
			last = ptr;
		    break;		
		} else if (';' == *ptr) {
		    if (!last)
			last = ptr;
		    break;		
		} else if (last) {
		    if (headername)
			process_header_error(header_error,
					     CATGETS(elm_msg_cat, MeSet,
						     MeSpaceOnValue,
						     "PARSE ERROR: Missing ; or space on param value on %s header"),
					     headername);
		    last = NULL;
		}
		
		ptr++;
	    }
	}

	if (!last)
	    last = ptr;

	while (*ptr && isascii(*ptr) && isspace(*ptr))
	    ptr++;


	if (';' == *ptr)
	    ptr++;
	else if (*ptr) {
	    if (headername)
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeMissingSemicolonHeader,
					     "PARSE ERROR: Missing ; on %s header"),
				     headername);
	}


	if (last == start) {
	    if (headername)
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet,
					     MeMissingValue,
					     "PARSE ERROR: Missing value of param on %s header"),
				     headername);
	    
	}

	len = last-start;

	mp->raw_value = safe_malloc(len+1);

	strncpy(mp->raw_value,start,len);
	mp->raw_value[len] = '\0';
	
	DPRINT(Debug,9,(&Debug,"parse_mime_param: [%d] value=%s\n",
			P->param_count-1,mp->raw_value));
	
	 
    }

    return P;
}

static CONST char *find_ascii_param P_((struct mime_param *P,
					char *name,
					int compat));
static CONST char *find_ascii_param(P,name,compat)
     struct mime_param *P;
     char *name;
     int compat;
{
    int i;
    
    for (i = 0; i < P->ascii_param_count; i++) {
	if (P->ascii_params[i].is_compat == compat &&
	    P->ascii_params[i].param &&
	    0 == istrcmp(P->ascii_params[i].param,name))
	    return P->ascii_params[i].value;
    }
    return NULL;
}

static CONST struct string *find_string_param P_((struct mime_param *P,
						  char *name));
static CONST struct string *find_string_param(P,name)
     struct mime_param *P;
     char *name;
{
    int i;
    
    for (i = 0; i < P->string_param_count; i++) {
	if (P->string_params[i].param &&
	    0 == istrcmp(P->string_params[i].param,name))
	    return P->string_params[i].value;
    }
    return NULL;
}

CONST char * get_mime_param_compat(P,name)
     struct mime_param *P; 
     char *name;
{
    if (!P)
	return NULL;

    if (MIME_PARAM_magic != P->magic)
	mime_panic(__FILE__,__LINE__,"get_mime_param_compat",
		   "Bad magic number");
    
    if (! P->ascii_params && ! P->string_params)
	parse_raw_params(P);

    if (P->ascii_params) 
	return find_ascii_param(P,name,1);

    DPRINT(Debug,9,(&Debug, "get_mime_param_compat: No parameters\n"));

    return NULL;
}

CONST char * get_mime_param_ascii(P,name)
     struct mime_param *P; 
     char *name;
{
    int i;
   
    if (!P)
	return NULL;


    if (MIME_PARAM_magic != P->magic)
	mime_panic(__FILE__,__LINE__,"get_mime_param_ascii",
		   "Bad magic number");
	
    
    if (! P->ascii_params && ! P->string_params)
	parse_raw_params(P);

    if (P->ascii_params) {
	CONST char *found = find_ascii_param(P,name,0);
	
	if (found)
	    return found;

	return find_ascii_param(P,name,1);
    }

    DPRINT(Debug,9,(&Debug, "get_mime_param_ascii: No parameters\n"));

    return NULL;
}


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


CONST struct string * get_mime_param(P,name)
     struct mime_param *P; 
     char *name;
{
    if (!P)
	return NULL;

    if (MIME_PARAM_magic != P->magic)
	mime_panic(__FILE__,__LINE__,"get_mime_param",
		   "Bad magic number");
    
    if (! P->ascii_params && ! P->string_params)
	parse_raw_params(P);

    if (P->string_params) {
	CONST struct string *found = find_string_param(P,name);

	if (found)
	    return found;
    }
	
    if (P->ascii_params) {
	charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);

	CONST char *found = find_ascii_param(P,name,0);
	struct string_pair *S;

	if (!found)
	    found = find_ascii_param(P,name,1);

	if (!found)
	    return NULL;

	if (!ascii_ptr)
	    mime_panic(__FILE__,__LINE__,"get_mime_param",
		       "US-ASCII not found");

	S        = add_string_pair(P);
	S->param = safe_strdup(name);
	S->value = new_string2(ascii_ptr,cus_str(found));

	return S->value;
    }

    DPRINT(Debug,9,(&Debug, "get_mime_param: No parameters\n"));

    return NULL;
}

static int ascii_need_quote P_((char *value));
static int ascii_need_quote(value)
     char *value;
{
    char *p;

    if (! value[0])
	return 1;

    for (p = value; *p; p++) {
	if (*p <= ' ')
	    return 1;

	if (*p >= '"' &&
	    *p <= '/')
	    return 1;

	if (*p >= ':' &&
	    *p <= '?')
	    return 1;

	if (*p == '\\')
	    return 1;       
    }

    return 0;
}

static int string_is_ascii_control P_((struct string * value));
static int string_is_ascii_control(value)
     struct string * value;
{
    int len = string_len(value);
    int i;

    for (i = 0; i < len; i++) {
	uint16 code = give_unicode_from_string(value,i);

	if (code <= 0x0020  /* space */ )
	    return 1;

	if (code >= 0x007F  /* DEL */)
	    return 1;
    }

    return 0;
}

static int string_need_quote P_((struct string * value));
static int string_need_quote(value)
     struct string * value;
{
    int len = string_len(value);
    int i;
    
    if (0 == len)
	return 1;

    for (i = 0; i < len; i++) {
	uint16 code = give_unicode_from_string(value,i);

	if (code <= 0x0020  /* space */ )
	    return 1;

	if (code >= 0x0022  /*  " */  &&
	    code <= 0x002F  /* / */)
	    return 1;

	if (code >= 0x003A  /*  : */  &&
	    code >= 0x003F  /*  ? */)
	    return 1;
	
	switch(code) {
	    
	case 0x005C  /* \ */:
	    return 1;	    
	}
    }

    return 0;
}

static void append_quoted_to_string P_((struct string ** ret,
					struct string * value));
static void append_quoted_to_string(ret,value)
     struct string ** ret;
     struct string * value;
{
    charset_t cs = value->string_type;
    int len = string_len(value);
    int i;

    struct string * tmp = new_string(cs);

    add_ascii_to_string(tmp,us_str("\""));
    
    for (i = 0; i < len; i++) {
	uint16 code = give_unicode_from_string(value,i);

	if (0x0022  /*  "  */   == code &&
	    0x005C  /*  \  */ == code)
	    add_ascii_to_string(tmp,us_str("\\"));

	add_unicode_to_string(tmp,1,&code);
    }

    add_ascii_to_string(tmp,us_str("\""));
    
    append_string(ret,tmp);
    
    free_string(&tmp);
}

struct string * unquote_string P_((struct string * value));
struct string * unquote_string(value)
     struct string * value;
{
    charset_t cs = value->string_type;
    int len = string_len(value);
    int i;
    int q = 0;

    struct string * ret = new_string(cs);

    for (i = 0; i < len; i++) {
	uint16 code = give_unicode_from_string(value,i);

	if (0x0022  /*  "  */   == code) {
	    q = !q;
	    continue;
	}

	/* Recognize \ escape only in quoted strings */
	if (0x005C  /*  \  */ == code && q) {
	    i++;

	    if (i < len)
		code = give_unicode_from_string(value,i);
	    else {
		lib_error(CATGETS(elm_msg_cat, MeSet,
				  MeTrailingBackslashOnString,
				  "Trailing Backslash (\\) on %S"),
			  value);
		break;

	    }
	}
	
	add_unicode_to_string(ret,1,&code);
    }
	
    if (q)
	lib_error(CATGETS(elm_msg_cat, MeSet,
			  MeMissingEndQuote,
			  "Missing ending quote (\") on %S"),
		  value);

    return ret;
}

static void add_param_string P_((struct string ** ret,
				 char *param,
				 struct string * value));
static void add_param_string(ret,param,value)
     struct string ** ret;
     char *param;
     struct string * value;
{   
    if (*ret)
      add_ascii_to_string(*ret,us_str("; "));

    if (!*ret)
	*ret = new_string(display_charset);

    add_ascii_to_string(*ret,us_str(param));
    add_ascii_to_string(*ret,us_str("="));

    if (string_need_quote(value))
	append_quoted_to_string(ret,value);
    else 
	append_string(ret,value);
}

static void add_param_ascii P_((struct string ** ret,
				 char *param,
				 char * value));
static void add_param_ascii(ret,param,value)
     struct string ** ret;
     char *param;
     char * value;
{
    struct string * tmp ;
    
    charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);
    
    if (!ascii_ptr)
	mime_panic(__FILE__,__LINE__,"add_param_ascii",
		   "US-ASCII not found");

    tmp = new_string2(ascii_ptr,us_str(value));
    add_param_string(ret,param,tmp);
    free_string(&tmp);
}

struct string * show_mime_params(P)
     struct mime_param *P;
{
    struct string * ret = NULL;

    if (MIME_PARAM_magic != P->magic)
	mime_panic(__FILE__,__LINE__,"show_mime_params",
		   "Bad magic number");

    if (! P->ascii_params && ! P->string_params)
	parse_raw_params(P);


    if (P->string_params) {
	int i;
    
	for (i = 0; i < P->string_param_count; i++) {
	    if (P->string_params[i].param &&
		P->string_params[i].value) 
		add_param_string(&ret,P->string_params[i].param,
				 P->string_params[i].value);	   
	}
    }

    if (P->ascii_params) {
	int i;

	for (i = 0; i < P->ascii_param_count; i++) {
	    if (!P->ascii_params[i].param)
		continue;

	    if (find_string_param(P,P->ascii_params[i].param))
		continue;

	    if (P->ascii_params[i].value)
		add_param_ascii(&ret,P->ascii_params[i].param,
				P->ascii_params[i].value);	   
	}
    }

    return ret;
}

char * encode_mime_params(P)
     struct mime_param *P;
{
    char * ret = NULL;

    if (MIME_PARAM_magic != P->magic)
	mime_panic(__FILE__,__LINE__,"encode_mime_params",
		   "Bad magic number");

    if (P->params) {
	int i;
    
	for (i = 0; i < P->param_count; i++) {
	    if (P->params[i].raw_param &&
		P->params[i].raw_value) {
		if (ret)
		    ret = strmcat(ret,"; ");

		ret = strmcat(ret,P->params[i].raw_param);
		ret = strmcat(ret,"=");
		ret = strmcat(ret,P->params[i].raw_value);
	    }

	}
    }

    return ret;
}



char ** encode_mime_params_v(P)
     struct mime_param *P;
{
    char ** ret = NULL;

    if (MIME_PARAM_magic != P->magic)
	mime_panic(__FILE__,__LINE__,"encode_mime_params",
		   "Bad magic number");

    if (P->params) {
	int i;
	int X = 0;
	
	ret = safe_malloc((P->param_count +1) * sizeof (ret[0]));

	for (i = 0; i < P->param_count; i++) {
	    if (P->params[i].raw_param &&
		P->params[i].raw_value) {

		ret[X] = safe_strdup(P->params[i].raw_param);
		ret[X] = strmcat(ret[X],"=");
		ret[X] = strmcat(ret[X],P->params[i].raw_value);
		X++;
	    }
	}
	ret[X] = NULL;
    }
    return ret;
}


static void gen_splitted_ascii_param P_((struct mime_param *P, char *name, char *value));
static void gen_splitted_ascii_param(P,name,value)
     struct mime_param *P; 
     char *name; 
     char *value;
{
    char * p, *p1;
    int counter = 0;

    for (p = value; *p; p = p1) {
	struct mp_pair * mp = add_mp_pair(P);
	char * tmp;
	int len;

	for (p1 = p; *p && p1-p < 70; p++)
	    /* nothing */;

	len = p1-p;

	mp->raw_param =  elm_message(FRM("%s*%d"),name,counter++);
	
	tmp = safe_malloc(len+1);
	strncpy(tmp,p,len);
	tmp[len] = '\0';
	
	if (ascii_need_quote(tmp)) {
	    mp->raw_value = elm_message(FRM("%Q"),tmp);
	    free(tmp);
	} else {
	    mp->raw_value = tmp;
	}	
    }

    if (counter == 0) {
	struct mp_pair * mp = add_mp_pair(P);
	mp->raw_param =  elm_message(FRM("%s*%d"),name,counter++);
	mp->raw_value = safe_strdup("\"\"");
    }

}

static char * get_encoded P_((struct string *value));
static char * get_encoded(value)
     struct string *value;
{
    char * ret = NULL;
    int alloclen = 0;
    int len;
    char * tmp;
    int ok = charset_ok_p(value->string_type);    
    int i;
    int count = 0;
    CONST char *y = get_string_MIME_name(value);

    if (y && (0 == istrcmp(y,"UTF-8") ||
	      0 == istrcmp(y,"UTF-7")))
	ok = 1;

    /* Allow producing \0 values also ... */
    bytestream_from_string(value,&tmp,&len);

    alloclen = len+10;

    if (!ok)
	alloclen = len*3+2;

    ret = safe_malloc(alloclen);

#define ADD(x) { if (count >= alloclen) { \
  alloclen += 20; ret = safe_realloc(ret,alloclen); } \
  ret[count++] = (x); }

    for (i = 0; i <len; i++) {
	unsigned char val =tmp[i];
	
	if (!ok || val == '%' || !isascii(val) || !isalnum(val)) {
	    ADD('%');
	    ADD(hexchars[val / 16]);
	    ADD(hexchars[val % 16]);
	} else {
	    ADD(val);
	}       
    }
    
    ADD('\0');
    
#undef ADD

    free(tmp);
    return ret;
}

static char * get_first_encoded P_((struct string *value));
static char * get_first_encoded(value)
     struct string *value;
{
    CONST char *cs;
    CONST char *lang;
    char *ret;
    char *tmp;

    if (value->string_type == RAW_BUFFER)   /* charset is not relevant to parameter value! */
	cs = "";
    else {
	cs = get_string_MIME_name(value);

	if (!cs || NULL != strpbrk(cs," \t\r\n()\"?*'="))
	    cs = "UNKNOWN-8BIT";
    }

    lang = get_string_lang(value);

    if (!lang || NULL != strpbrk(lang," \t\r\n()\"?*'="))
	    lang = "";

    tmp = get_encoded(value);

    ret = elm_message(FRM("%s'%s'%s"),cs,lang,tmp);

    free(tmp);

    return ret;
}

static void gen_string_param P_((struct mime_param *P, char *name, 
					  struct string *value));
static void gen_string_param(P,name,value)
     struct mime_param *P; 
     char *name; 
     struct string * value;
{
    struct mp_pair * mp = add_mp_pair(P);

    mp->raw_param =  elm_message(FRM("%s*"),name);
    mp->raw_value = get_first_encoded(value);

    DPRINT(Debug,9,(&Debug,
		    "gen_string_param: *** %s=%s\n",
		    mp->raw_param,mp->raw_value));
}

#define STRING_SPLIT_LEN   15

static void gen_splitted_string_param P_((struct mime_param *P, char *name, 
					  struct string *value));
static void gen_splitted_string_param(P,name,value)
     struct mime_param *P; 
     char *name; 
     struct string * value;
{
    int len = string_len(value);
    int pos = 0;
    struct string *buffer;
    int counter = 0;
    struct mp_pair * mp = add_mp_pair(P);

    buffer = clip_from_string(value,&pos,STRING_SPLIT_LEN);

    mp->raw_param =  elm_message(FRM("%s*%d*"),name,counter++);
    mp->raw_value = get_first_encoded(buffer);

    DPRINT(Debug,9,(&Debug,
		    "gen_splitted_string_param: *** %s=%s\n",
		    mp->raw_param,mp->raw_value));

    free_string(&buffer);

    while (pos < len) {
	buffer = clip_from_string(value,&pos,STRING_SPLIT_LEN);

	mp = add_mp_pair(P);
	mp->raw_param = elm_message(FRM("%s*%d*"),name,counter++);
	mp->raw_value = get_encoded(buffer);

	DPRINT(Debug,9,(&Debug,
			"gen_splitted_string_param: *** %s=%s\n",
			mp->raw_param,mp->raw_value));

	free_string(&buffer);	
    }
}

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


static void parse_rest P_((struct mime_param *P, struct string_token *X, 
			   int gen_compat_len,
			   int plain_mode));
static void parse_rest(P,X, gen_compat_len,plain_mode)
     struct mime_param *P;
     struct string_token *X;
     int gen_compat_len;
     int plain_mode;
{

    charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);

    int i = 0;

    struct string_pair * tmp_list     = NULL;
    
    int                  tmp_list_len = 0;
    
    struct ascii_pair  *  a_list       = NULL;
    int                   a_list_len   = 0;
    
    struct mp_pair     * mp_list       = NULL;
    int                  mp_list_len   = 0;

    if (!ascii_ptr)
	mime_panic(__FILE__,__LINE__,"parse_rest",
		   "US-ASCII not found");
    
    while (X[i].token) {
	
	struct string * name = NULL;
	struct string *Y = NULL;
	char * ascname = NULL;
	CONST char *lang = NULL;

	while (X[i].token && (
			      0x0020 /* SPACE  */ == X[i].special ||
			      0x0028  /* ( */ == X[i].special
			      ) 
	       ) {
	    i++;
	}
	
	if (! X[i].token)
	    break;

	if (0 == X[i].special)
	    name = X[i].token;
	else {
	    lib_error(CATGETS(elm_msg_cat, MeSet,
			      MeParseErrorNameExpected,
			      "Parse error on %S -- name expected"),
		      X[i].token);
	    goto recover;
	}
	
	if (!can_ascii_string(X[i].token))
	    lib_error(CATGETS(elm_msg_cat, MeSet,
			      MeAsciiRequiredTok,
			      "Ascii required for token %S"),
		      X[i].token);
	
	i++;
	while (X[i].token && ( 
			      0x0020 /* SPACE  */ == X[i].special ||
			      0x0028  /* ( */ == X[i].special
			      )
	       ) {
	    i++;
	}
	
	if (! X[i].token) {
	    lib_error(CATGETS(elm_msg_cat, MeSet,
			      MeParseErrorEqualExpectedEOS,
			      "Parse error on end of string -- = expected"));
	    break;
	    
	}
	
	if (0x003D /* = */ != X[i].special) {
	    lib_error(CATGETS(elm_msg_cat, MeSet,
			      MeParseErrorEqualExpected,
			      "Parse error on %S -- = expected"),
		      X[i].token);
	    goto recover;
	}
	
	i++;
	while (X[i].token && (
			      0x0020 /* SPACE  */ == X[i].special ||
			      0x0028  /* ( */ == X[i].special
			      )
	       ) {
	    i++;
	}
	
	if (! X[i].token) {
	    lib_error(CATGETS(elm_msg_cat, MeSet,
			      MeParseErrorValueExpectedEOS,
			      "Parse error on end of string -- value of param expected"));
	    break;
	    
	}
	
	if (0x003B    /* ';' */  == X[i].special) {
	    
	    lib_error(CATGETS(elm_msg_cat, MeSet,
			      MeParseErrorValueExpected,
			      "Parse error on %S -- value of param expected"),
		      X[i].token);
	    continue;
	}
	
	
	Y = convert_string(ascii_ptr,name,0);
	ascname = us2s(stream_from_string(Y,0,NULL));
	free_string(&Y);
	
	if (NULL != strchr(ascname,'*')) {
	    struct mp_pair *mp = add_mp_pair_1(&mp_list,&mp_list_len);
	    
	    DPRINT(Debug,9,(&Debug, 
			    "parse_rest: %s: String as raw parameter: %S\n",
			    ascname,X[i].token));
	    
	    if (!can_ascii_string(X[i].token))
		lib_error(CATGETS(elm_msg_cat, MeSet,
				  MeAsciiRequiredTok,
				  "Ascii required for token %S"),
			  X[i].token);
	    
	    Y = convert_string(ascii_ptr,X[i].token,0);
	    
	    mp->raw_param = ascname;
	    mp->raw_value = us2s(stream_from_string(Y,0,NULL));
	    free_string(&Y);

	} else {
	    struct string * value1 = NULL;
	    struct string * value0 = NULL;

	    if (0x0022  /* " */ == X[i].special)
		value1  = unquote_string(X[i].token);
	    else
		value1  = dup_string(X[i].token);

	    value0  = value1;  /* synonym */

	    if (plain_mode) {
		
		struct ascii_pair *A = NULL;
		struct string *Y;
		
		if (!can_ascii_string(value1))
		    lib_error(CATGETS(elm_msg_cat, MeSet,
				      MeAsciiRequiredTok,
				      "Ascii required for token %S"),
			      value1);
	    
		Y = convert_string(ascii_ptr,value1,0);

		
		A = add_ascii_pair_1(&a_list,&a_list_len);
		
		A->param     = ascname;
		A->value     = us2s(stream_from_string(Y,0,NULL));
		A->is_compat = 1;
		
		free_string(& Y);		    

		DPRINT(Debug,9,(&Debug, 
				"parse_rest: [compat] %s=%s\n",
				A->param,
				A->value));
	    
	    } else {
		
		/* Ascii version ... */
		
		lang = get_string_lang(value1);
	    
		if (plain_mode ||
		    !lang && can_ascii_string(value1) && 
		    !string_is_ascii_control(value1)) {
		    struct ascii_pair *A = NULL;
		    
		    struct string *Y = convert_string(ascii_ptr,value1,0);
		    /* Is really ascii */
		    
		    A = add_ascii_pair_1(&a_list,&a_list_len);
		    
		    A->param = safe_strdup(ascname);
		    A->value = us2s(stream_from_string(Y,0,NULL));
		    
		    A->is_compat = string_len(Y) < gen_compat_len;


		    DPRINT(Debug,9,(&Debug, 
				    "parse_rest: [%s] %s=%s\n",
				    A->is_compat ? "compat" : "ascii",
				    A->param,
				    A->value));
		    
		    if (!A->is_compat) 
			goto gencompat;
		    
		} else {
		    
		    CONST struct string * Y1;
		    CONST char          * Y2;

		    {
			struct string_pair *S = NULL;
	    		
			S = add_string_pair_1(&tmp_list,&tmp_list_len);
			S->param = ascname;  
			S->value   = value1;

			value1 = NULL;
			
			DPRINT(Debug,9,(&Debug, 
					"parse_rest: [string] %s=%S\n",
					S->param,
					S->value));
		    }

		gencompat:
		
		    Y1 = get_mime_param(P,ascname);
		    Y2 = find_ascii_param(P,ascname,1);
		 		    
		    if (Y1 && 0 == string_cmp(Y1,value0,999) &&
			Y2) {
			/* Preserve existing compat parameter */
			
			struct ascii_pair *A = NULL;
			
			DPRINT(Debug,9,(&Debug, 
					"parse_rest: %s: Preserving existing compat parameter: %s\n",
					ascname,Y2));
			
			A = add_ascii_pair_1(&a_list,&a_list_len);
			A->param = safe_strdup(ascname);
			A->value = safe_strdup(Y2);
			A->is_compat = 1;

			DPRINT(Debug,9,(&Debug, 
					"parse_rest: [compat] %s=%s\n",
					A->param,
					A->value));

			
		    } else { 

			if (Y1) {
			    DPRINT(Debug,9,(&Debug, 
					    "parse_rest: old string param was: %S\n",
					    Y1));
			}
			if (Y2) {
			    DPRINT(Debug,9,(&Debug, 
					    "parse_rest: old compat param was: %s\n",
					    Y2));
			}

			if (gen_compat_len) {
			    
			    struct ascii_pair *A = NULL;
			    struct string *Y = convert_string(ascii_ptr,value0,0);
			    int pos = 0;
			    
			    A = add_ascii_pair_1(&a_list,&a_list_len);
			    A->param = safe_strdup(ascname);
			    A->value = us2s(streamclip_from_string(Y,&pos,gen_compat_len,NULL,NULL));
			    A->is_compat = 1;
			    
			    DPRINT(Debug,9,(&Debug, 
					    "parse_rest: [compat] %s=%s\n",
					    A->param,
					    A->value));
			}
		    }
		}
		
		if (value1)
		    free_string(& value1);
	    }
	}
	
	i++;
	while (X[i].token && (
			      0x0020 /* SPACE  */ == X[i].special ||
			      0x0028  /* ( */ == X[i].special
			      )
	       ) {
	    i++;
	}
		
	if (X[i].token &&
	    0x003B    /* ';' */  != X[i].special) {
	    
		lib_error(CATGETS(elm_msg_cat, MeSet,
				  MeParseErrorSemicolonExpected,
				  "Parse error on %S -- ; or end of string expected"),
			  X[i].token);
		
		/* syncronize */
	    recover:
		while (X[i].token &&
		       0x003B    /* ';' */  != X[i].special  )
		    i++;
		
	}	
	
	if (X[i].token &&
	    0x003B    /* ';' */  == X[i].special)
	    i++;
       
    }
    
    if (X[i].token)
	lib_error(CATGETS(elm_msg_cat, MeSet,
			  MeParseErrorFailed,
			  "Parse error on %S -- failed to parse parameters"),
		  X[i].token);
    
    
    free_parsed(P);
    
    P->string_params      = tmp_list;
    P->string_param_count = tmp_list_len;
    
    P->ascii_params       = a_list;
    P->ascii_param_count  = a_list_len;
    
    free_raw(P);
    
    P->params             = mp_list;
    P->param_count        = mp_list_len;
        
    for (i = 0; i < a_list_len; i++) {
	if (a_list[i].is_compat) {
	    struct mp_pair * mp = add_mp_pair(P);
	    
	    mp->raw_param = safe_strdup(a_list[i].param);
	    if (ascii_need_quote(a_list[i].value))
		mp->raw_value = elm_message(FRM("%Q"),a_list[i].value);
	    else
		mp->raw_value = safe_strdup(a_list[i].value);
	    
	    DPRINT(Debug,9,(&Debug,
			    "parse_rest: *** %s=%s\n",
			    mp->raw_param,mp->raw_value));
	    
	} else {
	    gen_splitted_ascii_param(P,a_list[i].param,a_list[i].value);
	}
    }
    
    for (i = 0; i < tmp_list_len; i++) {
	/* If there exists ascii -- non-compat version of same
	   parameter, do not add this ...
	*/
	
	if (find_ascii_param(P,tmp_list[i].param,0))
	    continue;
	
	if (string_len(tmp_list[i].value) > STRING_SPLIT_LEN)
	    gen_splitted_string_param(P,tmp_list[i].param,tmp_list[i].value);
	else
	    gen_string_param(P,tmp_list[i].param,tmp_list[i].value);
    }    
}

struct mime_param * parse_mime_param_string(value, gen_compat_len,
					    plain_mode)
     struct string * value;
     int gen_compat_len;
     int plain_mode;
{
    struct mime_param *P = alloc_mime_param();

    struct string_token *X = string_tokenize(value,TOK_mime);

    P->def               = value->string_type;
    
    if (X) {
	parse_rest(P,X,gen_compat_len, plain_mode);

	free_string_tokenized(&X);
    }

    return P;
}

char ** split_mime_params(P,in_buffer,gen_compat_len,plain_mode)
     struct mime_param **P;
     struct string *in_buffer;
     int gen_compat_len;
     int plain_mode;
{
    char **ret = NULL;
    struct string_token *X;
    int i,count = 0;


    if (!*P)
	*P = alloc_mime_param();

    if (MIME_PARAM_magic != (*P)->magic)
	mime_panic(__FILE__,__LINE__,"split_mime_params",
		   "Bad magic number");



    X = string_tokenize(in_buffer,TOK_mime);
    if (!X)
	return NULL;

    for (i=0; X[i].token; i++) {
	if (0x003B    /* ';' */  == X[i].special)
	    break;
	count++;
    }

    if (count) {
	int j;
	int k = 0;

	charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);

	if (!ascii_ptr)
	    mime_panic(__FILE__,__LINE__,"split_mime_params",
		       "US-ASCII not found");
	
	ret = safe_malloc((count+1) * sizeof(ret[0]));
	
	for (j = 0; j < count; j++) {
	    struct string *Y;

	    if (0x0020 /* SPACE  */ == X[j].special)
		continue;

	    /* Skips comment */
	    if (0x0028  /* ( */ == X[j].special)
		continue;

	    if (!can_ascii_string(X[j].token))
		lib_error(CATGETS(elm_msg_cat, MeSet,
				  MeAsciiRequiredTok,
				  "Ascii required for token %S"),
			  X[j].token);
	    
	    Y = convert_string(ascii_ptr,X[j].token,0);

	    ret[k] = us2s(stream_from_string(Y,0,NULL));

	    DPRINT(Debug,9,(&Debug,
			    "split_mime_params: ret[%d]=%s\n",
			    k,ret[k]));

	    k++;

	    free_string(&Y);
	}

	ret[k] = NULL;
    }

    
    if (0x003B    /* ';' */  == X[i].special) {

	i++;
	
	parse_rest(*P,X+i,gen_compat_len, plain_mode);

    } else {

	DPRINT(Debug,9,(&Debug,
			"split_mime_params: Removing all parameters\n"));
		
	free_raw(*P);
	free_parsed(*P);
    }

    free_string_tokenized(&X);
    return ret;
}

void mime_params_add_compat(P,name,value)
     struct mime_param **P;
     char *name;
     char *value;
{
    struct ascii_pair *A = NULL;
    struct mp_pair * mp = NULL;

    if (!*P)
	*P = alloc_mime_param();

    if (MIME_PARAM_magic != (*P)->magic)
	mime_panic(__FILE__,__LINE__,"mime_params_add_compat",
		   "Bad magic number");

    A = add_ascii_pair(*P);

    A->param = safe_strdup(name);
    A->value = safe_strdup(value);
    A->is_compat = 1;

    DPRINT(Debug,9,(&Debug,
		    "mime_params_add_compat: [compat] %s=%s\n",
		    A->param,A->value));
    
    mp = add_mp_pair(*P);
    mp->raw_param = safe_strdup(name);

    if (ascii_need_quote(value))
	mp->raw_value = elm_message(FRM("%Q"),value);
    else
	mp->raw_value = safe_strdup(value);

    DPRINT(Debug,9,(&Debug,
		    "mime_params_add_compat: *** %s=%s\n",
		    mp->raw_param,mp->raw_value));

}

void mime_params_add(P,name,value)
     struct mime_param **P;
     char *name;
     struct string *value;
{
    struct string_pair *S = NULL;
    CONST char *lang;

    charset_t ascii_ptr = MIME_name_to_charset("US-ASCII",0);

    if (!*P)
	*P = alloc_mime_param();
    
    if (MIME_PARAM_magic != (*P)->magic)
	mime_panic(__FILE__,__LINE__,"mime_params_add",
		   "Bad magic number");

    if (!ascii_ptr)
	mime_panic(__FILE__,__LINE__,"mime_params_add",
		   "US-ASCII not found");

    S = add_string_pair(*P);

    S->param = safe_strdup(name);
    S->value = dup_string(value);

    DPRINT(Debug,9,(&Debug,
		    "mime_params_add: [string] %s=%S\n",
		    S->param,S->value));

    /* Compat parameter is not generated -- it is asusmed that
       caller calls mime_params_add_compat instead ...
    */

    lang = get_string_lang(value);

    if (!lang && can_ascii_string(value) &&
	!string_is_ascii_control(value)) {

	struct ascii_pair *A = NULL;
	
	struct string *Y = convert_string(ascii_ptr,value,0);
	/* Is really ascii */
		    
	A = add_ascii_pair(*P);
		    
	A->param = safe_strdup(name);
	A->value = us2s(stream_from_string(Y,0,NULL));
	A->is_compat = 0;


	DPRINT(Debug,9,(&Debug,
			"mime_params_add: [ascii] %s=%s\n",
			A->param,A->value));

	gen_splitted_ascii_param(*P,A->param,A->value);
    } else {
	if (string_len(S->value) > STRING_SPLIT_LEN)
	    gen_splitted_string_param(*P,S->param,S->value);
	else
	    gen_string_param(*P,S->param,S->value);

    }
}


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


syntax highlighted by Code2HTML, v. 0.9.1