/*
 * type 3 ASCII to type 2 packet converter
 * Public Domain
 *
 * command line arguments:  <name-of-type-3-ASCII-pkt> <name-of-type-2-pkt>
 *
 * this needs heavier error checking and some prettying up before it
 * "real" use...should do in a pinch, though.
 *
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <share.h>
#include "3mail.h"

struct address {
  unsigned int
        zone,net,node,point;
  char domain[9];
};

struct privdata {
  int handle;
  char *fname;
  struct address me;
  struct address from;
  struct address to;
};

/* Packet header structure per FSC-0045 */

struct pkthdr2 {
    unsigned int
        onode,
        dnode,
        opoint,
        dpoint;
    char
        zeros[8];
    unsigned int
        subver,
        version,
        onet,
        dnet;
    char
        product,
        rev_lev,
        password[8];
    unsigned int
        ozone,
        dzone;
    char
        odomain[8],
        ddomain[8];
    long
        specific;
};

extern int parse_addr3 (char *addr3,struct address *addr);  /* 3supp.c */
extern char * type3a_2_fido (char *a,int sea);              /* 3supp.c */



int import_3msg (void *priv,PHDR3 *pkt3,MHDR3 *msg3,char *text,
                 int textlen,int *error) {

  /* this function is called to import/forward the message
   * in this case, it writes a header and message text to
   * a type 2 packet.
   */

  struct privdata *pd;
  char             pmsg[192],*p,*pp;
  unsigned int     x;
  struct address   addr;
  TAG3            *info;
  long             pos;
  static long      nummsgs = 0L;

  pd = (struct privdata *)priv;
  if((!msg3->to && !msg3->area && !pkt3->area) || !msg3->date ||
      !msg3->from || !msg3->id) {
    *error = BADPKTHDR3;
    return *error;
  }
  *error = NOERR3;

  memset (pmsg, 0, 192);
  *pmsg = 0x02;
  pmsg[1] = 0x00;

  if(!parse_addr3(msg3->from,&addr)) {
    memcpy (&pmsg[2], &addr.node, 2);
    memcpy (&pmsg[6], &addr.net, 2);
  }
  else {
    memcpy (&pmsg[2], &pd->from.node, 2);
    memcpy (&pmsg[6], &pd->from.net, 2);
  }
  if(!parse_addr3(msg3->to,&addr)) {
    memcpy (&pmsg[4], &addr.node, 2);
    memcpy (&pmsg[8], &addr.net, 2);
  }
  else {
    memcpy (&pmsg[4], &pd->to.node, 2);
    memcpy (&pmsg[8], &pd->to.net, 2);
  }
  x = 0;
  info = msg3->head;
  while(info) {
    if(info->tag && !stricmp(info->tag,"PRIV")) {
      x |= 1;
      break;
    }
    info = info->next;
  }
  memcpy (&pmsg[10], &x, 2);
  x = 0;
  memcpy (&pmsg[12], &x, 2);
  pp = type3a_2_fido (msg3->date,1);
  memcpy (&pmsg[14], pp, 20);
  p = &pmsg[34];
  if(msg3->to && *msg3->to != '@' && (pp = strchr(msg3->to,'@')) != NULL) {
    strncpy(p,msg3->to,min(35,(int)pp - (int)msg3->to));
  }
  else strcpy(p,"All");
  x = 34;
  while (*p) {
    x++;
    p++;
  }
  p++;
  x++;
  if(msg3->from && *msg3->from != '@' && (pp = strchr(msg3->from,'@')) != NULL) {
    strncpy (p, msg3->from, min(35,(int)pp - (int)msg3->from));
  }
  else strcpy(p,"Sysop");
  while (*p) {
    x++;
    p++;
  }
  x++;
  p++;
  if(msg3->subj) {
    strncpy(p,msg3->subj,71);
  }
  while (*p) {
    x++;
    p++;
  }
  x++;
  p++;
  write (pd->handle, pmsg, x);

  if(pkt3->area) {
    write(pd->handle,"AREA:",5);
    write(pd->handle,pkt3->area,strlen(pkt3->area));
    write(pd->handle,"\r",1);
  }
  else if(msg3->area) {
    write(pd->handle,"AREA:",5);
    write(pd->handle,msg3->area,strlen(msg3->area));
    write(pd->handle,"\r",1);
  }
  write(pd->handle,"\01MSGID: ",8);
  if(*msg3->id != ' ') write(pd->handle,msg3->id,strlen(msg3->id));
  else {
    pp = strchr(msg3->from,'@');
    if(pp) pp++;
    else pp = msg3->from;
    write(pd->handle,pp,strlen(pp));
    write(pd->handle,msg3->id,strlen(msg3->id));
  }
  write(pd->handle,"\r",1);
  if(msg3->ref) {
    write(pd->handle,"\01REPLY: ",8);
    write(pd->handle,msg3->ref,strlen(msg3->ref));
    write(pd->handle,"\r",1);
  }
  info = msg3->head;
  while(info) {
    if(info->tag) {
      write(pd->handle,"\01",1);
      write(pd->handle,info->tag,strlen(info->tag));
    }
    else if(info->data) write(pd->handle,"\01NOTAG",6);
    if(info->data) {
      write(pd->handle,":",1);
      write(pd->handle,info->data,strlen(info->data));
    }
    if(info->tag || info->data) write(pd->handle,"\r",1);
    info = info->next;
  }

  write(pd->handle,text,textlen - 1);
  write(pd->handle,"\0\0",3);
  pos = tell(pd->handle);
  if(pos) lseek(pd->handle,pos - 2L,SEEK_SET);

  printf("%ld\r",++nummsgs);
  return 0;
}


