<> <> <> <> <> <> <> <> <> <<>> 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> <<>> Simple: PROC [node: TiogaOps.Ref] RETURNS [BOOL] = { n: TextNode.RefTextNode; TRUSTED { n _ TextNode.NarrowToTextNode[LOOPHOLE[node]] }; IF n=NIL THEN RETURN [FALSE]; singleNode _ n.child=NIL AND n.last; IF ~singleNode THEN RETURN [FALSE]; RETURN [n.runs=NIL] }; Cleanup: PROC = { IF lockedPrimary THEN TiogaOps.UnlockSel[primary]; IF lockedSecondary THEN TiogaOps.UnlockSel[secondary]; IF lockedDest THEN TiogaOps.Unlock[destDoc]; IF lockedSource THEN TiogaOps.Unlock[sourceDoc]; }; IF needToAuthenticate AND NOT AuthenticateUser[] THEN RETURN; TiogaOps.LockSel[primary]; lockedPrimary _ TRUE; [sourceViewer, start, end, ----, ----, ----] _ TiogaOps.GetSelection[]; IF sourceViewer = NIL THEN { TiogaOps.UnlockSel[primary]; InternalReport["\nSelect message to be forwarded."]; RETURN }; IF start.node=end.node AND Simple[start.node] THEN { -- send without Tioga formatting ForwardWriter: PROC [writer: TiogaAccess.Writer] ~ { PutHeaderFields[writer]; TiogaAccessPutNode[writer: writer, rope: "\001CoveringMessage\002", format: PeanutProfile.messageNodeFormat]; PutSignatureField[writer]; TiogaAccessPutNode[writer: writer]; -- empty node TiogaAccessPutNode[writer: writer, rope: "------------------------------------------------------------"]; TiogaAccessPutNode[writer: writer, rope: TiogaOps.GetRope[start.node]]; TiogaAccessPutNode[writer: writer, rope: "------------------------------------------------------------"]; }; NewForm[ForwardWriter]; } ELSE { -- forward with Tioga formatting destViewer: Viewer; sourceDoc _ TiogaOps.ViewerDoc[sourceViewer]; messageHeader _ TopParent[start.node, sourceDoc]; IF TopParent[end.node, sourceDoc] # messageHeader THEN { TiogaOps.UnlockSel[primary]; InternalReport["\nSelect single message to be forwarded."]; RETURN }; destViewer _ ViewerTools.MakeNewTextViewer[info: [name: "Message", icon: PeanutWindow.mailMessageIcon, iconic: FALSE]]; [] _ ViewerEvents.RegisterEventProc[proc: MailMessageHasBeenEdited, event: edit, filter: destViewer, before: TRUE]; [] _ ViewerEvents.RegisterEventProc[proc: MailMessageHasBeenSaved, event: save, filter: destViewer, before: FALSE]; destDoc _ TiogaOps.ViewerDoc[destViewer]; TiogaOps.LockSel[secondary]; lockedSecondary _ TRUE; TiogaOps.Lock[destDoc]; lockedDest _ TRUE; IF sourceDoc#destDoc THEN { TiogaOps.Lock[sourceDoc]; lockedSource _ TRUE }; TiogaOps.SelectPoint[destViewer, [TiogaOps.FirstChild[destDoc],0], primary]; TiogaOps.InsertRope["Subject: \001Topic\002\nTo: \001Recipients\002\nReply-To: "]; TiogaOps.InsertRope[userRName]; TiogaOps.InsertRope["\ncc: \001Copies To\002\n\n"]; TiogaOps.InsertRope["\001CoveringMessage\002\n\n"]; IF PeanutProfile.signature#NIL THEN TiogaOps.InsertRope[PeanutProfile.signature]; TiogaOps.InsertRope["-------------------------------------\n"]; IF ~singleNode THEN TiogaOps.BackSpace[]; -- get rid of CR at end TiogaOps.SelectBranches[ -- source viewer: sourceViewer, level: IF singleNode THEN char ELSE branch, caretBefore: FALSE, pendingDelete: FALSE, which: secondary, start: TiogaOps.FirstChild[messageHeader], end: TiogaOps.LastChild[messageHeader]]; TiogaOps.ToPrimary[]; TiogaOps.SelectPoint[destViewer, [TiogaOps.FirstChild[destDoc],0], primary]; [] _ TiogaOps.NextPlaceholder[gotoend: TRUE]; }; Cleanup[]; }; PutToField: PROC [writer: TiogaAccess.Writer] = { TiogaAccessPutField[writer: writer, key: "To", val: Rope.Cat["\001", PeanutProfile.recipients, "\002"]]; }; PutSubjectField: PROC [writer: TiogaAccess.Writer] = { TiogaAccessPutField[writer: writer, key: "Subject", val: "\001Topic\002"]; }; PutSignatureField: PROC [writer: TiogaAccess.Writer] = { IF PeanutProfile.signature#NIL THEN TiogaAccessPutNode[writer: writer, rope: PeanutProfile.signature]; }; PutHeaderFields: PROC [writer: TiogaAccess.Writer] = { IF PeanutProfile.toBeforeSubject THEN {PutToField[writer]; PutSubjectField[writer]} ELSE {PutSubjectField[writer]; PutToField[writer]}; IF PeanutProfile.ccField THEN TiogaAccessPutField[writer: writer, key: "cc", val: IF PeanutProfile.ccToSelf THEN simpleUserName ELSE "\001Copies To\002"]; TiogaAccessPutField[writer: writer, key: "Reply-To", val: simpleUserName]; TiogaAccessEndNode[writer: writer]; -- empty node }; NewMsgForm: PUBLIC PROC [mouseButton: Menus.MouseButton, shift, control: BOOL] = { NewFormWriter: PROC [writer: TiogaAccess.Writer] ~ { PutHeaderFields[writer]; TiogaAccessPutNode[writer: writer, rope: "\001Message\002", format: PeanutProfile.messageNodeFormat]; PutSignatureField[writer]; }; NewForm[NewFormWriter]; }; InternalReport: PROC [r: ROPE] = INLINE { PeanutWindow.OutputRope[r] }; CheckForAbort: PROC RETURNS [BOOL] = INLINE { RETURN [PeanutWindow.abortFlag] }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> RFC822Date: PROC[gmt: BasicTime.GMT _ BasicTime.nullGMT] RETURNS[date: ROPE] = { <> <> <<>> upt: BasicTime.Unpacked _ BasicTime.Unpack[IF gmt = BasicTime.nullGMT THEN BasicTime.Now[] ELSE gmt]; zone: ROPE; month, tyme, year: ROPE; timeFormat: ROPE = "%02g:%02g:%02g %g"; -- "hh:mm:ss zzz" dateFormat: ROPE = "%2g %g %g %g"; -- "dd mmm yy timeFormat" arpaNeg: BOOL_ upt.zone > 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.