#include <libdbox.h>

#include "rfc822.h"

using namespace std;

static bool is_special_char(char ch, const string &extra_specials)
 {
  switch(ch)
   {
   case '(':
   case ')':
   case '<':
   case '>':
   case '@':
   case ',':
   case ';':
   case ':':
   case '\\':
   case '"':
   case '.':
   case '[':
   case ']':
    return TRUE;
   }

  if(extra_specials.find(ch)!=string::npos)
    return TRUE;

  return FALSE;
 }

static void getatom(const string &text, unsigned &i,
                    rfc822symbolt &symbol,
                    const string &extra_specials)
 {
  symbol.type=rfc822symbolt::ATOM;

  for(; i<text.size(); i++)
   {
    char ch=text[i];

    if(is_special_char(ch, extra_specials) ||
       ch==' ' || ch=='\t') return;

    symbol.text+=ch;
   }
 }

static void getquoted(const string &text, unsigned &i,
                      rfc822symbolt &symbol,
                      const string &extra_specials)
 {
  i++; // skip '"'

  symbol.type=rfc822symbolt::QUOTED;

  while(i<text.size())
   {
    char ch=text[i++];

    if(ch=='\\') // quoted-pair
     {
      if(i<text.size()) symbol.text+=text[i++];
     }
    else if(ch=='"')
      return;
    else
      symbol.text+=ch;
   }
 }

static void getdomain(const string &text, unsigned &i,
                      rfc822symbolt &symbol,
                      const string &extra_specials)
 {
  i++; // skip '['

  symbol.type=rfc822symbolt::DOMAIN;

  while(i<text.size())
   {
    char ch=text[i++];

    if(ch=='\\') // quoted-pair
     {
      if(i<text.size()) symbol.text+=text[i++];
     }
    else if(ch==']')
      return;
    else
      symbol.text+=ch;
   }
 }

static void getcomment(const string &text, unsigned &i,
                       rfc822symbolt &symbol,
                       const string &extra_specials)
 {
  int nested=0;

  symbol.type=rfc822symbolt::COMMENT;

  i++; // skip '('

  while(i<text.size())
   {
    char ch=text[i++];

    if(ch=='\\') // quoted-pair
     {
      if(i<text.size()) symbol.text+=text[i++];
     }
    else if(ch==')' && nested==0)
      return;
    else 
     {
      if(ch=='(') nested++;
      symbol.text+=ch;
     }
   }
 }

void parserfc822symbols(const string &text, 
                        vector<rfc822symbolt> &symbols,
                        const string &extra_specials)
 {
  unsigned i=0;

  while(i<text.size())
   {
    char ch=text[i];

    if(ch==' ' || ch=='\t') // ignore spaces
      i++;
    else
     {
      rfc822symbolt symbol;

      if(is_special_char(ch, extra_specials))
       {
        switch(ch)
         {
         case '"': // quoted
          getquoted(text, i, symbol, extra_specials);
          break;

         case '(': // comment
          getcomment(text, i, symbol, extra_specials);
          break; 

         case '[': // domain-literal
          getdomain(text, i, symbol, extra_specials);
          break; 

         default:
          symbol.text+=ch;
          symbol.type=rfc822symbolt::SPECIAL;
          i++;
         }
       }
      else // atom
        getatom(text, i, symbol, extra_specials);

      symbols.push_back(symbol);
     }
   }

   {
    rfc822symbolt symbol;
    symbol.type=rfc822symbolt::END_OF_LIST;
    symbols.push_back(symbol);
   }
 }

#ifdef TESTIT
void main()
 {
  vector<rfc822symbolt> symbols;

  // example from rfc822
  #if 0
  parserfc822symbols(
    "\":sysmail\"@  Some-Group. Some-Org, "
    "Muhammed.(I am  the greatest) Ali @(the)Vegas.WBA",
    symbols);
  #else
  //parserfc822symbols("Hans Mueller <hans@mueller.de>", symbols);
  parserfc822symbols("hans@ (Ha\"ns ?Mü\\)ller) mueller. de", symbols);

  //parserfc822symbols("text/plain; charset=\"us-ascii\" "
  //                   "(Plain text)", symbols, ";/=");
  #endif

  cout << "Parsing done.\n";

  for(unsigned i=0; i<symbols.size(); i++)
   {
    cout.form("%2d: %-30s ", i, symbols[i].text.c_str());
    switch(symbols[i].type)
     {
     case rfc822symbolt::DOMAIN: cout << "domain-literal"; break;
     case rfc822symbolt::ATOM: cout << "atom"; break;
     case rfc822symbolt::SPECIAL: cout << "special"; break;
     case rfc822symbolt::QUOTED: cout << "quoted-string"; break;
     case rfc822symbolt::COMMENT: cout << "comment"; break;
     case rfc822symbolt::END_OF_LIST: cout << "end-of-list"; break;
     }
    cout << "\n";
   }
 }
#endif


syntax highlighted by Code2HTML, v. 0.9.1