int appendin_3msg (void *priv,PHDR3 *phdr,MHDR3 *mhdr,char *text,
                   int textlen,int *error) {

  /* this function is called if more than one pass at the message
   * text is required.  in this case, it appends the message text
   * to the type 2 packet being created.
   */

  struct privdata *pd;
  long             pos;

  *error = NOERR3;
  pd = (struct privdata *)priv;
  pos = tell(pd->handle);
  if(pos) lseek(pd->handle,pos - 1L,SEEK_SET);
  write(pd->handle,text,textlen - 1);
  write(pd->handle,"\0\0",3);
  pos = tell(pd->handle);
  if(pos) lseek(pd->handle,pos - 2L,SEEK_SET);
  return 0;
}


int check_3pkthdr (void *priv,char *fname,PHDR3 *pkt3,int *error) {

  /* this "callback" function should check packet header and decide
   * if you want to process the packet.  return 0 if so, anything
   * else if not.  in this case it creates the type 2 packet and
   * writes its header.  Note that data may be lost from the 3a pkt
   * header to the 2 pkt header as there's no provision in 2 pkts for
   * something like kludge lines...
   */

  struct privdata *pd;
  struct pkthdr2   pkt2;
  struct address   addr;
  long             pos;

  *error = NOERR3;
  pd = (struct privdata *)priv;
  unlink(pd->fname);
  pd->handle = sopen(pd->fname,O_RDWR | O_BINARY | O_CREAT,SH_DENYWR,S_IWRITE | S_IREAD);
  if(pd->handle != -1) {
    memset(&pkt2,0,sizeof(struct pkthdr2));
    pkt2.version = 2;
    pkt2.subver = 2;
    if(pkt3->pword) strncpy(pkt2.password,pkt3->pword,8);
    if(!parse_addr3(pkt3->from,&addr)) {
      memcpy(&pd->from,&addr,sizeof(struct address));
      pkt2.ozone = addr.zone;
      pkt2.onet = addr.net;
      pkt2.onode = addr.node;
      strncpy(pkt2.odomain,addr.domain,8);
      if(pkt3->to) {
        if(parse_addr3(pkt3->to,&addr)) {
          printf("\nTo: field grunged; bad packet\n");
          *error = BADPKTHDR3;
          return *error;
        }
      }
      else memcpy(&addr,&pd->me,sizeof(struct address));
      memcpy(&pd->to,&addr,sizeof(struct address));
      pkt2.dzone = addr.zone;
      pkt2.dnet = addr.net;
      pkt2.dnode = addr.node;
      pkt2.dpoint = addr.point;
      strncpy(pkt2.ddomain,addr.domain,8);
      write(pd->handle,&pkt2,sizeof(struct pkthdr2));
      write(pd->handle,"\0\0",3);
      pos = tell(pd->handle);
      if(pos) lseek(pd->handle,pos - 3L,SEEK_SET);
    }
    else *error = BADPKTHDR3;
  }
  else {
    *error = NOOPEN3;
    printf("\nOpen of %s failed.\n",pd->fname);
  }
  return *error;
}



