-- File: MailerMTP.mesa,  Last Edit: HGM  March 24, 1981  1:08 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],
  PupDefs USING [AppendMyName],
  FTPDefs USING [
    FTPBeginDeliveryOfMessage, FTPCloseConnection, FTPCreateUser, FTPDestroyUser,
    FTPEndDeliveryOfMessage, FTPError, FTPFinalize, FTPSetCredentials,
    FTPIdentifyNextRejectedRecipient, FTPInitialize, FTPOpenConnection,
    FTPSendBlockOfMessage, FTPSendRecipientOfMessage, FTPUser,
    PupCommunicationPrimitives];

MailerMTP: PROGRAM IMPORTS Storage, String, Time, PupDefs, FTPDefs
  EXPORTS Mailer =
  BEGIN

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

    -- This is the list of mail servers to try.
    nMailServers: CARDINAL = 11;
    mailServerNames: ARRAY [0..nMailServers) OF STRING = [
      "Erie"L,  -- Webster
      "Aklak"L,  -- Toronto
      "Aztec"L,  -- Henrietta
      "Wind"L,  -- El Segundo
      "Maxc"L,  -- Palo Alto
      "Calypso"L,  -- Leesburg
      "Eagle"L,  -- Stamford
      "RoseBowl"L,  -- Pasadena
      "Cactus"L,  -- Dallas
      "Rank"L,  -- Sweden
      "Jaws"L];  -- England

    ftpUser: FTPUser ← NIL;
    recipients, rejections: CARDINAL ← 0;
    nameList, nameListTail: NameHandle ← NIL;
    mailServerIndex: CARDINAL;

    SendHeaderLine: PROCEDURE [field, contents: STRING] =
      BEGIN
      IF contents = NIL OR contents.length = 0 THEN RETURN;
      FTPSendBlockOfMessage[ftpUser, @field.text, field.length];
      FTPSendBlockOfMessage[ftpUser, @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];
      String.AppendString[temp, " (via "L];
      String.AppendString[temp, mailServerNames[mailServerIndex]];
      String.AppendString[temp, ")"L];
      SendHeaderLine["From: "L, temp];
      END;

    SendCR: PROCEDURE =
      BEGIN
      endOfLine: STRING ← [1];
      endOfLine.length ← 1;
      endOfLine[0] ← Ascii.CR;
      FTPSendBlockOfMessage[ftpUser, @endOfLine.text, endOfLine.length];
      END;

    SendLongString: PROCEDURE [s: LONG STRING] =
      BEGIN
      max: CARDINAL = 200;
      temp: PACKED ARRAY [0..max) OF CHARACTER;
      i, k: CARDINAL;
      k ← 0;
      FOR i IN [0..s.length) DO
        temp[k] ← s[i];
        k ← k + 1;
        IF k = max THEN
          BEGIN FTPSendBlockOfMessage[ftpUser, @temp, k]; k ← 0; END;
        ENDLOOP;
      IF k # 0 THEN FTPSendBlockOfMessage[ftpUser, @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, "MailerMTP: 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
      recipient: NameHandle;
      recipients ← rejections ← 0;
      FOR recipient ← nameList, recipient.next UNTIL recipient = NIL DO
        recipients ← recipients + 1;
        FTPSendRecipientOfMessage[ftpUser, recipient.name];
        ENDLOOP;
      END;

    CheckForRejections: PROCEDURE =
      BEGIN
      n, errorCount: CARDINAL ← 0;
      name: NameHandle;
      THROUGH [0..recipients) DO
        why: STRING = [100];
        n ← FTPIdentifyNextRejectedRecipient[ftpUser, why].recipientNumber;
        IF n = 0 THEN EXIT;
        recipients ← recipients - 1;
        rejections ← rejections + 1;
        FOR name ← nameList, name.next UNTIL name = NIL OR (n ← n - 1) = 0 DO
          ENDLOOP;
        IF name # NIL THEN
          BEGIN OPEN String;
          s: STRING ← [200];
          AppendString[s, "MailerMTP: Bad recipient: "L];
          AppendString[s, name.name];
          AppendString[s, " - "L];
          AppendString[s, why];
          IF info # NIL THEN info[s, rejection];
          END;
        ENDLOOP;
      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, "MailerMTP: Sent it"L];
      String.AppendString[temp, " (via "L];
      String.AppendString[temp, mailServerNames[mailServerIndex]];
      String.AppendString[temp, ")"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;

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

    FTPInitialize[];
    ftpUser ← FTPCreateUser[NIL, PupCommunicationPrimitives[]];
    FTPSetCredentials[ftpUser, primary, sender, password];
    FOR mailServerIndex IN [0..nMailServers) DO
      ENABLE
        FTPError =>
          BEGIN
          temp: STRING = [100];
          String.AppendString[temp, "MailerMTP: Can't send it via "L];
          String.AppendString[temp, mailServerNames[mailServerIndex]];
          IF message # NIL THEN
            BEGIN
            String.AppendString[temp, ": "L];
            String.AppendString[temp, message];
            END;
          String.AppendChar[temp, '.];
          info[temp, trouble];
          CONTINUE;
          END;
      IF mailServerNames[mailServerIndex] = NIL THEN LOOP;
      FTPCloseConnection[ftpUser];  -- can't close it in CATCH phrase
      FTPOpenConnection[
        ftpUser, mailServerNames[mailServerIndex], mail, NIL ! FTPError => LOOP];
      FTPBeginDeliveryOfMessage[ftpUser];
      SendRecipientList[];
      CheckForRejections[];
      IF recipients = 0 THEN GOTO NoValidRecipients;
      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[];
      CheckForRejections[];
      IF recipients = 0 THEN GOTO NoValidRecipients;
      FTPEndDeliveryOfMessage[ftpUser];
      FTPCloseConnection[ftpUser];
      TellHimItWorked[];
      worked ← TRUE;
      EXIT;
      REPEAT
        NoValidRecipients => info["MailerMTP: No valid recipients."L, trouble];
        FINISHED => info["MailerMTP: No mail server responded."L, trouble];
      ENDLOOP;

    FreeNames[];
    FTPDestroyUser[ftpUser];
    FTPFinalize[];

    END;


  END.