-- IntSendCom.mesa
-- Edited by Levin, October 16, 1980 5:07 PM
-- Edited by Schroeder, January 6, 1982 11:49 AM
-- Edited by Brotz, March 7, 1983 11:09 AM

DIRECTORY
Ascii USING [ControlA, CR, DEL, ESC, SP],
Core USING [DMSUser],
Editor USING [cancelCode, DeUnderlineSelection, FormatMessage, MapCharIndexToLine,
RefreshFromFirstChange, RefreshToPlaceCharOnLine, ResetBuffers, UnderlineSelection],
exD: FROM "ExceptionDefs" USING [AppendExceptionToExceptionLine, arpaAtExpansion,
arpaNeedsRegistry, cancelingDelivery, cannotExpandDL, cantConnectToMailServer,
deliveryCanceled, DisplayException, DisplayExceptionLine,
DisplayExceptionOrStringOnLine, DisplayExceptionString, errorInDL, errorNearSelection,
Exception, ExceptionLineOverflow, FlashExceptionsRegion, maybeDelivered, nil,
noMessage, noRecipients, noReplyTo, notAuthentic, notDelivered, noValidRecipients,
spare133, spare134, tryAgainOrCancel, uncertainClosing, unexpectedServerResp,
unqualNameInDL, wishToCancelDelivery],
inD: FROM "InteractorDefs" USING [CharIndex, CommandProcedure, Confirm,
ConfirmInner, HousePtr, IdleLoop, MessageTextNbrPtr, NewFormCommand, RefreshHouse,
SendOperationResult, TextSelection, TextSelectionPtr],
Inline USING [BITAND],
intCommon USING [actionPoint, cmTextNbr, commandType, composedMessageEdited,
deliverCommandHouse, deliverWithCR, keystream, newFormAfterDelivery,
newFormCommandHouse, newTargetSelection, pendingDeleteSetByControl, profileRegistry,
profileSendMode, retrieveHandle, target, user],
LaurelSendDefs USING [FlushRecipientList, FromState, GVSend, InitializeRecipientList,
MTPSend, ParseForSend, ProtocolType, SendErrorType, SendMode],
MailParseDefs USING [endOfInput, FinalizeParse, GetFieldBody, GetFieldName,
InitializeParse, ParseError, ParseHandle],
ovD: FROM "OverviewDefs" USING [CharMask, LineBreakValue],
Process USING [Yield],
PupDefs USING [EnumeratePupAddresses, PupAddress, PupNameTrouble],
RetrieveDefs USING [MailboxState],
vmD: FROM "VirtualMgrDefs" USING [CharIndex, ComposedMessage, ComposedMessagePtr,
GetMessageChar, GetMessageSize, InsertMessageChar, InsertRangeInMessage,
InsertStringInMessage, MessageRange, StartMessageInsertion, StopMessageInsertion];

IntSendCom: MONITOR
IMPORTS Editor, exD, inD, Inline, intC: intCommon, LaurelSendDefs, MailParseDefs,
Process, PupDefs, RetrieveDefs, vmD
EXPORTS inD, LaurelSendDefs =

BEGIN
OPEN LaurelSendDefs;

SendError: ERROR [error: SendErrorType] = CODE;

userFeedbackGlobal: BOOLEAN ← TRUE;

invocationMode: SendMode;

sendProtocol: ProtocolType = DetermineSendProtocol[];

userAbortGiven: BOOLEAN;


SendCommand: PUBLIC inD.CommandProcedure =
BEGIN
target: inD.TextSelectionPtr ← @intC.target;
cm: inD.MessageTextNbrPtr = intC.cmTextNbr;
IF ~cm.haveMessage OR cm.message = NIL THEN
{exD.DisplayException[exD.noMessage]; RETURN};
IF RetrieveDefs.MailboxState[intC.retrieveHandle] IN [unknown..cantAuth] THEN
{exD.DisplayException[exD.notAuthentic]; RETURN};

IF target.pendingDelete THEN
BEGIN
Editor.DeUnderlineSelection[target, target];
target.pendingDelete ← FALSE;
Editor.UnderlineSelection[target, target];
END;

IF Editor.MapCharIndexToLine[0, cm] = NIL THEN
Editor.RefreshToPlaceCharOnLine[0, cm.lines, cm];