int check_3msghdr (void *priv,MHDR3 *msg3,int *error) {

  /* this "callback" function should check message header and decide
   * if you want to process the message.  return 0 if so, anything
   * else if not.  this one doesn't do anything...
   */

  *error = NOERR3;
  return 0;
}


void end_3msg (void *priv,PHDR3 *pkt3,MHDR3 *msg3) {

  /* make tiny seenbys to keep old type 2s from choking */

  struct privdata *pd;
  long             pos;
  char             seenby[81];
  struct address   addr;

  if(!pkt3->area && !msg3->area) return;

  pd = (struct privdata *)priv;
  pos = tell(pd->handle);
  if(pos) lseek(pd->handle,pos - 1L,SEEK_SET);

  strcpy(seenby,"\rSEEN-BY");
  if(!parse_addr3(msg3->from,&addr)) {
    sprintf(&seenby[strlen(seenby)]," %u/%u",addr.net,addr.node);
    if(addr.net != pd->from.net || addr.node != pd->from.node) {
      sprintf(&seenby[strlen(seenby)]," %u/%u",pd->from.net,pd->from.node);
    }
  }
  else {
    sprintf(&seenby[strlen(seenby)]," %u/%u",pd->from.net,pd->from.node);
  }
  if(!parse_addr3(msg3->to,&addr)) {
    sprintf(&seenby[strlen(seenby)]," %u/%u",addr.net,addr.node);
    if(addr.net != pd->to.net || addr.node != pd->to.node) {
      sprintf(&seenby[strlen(seenby)]," %u/%u",pd->to.net,pd->to.node);
    }
    if((pd->to.net != pd->me.net || pd->to.node != pd->me.node) &&
       (addr.net != pd->me.net || addr.node != pd->me.node)) {
      sprintf(&seenby[strlen(seenby)]," %u/%u",pd->me.net,pd->me.node);
    }
  }
  else {
    sprintf(&seenby[strlen(seenby)]," %u/%u",pd->to.net,pd->to.node);
    if(pd->to.net != pd->me.net || pd->to.node != pd->me.node) {
      sprintf(&seenby[strlen(seenby)]," %u/%u",pd->me.net,pd->me.node);
    }
  }
  strcat(seenby,"\r");

  write(pd->handle,seenby,strlen(seenby));
  write(pd->handle,"\0\0",3);
  pos = tell(pd->handle);
  if(pos) lseek(pd->handle,pos - 2L,SEEK_SET);
  return;
}


int main (int argc,char *argv[]) {

  int error;
  struct privdata pd;

  if(argc < 4) {
    printf("\n\n3to2 packet converter\n"
           "Converts type 3 ASCII packets to type 2\n"
           "\nUsage:  3to2 <type3Apacket> <type2packet> <to_address>\n"
           "\n<to_address> format:  Domain#Zone:Net/Node.Point\n\n");
    return -1;
  }

  memset(&pd,0,sizeof(struct privdata));
  pd.handle = -1;
  pd.fname = argv[2];
  if(parse_addr3(argv[3],&pd.me)) {
    printf("\nError in <to_address> \"%s\"\n",argv[3]);
    return -1;
  }

  printf("\n3to2 packet converter\n%s -=> %s\n",argv[1],argv[2]);

  printf("\n\n ** Converted %ld msgs\n",
         process_3pkt((void *)&pd,argv[1],&error));

  if(error && error != EOP3)
    error3a(error);

  if(pd.handle != -1) close(pd.handle);

  return error;
}
