program SMTPI ;
{ By Peter Meiring, G0BSX. All rights Reserved.                           }
{ Program to take input from a text file anmd convert it to SMTP messages }
{ according to instructions in a second file.                             }
{ usage syntax:                                                           }
{   SMTP <import File> <List File> <mbox hostname> <mbox callsign>        }
{ Version 1.02: RFC822 compatible                                         }

const LineLength      = 255;
      Version         = 'Version 1.02 (c) Peter Meiring, G0BSX, June 1988.';
      CounterFilename = 'SEQUENCE.SEQ';
      SMTPDir         = '\SPOOL\MQUEUE\';
      IDText = '>> G0BSX  Mailbox->SMTP General Purpose Server.';
      tab = #$09;
      space = #$20;
      maxconds = 50;

type WorkString = string[LineLength];
     String40   = string[40];

var CurrentPath    : WorkString;
    Counter        : integer;
    TxtFilename, WrkFilename : String40 ;
    CallsFP, InFP, LckFP, TxtFP, WrkFP    : text;


function fopen(var fp : text; fname : WorkString; mode : char) : boolean;

begin
  assign(fp,fname);
  {$I-}
  case mode of
    'w', 'W' : rewrite(fp);
    'r', 'R' : reset(fp);
    'a', 'A' : append(fp)
  end;
  if IOResult <> 0 then begin
    close(fp);
    fopen := False;
  end else
    fopen := TRUE
  {$I+}
end;

function Now : String40;

type
  regpack = record
              ax,bx,cx,dx,bp,si,di,ds,es,flags: integer;
            end;

var
  recpack:          regpack;             {assign record}
  ah,al,ch,cl,dh:   byte;
  hour,min,sec,day:     string[2];
  month, year:          string[4];
  dx,cx,m :          integer;

