#include "process.h"
#include "lock_maildrop.h"
#include "rw.h"
#include "md5.h"
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>



#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

static struct mail * mails;
static int number_mails;

extern const char * tmp_dir;


void get_stats(long * msg, long * size) {
  long s = 0, m = 0;
  int i;
  for (i=0;i<number_mails;i++) {
    if (!mails[i].deleted) {
      m++;
      s+= mails[i].adjsize;
    }
  }
  *msg = m;
  *size = s;
}

static void list_all(int fd) {
  char line[MAXLINE+1];
  int i;
  write_line(fd,"+OK\r\n");
  for (i=0;i<number_mails;i++) {
    if (!mails[i].deleted) {
      snprintf(line,MAXLINE,"%u %lu\r\n",i+1,mails[i].adjsize);
      write_line(fd,line);
    }
  }
  write_line(fd,".\r\n");
}

static void list_msg(int fd, unsigned int num) {
  char line[MAXLINE+1];
  if (num<1 || (num-1) >= number_mails || mails[num-1].deleted==1) {
    write_line(fd,"-ERR no such message\r\n");
    return;
  }
  snprintf(line,sizeof(line),"+OK %u %ld\r\n",num,mails[num-1].adjsize);
  write_line(fd,line);
}

void do_list(int fd, char * line) {
  unsigned int msg_num = 0;
  if (sscanf(line+5,"%u",&msg_num)!=1) {
    list_all(fd);
  } else {
    list_msg(fd,msg_num);
  }
}

static void print_msg(int fd, unsigned int msg_num) {
  char line[MAXLINE+1];
  int done = 0;
  char * ptr;
  FILE * f = fopen( mails[msg_num].filename, "a+b" );
  if (!f) {
     /* print some kind of warning */
     syslog( LOG_ERR, "Failed to open tmp file `%s': %s",  mails[msg_num].filename, strerror(errno));
     return;
  }
  rewind(f);

  line[0] = 0;
  fgets(line,MAXLINE,f);

  while (!feof(f)) {
    if ((line[0]=='\n' || line[0]=='\r') && !done) {
      write_line(fd,"\r\n");
      done = 1;
    } else {
      ptr = strchr(line, '\r');
      if (ptr == NULL) ptr = strchr(line, '\n');
      if (ptr != NULL) ptr[0] = 0;
      if (line[0] == '.') write_line(fd, ".");
      write_line(fd, line);
      write_line(fd, "\r\n");
    }
    fgets(line,MAXLINE,f);
  }
  fclose( f );
}

void retrieve_msg(int fd, char * line) {
  unsigned int msg_num;
  if (sscanf(line+5,"%u",&msg_num)!=1) {
    write_line(fd,"-ERR invalid arguments\r\n");
    return;
  }
  if (msg_num<1 || (msg_num-1) >= number_mails || mails[msg_num-1].deleted==1) {
    write_line(fd,"-ERR no such message\r\n");
    return;
  }
  write_line(fd,"+OK\r\n");
  print_msg(fd,msg_num-1);
  write_line(fd,".\r\n");
}

void show_top_msg(int fd, char * l) {
  unsigned int msg_num, lines, i;
  char line[MAXLINE+1];
  char * ptr;
  FILE *f;

  if (sscanf(l+4,"%u %u",&msg_num,&lines)!=2) {
    write_line(fd,"-ERR invalid arguments\r\n");
    return;
  }
  if (msg_num<1 || 
      (msg_num-1) >= number_mails || 
      mails[msg_num-1].deleted==1) {
    write_line(fd,"-ERR no such message\r\n");
    return;
  }
  write_line(fd,"+OK\r\n");
  f = fopen( mails[msg_num-1].filename, "a+b" );
  if (!f) {
    /* throw some kind of warning */
    syslog( LOG_ERR, "Failed to open tmp file `%s': %s",  mails[msg_num].filename, strerror(errno));
    return;
  }

  rewind( f );
  line[0] = 0;
  fgets(line,MAXLINE,f);

  while ((line[0]!='\n' && line[0]!='\r') && !feof(f)) {
    ptr = strchr(line, '\r');
    if (ptr == NULL) ptr = strchr(line, '\n');
    if (ptr != NULL) ptr[0] = 0;
    if (line[0] == '.') write_line(fd, ".");
    write_line(fd, line);
    write_line(fd, "\r\n");
    fgets(line,MAXLINE,f);
  }
  if (!feof(f)) {
    write_line(fd,"\r\n");
    fgets(line,MAXLINE,f);
    for (i=0;i<lines && !feof(f);i++) {
      ptr = strchr(line, '\r');
      if (ptr == NULL) ptr = strchr(line, '\n');
      if (ptr != NULL) ptr[0] = 0;
      if (line[0] == '.') write_line(fd, ".");
      write_line(fd, line);
      write_line(fd, "\r\n");
      fgets(line,MAXLINE,f);
    }
  }
  write_line(fd,".\r\n");
  fclose( f );
}


void delete_msg(int fd, char * line) {
  unsigned int msg_num;
  if (sscanf(line+5,"%u",&msg_num)!=1) {
    write_line(fd,"-ERR invalid arguments\r\n");
    return;
  }
  if (msg_num<1 || (msg_num-1) >= number_mails) {
    write_line(fd,"-ERR no such message\r\n");
    return;
  }
  mails[msg_num-1].deleted = 1;
  write_line(fd,"+OK\r\n");
}

void reset_msg(void) {
  int i;
  for (i=0;i<number_mails;i++) {
    mails[i].deleted = 0;
  }
}