IF SendOperation[vmD.ComposedMessage[cm.message], @intC.user, Editor.FormatMessage,
confirmed, TRUE] = ok THEN
BEGIN
house: inD.HousePtr = intC.deliverCommandHouse;
intC.composedMessageEdited ← FALSE;
house.typeface ← italicFace;
inD.RefreshHouse[house, "delivered"L];
house.callable ← FALSE;
IF intC.newFormAfterDelivery THEN
inD.NewFormCommand[intC.newFormCommandHouse, TRUE];
END;
intC.keystream.reset[intC.keystream];
END; -- of SendCommand --


SendOperation: PUBLIC ENTRY PROCEDURE
[cm: vmD.ComposedMessagePtr, user: Core.DMSUser,
formatProc: PROC [vmD.ComposedMessagePtr], confirmed: BOOLEAN ← FALSE,
userFeedback: BOOLEAN ← TRUE]
RETURNS [result: inD.SendOperationResult] =
BEGIN
unexpandedDLs: CARDINAL;
replyTo: BOOLEAN;
fromState: FromState;

FormatProc: PROCEDURE = {formatProc[cm]};
--makes sure all pseudo carriage returns are inserted or otherwise modifies text of message.

invocationMode ← IF confirmed THEN blue ELSE red;
message ← cm;
userAbortGiven ← FALSE;
userFeedbackGlobal ← userFeedback;
InitializeRecipientList[];

BEGIN -- for SendError exit --
ENABLE SendError =>
BEGIN
result ← SELECT error FROM
cancelCode => cancelled,
ftpError, uncertainClosing, cantConnect, unexpectedResponse => commFailure,
ENDCASE => badMessage;
CONTINUE;
END;
[unexpandedDLs, fromState, replyTo]
← ParseForSend[user, (sendProtocol = mtp), invocationMode, userFeedback];
IF sendProtocol = gv THEN GVSend
[unexpandedDLs, invocationMode, fromState, replyTo, userFeedback, FormatProc, user]
ELSE MTPSend[invocationMode, fromState, replyTo, userFeedback, FormatProc, user];
result ← ok;
END; -- of SendError exit --

FlushRecipientList[];
END; -- of SendOperation --


GetSendProtocol: PUBLIC PROCEDURE RETURNS [ProtocolType] =
{RETURN[sendProtocol]};


DetermineSendProtocol: PROCEDURE RETURNS [t: ProtocolType] =
BEGIN
Work: PROCEDURE [addr: PupDefs.PupAddress] RETURNS [ignoreRest: BOOLEAN] =
BEGIN
IF t = mtp THEN {t ← gv; ignoreRest ← TRUE} ELSE {t ← mtp; ignoreRest ← FALSE};
END; -- of Work --

t ← gv;
SELECT intC.profileSendMode FROM
mtp => t ← mtp;
gv => NULL;
ENDCASE =>
[] ← PupDefs.EnumeratePupAddresses[intC.profileRegistry, Work
! PupDefs.PupNameTrouble => CONTINUE];
END; -- of DetermineSendProtocol --


CancelDelivery: PUBLIC PROCEDURE = {userAbortGiven ← TRUE; AbortPoint[]};


AbortPoint: PUBLIC PROCEDURE =
-- causes user requested termination.
BEGIN
IF ~userFeedbackGlobal THEN RETURN;
NoticeUserAbort[];
IF userAbortGiven THEN ReportError[cancelCode, NIL, 0, 0];
END; -- of AbortPoint --


NoticeUserAbort: PUBLIC PROCEDURE =
-- checks for user-requested termination.
BEGIN OPEN intC;
char: CHARACTER;
IF userAbortGiven OR ~userFeedbackGlobal THEN RETURN;
UNTIL keystream.endof[keystream] DO
IF (char ← keystream.get[keystream]) = Editor.cancelCode OR char = Ascii.DEL
THEN userAbortGiven ← TRUE;
ENDLOOP;
IF userAbortGiven THEN ReportProgress[exD.cancelingDelivery, NIL, FALSE];
END; -- of NoticeUserAbort --


AskUser: PUBLIC PROCEDURE [exception: exD.Exception, string: STRING] =
BEGIN
IF userFeedbackGlobal THEN
BEGIN
IF exception # exD.nil THEN exD.DisplayException[exception]
ELSE IF string # NIL THEN exD.DisplayExceptionString[string];
END;
IF ~userFeedbackGlobal OR ~inD.Confirm[2] THEN CancelDelivery[];
END; -- of AskUser --


