#include <iostream>
#include <fstream>
#include <list>
using namespace std;

enum { PARTSMULTI, PARTSALT, PARTSREL, STRING, PLAIN, HTML, IMG, BASE64 };

string randdelim() {
  static int i=0;
  char b[1000];
  sprintf(b,"---====-==_0000%d_._0000%d_._delim=_",i,rand());
  i++;
  return string(b);
}

struct part_t {
  part_t() {delim=randdelim();}
  
  int type;
  //string delim;
  string fname;
  string name;
  string id;
list<string> lb;
  list<string> l;
  bool headok;
  
  string delim;
  list<part_t> lpart;
};


string parsebound(const char *s) {
  char bu[1000];
  int i=0,j;
  
  while(s[i] && s[i]!='"') i++;
  if(s[i]==0) return string("");
  i++;
  j=i;
  while(s[i] && s[i]!='"' ) i++;
  if(s[i]==0) return string("");
  
  strncpy(bu,s+j,i-j);
  bu[i-j]=0;
  return string(bu);
}

void parsebound(list<string> &l, list<part_t> &lp, string &delim) {
  lp.clear();
  list<string> lb;
  int n=0;
  list<string>::iterator it;
  for(it=l.begin();it!=l.end();++it) {
    if(*it==delim) {
      if(n) {
	part_t p;
	p.headok=true;
	p.type=STRING;
	p.l.splice(p.l.begin(),lb);
	lp.push_back(p);
      } else
	lp.clear();
      n++;
    } else {
      lb.push_back(*it);
    }
  }
  //printf("n=%d\n",n);
}

void parse(const char *s, string &a, string &b, string &c) {
  char bu[1000];
  int i=0,j;
  
  a=b=c=string("");
  
  while(s[i]!=':') i++;
  i++;
  while(s[i] && s[i]!='"') i++;
  if(s[i]==0) return;
  i++;
  j=i;
  while(s[i] && s[i]!='"' ) i++;
  if(s[i]==0) return;
  
  strncpy(bu,s+j,i-j);
  bu[i-j]=0;
  a=string(bu);
  i++;

  while(s[i] && s[i]!='"') i++;
  if(s[i]==0) return;
  i++;
  j=i;
  while(s[i] && s[i]!='"' ) i++;
  if(s[i]==0) return;

  strncpy(bu,s+j,i-j);
  bu[i-j]=0;
  b=string(bu);
  i++;

  while(s[i] && s[i]!='"') i++;
  if(s[i]==0) return;
  i++;
  j=i;
  while(s[i] && s[i]!='"' ) i++;
  if(s[i]==0) return;

  strncpy(bu,s+j,i-j);
  bu[i-j]=0;
  c=string(bu);
  i++;
}

void add(ostream &o,const string &s) {
  o<<s<<endl;
}

void add(ostream &o,list<string> &s) {
  list<string>::iterator it;
  for(it=s.begin();it!=s.end();++it) 
    add(o,*it);
}


void addfileplain(ostream &o, string &name) {
  char bc[1000];
  FILE *f=fopen(name.c_str(),"rt");
  if(f==NULL) {
    printf("ERROR:fopen\n");
    exit(1);
    return;
  }
  int s;
  do {
    s=fread(bc,1,1000,f);
    bc[s]=0;
    if(s) o<<string(bc);
  } while(s);
  fclose(f);
}

void addfileb64(ostream &o, string &fn) {
  char bc[1000];
  sprintf(bc,"uuenview -b \"%s\" > tmpsys",fn.c_str());
  //printf(bc);
  if(system(bc)!=0) {
    printf("WARN: cannot open file\n");
    printf("command: %s\n",bc);
    exit(1);
    return;
  }
  FILE *f=fopen("tmpsys","rt");
  if(f==NULL) {
    printf("ERROR:fopen\n");
    exit(1);
    return;
  }
  int s;
  do {
    s=fread(bc,1,1000,f);
    bc[s]=0;
    if(s) o<<string(bc);
  } while(s);
  fclose(f);
}

//void addfiledelim(ostream &o, string &name, string &d) {
//  add(o,d);
//}

string getmime(string &fn) {
  char bc[1000];
  sprintf(bc,"file -ibr \"%s\" > tmpsys",fn.c_str());
  //printf(bc);
  if(system(bc)!=0) {
    printf("WARN: cannot open file\n");
    printf("command: %s\n",bc);
    exit(1);
    return string("");
  }
  FILE *f=fopen("tmpsys","rt");
  int s=fread(bc,1,999,f);
  fclose(f);
  if(s<=0) return string("");
  bc[s]=0;
  if(bc[s-1]=='\n') bc[s-1]=0;
  return string(bc);
}

void convertslash(string &s) {
  char b[1000];
  int i;
  strcpy(b,s.c_str());
  for(i=0;b[i];i++) if(b[i]=='\\') b[i]='/';
  s=string(b);
}

void addparts(ostream &o, list<part_t> &l, const string &d);

