/*
 * msgto3b.c
 * sample export code for 3binary messages
 * this is designed only to show how you might export locally generated
 * messages, not necessarily the best way to do it.  Forwarding messages
 * is pitifully easy, so no sample code.
 * PD from M. Kimes
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "3binary.h"
#include "3addr.h"

typedef struct {        /* per FTS-0001 */
      char   from[36];
      char   to[36];
      char   subj[72];
      char   date[20];
      USHORT timesread;
      USHORT d_node;
      USHORT o_node;
      USHORT cost;
      USHORT o_net;
      USHORT d_net;
      USHORT d_zone;
      USHORT o_zone;
      USHORT d_point;
      USHORT o_point;
      USHORT replyto;
      USHORT attr;
      USHORT nextreply;
} FIDOMSG;

/*----------------------------------------------------*/
/* FIDO Message attributes                            */
/*----------------------------------------------------*/
#define MSGPRIVATE 0x0001  /* private message,        */
#define MSGCRASH   0x0002  /* accept for forwarding   */
#define MSGREAD    0x0004  /* read by addressee       */
#define MSGSENT    0x0008  /* sent OK (remote)        */
#define MSGFILE    0x0010  /* file attached to msg    */
#define MSGFWD     0x0020  /* being forwarded         */
#define MSGORPHAN  0x0040  /* unknown dest node       */
#define MSGKILL    0x0080  /* kill after mailing      */
#define MSGLOCAL   0x0100  /* FidoNet vs. local       */
#define MSGXX1     0x0200  /*                         */
#define MSGXX2     0x0400  /* STRIPPED by FidoNet<tm> */
#define MSGFRQ     0x0800  /* file request            */
#define MSGRRQ     0x1000  /* receipt requested       */
#define MSGCPT     0x2000  /* is a return receipt     */
#define MSGARQ     0x4000  /* audit trail requested   */
#define MSGURQ     0x8000  /* update request          */
/*----------------------------------------------------*/

typedef struct AREAS {
  char *area;
  char *directory;
  /* nodes we feed could be kept here to flesh this out... */
  struct AREAS *next;
} AREAS;

time_t seed;

#define MAX3BUFSIZE 32767

extern BINDATE3 * fido_2_type3b (char *str,BINDATE3 *date);


size_t stripjnk3 (char *a,size_t len) {

  /*
   * remove some common 'illegal' chars
   * and strip off * Origin lines and tear lines
   */

  register char *p = a;
  char illegal[] = {'\x8d','\0'};  /* anything you want stripped in illegal */

  while (*p) {
    if(*p == '\t')
      *p = ' ';
    else if ((*p < 31 && *p != '\r') || strchr(illegal,*p)) {
      memmove (p, &p[1], len - ((int) p - (int)a));
      len--;
      if (!len)
        break;
      continue;
    }
    p++;
  }
  p = a;
  while((p = strchr(p,'\01')) != NULL) {  /* strip kludges */
    if((pp = strchr(p,'\r')) != NULL)
      memmove(p,pp + 1,strlen(pp));
    else
      p++;
  }
  return len;
}


AREAS * load_areasbbs (char *filename) {

  FILE  *handle;
  char   s[257],*p,*area,*dir;
  AREAS *head = NULL,*info,*last = NULL;

  handle = fopen(filename,"rt");
  if(handle) {
    fgets(s,256,handle);
    while(!feof(handle)) {
      if(!fgets(s,256,handle))
        break;
      if(s[strlen(s) - 1] == '\n')
        s[strlen(s) - 1] = 0;
      p = strchr(s,';');
      if(p)
        *p = 0;
      p = &s[strlen(s) - 1];
      while(p > s && (*p == ' ' || *p == '\t')) {
        *p = 0;
        p--;
      }
      p = s;
      while(*p && (*p == ' ' || *p == '\t'))
        p++;
      if(p != s)
        memmove(s,p,strlen(p) + 1);
      if(!*s)
        continue;
      area = strtok(s," ");
      dir = strtok(NULL," ");
      if(area && dir) {
        info = malloc(sizeof(AREAS));
        if(info) {
          info->area = malloc(strlen(area) + 1);
          if(info->area) {
            strcpy(info->area,area);
            info->directory = malloc(strlen(dir) + 1);
            if(info->directory) {
              strcpy(info->directory,dir);
              if(!head)
                head = info;
              if(last)
                last->next = info;
              last = info;
              info->next = NULL;
            }
            else {
              free(info->area);
              free(info);
              break;
            }
          }
          else {
            free(info);
            break;
          }
        }
        else
          break;
      }
    }
  }
  return head;
}


