/********************************************************
 * File: crashwrite.c
 * Created at Sun Jan 28 22:10:33 MSK 2001 by raorn // raorn@binec.ru
 * CrashWrite - text-to-pkt utility
 * $Id: crashwrite.c,v 1.18 2002/02/04 22:13:30 raorn Exp $
 *******************************************************/
#include <machine/defs.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#elif HAVE_SYS_TIME
# include <sys/time.h>
#else
# include <time.h>
#endif
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#else
# include <shared/getopt.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include <shared/jbstrcpy.h>
#include <shared/path.h>
#include <shared/mystrncpy.h>
#include <shared/node4d.h>

#include <shared/file.h>

#include <shared/fidonet.h>

uchar verstr[] = "CrashWrite/" PLATFORM_NAME " " VERSION;

#define PKTFROMADDR 128
#define PKTTOADDR   129
#define FROMNAME    130
#define FROMADDR    131
#define TONAME      132
#define TOADDR      133
#define NOMSGID     134
#define NOTEAR      135
#define SHOWVER     136

uchar PktMsgHeader[SIZE_PKTMSGHEADER];
uchar PktHeader[SIZE_PKTHEADER];

bool nomem, diskfull;

ushort getuword(uchar * buf, ulong offset)
{
  return (ushort) (buf[offset] + 256 * buf[offset + 1]);
}

void putuword(uchar * buf, ulong offset, ushort num)
{
  buf[offset] = num % 256;
  buf[offset + 1] = num / 256;
}

void MakeFidoDate(time_t tim, uchar * dest)
{
  struct tm *tp;
  time_t t;
  static uchar *monthnames[] =
    { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
    "Nov", "Dec", "???"
  };

  t = tim;
  tp = localtime(&t);

  snprintf(dest, 20, "%02d %s %02d  %02d:%02d:%02d", tp->tm_mday, monthnames[tp->tm_mon], tp->tm_year % 100, tp->tm_hour, tp->tm_min, tp->tm_sec);
}

void WriteNull(FILE *ofh, uchar * str)
{
  fwrite(str, (ulong) (strlen(str) + 1), 1, ofh);
}

