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 * or Kari Hurtta *****************************************************************************/ #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: */