-- File: MailerGV.mesa,  Last Edit: HGM  March 24, 1981  1:04 PM

DIRECTORY
  Ascii USING [CR, SP, TAB],
  Storage USING [Node, String, Free, FreeString, CopyString],
  String USING [AppendString, AppendChar, EquivalentString, AppendNumber],
  Time USING [AppendCurrent],

  Mailer USING [Level, SendMailViaMTP],
  PupDefs USING [AppendMyName],
  SendDefs USING [
    AddRecipient, AddToItem, CheckValidity, Create, Destroy, Handle,
    Send, SendFailed, StartSend, StartText];

MailerGV: PROGRAM IMPORTS Storage, String, Time, Mailer, PupDefs, SendDefs
  EXPORTS Mailer =
  BEGIN

  SendMail: PUBLIC PROCEDURE [
    from, subject, to, cc: STRING, body: LONG STRING,
    sender, password: STRING,
    info: PROCEDURE [s: STRING, level: Mailer.Level]]
    RETURNS [worked: BOOLEAN] =
    BEGIN
    worked ← SendMailViaGV[from, subject, to, cc, body, sender, password, info]
    OR Mailer.SendMailViaMTP[from, subject, to, cc, body, sender, password, info];
    END;

  SendMailViaGV: PUBLIC PROCEDURE [
    from, subject, to, cc: STRING, body: LONG STRING,
    sender, password: STRING,
    info: PROCEDURE [s: STRING, level: Mailer.Level]]
    RETURNS [worked: BOOLEAN] =
    BEGIN

    handle: SendDefs.Handle;
    recipients, rejections: CARDINAL ← 0;
    nameList, nameListTail: NameHandle ← NIL;

    SendHeaderLine: PROCEDURE [field, contents: STRING] =
      BEGIN
      IF contents = NIL OR contents.length = 0 THEN RETURN;
      SendDefs.AddToItem[handle, DESCRIPTOR[@field.text, field.length]];
      SendDefs.AddToItem[handle, DESCRIPTOR[@contents.text, contents.length]];
      SendCR[];
      END;

    SendDateLine: PROCEDURE =
      BEGIN
      date: STRING = [30];
      Time.AppendCurrent[date, TRUE];
      SendHeaderLine["Date: "L, date];
      END;

    SendFromLine: PROCEDURE =
      BEGIN
      temp: STRING = [200];
      String.AppendString[temp, from];
      String.AppendString[temp, " on "L];
      PupDefs.AppendMyName[temp];
      SendHeaderLine["From: "L, temp];
      END;

    SendCR: PROCEDURE =
      BEGIN
      endOfLine: STRING ← [1];
      endOfLine.length ← 1;
      endOfLine[0] ← Ascii.CR;
      SendDefs.AddToItem[handle, DESCRIPTOR[@endOfLine.text, endOfLine.length]];
      END;

    SendLongString: PROCEDURE [s: LONG STRING] =
      BEGIN
      max: CARDINAL = 200;
      temp: PACKED ARRAY [0..max) OF CHARACTER;
      k: CARDINAL ← 0;
      FOR i: CARDINAL IN [0..s.length) DO
        temp[k] ← s[i];
        k ← k + 1;
        IF k = max THEN
          BEGIN SendDefs.AddToItem[handle, DESCRIPTOR[@temp, k]]; k ← 0; END;
        ENDLOOP;
      IF k # 0 THEN SendDefs.AddToItem[handle, DESCRIPTOR[@temp, k]];
      END;

    NameHandle: TYPE = POINTER TO Name;
    Name: TYPE = RECORD [next: NameHandle, name: STRING];

    AddName: PROCEDURE [name: STRING] =
      BEGIN
      p: NameHandle;
      FOR p ← nameList, p.next UNTIL p = NIL DO
        IF String.EquivalentString[p.name, name] THEN RETURN; ENDLOOP;
      FOR i: CARDINAL IN [0..name.length) DO
        IF name[i] = '. THEN EXIT;
        REPEAT
          FINISHED =>
            BEGIN OPEN String;
            s: STRING ← [200];
            AppendString[s, "MailerGV: Registry missing: "L];
            AppendString[s, name];
            IF info # NIL THEN info[s, rejection];
            RETURN;
            END;
        ENDLOOP;
      recipients ← recipients + 1;
      p ← Storage.Node[SIZE[Name]];
      p↑ ← [NIL, Storage.CopyString[name]];
      IF nameList = NIL THEN nameList ← p ELSE nameListTail.next ← p;
      nameListTail ← p;
      END;

    SendRecipientList: PROCEDURE =
      BEGIN
      FOR recipient: NameHandle ← nameList, recipient.next UNTIL recipient = NIL DO
        SendDefs.AddRecipient[handle, recipient.name];
        ENDLOOP;
      END;

    CheckForRejections: PROCEDURE =
      BEGIN
      Complain: PROCEDURE [n: CARDINAL, who: STRING] =
        BEGIN
        s: STRING = [100];
        String.AppendString[s, "MailerGV: Invalid recipient: "L];
        String.AppendString[s, who];
        IF info # NIL THEN info[s, rejection];
        rejections ← rejections + 1;
        END;
      recipients ← SendDefs.CheckValidity[handle, Complain];
      END;

    FindUsers: PROCEDURE [names: STRING] =
      BEGIN
      name: STRING ← Storage.String[30];
      pos: CARDINAL ← 0;
      c: CHARACTER;
      NextName: PROCEDURE =
        BEGIN
        WHILE name.length > 0 AND name[name.length - 1] = Ascii.SP DO
          name.length ← name.length - 1; ENDLOOP;
        IF name.length > 0 THEN AddName[name];
        name.length ← 0;
        END;
      WHILE pos < names.length DO
        c ← names[pos];
        pos ← pos + 1;
        SELECT c FROM
          ', => NextName[];
          ENDCASE =>
            IF c # Ascii.SP AND c # Ascii.TAB AND c # Ascii.CR THEN
              String.AppendChar[name, c];
        ENDLOOP;
      NextName[];
      Storage.FreeString[name];
      END;

    FreeNames: PROCEDURE =
      BEGIN
      next: NameHandle;
      UNTIL nameList = NIL DO
        next ← nameList.next;
        Storage.FreeString[nameList.name];
        Storage.Free[nameList];
        nameList ← next;
        ENDLOOP;
      END;

    TellHimItWorked: PROCEDURE =
      BEGIN
      temp: STRING = [100];
      String.AppendString[temp, "MailerGV: Sent it"L];
      String.AppendString[temp, " to "L];
      String.AppendNumber[temp, recipients, 10];
      String.AppendString[temp, " reciepient"L];
      IF recipients # 1 THEN String.AppendString[temp, "s"L];
      IF rejections # 0 THEN
        BEGIN
        String.AppendString[temp, ", but there were "L];
        String.AppendNumber[temp, rejections, 10];
        String.AppendString[temp, " rejections"L];
        END;
      String.AppendString[temp, "."L];
      info[temp, ok];
      END;

    IF password = NIL THEN password ← ""L;  -- MakeKey gets an AddressFault ********

    -- Here begins the real thing
    worked ← FALSE;
    FindUsers[to];
    IF cc # NIL THEN FindUsers[cc];
    IF nameList = NIL THEN
      BEGIN
      info["MailerGV: No recipients in to or cc list."L, trouble];
      RETURN;
      END;

    handle ← SendDefs.Create[];
    FOR tries: CARDINAL IN [0..4) DO
      ENABLE
        SendDefs.SendFailed =>
          BEGIN
          info["MailerGV: Send Failed."L, trouble];
          CONTINUE;
          END;
      SELECT SendDefs.StartSend[handle, password, sender, NIL, TRUE] FROM
        ok => NULL;
        badPwd => GOTO BadPassword;
        badSender, badReturnTo => GOTO BadSender;
        allDown => GOTO AllDown;
        ENDCASE => GOTO Mixup;
      SendRecipientList[];
      CheckForRejections[];
      IF recipients = 0 THEN GOTO NoValidRecipients;
      SendDefs.StartText[handle];
      SendDateLine[];
      SendFromLine[];
      SendHeaderLine["Subject: "L, subject];
      SendHeaderLine["To: "L, to];
      SendHeaderLine["cc: "L, cc];
      -- A blank line separates header from body
      SendCR[];
      SendLongString[body];
      SendCR[];
      SendDefs.Send[handle];
      TellHimItWorked[];
      worked ← TRUE;
      EXIT;
      REPEAT
        Mixup => info["MailerGV: Mixup someplace."L, trouble];
        BadPassword => info["MailerGV: Invalid Sender password."L, trouble];
        BadSender => info["MailerGV: Sender rejected."L, trouble];
        AllDown => info["MailerGV: All Servers appear to be down."L, trouble];
        NoValidRecipients => info["MailerGV: No valid recipients."L, trouble];
        FINISHED => info["MailerGV: Too many retries."L, trouble];
      ENDLOOP;

    SendDefs.Destroy[handle];
    FreeNames[];

    END;


  END.