int main(int argc, char **argv)
{
  static struct Node4D from4d = {0, 0, 0, 0},
                       to4d = {0, 0, 0, 0},
                       pktfrom4d = {0, 0, 0, 0},
                       pktto4d = {0, 0, 0, 0};
  FILE *ifh, *ofh;
  time_t t;
  struct tm *tp;
  ulong pktnum, c, serial;
  ushort attr;
  static uchar fromname[36] = "CrashWrite",
               toname[36] = "All",
               subject[72] = "Information",
               origin[80] = "Another user of CrashEcho",
               datetime[20],
               password[8];
  uchar *area = NULL, *file = NULL;
  int fileattach = 0, nomsgid = 0, notear = 0;
  uchar pktname[30], fullname[200], *readbuf, *outdir = NULL;
  int got_pf = 0, got_pt = 0, got_tearline = 0;
  static struct option lopts[] = {
    {"help", 0, NULL, 'h'},
    {"out-dir", required_argument, NULL, 'd'},
    {"pkt-from", required_argument, NULL, PKTFROMADDR},
    {"pkt-to", required_argument, NULL, PKTTOADDR},
    {"password", required_argument, NULL, 'p'},
    {"from-name", required_argument, NULL, FROMNAME},
    {"from-addr", required_argument, NULL, FROMADDR},
    {"to-name", required_argument, NULL, TONAME},
    {"to-addr", required_argument, NULL, TOADDR},
    {"subject", required_argument, NULL, 'S'},
    {"area", required_argument, NULL, 'A'},
    {"origin", required_argument, NULL, 'O'},
    {"file-attach", 0, NULL, 'a'},
    {"no-msgid", 0, NULL, NOMSGID},
    {"no-tearline", 0, NULL, NOTEAR},
    {"file", required_argument, NULL, 'f'},
    {"version", 0, NULL, SHOWVER},
    {0, 0, 0, 0}
  };
  static uchar optstr[] = "+hd:p:S:A:O:af:";
  static uchar helpstr[] =
"Usage: crashwrite [OPTION]... [FILE]...\n"
"Write FILEs to Type-2+ FTN packet.\n"
"\n"
"  -h, --help            display this help and exit\n"
"  -d, --out-dir=PATH    output directory\n"
"                        this argument is mandatory!\n"
"      --pkt-from=ADDR   packet's originating address\n"
"      --pkt-to=ADDR     packet's destination address\n"
"  -p, --password=TEXT   packet's password\n"
"      --from-name=NAME  from name of the message\n"
"      --from-addr=ADDR  from address of the message\n"
"      --to-name=NAME    to name of the message\n"
"      --to-addr=ADDR    to address of the message\n"
"  -S, --subject=TEXT    the subject of the message (or file if file-attach)\n"
"  -A, --area=TAG        the area the message should be posted in; if you skip\n"
"                        this option, the message will be sent as netmail.\n"
"  -O, --origin=TEXT     the origin line for echomail message\n"
"  -a, --file-attach     sets the file-attach flag for netmails; the filename\n"
"                        should be put in the subject\n"
"      --no-msgid        prevents CrashWrite from adding a ^aMSGID kludge\n"
"      --no-tearline     prevents CrashWrite from adding tearline\n"
"  -f, --file=FILE       the name of a text file that should be included as\n"
"                        message's text\n"
"      --version         print version number and exit\n\n";
  int ch, lopt_index;

  while((ch=getopt_long(argc, argv, optstr, lopts, &lopt_index)) != -1){
    switch(ch){
      case 'h':
      case '?':
      default:
        fprintf(stderr, helpstr);
        exit(EXIT_OK);
        /* Not reached */
      case SHOWVER:
        fprintf(stderr, "%s\n", verstr);
        exit(EXIT_OK);
        /* Not reached */
      case 'd':
        outdir = optarg;
        break;
      case PKTFROMADDR:
        if (!(Parse4D((uchar *) optarg, &pktfrom4d))) {
          fprintf(stderr, "Invalid address \"%s\"\n", (uchar *) optarg);
          exit(EXIT_ERROR);
        }
        got_pf = 1;
        break;
      case PKTTOADDR:
        if (!(Parse4D((uchar *) optarg, &pktto4d))) {
          fprintf(stderr, "Invalid address \"%s\"\n", (uchar *) optarg);
          exit(EXIT_ERROR);
        }
        got_pt = 1;
        break;
      case 'p':
        strncpy(password, (uchar *) optarg, 8);
        break;
      case FROMNAME:
        mystrncpy(fromname, (uchar *) optarg, 36);
        break;
      case FROMADDR:
        if (!(Parse4D((uchar *) optarg, &from4d))) {
          fprintf(stderr, "Invalid address \"%s\"\n", (uchar *) optarg);
          exit(EXIT_ERROR);
        }
        break;
      case TONAME:
        mystrncpy(toname, (uchar *) optarg, 36);
        break;
      case TOADDR:
        if (!(Parse4D((uchar *) optarg, &to4d))) {
          fprintf(stderr, "Invalid address \"%s\"\n", (uchar *) optarg);
          exit(EXIT_ERROR);
        }
        break;
      case 'S':
        mystrncpy(subject, (uchar *) optarg, 72);
        break;
      case 'A':
        if (optarg[0])
          area = optarg;
        break;
      case 'O':
        mystrncpy(origin, (uchar *) optarg, 80);
        break;
      case 'a':
        fileattach = 1;
        break;
      case NOMSGID:
        nomsgid = 1;
        break;
      case NOTEAR:
        notear = 1;
        break;
      case 'f':
        file = optarg;
        break;
    }
  }

  argc -= optind;
  argv += optind;

  if(argc)
    file = argv[0];

  if(!outdir){
    fprintf(stderr, helpstr);
    exit(EXIT_ERROR);
  }

  if(!got_pf)
    Copy4D(&pktfrom4d, &from4d);

  if(!got_pt)
    Copy4D(&pktto4d, &to4d);

  if(!(readbuf = malloc(4096))){
    fprintf(stderr, "Out of memory\n");
    exit(EXIT_ERROR);
  }

  do {

    time(&t);
    tp = localtime(&t);

    /* Create pkt file */
    serial = 0;

    do {
      t = time(NULL);
      pktnum = (t << 8) + serial;
      serial++;
      snprintf(pktname, 30, "%08lx.pkt", pktnum);
      MakeFullPath(outdir, pktname, fullname, 200);
    } while (Exists(fullname));

    if (!(ofh = fopen(fullname, "wb"))) {
      fprintf(stderr, "Unable to create packet %s\n", fullname);
      fprintf(stderr, "Error: %s\n", strerror(errno));
      continue;
    }

    if (file && strcmp(file, "-")) {
      printf("Appending %s...\n", (uchar *) file);
      if (!(ifh = fopen(file, "rt"))) {
        fprintf(stderr, "Unable to open \"%s\" for reading\n", file);
        fprintf(stderr, "Error: %s\n", strerror(errno));
        fclose(ofh);
        unlink(fullname);
        continue;
      }
    } else
      ifh = stdin;

    /* Create packet header */
    putuword(PktHeader, PKTHEADER_ORIGNODE, pktfrom4d.Node);
    putuword(PktHeader, PKTHEADER_DESTNODE, pktto4d.Node);
    putuword(PktHeader, PKTHEADER_DAY, tp->tm_mday);
    putuword(PktHeader, PKTHEADER_MONTH, tp->tm_mon);
    putuword(PktHeader, PKTHEADER_YEAR, tp->tm_year + 1900);
    putuword(PktHeader, PKTHEADER_HOUR, tp->tm_hour);
    putuword(PktHeader, PKTHEADER_MINUTE, tp->tm_min);
    putuword(PktHeader, PKTHEADER_SECOND, tp->tm_sec);
    putuword(PktHeader, PKTHEADER_BAUD, 0);
    putuword(PktHeader, PKTHEADER_PKTTYPE, 2);
    putuword(PktHeader, PKTHEADER_ORIGNET, pktfrom4d.Net);
    putuword(PktHeader, PKTHEADER_DESTNET, pktto4d.Net);
    PktHeader[PKTHEADER_PRODCODELOW] = 0xfe;
    PktHeader[PKTHEADER_REVMAJOR] = VERSION_MAJOR;
    putuword(PktHeader, PKTHEADER_QORIGZONE, pktfrom4d.Zone);
    putuword(PktHeader, PKTHEADER_QDESTZONE, pktto4d.Zone);
    putuword(PktHeader, PKTHEADER_AUXNET, 0);
    putuword(PktHeader, PKTHEADER_CWVALIDCOPY, 0x0100);
    PktHeader[PKTHEADER_PRODCODEHIGH] = 0;
    PktHeader[PKTHEADER_REVMINOR] = VERSION_MINOR;
    putuword(PktHeader, PKTHEADER_CAPABILWORD, 0x0001);
    putuword(PktHeader, PKTHEADER_ORIGZONE, pktfrom4d.Zone);
    putuword(PktHeader, PKTHEADER_DESTZONE, pktto4d.Zone);
    putuword(PktHeader, PKTHEADER_ORIGPOINT, pktfrom4d.Point);
    putuword(PktHeader, PKTHEADER_DESTPOINT, pktto4d.Point);
    PktHeader[PKTHEADER_PRODDATA] = 0;
    PktHeader[PKTHEADER_PRODDATA + 1] = 0;
    PktHeader[PKTHEADER_PRODDATA + 2] = 0;
    PktHeader[PKTHEADER_PRODDATA + 3] = 0;

    for (c = 0; c < 8; c++)
      PktHeader[PKTHEADER_PASSWORD + c] = 0;

    if (password[0])
      strncpy(&PktHeader[PKTHEADER_PASSWORD], password, 8);

    /* Create message header */
    attr = 0;

    if (!area) {
      attr |= FLAG_PVT;

      if (fileattach)
        attr |= FLAG_FILEATTACH;
    }

    putuword(PktMsgHeader, PKTMSGHEADER_PKTTYPE, 0x0002);
    putuword(PktMsgHeader, PKTMSGHEADER_ORIGNODE, from4d.Node);
    putuword(PktMsgHeader, PKTMSGHEADER_DESTNODE, to4d.Node);
    putuword(PktMsgHeader, PKTMSGHEADER_ORIGNET, from4d.Net);
    putuword(PktMsgHeader, PKTMSGHEADER_DESTNET, to4d.Net);
    putuword(PktMsgHeader, PKTMSGHEADER_ATTR, attr);
    putuword(PktMsgHeader, PKTMSGHEADER_COST, 0);

    MakeFidoDate(t, datetime);

    printf("Writing...\n");
    printf(" From: %-36s (%u:%u/%u.%u)\n", fromname, from4d.Zone, from4d.Net, from4d.Node, from4d.Point);
    printf("   To: %-36s (%u:%u/%u.%u)\n", toname, to4d.Zone, to4d.Net, to4d.Node, to4d.Point);
    printf(" Subj: %s\n", subject);
    printf(" Date: %s\n", datetime);

    fwrite(PktHeader, SIZE_PKTHEADER, 1, ofh);
    fwrite(PktMsgHeader, SIZE_PKTMSGHEADER, 1, ofh);

    WriteNull(ofh, datetime);
    WriteNull(ofh, toname);
    WriteNull(ofh, fromname);
    WriteNull(ofh, subject);

    if (area) {
      fprintf(ofh, "AREA:%s\015", area);
    } else {
      if (from4d.Point)
        fprintf(ofh, "\001FMPT %u\015", from4d.Point);

      if (to4d.Point)
        fprintf(ofh, "\001TOPT %u\015", to4d.Point);

      fprintf(ofh, "\001INTL %u:%u/%u %u:%u/%u\015", to4d.Zone,
          to4d.Net, to4d.Node, from4d.Zone, from4d.Net, from4d.Node);
    }

    if (!nomsgid) {
      fprintf(ofh, "\001MSGID: %u:%u/%u.%u %08lx\015", from4d.Zone, from4d.Net, from4d.Node, from4d.Point, pktnum);
    }

    fprintf(ofh, "\001PID: CrashWrite " VERSION "\015");

    while (fgets(readbuf, 4096, ifh)) {
      if (readbuf[0] != 0)
        readbuf[strlen(readbuf) - 1] = 0;

      got_tearline = !strncmp(readbuf, "--- ", 4) || !strcmp(readbuf, "---") ? 1 : 0;
      fprintf(ofh, "%s\015", readbuf);
    }

    if(!notear && !got_tearline)
      fprintf(ofh, "--- %s\015", verstr);

    if (area)
      fprintf(ofh, " * Origin: %s (%u:%u/%u.%u)\015", origin, from4d.Zone, from4d.Net, from4d.Node, from4d.Point);

    fwrite("", 1, 1, ofh);
    fwrite("", 1, 1, ofh);
    fwrite("", 1, 1, ofh);

    fclose(ofh);

    if(ifh != stdin) fclose(ifh);

    argc--;
    argv++;

    if (argc > 0)
      file = argv[0];

  } while(argc > 0);

  free(readbuf);

  exit(EXIT_OK);
}


syntax highlighted by Code2HTML, v. 0.9.1