DIRECTORY BasicTime USING [GMT, Now, nullGMT, Unpack, Unpacked], GVAnswer USING [MakeHeader], GVBasics USING [ItemType, RName], GVMailParse USING [endOfInput, FinalizeParse, GetFieldBody, GetFieldName, InitializeParse, ParseError, ParseHandle, ParseNameList], GVNames USING [Authenticate, AuthenticateInfo], GVSend USING [AddRecipient, AddToItem, CheckValidity, Create, Handle, Send, SendFailed, StartItem, StartSend, StartSendInfo], IO USING [int, PutFR, rope], List USING [Append], Icons USING [IconFlavor], Menus USING [MouseButton], PeanutParse USING [MessageFieldIndex, MessageInfo], PeanutProfile USING [ccField, ccToSelf, messageNodeFormat, outgoingMailFile, toBeforeSubject, recipients, signature], PeanutRetrieve USING [CopyMessages], PeanutSendMail USING [SendingRec, SendMsgRecObject, SendParseStatus], PeanutWindow USING [abortFlag, dirtyMailMessageIcon, dirtyMessageSetIcon, mailMessageIcon, messageSetIcon, OutputRope], PutGet USING [ToRope], Rope USING [ActionType, Cat, Concat, Equal, Fetch, Find, Length, MakeRope, Map, ROPE, Size, Substr], TextNode USING [Body, Location, LocRelative, NarrowToTextNode, Ref, RefTextNode], TiogaAccess USING [CopyNode, Create, EndOf, FromViewer, GetNodeRefs, Looks, Put, Reader, SkipToNextNode, TiogaChar, Writer, WriteReader, WriteViewer], TiogaOps USING [BackSpace, CommandProc, FirstChild, GetRope, GetSelection, InsertRope, LastChild, Location, Lock, LockSel, NextPlaceholder, Parent, Ref, RegisterCommand, Root, SelectBranches, SelectDocument, SelectPoint, ToPrimary, Unlock, UnlockSel, ViewerDoc], TiogaOpsDefs USING [], UserCredentials USING [Get], UserProfile USING [CallWhenProfileChanges, ProfileChangedProc], ViewerClasses USING [Lock, Viewer], ViewerEvents USING [RegisterEventProc, ViewerEvent], ViewerOps USING [CloseViewer, DestroyViewer, OpenIcon, PaintViewer], ViewerTools USING [GetSelectedViewer, MakeNewTextViewer, SelPosRec, SetSelection, TiogaContents, TiogaContentsRec]; PeanutSendMailImpl: CEDAR MONITOR IMPORTS BasicTime, GVAnswer, GVMailParse, GVNames, GVSend, IO, List, PeanutProfile, PeanutRetrieve, PeanutWindow, PutGet, Rope, TextNode, TiogaAccess, TiogaOps, UserCredentials, UserProfile, ViewerEvents, ViewerOps, ViewerTools EXPORTS PeanutSendMail, PeanutParse, TiogaOpsDefs = BEGIN OPEN PeanutSendMail; NodeBody: PUBLIC TYPE ~ TextNode.Body; -- export to TiogaOpsDefs TiogaCTRL: GVBasics.ItemType = Tioga1; Viewer: TYPE = ViewerClasses.Viewer; ROPE: TYPE = Rope.ROPE; RName: TYPE = GVBasics.RName; userRName: PUBLIC ROPE_ NIL; -- user name with registry simpleUserName: PUBLIC ROPE_ NIL; -- user name without registry userRegistry: PUBLIC ROPE_ NIL; defaultRegistry: PUBLIC ROPE_ "pa"; needToAuthenticate: BOOL_ TRUE; messageParseArray: PUBLIC ARRAY PeanutParse.MessageFieldIndex OF PeanutParse.MessageInfo _ [ replyToF: ["Reply-To", simpleRope], -- this is really wrong, a special case for now senderF: ["Sender", simpleRope], fromF: ["From", simpleRope], toF: ["To", rNameList], ccF: ["cc", rNameList], cF: ["c", rNameList], bccF: ["bcc", rNameList], dateF: ["Date", simpleRope], subjectF: ["Subject", simpleRope], categoriesF: ["Categories", rCatList], inReplyToF: ["In-Reply-To", simpleRope], voiceF: ["VoiceFileID", simpleRope] ]; AuthenticateUser: PUBLIC PROC RETURNS [BOOL] = { uN: ROPE ~ UserCredentials.Get[].name; proc: PROC[r: ROPE] = {InternalReport[r]}; auth: GVNames.AuthenticateInfo; IF Rope.Length[uN] = 0 THEN { proc["Please Login"]; RETURN[FALSE] } ELSE { dot: INT ~ uN.Find["."]; IF dot<0 THEN { simpleUserName _ uN; userRegistry _ defaultRegistry; userRName _ Rope.Cat[simpleUserName, ".", userRegistry]; } ELSE { simpleUserName _ uN.Substr[len: dot]; userRegistry _ uN.Substr[start: dot+1]; userRName _ uN; }; }; auth _ GVNames.Authenticate[userRName, UserCredentials.Get[].password]; SELECT auth FROM group => proc["... Can't login as group"]; individual => {needToAuthenticate _ FALSE; RETURN[TRUE]}; notFound => {proc[userRName]; proc[" is invalid - please Login"]}; allDown => proc["... No server responded"]; badPwd => proc["... Your Password is invalid - please Login"]; ENDCASE; RETURN[FALSE]; }; PeanutSendInit: UserProfile.ProfileChangedProc = { needToAuthenticate _ TRUE }; CreateMessageRope: PUBLIC PROC [parent: TextNode.Ref] RETURNS [r: Rope.ROPE] = { firstMessageNode: TextNode.Ref ~ TiogaOps.FirstChild[parent]; RETURN[Rope.MakeRope[base: firstMessageNode, size: 2048, fetch: FetchFromMessageRope]]; }; FetchFromMessageRope: PROC [data: REF, index: INT] RETURNS [CHAR] = { firstMessageNode: TextNode.Ref _ NARROW[data]; loc: TextNode.Location = TextNode.LocRelative[[firstMessageNode,0], index]; n: TextNode.RefTextNode = TextNode.NarrowToTextNode[loc.node]; IF n=NIL THEN RETURN[0C]; IF loc.where >= Rope.Size[n.rope] THEN RETURN ['\n]; RETURN [Rope.Fetch[n.rope, loc.where]] }; TopParent: PROC [node, root: TiogaOps.Ref _ NIL] RETURNS [parent: TiogaOps.Ref] = { IF node=NIL THEN RETURN [NIL]; IF root=NIL THEN root _ TiogaOps.Root[node]; DO parent _ TiogaOps.Parent[node]; IF parent=root THEN RETURN [node]; node _ parent; ENDLOOP }; MailMessageHasBeenEdited: PROC [viewer: ViewerClasses.Viewer, event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS[abort: BOOL _ FALSE] = { IF before THEN { viewer.icon _ PeanutWindow.dirtyMailMessageIcon; IF viewer.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: all]; }; }; MailMessageHasBeenSaved: PROC [viewer: ViewerClasses.Viewer, event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS[abort: BOOL _ FALSE] = { IF NOT before AND viewer.file # NIL THEN { viewer.icon _ PeanutWindow.mailMessageIcon; IF viewer.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: all]; }; }; PeanutCheckForReset: TiogaOps.CommandProc = { oldIcon: Icons.IconFlavor ~ viewer.icon; SELECT oldIcon FROM PeanutWindow.dirtyMailMessageIcon => viewer.icon _ PeanutWindow.mailMessageIcon; PeanutWindow.dirtyMessageSetIcon => viewer.icon _ PeanutWindow.messageSetIcon; ENDCASE; IF viewer.iconic AND viewer.icon#oldIcon THEN ViewerOps.PaintViewer[viewer, all]; }; NewForm: PROC [write: PROC[TiogaAccess.Writer]] = { writer: TiogaAccess.Writer ~ TiogaAccess.Create[]; newForm: Viewer _ NIL; write[writer]; newForm _ ViewerTools.MakeNewTextViewer[info: [name: "Message", icon: PeanutWindow.mailMessageIcon, iconic: FALSE]]; TiogaAccess.WriteViewer[writer: writer, viewer: newForm]; [] _ ViewerEvents.RegisterEventProc[proc: MailMessageHasBeenEdited, event: edit, filter: newForm, before: TRUE]; [] _ ViewerEvents.RegisterEventProc[proc: MailMessageHasBeenSaved, event: save, filter: newForm, before: FALSE]; ViewerTools.SetSelection[newForm, NEW[ViewerTools.SelPosRec_ [0, 0]]]; newForm.class.notify[newForm, LIST[$NextPlaceholder]]; }; TiogaAccessPutRope: PROC [writer: TiogaAccess.Writer, rope: ROPE, looks: TiogaAccess.Looks _ ALL[FALSE]] ~ { tiogaChar: TiogaAccess.TiogaChar _ [charSet: 0, char: '\000, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL]; action: Rope.ActionType ~ { charLooks: TiogaAccess.Looks _ looks; SELECT c FROM '\001, '\002 => charLooks['r] _ charLooks['t] _ TRUE; ENDCASE; tiogaChar.char _ c; tiogaChar.looks _ charLooks; TiogaAccess.Put[writer, tiogaChar]; }; [] _ Rope.Map[base: rope, action: action]; }; TiogaAccessEndNode: PROC [writer: TiogaAccess.Writer, format: ATOM _ NIL, comment: BOOL _ FALSE] ~ { tiogaChar: TiogaAccess.TiogaChar _ [charSet: 0, char: '\000, looks: ALL[FALSE], format: format, comment: comment, endOfNode: TRUE, deltaLevel: 0, propList: NIL]; TiogaAccess.Put[writer, tiogaChar]; }; TiogaAccessPutNode: PROC [writer: TiogaAccess.Writer, rope: ROPE _ NIL, format: ATOM _ NIL] ~ { TiogaAccessPutRope[writer, rope]; TiogaAccessEndNode[writer, format]; }; TiogaAccessPutField: PROC [writer: TiogaAccess.Writer, key, val: ROPE, format: ATOM _ NIL] ~ { keyLooks: TiogaAccess.Looks _ ALL[FALSE]; keyLooks['b] _ keyLooks['s] _ TRUE; TiogaAccessPutRope[writer, key, keyLooks]; TiogaAccessPutRope[writer, ": "]; TiogaAccessPutRope[writer, val]; TiogaAccessEndNode[writer, format]; }; TiogaAccessPutHeader: PROC [writer: TiogaAccess.Writer, header: ROPE] ~ { size: INT ~ Rope.Size[header]; key, colon, val, cr: INT _ 0; WHILE key 0; aZone: INT_ ABS[upt.zone]; zDif: INT_ aZone / 60; zMul: INT_ zDif * 60; IF (zMul = aZone) AND arpaNeg THEN { IF upt.dst = yes THEN { SELECT zDif FROM 0 => zone_ "UT"; 4 => zone_ "EDT"; 5 => zone_ "CDT"; 6 => zone_ "MDT"; 8 => zone_ "PDT"; -- fix zone bug (wso) ENDCASE } ELSE { SELECT zDif FROM 0 => zone_ "UT"; 5 => zone_ "EST"; 6 => zone_ "CST"; 7 => zone_ "MST"; 8 => zone_ "PST"; ENDCASE; }; }; IF zone = NIL THEN { mm: INT_ aZone - zMul; zone_ IO.PutFR[IF arpaNeg THEN "-%02g%02g" ELSE "+%02g%02g", IO.int[zDif], IO.int[mm]]; }; SELECT upt.month FROM January => month_ "Jan"; February => month_ "Feb"; March => month_ "Mar"; April => month_ "Apr"; May => month_ "May"; June => month_ "Jun"; July => month_ "Jul"; August => month_ "Aug"; September => month_ "Sep"; October => month_ "Oct"; November => month_ "Nov"; December => month_ "Dec"; unspecified => ERROR; ENDCASE => ERROR; year_ Rope.Substr[IO.PutFR[NIL, IO.int[upt.year]], 2]; tyme_ IO.PutFR[timeFormat, IO.int[upt.hour], IO.int[upt.minute], IO.int[upt.second], IO.rope[zone]]; date_ IO.PutFR[dateFormat, IO.int[upt.day], IO.rope[month], IO.rope[year], IO.rope[tyme]]; }; SendMsg: PUBLIC PROC [mouseButton: Menus.MouseButton, shift, control: BOOL] = { senderV: Viewer _ NIL; oldLabel: ROPE _ NIL; restore: BOOL _ FALSE; Restore: PROC = { senderV.label _ oldLabel; ViewerOps.OpenIcon[senderV] }; BEGIN ENABLE UNWIND => { IF restore THEN Restore[] }; status: SendParseStatus; sPos, mPos: INT; formatting: ROPE; smr: SendingRec; reader: TiogaAccess.Reader _ NIL; writer: TiogaAccess.Writer _ NIL; contents: ViewerTools.TiogaContents; IF needToAuthenticate AND NOT AuthenticateUser[] THEN RETURN; PeanutWindow.abortFlag _ FALSE; senderV _ ViewerTools.GetSelectedViewer[]; IF senderV = NIL THEN { InternalReport["\nSelect message to be sent."]; RETURN }; { outgoing: ROPE = PeanutProfile.outgoingMailFile; IF Rope.Length[outgoing]>0 THEN { TiogaOps.SelectDocument[senderV]; PeanutRetrieve.CopyMessages[to: outgoing, delete: FALSE]; }; }; reader _ TiogaAccess.FromViewer[senderV]; oldLabel _ senderV.label; senderV.label _ "Sending"; restore _ TRUE; ViewerOps.CloseViewer[senderV]; smr _ NEW[SendMsgRecObject_ [fullText: CreateMessageRope[NARROW[TiogaAccess.GetNodeRefs[reader].root]]]]; InternalReport["\nParsing..."]; [status, sPos, mPos]_ ParseTextToBeSent[smr]; IF (status # ok) AND (status # includesPublicDL) THEN { SELECT status FROM fieldNotAllowed => IF sPos # mPos THEN { InternalReport[Rope.Substr[smr.fullText, sPos, mPos-sPos-1]]; InternalReport[" field is not allowed."] } ELSE InternalReport[IO.PutFR[" field at pos %g is not allowed", IO.int[sPos]]]; syntaxError => IF sPos # mPos THEN { InternalReport["\nSyntax error on line beginning with "]; InternalReport[Rope.Substr[smr.fullText, sPos, mPos-sPos-1]] } ELSE InternalReport[IO.PutFR["..... Syntax error at position %g ", IO.int[sPos]]]; includesPrivateDL => InternalReport[" Private dl's are not yet implemented"]; ENDCASE => ERROR; Restore; RETURN; }; IF CheckForAbort[] THEN { Restore; InternalReport["... Message NOT sent."]; RETURN }; writer _ TiogaAccess.Create[]; TiogaAccessPutField[writer, "Date", RFC822Date[]]; TiogaAccessPutField[writer, IF smr.from=NIL THEN "From" ELSE "Sender", userRName]; TiogaAccessPutFromViewer[writer, senderV]; contents _ TiogaAccessWriteTiogaContents[writer]; IF (formatting_ contents.formatting).Length[] = 0 THEN smr.fullText_ contents.contents ELSE { -- check for null at end of contents; move it to formatting last: INT_ contents.contents.Length[] - 1; IF contents.contents.Fetch[last] = '\000 THEN { -- NULL for padding smr.fullText_ Rope.Substr[contents.contents, 1, last-1]; formatting_ Rope.Concat["\000", contents.formatting] } ELSE smr.fullText_ Rope.Substr[contents.contents, 1] }; InternalReport["... Sending message..."]; IF Send[smr, formatting] THEN { InternalReport[" ... Message has been delivered"]; IF senderV.file#NIL AND senderV.newVersion THEN Restore[] ELSE ViewerOps.DestroyViewer[senderV]; } ELSE { Restore[]; InternalReport["... Message NOT sent."] }; END; }; Send: PROC[smr: SendingRec, formatting: ROPE] RETURNS[ sent: BOOLEAN] = { sendHandle: GVSend.Handle _ GVSend.Create[]; sent _ SendMessage[smr, formatting, sendHandle, TRUE ! GVSend.SendFailed => IF notDelivered THEN { InternalReport["\nCommunication failure during send."]; sent _ FALSE; CONTINUE } ELSE { InternalReport["\nCommunication failure, but message delivered."]; sent _ TRUE; CONTINUE; }; ]; }; SendMessage: PROC[smr: SendingRec, formatting: ROPE, h: GVSend.Handle, validateFlag: BOOL] RETURNS [sent: BOOLEAN] = { DO ENABLE GVSend.SendFailed => CONTINUE; -- try again if SendFailed startInfo: GVSend.StartSendInfo; stepper: LIST OF RName; numRecips: INT _ 0; numValidRecips: INT; firstInvalidUser: BOOL_ TRUE; numInvalidUsers: INTEGER_ 0; ReportFromSend: PROC[r: ROPE] = { InternalReport[r]}; InvalidUserProc: PROC [ userNum: INT, userName: RName ] = { IF firstInvalidUser THEN {ReportFromSend["\nInvalid user(s): "]; firstInvalidUser_ FALSE}; SELECT numInvalidUsers _ numInvalidUsers + 1 FROM 1 => ReportFromSend[userName]; IN [2..5] => {ReportFromSend[", "]; ReportFromSend[userName]}; 6 => ReportFromSend[", ..."]; ENDCASE; } ; sent _ FALSE ; startInfo _ GVSend.StartSend[ handle: h, senderPwd: UserCredentials.Get[].password, sender: userRName, returnTo: userRName, validate: validateFlag ] ; SELECT startInfo FROM badPwd => {ReportFromSend["\nInvalid password"]; RETURN}; badSender => {ReportFromSend["\nInvalid sender name"]; RETURN}; badReturnTo => {ReportFromSend["\nBad return-to field"]; RETURN}; allDown => {ReportFromSend["\nAll servers are down"]; RETURN}; ok => { stepper _ smr.to; WHILE stepper # NIL DO GVSend.AddRecipient[ h, stepper.first ]; numRecips _ numRecips + 1; stepper _ stepper.rest; ENDLOOP; IF CheckForAbort[] THEN RETURN; stepper _ smr.cc; WHILE stepper # NIL DO GVSend.AddRecipient[ h, stepper.first ] ; numRecips _ numRecips + 1 ; stepper _ stepper.rest ; ENDLOOP ; IF CheckForAbort[] THEN RETURN; IF validateFlag THEN { IF (numValidRecips_ GVSend.CheckValidity[ h, InvalidUserProc]) = 0 THEN { ReportFromSend["\nThere were NO valid recipients."]; RETURN }; IF numValidRecips # numRecips THEN { tempInteger: INT _ numRecips-numValidRecips; ReportFromSend[IO.PutFR["\nThere were %g invalid recipients,", IO.int[tempInteger] ]]; RETURN; }; }; IF CheckForAbort[] THEN RETURN; ReportFromSend[IO.PutFR["..sending to %g recipients", IO.int[numValidRecips]]]; validateFlag_ FALSE; -- if sending fails, don't need to re-validate GVSend.StartItem[h, Text]; AddToItem[h, smr.fullText]; IF formatting#NIL THEN { -- send the formatting info as a second item GVSend.StartItem[h, TiogaCTRL]; AddToItem[h, formatting] }; IF CheckForAbort[] THEN RETURN; GVSend.Send[ h ] ; sent_ TRUE; RETURN; } ; ENDCASE ; ENDLOOP; }; AddToItem: PROC [handle: GVSend.Handle, buffer: ROPE] = { maxChunkSize: INT ~ 10000; size: INT ~ Rope.Size[buffer]; i: INT _ 0; UNTIL i = size DO chunkSize: INT ~ MIN[size-i, maxChunkSize]; GVSend.AddToItem[handle, Rope.Substr[buffer, i, chunkSize]]; i _ i + chunkSize; ENDLOOP; }; CanonicalName: PUBLIC PROC [simpleName, registry: ROPE] RETURNS[name: ROPE] = { name_ simpleName; IF registry.Length[] = 0 THEN name_ name.Cat[".", userRegistry] ELSE name_ name.Cat[".", registry]; }; ParseTextToBeSent: PROC[msg: SendingRec] RETURNS[status: SendParseStatus, sPos, mPos: INT] = { mLF: PeanutParse.MessageInfo; tHeaders: LIST OF ROPE_ NIL; msgText: ROPE_ msg.fullText; lastCharPos: INT_ msgText.Length[] - 1; lastCharIsCR: BOOL_ (msgText.Fetch[lastCharPos] = '\n); countOfRecipients, dlCount: INT_ 0; GetNextMsgChar: PROC[] RETURNS [ch: CHAR] = { IF mPos <= lastCharPos THEN ch_ Rope.Fetch[msgText, mPos] ELSE IF (mPos=lastCharPos+1) AND ~lastCharIsCR THEN ch_ '\n ELSE ch_ GVMailParse.endOfInput; mPos_ mPos + 1; }; RNameListField: PROC[index: PeanutParse.MessageFieldIndex] = { fieldBody, fbEnd: LIST OF RName_ NIL; AnotherRName: PROC[r1, r2: ROPE, isFile, isNested: BOOL] RETURNS [ROPE, BOOLEAN] = { name: ROPE; name_ CanonicalName[r1, r2]; IF fbEnd=NIL THEN fbEnd_ fieldBody_ CONS[name, NIL] ELSE fbEnd_ fbEnd.rest_ CONS[name, NIL]; IF isFile THEN status_ includesPrivateDL ELSE IF name.Find["^"] < 0 THEN countOfRecipients_ countOfRecipients + 1 ELSE { IF status # includesPrivateDL THEN status_ includesPublicDL; dlCount_ dlCount + 1 }; RETURN[NIL, FALSE]; }; GVMailParse.ParseNameList[pH, GetNextMsgChar, AnotherRName, NIL]; SELECT index FROM toF => IF msg.to = NIL THEN msg.to_ fieldBody ELSE IF fieldBody#NIL THEN TRUSTED {msg.to_ LOOPHOLE[List.Append[LOOPHOLE[msg.to], LOOPHOLE[fieldBody]]]}; ccF, cF, bccF => IF msg.cc = NIL THEN msg.cc_ fieldBody ELSE IF fieldBody#NIL THEN TRUSTED {msg.cc_ LOOPHOLE[List.Append[LOOPHOLE[msg.cc], LOOPHOLE[fieldBody]]]}; fromF => msg.from_ fieldBody.first; -- needs to be non-NIL ENDCASE => ERROR; }; pH: GVMailParse.ParseHandle; field: ROPE_ NIL; fieldNotRecognized: BOOL; mPos_ 0; -- where we are in the fulltext, for parsing status_ ok; -- start with good status pH_ GVMailParse.InitializeParse[]; DO sPos_ mPos; [field, fieldNotRecognized]_ GVMailParse.GetFieldName[pH, GetNextMsgChar ! GVMailParse.ParseError => { GVMailParse.FinalizeParse[pH]; GOTO errorExit}]; IF ~fieldNotRecognized THEN EXIT; IF Rope.Equal[field, "Sender", FALSE] OR Rope.Equal[field, "Date", FALSE] THEN RETURN[fieldNotAllowed, sPos, mPos]; FOR i: PeanutParse.MessageFieldIndex IN PeanutParse.MessageFieldIndex DO { mLF_ messageParseArray[i]; IF Rope.Equal[messageParseArray[i].name, field, FALSE] THEN -- ignore case { fieldNotRecognized_ FALSE; SELECT mLF.fType FROM simpleRope => SELECT i FROM fromF => RNameListField[i ! GVMailParse.ParseError => GOTO errorExit]; replyToF => {msg.replyTo_ TRUE; []_ GVMailParse.GetFieldBody[pH, GetNextMsgChar, TRUE] }; subjectF => msg.subject_ GVMailParse.GetFieldBody[pH, GetNextMsgChar]; voiceF => msg.voiceID_ GVMailParse.GetFieldBody[pH, GetNextMsgChar]; ENDCASE => []_ GVMailParse.GetFieldBody[pH, GetNextMsgChar, TRUE]; rCatList => []_ GVMailParse.GetFieldBody[pH, GetNextMsgChar, TRUE]; rNameList => RNameListField[i ! GVMailParse.ParseError => GOTO errorExit]; ENDCASE => ERROR; EXIT }; }; ENDLOOP; IF fieldNotRecognized THEN []_ GVMailParse.GetFieldBody[pH, GetNextMsgChar]; -- skip anything not recognized ENDLOOP; GVMailParse.FinalizeParse[pH]; msg.endHeadersPos_ mPos - 1; msg.numRecipients_ countOfRecipients; msg.numDLs_ dlCount; EXITS errorExit => RETURN[syntaxError, sPos, mPos]; }; TiogaOps.RegisterCommand[name: $RedReset, proc: PeanutCheckForReset, before: FALSE]; TiogaOps.RegisterCommand[name: $YellowReset, proc: PeanutCheckForReset, before: FALSE]; TiogaOps.RegisterCommand[name: $BlueReset, proc: PeanutCheckForReset, before: FALSE]; UserProfile.CallWhenProfileChanges[PeanutSendInit]; END. |PeanutSendMailImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Created by Paxton, April 1, 1983 3:02 pm Last Edited by: Pausch, July 18, 1983 2:13 pm Last Edited by: Willie-Sue, July 29, 1986 4:51:54 pm PDT Last Edited by: Gasbarro October 9, 1985 6:30:55 pm PDT Bertrand Serlet June 25, 1986 5:35:26 pm PDT Michael Plass, September 25, 1985 3:11:14 pm PDT Doug Wyatt, August 29, 1985 4:40:59 pm PDT * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This probably should be revised to work if selection is in the message header rather than the message body. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * generates arpa standard time, dd mmm yy hh:mm:ss zzz Swiped from WalnutSendMailImpl of November 10, 1983 this needs to be done first, before the user can change the selection out from under me ... the "right" way would be to access the sending viewer later and make tioga lower level calls, but CopyMessages is easier to use, so I [R. Pausch] just call it before the selection can change. NOTE, however that if the message has troubles being sent, this copy will still work ... * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * IF sendHandle#NIL THEN GVSend.Close[sendHandle]; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * now we are positioned at the beginning of the body of the message if any recipient is at another arpa site, all recipients should be arpa qualified however, like Laurel, we'll only do the From/Sender field in full arpa regalia Κ[˜codešœ™Kšœ Οmœ1™Kšžœ˜—K˜Kšžœžœ˜K˜K˜—Kšœ<™<šœHžœ˜OK˜—š ‘œžœžœžœ žœ˜PKšœ=˜=KšžœQ˜WK˜K˜—š ‘œžœžœ žœžœžœ˜EKšœ!žœ˜.K˜KK˜>Kšžœžœžœžœ˜Kšžœ žœžœ˜4Kšžœ!˜'K˜K˜—š‘ œžœžœžœ˜SKš žœžœžœžœžœ˜Kšžœžœžœ˜,šž˜K˜Kšžœ žœžœ˜"K˜Kšžœ˜—K˜K˜—š ‘œžœIžœžœžœžœ˜šžœžœ˜K˜0Kšžœžœ3˜HK˜—K˜K˜—š ‘œžœIžœžœžœžœ˜Œš žœžœžœžœžœ˜*K˜+Kšžœžœ3˜HK˜—K˜K˜—š‘œ˜-K˜(šžœ ž˜KšœP˜PKšœN˜NKšžœ˜—Kšžœžœžœ$˜QK˜K˜—š‘œžœ žœ˜3K˜2Kšœžœ˜K˜Kšœlžœ˜tK˜9Kšœjžœ˜pKšœižœ˜pKšœ"žœ!˜FKšœžœ˜7K˜K˜—š ‘œžœ$žœžœžœ˜mKš œDžœžœ žœ žœ žœžœ˜ž˜Kšœ%˜%šžœž˜ Kšœ0žœ˜5Kšžœ˜—K˜Kšœ˜K˜#K˜—K˜*K˜K˜—š ‘œžœ'žœžœ žœžœ˜eKš œDžœžœ1žœžœ˜’K˜#K˜K˜—š ‘œžœ%žœžœ žœžœ˜`Kšœ!˜!Kšœ#˜#K˜K˜—š ‘œžœ)žœ žœžœ˜_Kšœžœžœ˜)Kšœžœ˜#Kšœ*˜*Kšœ!˜!Kšœ ˜ Kšœ#˜#K˜K˜—š‘œžœ&žœ˜IKšœžœ˜Kšœžœ˜šžœ ž˜Kšœ3˜3Kšœ˜Kšœ0˜0Kš žœ žœžœžœžœ˜'Kšœœ˜œK˜ Kšžœ˜—K˜K˜—š‘œžœ1˜OKšœ<˜K˜Kšžœ˜—K˜—K˜Kšœžœ˜˜(K˜*K˜K˜K˜K˜—šžœ ž˜Kšœ1žœ˜9Kšœ7žœ˜NKšœ9žœ˜AKšœ6žœ˜>˜K˜šžœ žœžœ˜K˜(K˜K˜Kšžœ˜—Kšžœžœžœ˜K˜šžœ žœžœ˜K˜)K˜K˜Kšžœ˜ —Kšžœžœžœ˜šžœžœ˜šžœAžœ˜IKšœ5žœ˜Kšœžœžœžœ˜%š‘ œžœ žœžœžœžœžœ˜TKšœžœ˜ K˜š žœžœžœžœžœ˜3Kšžœžœžœ˜(—šžœžœ˜(Kšžœžœžœ)˜HKšž˜Kšœžœžœ˜>˜K˜——Kšžœžœžœ˜K˜—K˜Kšœ<žœ˜Ašžœž˜šœžœ žœžœ˜-Kšžœžœ žœž˜Kšž˜Kšœ žœ žœ žœ˜G—šœžœ žœžœ˜7Kšžœžœ žœž˜Kšž˜Kšœ žœ žœ žœ˜G—Kšœ% ˜;Kšžœžœ˜—K˜—K˜K˜Kšœžœžœ˜Kšœžœ˜Kšœ  ,˜6Kšœ  ˜&K˜K˜"K˜šž˜K˜ ˜dKšœ!žœ ˜2—Kšžœžœžœ˜!Kš žœžœžœžœž˜NKšžœ˜$K˜šžœ"žœž˜H˜Kšžœ.žœžœ ˜Jšœžœ˜šžœ ž˜K˜ šžœž˜ Kšœ6žœ ˜Fšœžœ˜Kšœ1žœ˜6K˜—K˜FK˜DKšžœ5žœ˜B—Kšœ=žœ˜CKšœ:žœ ˜JKšžœžœ˜—Kšž˜K˜—K˜—Kšžœ˜—Kšžœž˜Kšœ2 ˜QKšžœ˜—K˜KšœA™AK˜K˜K˜KšœQ™QKšœN™NK˜K˜&K˜Kšž˜Kšœ žœ˜-K˜K˜—K˜KšœMžœ˜TKšœPžœ˜WKšœNžœ˜UK˜3K˜Kšžœ˜—…—h¦‰}