long export_msg (FILE *pkthandle,FILE *handle,char *area,ADDR *me,int *error) {

  /*
   * this could be optimized to use global chunks, check for short enough
   * text to write w/ header, etc.
   */

  FIDOMSG  fmsg;
  char     frombuild[257],tobuild[257],*buff,*p,*pp;
  long     sizeme = MAX3BUFSIZE,bufsize,pos,len = 0L,idholder;
  CHUNK3   text,from,to,subj,id,area3,date;
  BINDATE3 bdate;

  *error = NOERR3;
  fseek(handle,0L,SEEK_END);
  sizeme = ftell(handle);
  fseek(handle,0L,SEEK_SET);
  bufsize = min((long)MAX3BUFSIZE,sizeme - (long)sizeof(FIDOMSG));
  buff = malloc((int)bufsize);
  if(!buff) {
    *error = NOMEM3;
    return 0L;
  }

  if(fread(&fmsg,sizeof(FIDOMSG),1,handle) != 1) {
    free(buff);
    *error = READERR3;
    return 0L;
  }

  /*
   * build from chunk --
   * stupidly assumes all msgs are from command line address.
   * this would be the most likely chunk to make global on export
   * next would probably be subject...
   */
  sprintf(frombuild,"%s%s%s#%u:%u/%u",fmsg.from,(*fmsg.from) ? "@" : "",
          me->domain,me->zone,me->net,me->node);
  if(me->point)
    sprintf(&frombuild[strlen(frombuild) - 1],".%u",me->point);
  from.data = frombuild;
  from.type = FROM3;
  from.len = (SHORT)sizeof(USHORT) + (SHORT)strlen(frombuild);
  from.next = &date;

  /* build date chunk */
  date.data = &bdate;
  memset(&bdate,0,sizeof(BINDATE3));
  if(!fido_2_type3b(fmsg.date,&bdate)) {
    free(buff);
    *error = BADCHUNK3;
    return 0L;
  }
  date.type = DATE3;
  date.len = (SHORT)sizeof(USHORT) + (SHORT)sizeof(BINDATE3);
  date.next = (!area || *fmsg.to) ? &to : (*fmsg.subj) ? &subj : &id;

  /*
   * build to chunk if required
   * stupidly assumes our domain
   * should look for ^aMSGTO: and parse address w/ parse_addr3()
   * if the message is netmail...
   */
  if(*fmsg.to || !area) {
    if(!area) {
      sprintf(tobuild,"%s%s%s#%u:%u/%u",fmsg.to,(*fmsg.to) ? "@" : "",
              me->domain,fmsg.d_zone,fmsg.d_net,fmsg.d_node);
      if(fmsg.d_point)
        sprintf(&tobuild[strlen(tobuild) - 1],".%u",fmsg.d_point);
    }
    else
      strcpy(tobuild,fmsg.to);
    to.data = tobuild;
    to.type = TO3;
    to.len = (SHORT)sizeof(USHORT) + (SHORT)strlen(tobuild);
    to.next = (*fmsg.subj) ? &subj : &id;
  }

  /* build subject chunk if required */
  if(*fmsg.subj) {
    subj.data = fmsg.subj;
    subj.type = SUBJ3;
    subj.len = (SHORT)sizeof(USHORT) + strlen(fmsg.subj);
    subj.next = &id;
  }

  /*
   * build id chunk --
   * stupidly assumes there's no id for the message in the *.MSG
   * should probably check for msgid
   */
  id.data = &idholder;
  idholder = ++seed;  /* warning:  assumes time_t == long */
  id.type = ID3;
  id.len = (SHORT)sizeof(USHORT) + (SHORT)sizeof(LONG);
  id.next = (area) ? &area3 : NULL;

  /* build area chunk if required */
  if(area) {
    area3.type = AREA3;
    area3.data = area;
    area3.len = (SHORT)sizeof(USHORT) + (SHORT)strlen(area);
    area3.next = NULL;
  }

  pos = ftell(pkthandle);  /* remember for later... */
  len += write_chunk3_list(pkthandle,MSG3,&from,error); /* write header */

  text.next = NULL;
  text.type = TEXT3;
  text.data = buff;
  while(sizeme && !feof(handle)) {  /* write text chunks */

    int lenread;

    lenread = fread(buff,1,(int)min(bufsize,sizeme),handle);
    if(!lenread)
      break;
    buff[lenread] = 0;
    p = buff;
    while((p = strchr(p,'\01')) != NULL) {  /* strip kludges */
      if((pp = strchr(p,'\r')) != NULL)
        memmove(p,pp + 1,strlen(pp));
      else
        p++;
    }
    text.len = (SHORT)sizeof(USHORT) + (SHORT)stripjnk3(buff,strlen(buff));
    if(text.len)
      len += write_chunk3(pkthandle,&text,error);
    sizeme -= (long)lenread;
  }

  { /* update length to reflect text added */
    long waspos;

    waspos = ftell(pkthandle);
    fseek(pkthandle,pos + (long)sizeof(USHORT) + (long)sizeof(USHORT),SEEK_SET);
#ifdef MOTOROLA
    len = swap3l(len);
#endif
    fwrite(&len,sizeof(long),1,pkthandle);
    fseek(pkthandle,waspos,SEEK_SET);
  }

  free(buff);
  return 1L;
}


