#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>

#ifndef WIN32
#include <unistd.h>
#endif

#include <fstream.h>

#include <tools.h>
#include <folder.h>
#include <logf.h>
#include "getrfc.h"

const char *getrfct::getpath()
 {
  return fp.getpath();
 }

getrfct::getrfct()
 {
  cached_nr=0xffffffff;
  rfcm.data=(char *)NULL;
  mimestruct=(mimestructt *)NULL;
  index=header=-1;
  opened=FALSE;
 }

getrfct::~getrfct()
 {
  clear_cached();
  if(index>=0) ::close(index);
  if(header>=0) ::close(header);
 }

dword getrfct::get_number_of_messages()
 {
  if(index<0) return 0;

  return filelength(index)/sizeof fi;
 }

bool getrfct::close()
 {
  clear_cached();
  if(index>=0) ::close(index);
  if(header>=0) ::close(header);
  index=header=-1;
  opened=FALSE;
  return FALSE;
 }

bool getrfct::open_files()
 {
  char filename[DIRLEN];

  if(opened) close();

  snprintf(filename, DIRLEN, "%sindex", fp.getpath());
  index=::open(filename, O_RDWR|O_BINARY|O_CREAT, S_IREAD|S_IWRITE);

  snprintf(filename, DIRLEN, "%sheader", fp.getpath());
  header=::open(filename, O_RDWR|O_BINARY|O_CREAT, S_IREAD|S_IWRITE);

  return index<0 || header<0;
 }

bool getrfct::open_mail(const std::string &name, const std::string &subfolder)
 {
  std::string folder_name;

  if(subfolder=="")
   {
    folder_name="0@"; // default subfolder
    folder_name+=name;
   }
  else
    folder_name=subfolder+"@"+name;

  fp.setname(folder_name);
  fp.read();

  return open_files();
 }

bool getrfct::open_news(const std::string &name)
 {
  fp.setname(name);
  fp.read();

  return open_files();
 }

//
// Binary search for uids
//

bool getrfct::search_uid(dword uid, dword &nr)
 {
  if(index<0) return TRUE;

  dword number_of_messages=get_number_of_messages();

  #if 1
  dword begin=0, end=number_of_messages;

  while(begin<end)
   {
    nr=begin+(end-begin)/2;

    get_folderindex(nr);

    if(fi.id==uid)
      return FALSE;
    
    if(fi.id<uid)
      begin=nr+1;
    else // fi.uid>uid
      end=nr;
   }
  #else
  for(nr=0; nr<number_of_messages; nr++)
   {
    get_folderindex(nr);

    if(fi.id==uid)
      return FALSE;
   }
  #endif
  return TRUE;
 }

void getrfct::clear_cached()
 {
  if(rfcm.data!=(char *)NULL) { delete (char *)rfcm.data; rfcm.data=(char *)NULL; }
  if(mimestruct!=(mimestructt *)NULL) { delete mimestruct; mimestruct=(mimestructt *)NULL; }
  cached_body=cached_folderindex=cached_header=cached_structure=FALSE;
 }

void getrfct::check_cache(dword nr)
 {
  if(nr!=cached_nr)
   {
    clear_cached();
    cached_nr=nr;
   }
 }

bool getrfct::get_folderindex(dword nr)
 {
  check_cache(nr);

  if(!cached_folderindex) 
   {
    lseek(index, nr*sizeof(fi), SEEK_SET);
    if(read(index, &fi, sizeof fi)!=sizeof fi)
      return TRUE;

    cached_folderindex=TRUE;
   }

  return FALSE;
 }

bool getrfct::get_header(dword nr)
 {
  check_cache(nr);

  if(get_folderindex(nr))
    return TRUE;

  if(!cached_header)
   {
    char *data;

    data=new char[fi.headerlen];
    if(data==(char *)NULL)
      logf.internalerror("getrfct::getheader", "out of memory");

    lseek(header, fi.headeroffs, SEEK_SET);

    if(read(header, data, fi.headerlen)!=(ssize_t)fi.headerlen)
     {
      delete data;
      return TRUE;
     }

    if(fi.flags.rfc)
     {
      if(rfcm.data!=NULL) delete (char *)rfcm.data;
      rfcm.data=data;
      rfcm.endofmail=rfcm.endoffile=data+fi.headerlen;
      rfcm.parse();
     }
    else // Zconnect
     {
     }

    cached_header=TRUE;
   }

  return FALSE;
 }

bool getrfct::get_body(dword nr)
 {
  check_cache(nr);

  if(get_folderindex(nr))
    return TRUE;

  if(!cached_body)
   {
    char *data;

    data=new char[fi.len+fi.headerlen];
    if(data==(char *)NULL)
      logf.internalerror("getrfct::getbody", "out of memory");

    lseek(header, fi.headeroffs, SEEK_SET);

    if(!fi.flags.bodyextern)
     {
      dword total=fi.len+fi.headerlen;
      if(read(header, data, total)!=total)
        return TRUE;
     }
    else
     {
      char filename[DIRLEN];

      if(read(header, data, fi.headerlen)!=fi.headerlen)
        return TRUE;

      snprintf(filename, DIRLEN, "%s%s", fp.getpath(), fi.file);
      ifstream bodyfile(filename, ios::binary|ios::in);
      if(!bodyfile) return TRUE;
      if(!bodyfile.read(data+fi.headerlen, fi.len)) return TRUE;
     }

    if(fi.flags.rfc)
     {
      if(rfcm.data!=NULL) delete (char *)rfcm.data;
      rfcm.data=data;
      rfcm.endofmail=rfcm.endoffile=data+fi.len+fi.headerlen;
      rfcm.parse();
     }
    else // Zconnect
     {
     }

    cached_body=TRUE;
    cached_header=TRUE;
   }

  return FALSE;
 }

bool getrfct::get_structure(dword nr)
 {
  check_cache(nr);
 
  if(get_body(nr))
    return TRUE;

  if(!cached_structure)
   { 
    mimestruct=new mimestructt;
    mimestruct->mimeparse(rfcm);
    cached_structure=TRUE;
   }

  return FALSE;
 }


syntax highlighted by Code2HTML, v. 0.9.1