RetryThis: PUBLIC PROCEDURE [string: STRING, excep: exD.Exception ← exD.nil]
RETURNS [BOOLEAN] =
BEGIN
IF ~userFeedbackGlobal THEN RETURN[FALSE];
IF invocationMode = red THEN
BEGIN
exD.DisplayExceptionOrStringOnLine[excep, string, 1];
exD.DisplayExceptionLine[exD.tryAgainOrCancel, 2];
exD.FlashExceptionsRegion[];
RETURN[inD.Confirm[0]];
END
ELSE RETURN[FALSE];
END; -- of RetryThis --


ReportProgress: PUBLIC PROCEDURE
[exception: exD.Exception, string: STRING, displayCancelMessage: BOOLEAN] =
BEGIN
Process.Yield[];
IF ~userFeedbackGlobal THEN RETURN;
exD.DisplayExceptionOrStringOnLine[exception, string, 1];
exD.DisplayExceptionLine
[IF displayCancelMessage THEN exD.wishToCancelDelivery ELSE exD.nil, 2];
END; -- of ReportProgress --


ReportError: PUBLIC PROCEDURE
[erc: SendErrorType, msg: STRING, start, end: vmD.CharIndex] =
BEGIN OPEN intC;
IF userFeedbackGlobal THEN
BEGIN
exD.DisplayExceptionOrStringOnLine
[SELECT erc FROM
messageSyntaxError => exD.errorNearSelection,
dlSyntaxError => exD.errorInDL,
dlExpandError => exD.cannotExpandDL,
illegalRecipient => exD.unqualNameInDL,
missingQualification => exD.arpaNeedsRegistry,
noRecipientsSpecified => exD.noRecipients,
unexpectedResponse => exD.unexpectedServerResp,
cantConnect => exD.cantConnectToMailServer,
badSender => exD.notAuthentic,
cancelCode => exD.deliveryCanceled,
illegalFileExpansion => exD.arpaAtExpansion,
noValidRecipients => exD.noValidRecipients,
ENDCASE => exD.nil,
SELECT erc FROM ftpError, uncertainClosing => msg, ENDCASE => NIL,
1];
exD.DisplayExceptionOrStringOnLine
[SELECT erc FROM
dlExpandError, cancelCode => exD.nil,
unexpectedResponse => exD.maybeDelivered,
uncertainClosing => exD.uncertainClosing,
ENDCASE => exD.notDelivered,
SELECT erc FROM dlExpandError => msg, ENDCASE => NIL,
2];
MoveUnderline[start, end];
exD.FlashExceptionsRegion[];
keystream.reset[keystream];
END;
ERROR SendError[erc];
END; -- of ReportError --


MoveUnderline: PUBLIC PROCEDURE [start, end: vmD.CharIndex] =
BEGIN
target: inD.TextSelectionPtr = @intC.target;
IF ~userFeedbackGlobal THEN RETURN;
Editor.DeUnderlineSelection[selection: target, underline: target];
target↑ ← inD.TextSelection[intC.cmTextNbr, start, end, start, 0, char, FALSE];
intC.newTargetSelection ← TRUE;
Editor.UnderlineSelection[selection:target, underline:target];
inD.IdleLoop[];
END; -- of MoveUnderline --


InsertReplyToField: PUBLIC PROCEDURE [user: Core.DMSUser] =
BEGIN
cm: inD.MessageTextNbrPtr = intC.cmTextNbr;
message: vmD.ComposedMessagePtr = vmD.ComposedMessage[cm.message];
target: inD.TextSelectionPtr = @intC.target;
start, end: vmD.CharIndex;
name: STRING ← user.name;
reg: STRING ← user.registry;
replyToString: STRING = "Reply-To: "L;
dummyString: STRING = [0];
ph: MailParseDefs.ParseHandle;
confirmed: BOOLEAN;
messageLength: vmD.CharIndex ← vmD.GetMessageSize[message];

ReadChar: PROCEDURE RETURNS [char: CHARACTER] =
BEGIN
char ← IF start >= messageLength THEN MailParseDefs.endOfInput
ELSE vmD.GetMessageChar[message, start];
start ← start + 1;
END; -- of ReadChar --