#if 0
string getext(string s) {
  int i=s.size()-1;
  while(i>=0 && s.c_str()[i]!='.') i--;
  if(i>=0) {
    i++;
    char b[1000];
    strcpy(b,s.c_str()+i);
    //printf("ext: '%s'\n",b);
  }
  return string(b);
}
#endif

void addpart(ostream &o, part_t &p) {
  if(p.type==PARTSMULTI || p.type==PARTSALT || p.type==PARTSREL) {
    char bu[1000];
    if(p.type==PARTSMULTI)
      sprintf(bu,"Content-Type: multipart/mixed; boundary=\"%s\"",p.delim.c_str());
    else if(p.type==PARTSALT)
      sprintf(bu,"Content-Type: multipart/alternative; boundary=\"%s\"",p.delim.c_str());
    else 
      sprintf(bu,"Content-Type: multipart/related; boundary=\"%s\"",p.delim.c_str());
    add(o,string(bu));
    add(o,string(""));
    addparts(o,p.lpart,p.delim);
    return ;
  }

  if(p.type==STRING) {
    if(p.headok==false) {
      add(o,string("Content-Type: text/plain;"));
      add(o,string("              charset=\"iso-8859-1\""));
      add(o,string("Content-Transfer-Encoding: quoted-printable"));
      add(o,string(""));
    }
    add(o,p.l);
    add(o,string(""));
    return;
  }

  if(p.type==PLAIN ||p.type==HTML) {
    if(p.headok==false) {
      string type="";
      if(p.type==HTML) type="text/html";
      
      if(type=="") type=getmime(p.fname);
      
      if(type=="") {
	if(p.type==PLAIN)
	  type=string("text/plain");
	else 
	  type=string("text/html");
      }
      
      add(o,string("Content-Type: ")+type+"");
      add(o,string(""));
    }
    
    addfileplain(o,p.fname);
    add(o,string(""));
    return;
  }

  if(p.type==BASE64 || p.type==IMG) {
    if(p.headok==false && p.type==BASE64)  {
      string type=getmime(p.fname);;

      if(type=="") {
	//printf("WARN mime type= '%s' name='%s'\n",type.c_str(),p.name.c_str());
	type = string("application/octet-stream");
      }
      
      add(o,string("Content-Type: ")+type+";");
      add(o,string("              name=\"")+p.name+"\"");
      add(o,string("Content-Transfer-Encoding: base64"));
      add(o,string("Content-Disposition: attachment;"));
      add(o,string("              filename=\"")+p.name+"\"");
      add(o,string(""));
    }

    if(p.headok==false && p.type==IMG) {
      string type=getmime(p.fname);;
      
      if(type=="") {
	//printf("WARN mime type= '%s' name='%s'\n",type.c_str(),p.name.c_str());
	type = string("application/octet-stream");
      }
      
      add(o,string("Content-Type: ")+type+";");
      add(o,string("Content-ID: <"+p.id+">"));
      add(o,string("Content-Transfer-Encoding: base64"));
      add(o,string(""));
    }
    addfileb64(o,p.fname);
    add(o,string(""));
    return;
  }
}

void addparts(ostream &o, list<part_t> &l, const string &d) {
  list<part_t>::iterator it;
  for(it=l.begin();it!=l.end();++it) {
    if(it==l.begin()) {
      add(o,string("--")+d);
    }
    addpart(o,*it);
    add(o,string("--")+d);
  }
}

void extract(ifstream &o,list<string> &s) {
  char b[65536];
  s.clear();
  int i=0;
  streampos pos;
  bool end;
  do {
    if(i) {
      s.push_back(string(b));
    }
    pos=o.tellg();
    o.getline(b,65535);
    end=(strncmp(b,"From ",5)==0);
    //printf("toto '%s'\n",b);
    i++;
  } while ((!o.eof()) && (!end || i<=1 ));
  //printf("\n--------------------------------------\n\n");
  if(!o.eof()) {
    o.seekg(pos);
  }
}

void split(list<string> &l, list<string> &head, list<string> &boddy) {
  head.clear();
  boddy.clear();
  string s;
  do {
    if(l.empty()) return;
    s=l.front();
    l.pop_front();
    if(s.size()) head.push_back(s);
  } while(s.size());

  while(!l.empty()) {
    s=l.front();
    l.pop_front();
    boddy.push_back(s);
  };
}

