/* 3mailin.c
 * Public Domain
 *
 * contains functions to read a type 3 ASCII packet
 *
 * code as presented is memory thrifty.  if you've got plenty of memory,
 * here are some performance suggestions:
 *
 *      instead of reading headers a line at a time, read into a
 *      big buffer and parse in memory (32K guarantees getting full
 *      header under "worst" conditions).  use same buffer used for
 *      message text to (normally) prevent reloading message to get
 *      text.
 *
 *  and/or
 *
 *      dynamically adjust size of msg text buffer based on average
 *      size (or most common size) of msgs received to date.
 *
 *
 *  user-callable function:
 *
 *    long process_3pkt (void *anon, char *fname, int *error);
 *
 *      anon = private data for "callback" functions
 *      fname = name of packet to process
 *      error = returned error code
 *      returns number of messages processed
 *
 *    see comments @ function (end of file)
 */

#include "3mail.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <share.h>



int open_3pkt (int i3handle,char *fname, int *error) {

  /* open a type 3 packet for input
   * i3handle = file handle in use (or -1 if none)
   * fname = name of packet to open
   * *error = error return code
   */

  if(i3handle != -1) close(i3handle);   /* close if already open */
  i3handle = sopen(fname,O_RDONLY | O_BINARY,SH_DENYNO);
  if(i3handle == -1) *error = NOOPEN3;
  return i3handle;
}


int close_3pkt (int i3handle) { /* close a packet */

  if(i3handle != -1) close(i3handle);   /* close if good handle */
  i3handle = -1;                      /* in case you made it global... */
  return i3handle;
}


PHDR3 * free_3pkthdr (PHDR3 *cpkt) {

  /* free dynamically allocated memory of a packet header structure
   * including linked list of TAG3 structures attached, if any
   */

  TAG3 *info,*next;

  if(cpkt->from) free(cpkt->from);
  if(cpkt->to) free(cpkt->to);
  if(cpkt->creator) free(cpkt->creator);
  if(cpkt->pword) free(cpkt->pword);
  if(cpkt->area) free(cpkt->area);
  if(cpkt->head) {
    info = cpkt->head;
    while(info) {
      next = info->next;
      if(info->tag) free(info->tag);
      if(info->data) free(info->data);
      free(info);
      info = next;
    }
  }
  free(cpkt);
  cpkt = NULL;
  return cpkt;
}



MHDR3 * free_3msghdr (MHDR3 *cmsg) {

  /* free dynamically allocated memory of a message header structure
   * including linked list of TAG3 structures attached, if any
   */

  TAG3 *info,*next;

  if(cmsg->from) free(cmsg->from);
  if(cmsg->to) free(cmsg->to);
  if(cmsg->subj) free(cmsg->subj);
  if(cmsg->date) free(cmsg->date);
  if(cmsg->area) free(cmsg->area);
  if(cmsg->id) free(cmsg->id);
  if(cmsg->ref) free(cmsg->ref);
  if(cmsg->head) {
    info = cmsg->head;
    while(info) {
      next = info->next;
      if(info->tag) free(info->tag);
      if(info->data) free(info->data);
      free(info);
      info = next;
    }
  }
  free(cmsg);
  cmsg = NULL;
  return cmsg;
}


PHDR3 * create_3pkthdr (void) {

  /* create an empty PHDR3 pkt header structure
   * returns a pointer to the PHDR3 structure created
   */

  PHDR3 *info;

  info = (PHDR3 *)malloc(sizeof(PHDR3));
  if(info) {
    info->from = info->to = info->creator = info->pword = info->area = NULL;
    info->head = NULL;
  }
  return info;
}


MHDR3 * create_3msghdr (void) {

  /* create an empty MHDR3 msg header structure
   * returns a pointer to the MHDR3 structure created
   */

  MHDR3 * info;

  info = (MHDR3 *)malloc(sizeof(MHDR3));
  if(info) {
    info->from = info->to = info->subj = info->date = info->id =
      info->ref = info->area = NULL;
    info->head = NULL;
  }
  return info;
}


