static char rcsid[] = "@(#)$Id: list.c,v 1.8 2007/03/09 20:28:55 hurtta Exp $";

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

#include "def_list.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 struct list_info *alloc_list_info P_((void));
static struct list_info *alloc_list_info(void)
{
    struct list_info *ret = safe_malloc(sizeof (*ret));

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

    ret->magic  = LIST_INFO_magic;

    ret->list_phrase    = NULL;
    ret->list_id        = NULL;

    ret->list_help      = NULL;
    ret->list_help_len  = 0;

    ret->list_unsubscribe      = NULL;
    ret->list_unsubscribe_len  = 0;

    ret->list_subscribe      = NULL;
    ret->list_subscribe_len  = 0;

    ret->list_post      = NULL;
    ret->list_post_len  = 0;
    ret->list_post_no   = 0;
    ret->list_post_no_comment = NULL;

    ret->list_owner      = NULL;
    ret->list_owner_len  = 0;

    ret->list_archive      = NULL;
    ret->list_archive_len  = 0;

    return ret;
}

void free_list_info(list)
     struct list_info **list;
{
    if (LIST_INFO_magic != (*list)->magic) 
	panic("LIST INFO PANIC",__FILE__,__LINE__,"free_list_info",
              "Bad magic number",0);

    if ((*list)->list_phrase)
	free_string(&((*list)->list_phrase));
    (*list)->list_phrase      = NULL;
    if ((*list)->list_id)
	free((*list)->list_id);
    (*list)->list_id          = NULL;

    if ((*list)->list_help) {
	int i;

	for (i = 0; i < (*list)->list_help_len; i++) {
	    if ((*list)->list_help[i].url)
		free_url(& ((*list)->list_help[i].url));
	    if ((*list)->list_help[i].comment)
		free_string(& ((*list)->list_help[i].comment));
	}
	     
	free((*list)->list_help);
	(*list)->list_help = NULL;
    }
    (*list)->list_help_len = 0;

    if ((*list)->list_unsubscribe) {
	int i;

	for (i = 0; i < (*list)->list_unsubscribe_len; i++) {
	    if ((*list)->list_unsubscribe[i].url)
		free_url(& ((*list)->list_unsubscribe[i].url));
	    if ((*list)->list_unsubscribe[i].comment)
		free_string(& ((*list)->list_unsubscribe[i].comment));
	}
	     
	free((*list)->list_unsubscribe);
	(*list)->list_unsubscribe = NULL;
    }
    (*list)->list_unsubscribe_len = 0;

    if ((*list)->list_subscribe) {
	int i;

	for (i = 0; i < (*list)->list_subscribe_len; i++) {
	    if ((*list)->list_subscribe[i].url)
		free_url(& ((*list)->list_subscribe[i].url));
	    if ((*list)->list_subscribe[i].comment)
		free_string(& ((*list)->list_subscribe[i].comment));
	}
	     
	free((*list)->list_subscribe);
	(*list)->list_subscribe = NULL;
    }
    (*list)->list_subscribe_len = 0;

    if ((*list)->list_post) {
	int i;

	for (i = 0; i < (*list)->list_post_len; i++) {
	    if ((*list)->list_post[i].url)
		free_url(& ((*list)->list_post[i].url));
	    if ((*list)->list_post[i].comment)
		free_string(& ((*list)->list_post[i].comment));
	}
	     
	free((*list)->list_post);
	(*list)->list_post = NULL;
    }
    (*list)->list_post_len = 0;

    if ((*list)->list_post_no_comment)
	free_string(& ((*list)->list_post_no_comment));

    if ((*list)->list_owner) {
	int i;

	for (i = 0; i < (*list)->list_owner_len; i++) {
	    if ((*list)->list_owner[i].url)
		free_url(& ((*list)->list_owner[i].url));
	    if ((*list)->list_owner[i].comment)
		free_string(& ((*list)->list_owner[i].comment));
	}
	     
	free((*list)->list_owner);
	(*list)->list_owner = NULL;
    }
    (*list)->list_owner_len = 0;

    if ((*list)->list_archive) {
	int i;

	for (i = 0; i < (*list)->list_archive_len; i++) {
	    if ((*list)->list_archive[i].url)
		free_url(& ((*list)->list_archive[i].url));
	    if ((*list)->list_archive[i].comment)
		free_string(& ((*list)->list_archive[i].comment));
	}
	     
	free((*list)->list_archive);
	(*list)->list_archive = NULL;
    }
    (*list)->list_archive_len = 0;