int main(int argc, char ** argv) {
  srand(time(NULL));

  if(argc!=3) {
    printf("usage...\n");
    exit(1);
  }

  ifstream is(argv[1],ios_base::in);
  ofstream os(argv[2],ios_base::out|ios_base::trunc);
  
  list<string> ls,lh,lb;
  int lss;

  int nbr=0;

  do {
    //printf("=============================================\n");

    bool dbg=false;

    int att=0;
    int html=0;
    int img=0;

    extract(is,ls);

    lss=ls.size();
    if(lss) {
      split(ls,lh,lb);
      
      if(lh.size()<=5) {
	printf("WARN sh=%d sb=%d\n",lh.size(),lb.size());
	dbg=true;
      }
      
      list<string>::iterator it,it2;
      list<part_t> latt;

      list<string> lhc;

      string bound;

      list<part_t> lparthtml;
      list<part_t> lpartimg;

      for(it=lh.begin();it!=lh.end();) {
	it2=it;
	++it2;
	if(dbg) {
	  if(0==strncmp(it->c_str(),"From",4))
	    cout << "'" << *it << "'\n";
	  if(0==strncmp(it->c_str(),"Subject",7))
	    cout << "'" << *it << "'\n";
	}
	/*
	if(0==strncmp(it->c_str(),"X-CalypsoHtml",9+4) || 0==strncmp(it->c_str(),"X-Attach",8))	{
	  string a,b,c;
	  parse(it->c_str(),a,b,c);
	  printf("parsage: '%s' '%s' '%s'\n",a.c_str(),b.c_str(),c.c_str());
	}
	*/
	if(0==strncmp(it->c_str(),"X-CalypsoHtmlBody",9+4+4)) {
	  html++;
	  string a,b,c;
	  parse(it->c_str(),a,b,c);
	  //printf("parsage html: '%s' '%s' '%s'\n",a.c_str(),b.c_str(),c.c_str());
	  part_t p;
	  p.headok=false;
	  p.type=HTML;
	  p.fname=a;
	  convertslash(p.fname);
	  p.name=b;
	  lparthtml.push_back(p);
	} else if(0==strncmp(it->c_str(),"Content-Type: multipart/"
			     ,strlen("Content-Type: multipart/"))) {
	  bound=parsebound(it->c_str());
	  if(bound!="") bound = string ("--")+ bound;
	  //printf("DELIM='%s'\n",bound.c_str());
	  lh.erase(it);
	} else if(0==strncmp(it->c_str(),"Content-"
			     ,strlen("Content-"))) {
	  lhc.push_back(*it);
	  lh.erase(it);
	} else if(0==strncmp(it->c_str(),"X-CalypsoHtmlImg",9+4+3)) {
	  img++;
	  string a,b,c;
	  parse(it->c_str(),a,b,c);
	  part_t p;
	  p.headok=false;
	  p.type=IMG;
	  p.fname=a;
	  convertslash(p.fname);
	  p.id=b;
	  p.name=c;
	  if(b=="") {
	    printf("WARN parsage img: '%s' '%s' '%s'\n",a.c_str(),b.c_str(),c.c_str());
	    p.type=BASE64;
	    latt.push_back(p);
	  } else 
	    lpartimg.push_back(p);
	} else if(0==strncmp(it->c_str(),"X-Attachment:",strlen("X-Attachment:"))) {
	  att++;
	  string a,b,c;
	  parse(it->c_str(),a,b,c);
	  part_t p;
	  p.headok=false;
	  p.type=BASE64;
	  p.fname=a;
	  convertslash(p.fname);
	  p.name=b;
	  latt.push_back(p);
	}
	it=it2;
      }

      if(html==0 && img>0) {
	printf("ERROR html=%d img=%d att=%d\n",html,img,att);
	exit(1);
      }

      if(html>1) {
	printf("WARN html=%d img=%d att=%d\n",html,img,att);
      }

      //if(html + img + att)
      //	printf("html=%d img=%d att=%d\n",html,img,att);

      list<part_t> lpart;
      
      if(bound!="") {
	parsebound(lb,lpart,bound);
      } 
      
      if(lpart.size()==0) {
	part_t p;
	p.headok=true;
	p.type=STRING;
	p.l.splice(p.l.begin(),lb);
	p.l.push_front(string(""));
	p.l.splice(p.l.begin(),lhc);
	lpart.push_back(p);
      }
      
      if(html) {
	if(lpart.size()>1) {
	  printf("WARN PART: %d html=%d\n",lpart.size(),html);
	  exit(1);
	}

	part_t p;
	p.type=PARTSALT;
	p.lpart.splice(p.lpart.begin(),lparthtml);
	p.lpart.splice(p.lpart.begin(),lpart);
	lpart.push_back(p);
      }

      if(img) {
	part_t p;
	p.type=PARTSREL;
	p.lpart.splice(p.lpart.begin(),lpartimg);
	p.lpart.splice(p.lpart.begin(),lpart);
	lpart.push_back(p);
      }
      
      if(lpart.size()<=0) {
	printf("WARN PART: %d\n",lpart.size());
	exit(1);
      }
      
      string delim=randdelim();
      
      lpart.splice(lpart.end(),latt);
      
      char bu[1000];
      sprintf(bu,"Content-Type: multipart/mixed; boundary=\"%s\"",delim.c_str());
      lh.push_back(string(bu));
      
      add(os,lh);
      add(os,string(""));
      addparts(os,lpart,delim);

      nbr++;
      
    }

  } while(lss);

  printf("%d messages\n",nbr);
  
  return 0;
}