int process_mails(char * maildrop) {
  FILE * f;
  char line[MAXLINE+1];
  int had_cr_or_nl;
  long adj;
  FILE *fTmp = NULL;
  int iTmpFile;

  f = fopen(maildrop,"a+");	/* open for reading and writing, create if doesn't exists */
  if (f==NULL)
    return 0;

  srand(time(NULL));

  /* fseek to the beginning */
  rewind(f);
  /* correct the permission */
  chmod(maildrop, 0600);

  lock_fd(fileno(f));
  mails = NULL;
  number_mails = 0;
  fgets(line,MAXLINE,f);
  if (strncmp(line,"From ",5)!=0) {
    if (feof(f))
      return 1;
    else
      return 0;
  }
  had_cr_or_nl = 1;
  adj = 0;
  while (!feof(f)) {
    if (strncmp(line,"From ",5)==0 && had_cr_or_nl) {
      if (number_mails>0) {
        mails[number_mails-1].size = ftell(fTmp);
        mails[number_mails-1].adjsize = mails[number_mails-1].size + adj;
	fclose( fTmp );
      }
      adj = 0;
      number_mails++;
      mails = realloc(mails,number_mails*sizeof(struct mail));
      if (mails==NULL) {
        return 0;
      }
      memset( mails[number_mails-1].filename, '\0', sizeof(mails[number_mails-1].filename));
      snprintf( mails[number_mails-1].filename, sizeof(mails[number_mails-1].filename), "%s/.akpop3d_%u_%u_XXXXXX", tmp_dir, getpid(), rand());
      iTmpFile = mkstemp( mails[number_mails-1].filename );
      if (iTmpFile == -1) {
         syslog(LOG_ERR, "Failed to open tmp file `%s': %s", mails[number_mails-1].filename, strerror(errno));
         return(0);
      }
      close( iTmpFile );
      fTmp = fopen( mails[number_mails-1].filename, "w+b" );
      if ( !fTmp )
      {
         syslog( LOG_ERR, "Failed to open tmp file `%s': %s",  mails[number_mails-1].filename, strerror(errno));
	 return( 0 );
      }
      mails[number_mails-1].deleted = 0;
    }
    fputs(line,fTmp);
    fgets(line,MAXLINE,f);
    if (strchr(line, '\r') != NULL) {
      had_cr_or_nl = 1;
    } else if (strchr(line, '\n') != NULL) {
      had_cr_or_nl = 1;
      adj++;
    } else {
      had_cr_or_nl = 0;
    }
  }
  unlock_fd(fileno(f));
  fclose(f);
  fputs(line,fTmp);
  mails[number_mails-1].size = ftell(fTmp);
  mails[number_mails-1].adjsize = mails[number_mails-1].size + adj;
  fclose( fTmp );
  return 1;
}

static void fcopy(FILE * f1, FILE * f2) {
  char line[MAXLINE+1];
  fgets(line,MAXLINE,f2);
  while (!feof(f2)) {
    fputs(line,f1);
    fgets(line,MAXLINE,f2);
  }
}

void do_update(char * maildrop) {
  int i;
  FILE * f = fopen(maildrop,"w");
  if (f==NULL) {
    syslog(LOG_ERR,"%s: %s: %s","failed to write to maildrop",maildrop,strerror(errno));
    return;
  }
  lock_fd(fileno(f));
  for (i=0;i<number_mails;i++) {
    if (!mails[i].deleted) {
      FILE *fTmp = fopen( mails[i].filename, "a+b" );
      if ( fTmp ) {
        rewind( fTmp );
        fcopy(f,fTmp);
        fclose( fTmp );
      } else {
         syslog( LOG_ERR, "Failed to open tmp file `%s': %s",  mails[i].filename, strerror(errno) );
         /* throw in some kind of warning */
      }
    }
  }
  unlock_fd(fileno(f));
  fclose(f);
  free(mails);
}

void do_cleanup()
{
	int i;
	for ( i = 0; i < number_mails; i++ ) 
		unlink( mails[i].filename );
}

void output_uidl(int fd, int msgnum) {
  char linebuf[128];
  unsigned char h[17];
  FILE * f;
  char buf[256];

  f = fopen( mails[msgnum].filename, "a+b" );
  if ( !f )
  {
      syslog( LOG_ERR, "Failed to open tmp file `%s': %s",  mails[msgnum].filename, strerror(errno));
      return;
  }

  rewind(f);
  fread(buf,255,1,f);
  fclose(f);
  md5_buffer(buf, 255, h);

  snprintf(linebuf, sizeof(linebuf), "%u %08lx%08lx%08lx%08lx\r\n", msgnum+1,
    (unsigned long) h[0] + (h[1]<<8) + (h[2]<<16) + (h[3]<<24),
    (unsigned long) h[4] + (h[5]<<8) + (h[6]<<16) + (h[7]<<24),
    (unsigned long) h[8] + (h[9]<<8) + (h[10]<<16) + (h[11]<<24),
    (unsigned long) h[12] + (h[13]<<8) + (h[14]<<16) + (h[15]<<24)
  );

  write_line(fd,linebuf);
}

void show_uidl(int fd, char * line) {
  int msgnum;

  while (line[0] == ' ') line ++;
  if (isdigit(line[0])) {
    msgnum = atoi(line);
    if ((msgnum < 1) || (msgnum > number_mails) || (mails[msgnum-1].deleted)) {
      write_line(fd, "-ERR no such message\r\n");
      return;
    }
    write_line(fd, "+OK ");
    output_uidl(fd, msgnum-1);
  } else {
    write_line(fd, "+OK\r\n");
    for (msgnum=0;msgnum<number_mails;msgnum++) {
      if (mails[msgnum].deleted) continue;
      output_uidl(fd, msgnum);
    }
    write_line(fd, ".\r\n");
  }
}


syntax highlighted by Code2HTML, v. 0.9.1