long get_highwater (char *dir) {

  /* this would need to be updated during renumbering... */

  FILE *handle;
  char  build[257];
  long  ret;

  sprintf(build,"%s/3BINHIGH.DAT",dir);
  handle = fopen(build,"rb");
  if(handle) {
    if(fread(&ret,sizeof(long),1,handle) == 1)
      return ret;
    fclose(handle);
  }
  return 3L;
}


void write_highwater (char *dir,long lastmsg) {

  FILE *handle;
  char  build[257];

  sprintf(build,"%s/3BINHIGH.DAT",dir);
  handle = fopen(build,"wb");
  if(handle) {
    fwrite(&lastmsg,sizeof(long),1,handle);
    fclose(handle);
  }
}


time_t get_seed (void) {

  time_t ret;
  FILE  *handle;

  handle = fopen("3IDSEED.DAT","rb");
  if(handle) {
    if(fread(&ret,sizeof(time_t),1,handle) == 1)
      return ret;
    fclose(handle);
  }
  return time(NULL);
}


void write_seed (time_t t) {

  FILE *handle;

  handle = fopen("3IDSEED.DAT","wb");
  if(handle) {
    fwrite(&t,sizeof(time_t),1,handle);
    fclose(handle);
  }
}


int main (int argc,char *argv[]) {

  int    error = NOERR3,gap;
  AREAS *areas = NULL;
  ADDR   me;
  FILE  *handle,*pkthandle;
  long   msgnum,nummsgs = 0L,lastmsg;
  char   build[257];
  CHUNK3 from,prod;

  if(argc < 4) {
    printf("\n\nmsgto3b exporter\n"
           "Exports *.MSG files to a type 3binary packet\n"
           "\nUsage:  msgto3b <type3bpacket> <netmail directory> <myFTNaddress> [areas.bbs]\n");
    return -1;
  }

  if(parse_addr3(argv[3],&me)) {
    printf("\nError in <to_address>\n");
    return BADADDR3;
  }

  printf("\nmsgto3b exporter\n%s <=- %s%s%s%s\n",argv[1],argv[2],
         (argv[4]) ? " (" : "",(argv[4]) ? argv[4] : "",(argv[4]) ? ")" : "");

  areas = load_areasbbs((argv[4]) ? argv[4] : "AREAS.BBS");
  if(!areas)
    printf("\n *Processing netmail only\n");

  /* build minimal header */
  sprintf(build,"%s#%u:%u/%u",me.domain,me.zone,me.net,me.node);
  if(me.point)
    sprintf(&build[strlen(build) - 1],".%u",me.point);
  from.len = (SHORT)sizeof(USHORT) + (SHORT)strlen(build);
  from.type = FROM3;
  from.data = build;
  from.next = &prod;
  prod.data = "MSGTO3B";
  prod.type = PRODUCT3;
  prod.len = (SHORT)sizeof(USHORT) + (SHORT)strlen(prod.data);
  prod.next = NULL;

  seed = get_seed();
  unlink(argv[1]);

  /* open packet */
  pkthandle = wopen_pkt3(argv[1],&from,&error);
  if(pkthandle) {
    /* export netmail */
    msgnum = get_highwater(argv[2]);
    lastmsg = msgnum + 1L;
    gap = 0;
    printf("\nNETMAIL\n");
    while(gap < 100) { /* yes, this is dumb & slow. this is only example code */
      sprintf(build,"%s/%ld.MSG",argv[2],msgnum++);
      handle = fopen(build,"rb");
      if(!handle)
        gap++;
      else {
        lastmsg = msgnum;
        nummsgs += export_msg(pkthandle,handle,NULL,&me,&error);
        fclose(handle);
        if(error)
          error3(error);
        printf("%ld\r",msgnum - 1L);
        gap = 0;
      }
    }
    write_highwater(argv[2],lastmsg);

    /* export echomail */
    while(areas) {
      printf("\nAREA: \"%s\"\n",areas->area);
      msgnum = get_highwater(areas->directory);
      lastmsg = msgnum + 1L;
      gap = 0;
      while(gap < 100) {
        sprintf(build,"%s/%ld.MSG",areas->directory,msgnum++);
        handle = fopen(build,"rb");
        if(!handle)
          gap++;
        else {
          lastmsg = msgnum;
          nummsgs += export_msg(pkthandle,handle,areas->area,&me,&error);
          if(error)
            error3(error);
          printf("%ld\r",msgnum - 1L);
          fclose(handle);
          gap = 0;
        }
      }
      write_highwater(areas->directory,lastmsg);
      areas = areas->next;
    }
    wclose_pkt3(pkthandle);
    if(!nummsgs)
      unlink(argv[1]);
    else
      write_seed(seed);
  }
  else
    error3(error);

  printf("\n ***Exported %ld message%s\n",nummsgs,&"s"[nummsgs == 1L]);

  return error;
}