    (*list)->magic  = 0;  /* Invalidate */
    free(*list);
    *list = NULL;
}

/* RFC 2369:

  To allow for future extension, client applications MUST follow the
   following guidelines for handling the contents of the header fields
   described in this document:

   1) Except where noted for specific fields, if the content of the
      field (following any leading whitespace, including comments)
      begins with any character other than the opening angle bracket
      '<', the field SHOULD be ignored.

   2) Any characters following an angle bracket enclosed URL SHOULD be
      ignored, unless a comma is the first non-whitespace/comment
      character after the closing angle bracket.

   3) If a sub-item (comma-separated item) within the field is not an
      angle-bracket enclosed URL, the remainder of the field (the
      current, and all subsequent, sub-items) SHOULD be ignored.

*/


static int parse_list_header_no P_((struct list_elem **listhdr, 
				    int *listhdr_len,
				    int mime, 
				    charset_t cs,
				    header_list_ptr tmphdr,
				    const char *name,
				    int *no_flag,
				    struct string **no_comment,
				    struct header_errors   ** header_error));
static int parse_list_header_no(listhdr,listhdr_len,mime,cs,tmphdr,name,
				no_flag,
				no_comment, header_error)
     struct list_elem **listhdr; 
     int *listhdr_len;
     int mime;
     charset_t cs;
     header_list_ptr tmphdr;
     CONST char *name;
     int *no_flag;
     struct string **no_comment;
     struct header_errors   ** header_error;
{
    char ** tokens = rfc822_tokenize(tmphdr->body);
    int count = 1;
    int max;
    int ret = 1;
    int tok_end = 0;
    int outer, inner;

    for (tok_end = 0; tokens[tok_end]; tok_end++)
	if (',' == tokens[tok_end][0])
	    count++;
    
    DPRINT(Debug,25,(&Debug, "parse_list_header_no: count=%d\n",count));

    max = *listhdr_len + count;

    *listhdr = safe_realloc(*listhdr, max * sizeof ((*listhdr)[0]));

    for (outer = 0; outer < tok_end; outer = inner) {

	char **scanned = NULL;
	struct string * raw_url = NULL;
	struct string *comments = NULL;
	char tok = '\0';
	int quit = 0;
	int i;

	look_special_tokens(tokens,",",outer,&inner,mime,
			    cs,&comments,&scanned);

	if (inner < tok_end) {
	    tok = tokens[inner][0];
	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: [%d] token=%c (%s)\n",inner,tok,tokens[inner]));
	    inner++;
	} else {
	    tok = '\0';
	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: [%d] token=EOS\n",inner));
	}

	if (!scanned || !scanned[0]) {
	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: No URL\n"));

	    quit = 1;
	    ret = 0;
	    
	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet, 
					 MeParseErrorListHdr1,
					 "PARSE ERROR: Failed to parse %s header!"),
				 name);
	    	    