IF ~userFeedbackGlobal THEN RETURN;
exD.AppendExceptionToExceptionLine
[exD.noReplyTo, 1 ! exD.ExceptionLineOverflow => CONTINUE];
-- ". Please choose ""Reply-To"" option."
exD.DisplayExceptionLine[exD.spare133, 2];
SELECT inD.ConfirmInner[0] FROM
’a, ’A => RETURN;
Ascii.ESC, Ascii.SP, ’Y, ’y, Ascii.CR => confirmed ← TRUE;
ENDCASE => confirmed ← FALSE;
Editor.ResetBuffers[cm];
Editor.DeUnderlineSelection[target, target];
start ← 0;
ph ← MailParseDefs.InitializeParse[ReadChar];
BEGIN
UNTIL ~MailParseDefs.GetFieldName
[ph, dummyString ! MailParseDefs.ParseError => GO TO out] DO
MailParseDefs.GetFieldBody[ph, dummyString ! MailParseDefs.ParseError => GO TO out];
ENDLOOP;
start ← start - 1;
EXITS
out => start ← 0;
END;
MailParseDefs.FinalizeParse[ph];
vmD.StartMessageInsertion[message, start];
[] ← vmD.InsertStringInMessage[message, replyToString];
[] ← vmD.InsertStringInMessage[message, name];
[] ← vmD.InsertMessageChar[message, ’.];
[] ← vmD.InsertStringInMessage[message, reg];
[] ← vmD.InsertMessageChar[message, Ascii.CR];
vmD.StopMessageInsertion[message];
end ← start + replyToString.length + name.length + 1 + reg.length + 1;
[] ← vmD.InsertRangeInMessage
[targetIndex: 0, targetMessage: cm.insertionBuffer,
from: vmD.MessageRange[start, end, message]];
intC.commandType ← insert;
intC.actionPoint ← start;
intC.newTargetSelection ← TRUE;
target↑ ← inD.TextSelection[cm, start, end, start, 0, word, FALSE];
intC.pendingDeleteSetByControl ← FALSE;
Editor.RefreshFromFirstChange
[actionIndex: start, deletedChars: 0, insertedChars: end - start, mnp: cm];
Editor.UnderlineSelection[target, target];
IF ~confirmed THEN
CancelDelivery[ ! SendError => exD.DisplayExceptionLine[exD.spare134, 2]];
END; -- of InsertReplyToField --


inHeader, endOfMessage: BOOLEAN;

message: vmD.ComposedMessagePtr;

messageSize, currentIndex: vmD.CharIndex;

stockChars: PACKED ARRAY [1 .. 2] OF CHARACTER;

charsInStock: CARDINAL;

InitReadChar: PUBLIC PROCEDURE =
BEGIN
inHeader ← TRUE;
endOfMessage ← FALSE;
charsInStock ← currentIndex ← 0;
messageSize ← vmD.GetMessageSize[message];
END; -- of InitReadChar --


ReadChar: PUBLIC PROCEDURE RETURNS [ch: CHARACTER] =
BEGIN
IF charsInStock > 0 THEN
{ch ← stockChars[charsInStock]; charsInStock ← charsInStock - 1; RETURN};
IF currentIndex >= messageSize THEN
{endOfMessage ← TRUE; inHeader ← FALSE; RETURN[MailParseDefs.endOfInput]};
ch ← vmD.GetMessageChar[message, currentIndex];
currentIndex ← currentIndex + 1;
SELECT ch FROM
Ascii.CR =>
IF inHeader AND (currentIndex >= messageSize
OR vmD.GetMessageChar[message, currentIndex] = Ascii.CR)
THEN inHeader ← FALSE;
>= ovD.LineBreakValue =>
BEGIN
ch ← Inline.BITAND[ch, ovD.CharMask];
SELECT TRUE FROM
~intC.deliverWithCR => NULL;
inHeader =>
BEGIN
IF ch = Ascii.SP THEN {ch ← Ascii.CR; charsInStock ← 1}
ELSE stockChars[charsInStock ← 2] ← Ascii.CR;
stockChars[1] ← Ascii.SP;
END;
(ch = Ascii.SP) => ch ← Ascii.CR;
ENDCASE => stockChars[charsInStock ← 1] ← Ascii.CR;
END;
ENDCASE;
IF inHeader AND ch = Ascii.ControlA AND userFeedbackGlobal THEN
ERROR MailParseDefs.ParseError[badFieldBody];
END; -- of ReadChar --


GetCharPosition: PUBLIC PROCEDURE RETURNS [vmD.CharIndex] =
{RETURN[currentIndex]};


END. -- of IntSendCom --