diff src/parse.c @ 0:08114f7dcc23 0.2.21

this is masqmail-0.2.21 from oliver kurth
author meillo@marmaro.de
date Fri, 26 Sep 2008 17:05:23 +0200
parents
children 26e34ae9a3e3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/parse.c	Fri Sep 26 17:05:23 2008 +0200
@@ -0,0 +1,450 @@
+/*  MasqMail
+    Copyright (C) 1999-2001 Oliver Kurth
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef PARSE_TEST
+#include "masqmail.h"
+#endif
+
+/* This is really dangerous. I hope that I was careful enough,
+   but maybe there is some malformed address possible that causes
+   this to segfault or be caught in endless loops.
+
+   If you find something like that, PLEASE mail the string to me
+   (no matter how idiotic it is), so that I can debug that.
+   Those things really should not happen.
+*/
+
+static gchar *specials = "()<>@,;:\\\".[]`";
+
+char *parse_error = NULL;
+
+static
+gchar *skip_comment(gchar *p)
+{
+
+#ifdef PARSE_TEST
+  g_print("skip_comment: %s\n", p);
+#endif
+
+  p++;
+  while(*p && *p != ')'){
+    p++;
+    if(*p == '(')
+      p = skip_comment(p);
+  }
+  p++;
+
+  return p;
+}
+
+static
+gboolean read_word(gchar *p, gchar **b, gchar **e)
+{
+#ifdef PARSE_TEST
+  g_print("read_word: %s\n", p);
+#endif
+  /* eat leading spaces */
+  while(*p && isspace(*p)) p++;
+  
+  *b = p;
+  /*  b = &p;*/
+  if(*p == '\"'){
+    /* quoted-string */
+    p++;
+    while(*p && (*p != '\"')) p++;
+    p++;
+  }else{
+    /* atom */
+    while(*p && !strchr(specials, *p) && !iscntrl(*p) && !isspace(*p))
+      p++;
+  }
+  *e = p;
+  return TRUE;
+}
+
+static
+gboolean read_word_with_dots(gchar *p, gchar **b, gchar **e)
+{
+  gchar *b0 = p;
+
+#ifdef PARSE_TEST
+  g_print("read_word_with_dots: %s\n", p);
+#endif
+  while(TRUE){
+    if(!read_word(p, b, e))
+      return FALSE;
+    p = *e;
+    if(*p != '.') break;
+    p++;
+  }
+  *b = b0;
+  *e = p;
+  return TRUE;
+}
+
+static
+gboolean read_domain(gchar *p, gchar **b, gchar **e)
+{
+#ifdef PARSE_TEST
+  g_print("read_domain: %s\n", p);
+#endif
+  *b = p;
+  if(*p != '['){
+    while(isalnum(*p) || (*p == '-') || (*p == '.'))
+      p++;
+  }else{
+    p++;
+    while(isalpha(*p) || (*p == '.'))
+      p++;
+    if(*p != ']'){
+      parse_error =
+	g_strdup_printf("']' expected at end of literal address %s", *b);
+      return FALSE;
+    }
+    p++;
+  }
+  *e = p;
+  return TRUE;
+}
+
+gboolean parse_address_rfc822(gchar *string,
+		       gchar **local_begin, gchar **local_end,
+		       gchar **domain_begin, gchar **domain_end,
+		       gchar **address_end)
+{
+  gint angle_brackets = 0;
+
+  gchar *p = string;
+  gchar *b, *e;
+
+  *local_begin = *local_end = NULL;
+  *domain_begin = *domain_end = NULL;
+
+  /* might be some memory left from previous call: */
+  if(parse_error != NULL){
+    g_free(parse_error);
+    parse_error = NULL;
+  }
+
+  /* leading spaces and angle brackets */
+  while(*p && (isspace(*p) || (*p == '<'))){
+    if(*p == '<')
+      angle_brackets++;
+    p++;
+  }
+ 
+  if(*p){
+    while(TRUE){
+      if(read_word_with_dots(p, &b, &e)){
+	p = e;
+#ifdef PARSE_TEST
+	g_print("after read_word_with_dots: %s\n", p);
+#endif
+	/* eat white spaces and comments */
+	while((*p && (isspace(*p))) || (*p == '(')){
+	  if(*p == '('){
+	    if(!(p = skip_comment(p))){
+	      parse_error =
+		g_strdup("missing right bracket ')'");
+	      return FALSE;
+	    }
+	  }else
+	    p++;
+	}
+	/* we now have a non-space char that is not
+	   the beginning of a comment */
+
+	if(*p == '@'){
+	  /* the last word was the local_part
+	     of an addr-spec */
+	  *local_begin = b;
+	  *local_end = e;
+#ifdef PARSE_TEST
+	  g_print("found local part: %s\n", *local_begin);
+#endif
+	  if(*p == '@'){
+	    p++; /* skip @ */
+	    /* now the domain */
+	    if(read_domain(p, &b, &e)){
+	      p = e;
+	      *domain_begin = b;
+	      *domain_end = e;
+	    }
+	    else
+	      return FALSE;
+	  }else{
+	    /* unqualified? */
+	    *domain_begin = *domain_end = NULL;
+	  }
+	  break;
+	}else if(*p == '<'){
+	  /* addr-spec follows */
+	  while(isspace(*p) || (*p == '<')){
+	    if(*p == '<')
+	      angle_brackets++;
+	    p++;
+	  }
+	  if(read_word_with_dots(p, &b, &e)){
+	    p = e;
+	    *local_begin = b;
+	    *local_end = e;
+#ifdef PARSE_TEST
+	  g_print("found local part: %s\n", *local_begin);
+#endif
+	  }else
+	    return FALSE;
+	  if(*p == '@'){
+	    p++;
+	    if(read_domain(p, &b, &e)){
+	      p = e;
+	      *domain_begin = b;
+	      *domain_end = e;
+	    }else
+	      return FALSE;
+	  }else{
+	    /* may be unqualified address */
+	    *domain_begin = *domain_end = NULL;
+	  }
+	  break;
+	}else if(!*p || *p == '>'){
+	  *local_begin = b;
+	  *local_end = e;
+#ifdef PARSE_TEST
+	  g_print("found local part: %s\n", *local_begin);
+#endif
+	  *domain_begin = *domain_end = NULL;
+	  break;
+	}else if(strchr(specials, *p) || iscntrl(*p) || isspace(*p)){
+	  parse_error = g_strdup_printf("unexpected character: %c", *p);
+	  return FALSE;
+	}
+      }else
+	return FALSE;
+    }
+   /* trailing spaces and angle brackets */
+#ifdef PARSE_TEST
+    g_print("down counting trailing '>'\n");
+#endif
+    while(*p && (isspace(*p) || (*p == '>'))){
+      if(*p == '>')
+	angle_brackets--;
+      p++;
+    }
+
+    *address_end = p;
+
+    if(angle_brackets != 0){
+      if(angle_brackets > 0)
+	parse_error = g_strdup("missing '>' at end of string");
+      else
+	parse_error = g_strdup("superfluous '>' at end of string");
+      return FALSE;
+    }else{
+      /* we successfully parsed the address */
+      return TRUE;
+    }
+    /* we never get here */
+  }
+  return FALSE;
+}
+
+gboolean parse_address_rfc821(gchar *string,
+			      gchar **local_begin, gchar **local_end,
+			      gchar **domain_begin, gchar **domain_end,
+			      gchar **address_end)
+{
+  gint angle_brackets = 0;
+
+  gchar *p = string;
+  gchar *b, *e;
+
+  *local_begin = *local_end = NULL;
+  *domain_begin = *domain_end = NULL;
+
+  /* might be some memory left from previous call: */
+  if(parse_error != NULL){
+    g_free(parse_error);
+    parse_error = NULL;
+  }
+
+  /* leading spaces and angle brackets */
+  while(*p && (isspace(*p) || (*p == '<'))){
+    if(*p == '<')
+      angle_brackets++;
+    p++;
+  }
+ 
+  if(*p){
+    while(TRUE){
+      if(read_word_with_dots(p, &b, &e)){
+	p = e;
+#ifdef PARSE_TEST
+	g_print("after read_word_with_dots: %s\n", p);
+#endif
+	*local_begin = b;
+	*local_end = e;
+#ifdef PARSE_TEST
+	  g_print("found local part: %s\n", *local_begin);
+	  g_print("local_end = %s\n", *local_end);
+#endif
+	if(!(*p) || isspace(*p) || (*p == '>')){
+	  /* unqualified ?*/
+	  domain_begin = domain_end = NULL;
+	  break;
+	}else if(*p == '@'){
+	  p++;
+	  if(read_domain(p, &b, &e)){
+	    p = e;
+	    *domain_begin = b;
+	    *domain_end = e;
+	  }
+	  break;
+	}else{
+	  parse_error =
+	      g_strdup_printf("unexpected character after local part '%c'",*p);
+	  return FALSE;
+	}
+      } else
+         return FALSE;
+    }
+
+    /* trailing spaces and angle brackets */
+#ifdef PARSE_TEST
+    g_print("down counting trailing '>'\n");
+#endif
+    while(*p && (isspace(*p) || (*p == '>'))){
+      if(*p == '>')
+	angle_brackets--;
+      p++;
+    }
+    *address_end = p;
+
+    if(angle_brackets != 0){
+      if(angle_brackets > 0)
+	parse_error = g_strdup("missing '>' at end of string");
+      else
+	parse_error = g_strdup("superfluous '>' at end of string");
+      return FALSE;
+    }else{
+      /* we successfully parsed the address */
+      return TRUE;
+    }
+    /* we never get here */
+  }
+  return FALSE;
+}
+
+/*
+  allocate address, reading from string.
+  On failure, returns NULL.
+  after call, end contatins a pointer to the end of the parsed string
+  end may be NULL, if we are not interested.
+
+  parses both rfc 821 and rfc 822 addresses, depending on flag is_rfc821
+*/
+
+address *_create_address(gchar *string, gchar **end, gboolean is_rfc821)
+{
+  gchar *loc_beg, *loc_end;
+  gchar *dom_beg, *dom_end;
+  gchar *addr_end;
+
+  if (string && (string[0] == 0)) {
+    address *addr = g_malloc(sizeof(address));
+    addr->address = g_strdup("");
+    addr->local_part = g_strdup("");
+    addr->domain = g_strdup(""); /* 'NULL' address (failure notice),
+        "" makes sure it will not be qualified with a hostname */
+    return addr;
+  }
+
+  if(is_rfc821 ?
+     parse_address_rfc821(string,
+			  &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end) :
+     parse_address_rfc822(string,
+			  &loc_beg, &loc_end, &dom_beg, &dom_end, &addr_end)){
+    address *addr = g_malloc(sizeof(address));
+    gchar *p = addr_end;
+    
+
+    memset(addr, 0, sizeof(address));
+
+    if(loc_beg[0] == '|'){
+      parse_error = g_strdup("no pipe allowed for RFC 822/821 address");
+      return NULL;
+    }
+
+    while(*p && (*p != ',')) p++;
+    addr->address = g_strndup(string, p - string);
+
+    addr->local_part = g_strndup(loc_beg, loc_end - loc_beg);
+
+#ifdef PARSE_TEST
+    g_print("addr->local_part = %s\n", addr->local_part);
+#endif
+
+    if(dom_beg != NULL){
+      addr->domain = g_strndup(dom_beg, dom_end - dom_beg);
+    }else{
+      if(addr->local_part[0] == 0)
+	addr->domain = g_strdup(""); /* 'NULL' address (failure notice),
+		      "" makes sure it will not be qualified with a hostname */
+      else
+	addr->domain = NULL;
+    }
+
+    if(end != NULL)
+      *end = p;
+
+#ifndef PARSE_TEST
+    addr_unmark_delivered(addr);
+#endif
+
+    return addr;
+  }
+  return NULL;
+}
+
+address *create_address_rfc822(gchar *string, gchar **end){
+  return _create_address(string, end, FALSE);
+}
+
+address *create_address_rfc821(gchar *string, gchar **end){
+  return _create_address(string, end, TRUE);
+}
+
+GList *addr_list_append_rfc822(GList *addr_list, gchar *string, gchar *domain)
+{
+  gchar *p = string;
+  gchar *end;
+
+  while(*p){
+    address *addr = _create_address(p, &end, FALSE);
+    if(addr){
+      if(domain)
+	if(addr->domain == NULL)
+	  addr->domain = g_strdup(domain);
+
+      addr_list = g_list_append(addr_list, addr);
+      p = end;
+    }else
+      break;
+    while(*p == ',' || isspace(*p)) p++;
+  }
+  return addr_list;
+}