begin
  ah := $2c;                             {initialize correct registers}
  with recpack do
  begin
    ax := ah shl 8 + al;
  end;
  intr($21,recpack);                     {call interrupt}
  with recpack do
  begin
    str(cx shr 8,hour);                  {convert to string}
    if length(hour) < 2 then hour := '0'+hour;
    str(cx mod 256,min);                       { " }
    if length(min) < 2 then min := '0'+min;
    str(dx shr 8,sec);                         { " }
    if length(sec) < 2 then sec := '0'+sec
  end;
  with recpack do
  begin
    ax := $2a shl 8;
  end;
  MsDos(recpack);                        { call function }
  with recpack do
  begin
    str(cx,year);                        {convert to string}
    str(dx mod 256,day);                     { " }
    m := dx shr 8
  end;
  case m of
    1 : month := 'Jan';
    2 : month := 'Feb';
    3 : month := 'Mar';
    4 : month := 'Apr';
    5 : month := 'May';
    6 : Month := 'Jun';
    7 : month := 'Jul';
    8 : month := 'Aug';
    9 : month := 'Sep';
    10 : month := 'Oct';
    11 : month := 'Nov';
    12 : month := 'Dec'
  end;
  Now := day+' '+month+' '+copy(year, 3, 2)+'    '+hour+ ':'+min+':'+sec
end;


function word( n : integer; s : WorkString) : string40;

var c,p,q : integer;
    t,a : WorkString;

begin
  t := s;
  for c := 1 to n do
    if length(t) > 0 then begin
      while (length(t) > 1) and ((t[1] = space)or(t[1] = Tab)) do
        t := copy( t, 2, length(t)-1);
      if (t = space) or (t = tab) then begin
        t := '';
        a := '';
      end;
      if t <> '' then
        p := pos( space, t);
        q := pos( tab, t);
        if ((p > q) and (q > 0)) or ((q > p) and (p = 0)) then p := q;
        if p <> 0 then begin
          a := copy( t, 1, p-1);
          t := copy( t, p+1, length(t) - p)
        end else begin
          a := t;
          t := ''
        end
    end;
  word := a
end;

function words( s : workstring) : integer;

var n,c : integer;
    white : boolean;

begin
  white := true;
  c := 0;
  for n := 1 to length( s ) do begin
    if (s[n] <> space) and (s[n] <> tab) and white then c := succ(c);
    if (s[n] = space) or (s[n] = tab) then white := true else white := false
  end;
  words := c
end;


function NxtMsg : integer;

{ Function to read the SMTP mailer sequence file, increment it amd return }
{ the number that can be used for the next SMTP mail file. }

var fp : text;
    fname : WorkString;
    n : integer;

begin
  fname := SMTPDir + CounterFilename;
  if fopen(fp, fname, 'r') then
    read(fp,n)
  else writeln( '*** Error accessing: ',fname);
  n := Succ(n);
  rewrite(fp);
  writeln(fp,n);
  close(fp);
  Writeln('  SMTP msg: ',n);
  NxtMsg := n
end;

procedure Process;

var Line, fields : WorkString;
    Dest, From, At, Title, hostname, SMTPAddress, Day : String40;
    Home, ToLine, FromLine, MDate, MID : String40;
    condition : array[1..maxconds] of string[80];
    x,l,j,n,field : integer;
    ok, yes, Private, PrivateOK : boolean;

function toUpper( str : WorkString ) : WorkString;

var i : integer;
    t : Workstring;

begin
  t := '';
  for i := 1 to length(str) do
    t := t + UpCase(str[i]);
  ToUpper := t
end;


function match( s1, s2 : string40 ) : boolean;

var i, j : integer;
    f, exclude : boolean;

begin
  if s1[1] = '!' then i := 2 else i := 1;
  f := true;
  j := 1;
  repeat
    if (s1[i] <> '*') and (s1[i] <> s2[j]) then f := false;
    i := succ(i);
    j := succ(j)
  until (j >= length(s2)) or (i >= length(s1));
  if s1[1] = '!' then f := not f;
  match := f
end;

procedure Lock( var fp : text; n : integer );

var fname : WorkString;

begin
  str(n,fname);
  fname := SMTPDir + fname + '.LCK';
  if not fopen(fp, fname, 'w') then begin
     writeln( '*** Error writing :', fname);
     close(fp);
     halt
  end;
  close(fp)
end;


procedure TxtOpen( var fp : text; n : integer );

var fname : Workstring;
begin
  str(n,fname);
  fname := SMTPDir + fname + '.TXT';
  if not fopen(fp,fname,'w')then begin
    writeln('*** Error accessing: ',fname);
    halt
  end
end;

procedure WrkOpen( var fp : text; n : integer );

var fname : workstring;

begin
  str( n, fname);
  fname := SMTPDIR + fname + '.WRK';
  if not fopen( fp, fname, 'w') then begin
    writeln('*** Error accessing: ', fname);
    halt
  end
end;

begin
  writeln('> Reading File: ', ParamStr(2));
  readln(CallsFP, Fields);
  while not EOF(CallsFP) do begin
    field := 1;
    hostname := '';
    SMTPAddress := '';
    repeat
      if Fields[1] <> ';' then begin
        if word(1, Fields) = 'host' then hostname := word(2, Fields);
        if word(1, Fields) = 'address' then SMTPAddress := word(2, Fields);
        if (word(1,Fields) = 'P') or (word(1,Fields) = 'B') then begin
           condition[field] := Fields;
           field := succ(field);
        end
      end;
      readln(CallsFP,Fields)
    until (word(1, Fields) = '***') or eof(CallsFP) or (field > maxconds);
    if field <= maxconds then condition[field] := '';

    reset(InFP);

    readln(InFP, Line);
    while not eof(InFP) do begin
      MDate := '';
      MID := '';
      From := '';
      Dest := '';
      Home := '';
      At := '';
      Title := '';
      ToLine := '';
      FromLine := '';
      while Line <> '' do begin
        if word(1, Line) = 'Date:' then MDate := Line;
        if word(1, Line) = 'Message-ID:' then MID := Line;
        if word(1, Line) = 'X-msgtype:' then
          Private := pos('P',Line)>0;
        if word(1, Line) = 'From:' then begin
          FromLine := Line;
            if pos('@', Line) > 0 then begin
              Line[pos('@', Line)]:= chr(32);
              Home := Word(3,Line)
            end else Home := '';
          From := Word(2, Line)
        end ;
        if Word(1, Line) = 'To:' then begin
          ToLine := Line;
          if pos('@', Line) > 0 then begin
            Line[pos('@', Line)] := chr(32);
            At := Word(3, Line)
          end else At := '';
          Dest := Word(2, Line)
        end ;
        if Word(1, Line) = 'Subject:' then
          Title := Line;
        readln(InFP,Line)
      end;

      Writeln( '> To: ', Dest, ' @ ', At, '  From: ', From, ' @ ', Home);

      Field := 1 ;
      ok := false;
      while (condition[field] <> '') AND (field <= Maxconds) and (NOT ok) do begin
        n := 2;
        PrivateOK := (word(1,condition[field]) = 'P');
        yes := ((Private = PrivateOK) or not Private);
        writeln('Condition: ',condition[field]);
        while yes and (n<words(condition[Field])) do begin
          if word(n, condition[field]) = '>' then
             yes := yes and match( word( n+1, condition[field] ), Dest);
          if word(n, condition[field]) = '@' then
             yes := yes and match( word( n+1, condition[field] ), At);
          if word(n, condition[field]) = '<' then
            yes := yes and match( word(n+1, condition[field]), From);
          n := n+2
        end;
        ok := yes;
        if ok then begin
          write('> Writing: > ',hostname,' @ ',SMTPAddress);
          n := NxtMsg;
          Lock(LckFP, n);
          TxtOpen( TxtFP, n );
          Writeln( TxtFP, IDText);
          If Mdate = '' then MDate := 'Date:' + Now ;
          Writeln( TxtFP, Mdate );
          If MID = '' then Writeln(TxtFP, 'Message-ID: <', n, '@', ParamStr(3), '>')
          else Writeln( TxtFP, MID );
          Writeln( TxtFP, FromLine);
          Writeln( TxtFP, ToLine);
          Line := 'Reply-to: ' + From;
          if Home <> '' then Line := Line + '%' + Home;
          Line := Line + '@' + ParamStr(3);
          Writeln( TxtFP, Line);
          If Title = '' then Title := 'Subject: Unknown' ;
          Writeln( TxtFP, Title);
          Readln(InFP,Line);
          Writeln(TxtFP);
          x := 6;
          while (pos('R:', Line)>0) and (not EOF(InFP)) do begin
            Day := '-'+copy(Line,3,11)+' ';
            Line := copy( Line, pos('@', Line)+1, length(Line)-pos('@',Line)-1);
            l := pos(' ',Line);
            if line[1] = ':' then j := 2 else j := 1;
            if x < 10 then Write(TxtFP,'Path: ') ;
            At := copy(Line, j, l-j);
            if At[1] = ':' then At := copy( At, 2, length(At)-1);
            Write( TxtFP, At, day);
            Readln(InFP,Line);
            x := x + length(Day) + l-j + 1;
            if x > 60 then begin
              writeln(TxtFP);
              x := 6
            end
          end;
          if x > 6 then Writeln( TxtFP);
          While (pos('/EX', Line)<>1) and (not EOF(InFP)) do begin
            writeln(TxtFP,Line);
            readln(InFP,Line)
          end;
          close(TxtFP);
          WrkOpen( WrkFP, n);
          Writeln( WRKFP, hostname );
          Writeln( WRKFP, From,'%',Home,'@',ParamStr(3) );
          writeln( WRKFP, SMTPAddress);
          close(WRKFP);
          erase(LckFP)
        end;
        field := succ(field);
      end;
      if not ok then
        repeat
          readln(InFP, Line)
        until eof(InFP) or (pos('/EX', Line)>0);
      readln(InFP,Line)
    end
  end
end;

begin
  writeln('G0BSX Mailbox -> SMTP General Purpose Server');
  writeln(Version);
  if ParamCount < 4 then begin
    writeln( '**** Not enough Parameters' );
    writeln( 'Usage: SMTPI ImportFile CallsFile hostID BBSCallsign');
    halt
  end;
  writeln( '> Opening file: ',Paramstr(1));
  if not fopen( InFP, ParamStr(1), 'r') then begin
    writeln('*** File: ',ParamStr(1),' not found');
    close(InFP);
    Halt
  end;
  writeln( '> Opening file: ', Paramstr(2));
  if not fopen( CallsFP, ParamStr(2), 'r') then begin
    writeln('*** File: ', ParamStr(2), ' not found');
    close(InFP);
    close(CallsFP);
    halt;
  end;
  writeln( 'G0BSX Mailbox > SMTP Import: ', Now );
  Process;
  close(InFP);
  writeln( '*** Erasing: ', Paramstr(1));
  erase(InFP);
  close(CallsFP);
  Writeln( '*** Done: ', Now )
end.