	    goto fail1;
	}


	if (no_flag && 
	    0 == istrcmp(scanned[0],"NO")) {

	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: NO flag\n"));

	    *no_flag = 1;

	    if (! *no_comment) {
		*no_comment = comments;
		comments = NULL;
	    }

	    if (scanned[1]) {
		DPRINT(Debug,25,(&Debug, 
				 "parse_list_header_no: Garbage after NO\n"));
		quit = 1;
	    }

	    if (*listhdr_len > 0) {
		DPRINT(Debug,25,(&Debug, 
				 "parse_list_header_no: NO and URL given\n"));
		
		quit = 1;
		ret = 0;
		
		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet, MeParseErrorListHdr2,
					     "PARSE ERROR: Both NO and URL given on %s header!"),
				     name);
	    }	

	    goto fail1;
	}
	

	if ('<' != scanned[0][0]) {
	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: URL not start with '<'\n"));
	    goto fail1;
	}


	for (i = 1; scanned[i] && '>' != scanned[i][0]; i++) {

	    struct string * X;

	    if ('<' == scanned[i][0]) {
		DPRINT(Debug,25,(&Debug, 
				 "parse_list_header_no: Multiple < \n"));
		quit = 1;
		ret = 0;

		process_header_error(header_error,
				     CATGETS(elm_msg_cat, MeSet, MeParseErrorListHdr1,
					     "PARSE ERROR: Failed to parse %s header!"),
			      name);

		goto fail1;
	    }

	    X = new_string2(cs,s2us(scanned[i]));

	    append_string(&raw_url,X);
	    free_string(&X);
	}

	if (!raw_url) {
	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: No url inside <>\n"));
	    quit = 1;
	    ret = 0;

	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet, MeParseErrorListHdr1,
					 "PARSE ERROR: Failed to parse %s header!"),
			  name);

	    goto fail1;
	}

	if (!scanned[i]) {
	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: No ending >\n"));
	    quit = 1;
	    ret = 0;

	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet, MeParseErrorListHdr1,
					 "PARSE ERROR: Failed to parse %s header!"),
				 name);

	    goto fail1;	   
	}

	if (scanned[i+1]) {
	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: Garbage after URL\n"));
	    quit = 1;
	}


	if (*listhdr_len >= max) {
	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: OVERFLOW\n"));
	    goto fail1;
	}

	if (no_flag && *no_flag) {

	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: NO and URL given\n"));

	    quit = 1;
	    ret = 0;

	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet, MeParseErrorListHdr2,
					 "PARSE ERROR: Both NO and URL given on %s header!"),
				 name);

	    goto fail1;	   
	}

	(*listhdr)[*listhdr_len].url = url_from_raw(raw_url,NULL,
						    header_error);
	if (!(*listhdr)[*listhdr_len].url) {
	    ret = 0;
	    goto fail1;
	}

	(*listhdr)[*listhdr_len].comment = comments;
	comments = NULL;
	    
	(*listhdr_len)++;

    fail1:
	if (raw_url)
	    free_string(&raw_url);
	if (comments)
	    free_string(&comments);
	if (scanned)
	    free_rfc822tokenized(scanned);	

	if (quit) {
	    DPRINT(Debug,25,(&Debug, 
			     "parse_list_header_no: Parsing of header stopped\n"));
	    break;
	}
    }

    free_rfc822tokenized(tokens);

    return ret;
}

static int parse_list_header P_((struct list_elem **listhdr, int *listhdr_len,
				 int mime, 
				 charset_t cs,
				 header_list_ptr tmphdr,
				 const char *name,
				 struct header_errors ** header_error));
static int parse_list_header(listhdr,listhdr_len,mime,cs,tmphdr,name,
			     header_error)
     struct list_elem **listhdr; 
     int *listhdr_len;
     int mime;
     charset_t cs;
     header_list_ptr tmphdr;
     CONST char *name;
     struct header_errors   ** header_error;
{
    return parse_list_header_no(listhdr,listhdr_len,mime,cs,tmphdr,name,
				NULL,NULL,header_error);
}

static int parse_list_id P_((struct list_info *list,
			     int mime, 
			     charset_t cs,
			     header_list_ptr tmphdr));
static int parse_list_id(list,mime,cs,tmphdr)
     struct list_info *list;
     int mime;
     charset_t cs;
     header_list_ptr tmphdr;
{
    char ** tokens = rfc822_tokenize(tmphdr->body);
    int i;
    char * phrase = NULL;
    char * id = NULL;
    int ret = 0;

    /* 1) read phrase -- comments are not allowed */

    for (i = 0; tokens[i]; i++) {

	if ('\n' == tokens[i][0] &&
	    !tokens[i+1])
	    break;

	if ('<' == tokens[i][0])
	    break;

	if ('(' == tokens[i][0])
	    goto error;

	if ('\n' == tokens[i][0]) 
	    phrase = strmcat(phrase," ");	
	else
	    phrase = strmcat(phrase,tokens[i]);	
    }

    if (!tokens[i] || '<' != tokens[i][0])
	goto error;

    if (list->list_phrase || 
	list->list_id)
	goto error;

    if (phrase)
	list->list_phrase = 
	    hdr_to_string(HDR_PHRASE,phrase,
			  cs,
			  mime && is_rfc1522(phrase));

    /* 2) read id -- comments and whitespace is not allowed */

    for (i++; tokens[i]; i++) {

	if ('\n' == tokens[i][0] &&
	    !tokens[i+1])
	    break;

	if ('>' == tokens[i][0])
	    break;

	if ('(' == tokens[i][0])
	    goto error;
	if (' ' == tokens[i][0])
	    goto error;
	if ('\n' == tokens[i][0])
	    goto error;
	
	id = strmcat(id,tokens[i]);	
    }

    if (!tokens[i] || '>' != tokens[i][0])
	goto error;

    ret = 1;
    list->list_id = id; 
    id = NULL;

 error:
    if (phrase)
	free(phrase);
    if (id)
	free(id);

    free_rfc822tokenized(tokens);
    return ret;
}

