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