PHDR3 * read_3pkthdr (int i3handle, PHDR3 *cpkt, int *error) {

  /* could use more error & grunge checking
   * returns NULL on error OR EOP
   * *error may contain more info
   * fills in cpkt from file represented by i3handle
   */

  char   s[257];

  *error = NOTPKT3;
  if(cpkt) free_3pkthdr(cpkt);
  if(fgets3(s,256,i3handle)) {
    if(!strcmp(s,ASCII3ID)) {
      *error = BADPKTHDR3;
      cpkt = create_3pkthdr();
      if(cpkt) {
        if(fgets3(s,256,i3handle)) {
          cpkt->from = strdup3(s);
          if(fgets3(s,256,i3handle)) {
            cpkt->to = strdup3(s);
            if(fgets3(s,256,i3handle)) {
              cpkt->creator = strdup3(s);
              if(cpkt->creator) {
                if(fgets3(s,256,i3handle)) {
                  cpkt->pword = strdup3(s);
                  if(fgets3(s,256,i3handle)) {
                    cpkt->area = strdup3(s);
                    if(!(!cpkt->area && !cpkt->to)) {

                      while(!eof(i3handle)) {

                        char *data;
                        TAG3 *info;
                        TAG3 *here = cpkt->head;

                        if(!fgets3(s,256,i3handle)) break;
                        if(!*s) break;      /* end of packet header */
                        data = strchr(s,' ');
                        if(data) {
                          *data = 0;
                          data++;
                          while(*data == ' ' || *data == '\t') data++;
                        }

                        /* code dealing with experimental tags goes here */

                        if(!strnicmp(s,"X-",2)) continue;  /* then "strip"
                                                            * unrecognized
                                                            * experimental
                                                            * tags */

                        info = (TAG3 *)malloc(sizeof(TAG3));
                        if(!info) break;        /* tsk, tsk */
                        info->tag = strdup3(s);
                        if(!info->tag) {
                          free(info);
                          break;
                        }
                        info->data = strdup3(data);
                        if(!cpkt->head) cpkt->head = info;
                        else here->next = info;
                        info->next = NULL;
                        here = info;
                      }
                      *error = NOERR3;
                      return cpkt;   /* success */

                    }
                  }
                }
              }
            }
          }
        }
      }
      else *error = NOMEM3;
    }
  }

  if(cpkt) cpkt = free_3pkthdr(cpkt); /* failure */
  return cpkt;
}



long find_next_3msg (int i3handle,int *error) {

  /* find the next message in a packet (used when a grunged header is
   * encountered to attempt to resynch
   * just searches for a NUL in the file
   */

  char *buf,*p;
  int   len;
  long  pos;

  buf = (char *)malloc(BLKSIZE3);
  if(!buf) {
    *error = NOMEM3;
    return -1L;
  }

  while(!eof(i3handle)) {
    pos = tell(i3handle);
    len = read(i3handle,buf,BLKSIZE3);
    if(len == -1) {
      *error = READERR3;
      free(buf);
      return -1L;
    }
    if(!len) break;
    p = buf;
    while(p < buf + len) {
      if(!*p) {
        p++;
        lseek (i3handle, pos + ((int)p - (int)buf), SEEK_SET);
        free(buf);
        return tell(i3handle);
      }
      p++;
    }
  }
  *error = EOP3;
  free(buf);
  return -1L;
}