struct list_info * parse_list_headers(parsed_headers,mime,cs,header_error)
     header_list_ptr parsed_headers;
     int mime;
     charset_t cs;
     struct header_errors   ** header_error;
{
    struct list_info *ret = NULL;
    header_list_ptr tmphdr;

    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"List-Id"))) {

	if (!ret)
	    ret = alloc_list_info();

	if (! parse_list_id(ret,mime,cs,tmphdr)) {
	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet, 
					 MeParseErrorListID1,
					 "PARSE ERROR: Failed to parse List-ID header!"));
	    
	} else if (tmphdr->next_this_header) {
	    process_header_error(header_error,
				 CATGETS(elm_msg_cat, MeSet, 
					 MeParseErrorListID2,
					 "PARSE ERROR: Several List-ID headers!"));
	}
    }

    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"List-Help"))) {

	if (!ret)
	    ret = alloc_list_info();

	while (tmphdr) {
	    if (parse_list_header(& (ret->list_help),
				  & (ret->list_help_len),
				  mime,
				  cs,
				  tmphdr,
				  give_header_name(tmphdr->header_name),
				  header_error))
		tmphdr = tmphdr->next_this_header;
	    else
		break;
	}
    }

    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"List-Unsubscribe"))) {

	if (!ret)
	    ret = alloc_list_info();

	while (tmphdr) {
	    if (parse_list_header(& (ret->list_unsubscribe),
				  & (ret->list_unsubscribe_len),
				  mime,
				  cs,
				  tmphdr,
				  give_header_name(tmphdr->header_name),
				  header_error))
		tmphdr = tmphdr->next_this_header;
	    else
		break;
	}
    }

    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"List-Subscribe"))) {

	if (!ret)
	    ret = alloc_list_info();

	while (tmphdr) {
	    if (parse_list_header(& (ret->list_subscribe),
				  & (ret->list_subscribe_len),
				  mime,
				  cs,
				  tmphdr,
				  give_header_name(tmphdr->header_name),
				  header_error))
		tmphdr = tmphdr->next_this_header;
	    else
		break;
	}
    }

    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"List-Post"))) {

	if (!ret)
	    ret = alloc_list_info();

	while (tmphdr) {
	    if (parse_list_header_no(& (ret->list_post),
				     & (ret->list_post_len),
				     mime,
				     cs,
				     tmphdr,
				     give_header_name(tmphdr->header_name),
				     & (ret->list_post_no),
				     & (ret->list_post_no_comment),
				     header_error))
		tmphdr = tmphdr->next_this_header;
	    else
		break;
	}
    }

    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"List-Owner"))) {
	if (!ret)
	    ret = alloc_list_info();

	while (tmphdr) {
	    if (parse_list_header(& (ret->list_owner),
				  & (ret->list_owner_len),
				  mime,
				  cs,
				  tmphdr,
				  give_header_name(tmphdr->header_name),
				  header_error))
		tmphdr = tmphdr->next_this_header;
	    else
		break;
	}
    }

    if (NULL != (tmphdr = locate_header_by_name(parsed_headers,
						"List-Archive"))) {
	if (!ret)
	    ret = alloc_list_info();

	while (tmphdr) {
	    if (parse_list_header(& (ret->list_archive),
				  & (ret->list_archive_len),
				  mime,
				  cs,
				  tmphdr,
				  give_header_name(tmphdr->header_name),
				  header_error))
		tmphdr = tmphdr->next_this_header;
	    else
		break;
	}
    }

    return ret;
}

void metapager_list_note(buffer,list)
     out_state_t *buffer;
     struct list_info *list;
{

    if (list->list_post_len >0)
	state_printf(buffer, CATGETS(elm_msg_cat, MeSet, 
				      MeListPostInfo,
				      "[ Use 'Rl' to reply to list or 'I' for list info. ]\n"));
    else
	state_printf(buffer, CATGETS(elm_msg_cat, MeSet, 
				      MeListPostInfo1,
				      "[ You can use 'I' for list info. ]\n"));

    state_putc('\n',buffer);
			
}

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



syntax highlighted by Code2HTML, v. 0.9.1