MHDR3 * read_3msghdr (int i3handle, MHDR3 *cmsg, int *error) {

  /* could use more error & grunge checking
   * return NULL on error OR EOP
   * *error may contain more info
   * fills in msg header structure cmsg from file represented
   * by file handle i3handle
   */

  char        s[257];
  int         tries = 0;

ReTry:

  *error = EOP3;
  if(cmsg) free_3msghdr(cmsg);
  if(fgets3(s,256,i3handle) && *s) {
    cmsg = create_3msghdr();
    if(cmsg) {
      *error = BADMSGHDR3;
      cmsg->from = strdup3(s);
      if(cmsg->from) {
        if(fgets3(s,256,i3handle)) {
          cmsg->to = strdup3(s);
          if(fgets3(s,256,i3handle)) {
            cmsg->subj = strdup3(s);
            if(fgets3(s,256,i3handle)) {
              cmsg->date = strdup3(s);
              if(cmsg->date) {
                if(fgets3(s,256,i3handle)) {
                  cmsg->area = strdup3(s);
                  if(fgets3(s,256,i3handle)) {
                    cmsg->id = strdup3(s);
                    if(cmsg->id) {
                      if(fgets3(s,256,i3handle)) {
                        cmsg->ref = strdup3(s);
                        while(!eof(i3handle)) {

                          char *data = NULL;
                          TAG3 *info;
                          TAG3 *here = cmsg->head;

                          if(!fgets3(s,256,i3handle)) break;
                          if(!*s) break;        /* end of message header */
                          data = strchr(s,' ');
                          if(data) {
                            *data = 0;
                            data++;
                            while(*data == ' ' || *data == '\t') data++;
                          }

                          /* code dealing with experimental tags goes here */

                          if(!strnicmp(s,"X-",2)) continue;  /* then "strip"
                                                              * unrecognized
                                                              * experimental
                                                              * tags */

                          info = (TAG3 *)malloc(sizeof(TAG3));
                          if(!info) break;              /* tsk, tsk */
                          info->tag = strdup3(s);
                          if(!info->tag) {
                            free(info);
                            break;
                          }
                          info->data = strdup3(data);
                          if(!cmsg->head) cmsg->head = info;
                          else here->next = info;
                          info->next = NULL;
                          here = info;
                        }
                        *error = NOERR3;
                        return cmsg;   /* success */
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
      if(find_next_3msg(i3handle,error) != -1L && tries++ < MAXRESYNC3)
        goto ReTry;  /* skip to next msg -- goto?  suffer!!! */
    }
    else *error = NOMEM3;
  }

  if(cmsg) cmsg = free_3msghdr(cmsg);
  return cmsg;
}


int read_3msgtext (int i3handle,char *msg, int buflen, int *error) {

  /* call until 0 or -1 is returned.  0 = EOM  -1 = EOP
   * i3handle is file handle for packet
   * msg = text buffer
   * buflen = length of buffer
   */

  int   bufread = 0;
  long  sizefile,pos;
  char *p;

  *error = NOERR3;
  if(!eof(i3handle)) {
    sizefile = filelength(i3handle);
    pos = tell(i3handle);
    if(sizefile > pos)
      bufread = read(i3handle,msg,min(buflen,(int)(sizefile - pos)));
    if(bufread > 0) {
      p = strchr(msg,'\0'); /* EOM marker */
      if(p) {
        p++;
        bufread = (int)p - (int)msg;
        lseek(i3handle,pos + (long)bufread,SEEK_SET);  /* pos to start of next msg */
      }
    }
    else if(bufread == -1) *error = READERR3;
  }

  return bufread;
}


long process_3pkt (void *anon,char *fname,int *error) {

  /* process a type 3ASCII packet.  four functions are external ("callback"):
   *
   * int import_3msg(void *anon,PHDR3 *,MHDR3 *,char *text,
   *                 int length,int *error);
   * int appendin_3msg(void *anon,PHDR3 *,MHDR3 *,char *text,
   *                   int length,int *error);
   *
   * these functions should handle import to the message base and
   * export to outgoing packets as required by the bbs/mailer, as
   * well as dupe checking on the message header id field.  return
   * non-zero to abort processing of packet.
   *
   * int check_3pkthdr(void *anon,char *fname,PHDR3 *,int *error);
   *
   * this function should return 0 if the packet should be processed,
   * or anything else to abort processing and return control to caller.
   *
   * int check_3msghdr(void *anon,MHDR3 *,int *error);
   *
   * this function should return 0 if the message should be processed,
   * or anything else to skip it and go for the next message.
   *
   * anon = private data for "callback" functions
   * fname = name of packet to process
   * *error = returned error code
   * returns number of msgs processed
   */

  PHDR3 *cpkt = NULL;
  MHDR3 *cmsg = NULL;
  char  *msgtext;
  int   textlen;
  int   i3handle = -1;
  long  nummsgs = 0;

  *error = NOERR3;
  msgtext = (char *)malloc(BLKSIZE3 + 1);
  if(msgtext) {
    i3handle = open_3pkt(i3handle,fname,error);
    if(i3handle != -1) {
      cpkt = read_3pkthdr(i3handle,cpkt,error);
      if(cpkt) {
        if(!check_3pkthdr(anon,fname,cpkt,error)) {
          do {
            cmsg = read_3msghdr(i3handle,cmsg,error);
            if(*error) break;
            if(cmsg) {
              textlen = read_3msgtext(i3handle,msgtext,BLKSIZE3,error);
              if(*error) break;
              if(check_3msghdr(anon,cmsg,error)) {
                *error = NOERR3;
                if(find_next_3msg(i3handle,error) == -1L) break;
                continue;
              }
              if(textlen > 0) {
                nummsgs++;
                if(import_3msg(anon,cpkt,cmsg,msgtext,textlen,error)) break;
                while(textlen == BLKSIZE3) {
                  textlen = read_3msgtext(i3handle,msgtext,BLKSIZE3,error);
                  if(*error) break;
                  if(textlen > 0) {
                    if(appendin_3msg(anon,cpkt,cmsg,msgtext,textlen,error)) break;
                  }
                }
#ifdef TRANSLATING
                end_3msg(anon,cpkt,cmsg);
#endif
              }
            }
          } while(cmsg);
        }
      }
      i3handle = close_3pkt(i3handle);
    }
  }
  else *error = NOMEM3;

  if(cpkt) free_3pkthdr(cpkt);
  if(cmsg) free_3msghdr(cmsg);
  if(msgtext) free(msgtext);

  return nummsgs;
}
