<> <> <> <> <> <> <> <> DIRECTORY AlpineWalnutCmds USING [CopyForExpunge], Buttons USING [ButtonProc], Commander USING [CommandProc, Handle, Register], CommandTool USING [Run], Containers, DefaultRemoteNames USING [Get], DB USING [Aborted, Failure, Transaction, AbortTransaction, DeclareSegment, Initialize, TransactionOf], Labels USING [Set], FS USING [Error, SetKeep], GVRetrieve USING [MBXState], Icons USING [IconFlavor, NewIconFromFile], IO, MBQueue USING [Create, QueueClientAction], Menus, MessageWindow USING [Append], PrincOpsUtils USING [IsBound], Process USING [Detach], Rope, Rules USING [Create], UserCredentials USING [Get], UserProfile USING[ProfileChangedProc, Boolean, CallWhenProfileChanges, Token], ViewerBLT USING[ChangeNumberOfLines], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc], ViewerOps, ViewerSpecs USING[openLeftWidth, openRightTopY, openRightWidth], ViewerIO USING [CreateViewerStreams], WalnutControlPrivate USING [CloseTransactions, ClosingWalnut, FlushWQueue, QuitWalnut, StartOrRestartWalnut, WalnutNotifier], WalnutControlMonitorImpl, WalnutDB USING [RegisterUpdatesPendingProc, UnRegisterUpdatesPendingProc], WalnutDBLog USING [GetStartExpungePos], WalnutDisplayerOps USING [msgName, msgSetName], WalnutLog USING [ExpungeMsgs, LogLength, MarkWalnutTransaction], WalnutLogExtras USING [onlyOneTempLog, walnutTempLogName, QuietlyMarkTransaction], WalnutExtras USING [ChangeDBProc, DoWaitCall, DumpProc, EnumWalnutViewers, EstablishFileNames, ExpungeProc, ReadMailProc, RetrieveNewMailProc, ScavengeProc, SetWalnutIcons, TakeDownWalnutViewers], WalnutRetrieve USING [CloseConnection], WalnutSendOps USING [userRName, RegisterReporter, UnregisterReporter, WalnutSendProc], WalnutViewer USING [CreateMenuEntry, FirstLabel, MakeRuler, MakeTypescript], WalnutVoice USING [CheckForWalnuthatch], WalnutWindow USING [Queue, firstMsgSetButton, lastMsgSetButton, selectedMsgSetButtons, AppendButtonProc, ArchiveButtonProc, CreateMsgSetButtonProc, DeleteMsgSetsProc, DestroyAllMsgSetButtons, DoNewMail, PrintMsgSetsProc, SizeOfMsgSetsProc]; WalnutWindowImpl: CEDAR MONITOR LOCKS walnutControlLock IMPORTS AlpineWalnutCmds, DB, FS, Commander, CommandTool, DefaultRemoteNames, Containers, Icons, Labels, MBQueue, Menus, MessageWindow, Rules, IO, PrincOpsUtils, Process, Rope, UserCredentials, UserProfile, ViewerBLT, ViewerEvents, ViewerOps, ViewerIO, ViewerSpecs, walnutControlLock: WalnutControlMonitorImpl, WalnutControlPrivate, WalnutDB, WalnutDBLog, WalnutDisplayerOps, WalnutExtras, WalnutLog, WalnutLogExtras, WalnutRetrieve, WalnutSendOps, WalnutViewer, WalnutVoice, WalnutWindow EXPORTS WalnutControlPrivate, WalnutExtras, WalnutWindow SHARES WalnutControlMonitorImpl, WalnutWindow = BEGIN OPEN WalnutControlPrivate, WalnutExtras, WalnutWindow; <> ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; InitialNotifyLabel: PUBLIC ROPE_ " "; <> walnutVersion: ROPE_ "Walnut"; walnut: PUBLIC Viewer_ NIL; wtsOut: IO.STREAM_ NIL; walnutIcon: PUBLIC Icons.IconFlavor_ tool; newMailIcon: PUBLIC Icons.IconFlavor_ tool; msgSetIcon: PUBLIC Icons.IconFlavor_ tool; msgIcon: PUBLIC Icons.IconFlavor_ tool; iconFilePathName: PUBLIC ROPE_ NIL; walnutEventReg: ViewerEvents.EventRegistration_ NIL; mustQuitWalnut: PUBLIC ROPE_ NIL; previousUser: PUBLIC ROPE_ NIL; doingCheckpoint: PUBLIC BOOL_ FALSE; rollbackFinished: PUBLIC CONDITION; initialActiveIconic: PUBLIC BOOL_ FALSE; initialActiveRight: PUBLIC BOOL_ TRUE; initialActiveOpen: PUBLIC BOOL_ TRUE; msgSetBorders: PUBLIC BOOL_ FALSE; enableTailRewrite: PUBLIC BOOL_ FALSE; excessBytesInLogFile: PUBLIC INT_ 300000; walnutRulerBefore, walnutRulerAfter, walnutTS: PUBLIC Viewer; msgSetText: PUBLIC Viewer; -- the text argument for Create MsgSet fileNameText: PUBLIC Viewer; -- fileName for Archiving mailNotifyLabel: PUBLIC Viewer; -- is there new mail or not autoNewMail: BOOL_ FALSE; tryForNewMail: BOOL_ FALSE; doAutoCommit: BOOL_ FALSE; walnutSegmentFile: PUBLIC ROPE; walnutLogName: PUBLIC ROPE; readOnlyAccess: PUBLIC BOOL; logIsAlpineFile: PUBLIC BOOL; personalMailDB: PUBLIC BOOL; segmentName: PUBLIC ATOM_ $Walnut; walnutQueue: PUBLIC Queue; walnutNullTrans: PUBLIC DB.Transaction; responseSynch: CONDITION; userHasResponded, userConfirmed: BOOL_ FALSE; alpineServer: ROPE = "[Luther.alpine]"; -- for now lastStateReported: PUBLIC GVRetrieve.MBXState_ unknown; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> BuildWalnut: PUBLIC ENTRY Commander.CommandProc = BEGIN ENABLE UNWIND => NULL; h: IO.STREAM; dirName, segName, logName, captionName: ROPE; alpNeeded, logIsAlp: BOOL; IF doingCheckpoint THEN WAIT rollbackFinished; personalMailDB_ TRUE; IF cmd # NIL THEN { h_ IO.RIS[cmd.commandLine]; dirName_ h.GetTokenRope[IO.IDProc ! IO.EndOfStream => {dirName_ NIL; CONTINUE}].token; }; [segName, logName, captionName, logIsAlp]_ CmdProcCall[dirName, cmd]; IF segName=NIL THEN RETURN; IF walnut # NIL THEN IF segName.Equal[walnutSegmentFile, FALSE] THEN { Report["\nWalnut is already running"]; RETURN} ELSE { cdb: ChangeDBParams_ NEW[ChangeDBObject_ [segName: segName, logName: logName, logIsAlp: logIsAlp, alpNeeded: alpNeeded]]; walnut.name_ captionName; ViewerOps.PaintViewer[walnut, caption]; TRUSTED { Process.Detach[FORK ChangeDB[cdb]]}; WAIT cdb.cond; IF walnut # NIL THEN ViewerOps.AddProp[walnut, $IconLabel, dirName]; RETURN }; IF Rope.Length[UserCredentials.Get[].name] = 0 THEN { IF cmd # NIL THEN cmd.out.PutRope["Please Login"]; RETURN}; walnutSegmentFile_ segName; walnutLogName_ logName; logIsAlpineFile_ logIsAlp; WalnutDisplayerOps.msgName _ Rope.Concat[walnutSegmentFile, "!Msg!"]; WalnutDisplayerOps.msgSetName _ Rope.Concat[walnutSegmentFile, "!MsgSet!"]; StartUp[FALSE, captionName]; IF walnut # NIL THEN { ViewerOps.AddProp[walnut, $IconLabel, dirName]; IF personalMailDB AND autoNewMail THEN { tryForNewMail_ FALSE; TRUSTED {Process.Detach[FORK AutoNewMailProc]}; }; }; END; CmdProcCall: INTERNAL PROC[dirName: ROPE, cmd: Commander.Handle] RETURNS[segName, logName, captionName: ROPE, logIsAlp: BOOL] = BEGIN sName, lName: ROPE; alpServer: ROPE = "[Luther.alpine]<"; seg: ROPE = ">Walnut.segment"; log: ROPE = ">Walnut.DBLog"; user: ROPE_ UserCredentials.Get[].name; personalMailDB_ TRUE; IF dirName.Length = 0 THEN { sName_ UserProfile.Token[key: "Walnut.WalnutSegmentFile", default: ""]; lName_ UserProfile.Token[key: "Walnut.WalnutLogFile", default: ""]; IF sName.Length[] = 0 THEN sName_ Rope.Cat[alpServer, user, seg]; IF lName.Length[] = 0 THEN lName_ Rope.Cat[alpServer, user, log]; captionName_ walnutVersion; } ELSE { sName_ Rope.Cat[alpServer, dirName, seg]; lName_ Rope.Cat[alpServer, dirName, log]; personalMailDB_ FALSE; captionName_ Rope.Cat[walnutVersion, " <", dirName, ">"]; }; [segName, logName, logIsAlp]_ EstablishFileNames[sName, lName]; walnutNullTrans_ NIL; IF ~segName.Find[".alpine]", 0, FALSE] > 0 THEN { msg: ROPE_ Rope.Concat[segName, ": is not a valid Walnut segment file; it MUST be on alpine"]; IF cmd # NIL THEN {cmd.out.PutRope[msg]; cmd.out.PutChar['\n]} ELSE ReportRope[msg]; segName_ NIL; }; END; StartUp: INTERNAL PROC[scavengeFirst: BOOL, captionName: ROPE] = BEGIN doAutoCommit_ UserProfile.Boolean[key: "Walnut.DoAutoCommit", default: TRUE]; WalnutLogExtras.onlyOneTempLog_ UserProfile.Boolean[key: "Walnut.OnlyOneTempLog", default: TRUE]; SetTempLogName[]; IF ~BuildWalnutControlWindow[captionName] THEN RETURN; <> IF doAutoCommit THEN BEGIN old: Menus.MenuEntry; IF (old_ Menus.FindEntry[mailDBMenu, "Commit"])#NIL THEN Menus.ReplaceMenuEntry[mailDBMenu, old, NIL]; IF (old_ Menus.FindEntry[nonMailDBMenu, "Commit"])#NIL THEN Menus.ReplaceMenuEntry[nonMailDBMenu, old, NIL]; END ELSE BEGIN IF Menus.FindEntry[mailDBMenu, "Commit"] = NIL THEN Menus.AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Commit", MyCommitProc]]; IF Menus.FindEntry[nonMailDBMenu, "Commit"] = NIL THEN Menus.AppendMenuEntry[nonMailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Commit", MyCommitProc]]; END; IF ~StartOrRestartWalnut[TRUE, scavengeFirst] THEN RETURN; FlushWQueue[]; -- for good measure Report[" ...Ready"]; TRUSTED { Process.Detach[FORK WalnutNotifier[]] }; END; CloseDownWalnut: PUBLIC INTERNAL PROC[doCommit: BOOL] = <> BEGIN v: Viewer_ walnut; walnut.inhibitDestroy_ TRUE; FlushWQueue[]; InternalChangeMenu[workingMenu]; WalnutDB.UnRegisterUpdatesPendingProc[SetWalnutUpdatesPending]; selectedMsgSetButtons_ firstMsgSetButton_ lastMsgSetButton_ NIL; TakeDownWalnutViewers[]; ReportRope["Closing database and saving log ..."]; BEGIN CloseTransactions[doCommit ! DB.Failure, IO.Error => GOTO failure; -- IO.Error replaces FileIOAlpine.Failure UNWIND => CONTINUE ]; EXITS failure => IF DB.TransactionOf[segmentName] # NIL THEN DB.AbortTransaction[DB.TransactionOf[segmentName ! IO.Error => CONTINUE]]; END; IF walnutEventReg # NIL THEN ViewerEvents.UnRegisterEventProc[walnutEventReg, destroy]; walnutEventReg_ NIL; walnut_ NIL; -- don't let others try to use Report WalnutRetrieve.CloseConnection[]; -- NOP if no open connection lastStateReported_ unknown; mailNotifyLabel_ NIL; WalnutSendOps.UnregisterReporter[wtsOut]; wtsOut.Close[]; wtsOut_ NIL; mustQuitWalnut_ NIL; MBQueue.QueueClientAction[walnutQueue, ClosingWalnut, NIL]; v.inhibitDestroy_ FALSE; doingCheckpoint_ FALSE; ViewerOps.DestroyViewer[v]; END; WalnutReadMailFile: PUBLIC ENTRY Commander.CommandProc = BEGIN ENABLE UNWIND => NULL; h: IO.STREAM; fName, messageSet: ROPE; IF walnut = NIL THEN {NoWalnutReport[cmd, "read old mail files"]; RETURN}; IF doingCheckpoint THEN WAIT rollbackFinished; h_ IO.RIS[cmd.commandLine]; fName_ h.GetTokenRope[IO.IDProc ! IO.EndOfStream => GOTO noFile].token; messageSet_ h.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token; ReadMailFile[fName, messageSet]; EXITS noFile => Report["No file specified"]; END; <> ReadMailFile: PUBLIC PROC[fName, msgSet: ROPE] = BEGIN mfp: LIST OF ROPE_ LIST[fName, IF msgSet=NIL THEN "Active" ELSE msgSet]; DoWaitCall[ReadMailProc, mfp]; END; WalnutExpunge: PUBLIC ENTRY Commander.CommandProc = BEGIN ENABLE UNWIND => NULL; IF walnut = NIL THEN {NoWalnutReport[cmd, "do Expunge"]; RETURN}; IF doingCheckpoint THEN WAIT rollbackFinished; DoWaitCall[ExpungeProc]; FinishCall[]; END; WalnutDump: PUBLIC ENTRY Commander.CommandProc = BEGIN ENABLE UNWIND => NULL; IF walnut = NIL THEN {NoWalnutReport[cmd, "do Dump"]; RETURN}; IF doingCheckpoint THEN WAIT rollbackFinished; DoWaitCall[DumpProc]; FinishCall[]; END; WalnutScavenge: PUBLIC ENTRY Commander.CommandProc = BEGIN ENABLE UNWIND => NULL; IF walnut = NIL THEN { segName, logName, captionName: ROPE; logIsAlp: BOOL; personalMailDB_ TRUE; [segName, logName, captionName, logIsAlp]_ CmdProcCall[NIL, cmd]; IF segName=NIL THEN RETURN; walnutSegmentFile_ segName; walnutLogName_ logName; logIsAlpineFile_ logIsAlp; WalnutDisplayerOps.msgName _ Rope.Concat[walnutSegmentFile, "!Msg!"]; WalnutDisplayerOps.msgSetName _ Rope.Concat[walnutSegmentFile, "!MsgSet!"]; StartUp[TRUE, walnutVersion]; IF walnut # NIL THEN ViewerOps.AddProp[walnut, $IconLabel, NIL]; RETURN }; IF doingCheckpoint THEN WAIT rollbackFinished; InternalChangeMenu[workingMenu]; DoWaitCall[ScavengeProc]; FinishCall[]; END; FinishCall: INTERNAL PROC = BEGIN Report["\n... Restarting"]; CloseTransactions[ TRUE ! DB.Failure, IO.Error => CONTINUE]; IF ~StartOrRestartWalnut[TRUE] THEN RETURN; Report[" ... done"]; END; GetNewMail: PUBLIC PROC = {[]_ WalnutNewMail[NIL]}; WalnutNewMail: PUBLIC ENTRY Commander.CommandProc = BEGIN ENABLE UNWIND => NULL; IF walnut = NIL THEN {NoWalnutReport[cmd, "retrieve new mail"]; RETURN}; IF doingCheckpoint THEN WAIT rollbackFinished; DoWaitCall[RetrieveNewMailProc, NIL] END; NoWalnutReport: INTERNAL PROC[cmd: Commander.Handle, msg: ROPE] = BEGIN r: ROPE_ Rope.Concat["Walnut must be running to ", msg]; IF cmd # NIL THEN cmd.out.PutRope[r] ELSE MessageWindow.Append[r] END; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> BuildWalnutControlWindow: INTERNAL PROC[captionName: ROPE] RETURNS[ok: BOOL] = BEGIN bt: Viewer; walnutWidth: INTEGER; transOK: BOOL_ TRUE; IF iconFilePathName=NIL THEN iconFilePathName_ "Walnut.icons"; -- try local one first IF walnutIcon = tool THEN { BEGIN ENABLE FS.Error => GOTO notFound; walnutIcon_ Icons.NewIconFromFile[iconFilePathName, 0]; EXITS notFound => BEGIN ENABLE FS.Error => { iconFilePathName_ NIL; CONTINUE}; remote: ROPE_ DefaultRemoteNames.Get[].current; iconFilePathName_ Rope.Concat[remote, "Walnut>Walnut.icons"]; walnutIcon _ Icons.NewIconFromFile[iconFilePathName, 0]; END; END; IF iconFilePathName#NIL THEN SetWalnutIcons[iconFilePathName]; }; walnut_ ViewerOps.CreateViewer[ flavor: $Container, info: [name: captionName, iconic: FALSE, column: right, scrollable: FALSE, icon: walnutIcon, openHeight: ViewerSpecs.openRightTopY/4]]; walnut.inhibitDestroy_ TRUE; walnutWidth_ IF walnut.column = right THEN ViewerSpecs.openRightWidth ELSE ViewerSpecs.openLeftWidth; ViewerOps.SetMenu[walnut, blankMenu]; mailNotifyLabel_ WalnutViewer.FirstLabel[name: InitialNotifyLabel, parent: walnut]; bt_ walnutRulerBefore_ WalnutViewer.MakeRuler[sib: mailNotifyLabel, h: 2]; walnutRulerAfter_ Rules.Create[ info: [parent: walnut, wy: bt.wy+bt.wh+14+3, ww: walnutWidth, wh: 1]]; Containers.ChildXBound[walnut, walnutRulerAfter]; walnutTS_ WalnutViewer.MakeTypescript[sib: walnutRulerAfter]; FS.SetKeep[tsLogName, 2 ! FS.Error => CONTINUE]; -- if tsLogName doesn't exist OpenTS[doRegister: FALSE]; TRUSTED BEGIN DO IF ~PrincOpsUtils.IsBound[WalnutSendOps.RegisterReporter] THEN {IF ~ContinueThisWalnut["WalnutSend"] THEN RETURN[FALSE] ELSE LOOP}; IF ~PrincOpsUtils.IsBound[AlpineWalnutCmds.CopyForExpunge] THEN {IF ~ContinueThisWalnut["AlpineUserImpls"] THEN RETURN[FALSE] ELSE LOOP}; IF ~PrincOpsUtils.IsBound[DB.DeclareSegment] THEN {IF ~ContinueThisWalnut["Cypress"] THEN RETURN[FALSE] ELSE LOOP}; EXIT; ENDLOOP; END; ViewerOps.SetMenu[walnut, workingMenu]; walnutEventReg_ ViewerEvents.RegisterEventProc[proc: QuitProc, event: destroy, filter: walnut]; WalnutDB.RegisterUpdatesPendingProc[SetWalnutUpdatesPending]; WalnutSendOps.RegisterReporter[wtsOut]; DB.Initialize[nCachePages: 256]; []_ WalnutVoice.CheckForWalnuthatch[]; RETURN[TRUE]; END; tsLogName: ROPE = "///Temp/Walnut/Walnut.TypescriptLog"; OpenTS: PUBLIC PROC[msg: ROPE_ NIL, doRegister: BOOL_ TRUE] = BEGIN IF wtsOut = NIL THEN { wtsOut_ ViewerIO.CreateViewerStreams[NIL, walnutTS, tsLogName].out; IF msg # NIL THEN wtsOut.PutRope[msg]; IF doRegister THEN WalnutSendOps.RegisterReporter[wtsOut]; }; END; CloseTS: PUBLIC PROC = BEGIN IF wtsOut # NIL THEN wtsOut.Close[]; WalnutSendOps.UnregisterReporter[wtsOut]; wtsOut_ NIL; END; ContinueThisWalnut: INTERNAL PROC[who: ROPE] RETURNS[reTry: BOOL]= BEGIN v: Viewer; IF InternalLoadBcd[who] THEN RETURN[TRUE]; Report["Click Continue to push on anyway, Quit to quit out of Walnut"]; IF InternalConfirm[startupConfirmMenu] THEN RETURN[TRUE]; mailNotifyLabel_ NIL; v_ walnut; walnut_ NIL; CloseTS[]; ViewerOps.DestroyViewer[v]; RETURN[FALSE] END; LoadBcdForWalnut: PUBLIC ENTRY PROC[who: ROPE] RETURNS[ok: BOOL] = { ENABLE UNWIND => NULL; RETURN[InternalLoadBcd[who]] }; InternalLoadBcd: INTERNAL PROC[who: ROPE] RETURNS[ok: BOOL] = BEGIN errMsg: ROPE; error: BOOL; [errMsg, error]_ CommandTool.Run[who]; Report[errMsg]; IF ~error THEN RETURN[errMsg=NIL]; IF errMsg.Find["unbound imports", 0, FALSE] >= 0 THEN RETURN[TRUE]; RETURN[FALSE] END; QueueExecCall: PUBLIC ENTRY PROC[proc: PROC[REF ANY], ra: REF ANY] = BEGIN ENABLE UNWIND => NULL; IF walnut # NIL THEN IF doingCheckpoint THEN WAIT rollbackFinished; MBQueue.QueueClientAction[walnutQueue, proc, ra]; END; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> ChangeWalnutMenu: PUBLIC ENTRY PROC[menu: Menus.Menu] = BEGIN ENABLE UNWIND => NULL; InternalChangeMenu[menu] END; InternalChangeMenu: PUBLIC INTERNAL PROC[menu: Menus.Menu] = BEGIN ENABLE UNWIND => NULL; oldCount, newCount: Menus.MenuLine; IF walnut = NIL THEN RETURN; oldCount_ Menus.GetNumberOfLines[walnut.menu]; newCount_ Menus.GetNumberOfLines[menu]; walnut.menu _ menu; IF oldCount # newCount THEN ViewerBLT.ChangeNumberOfLines[walnut, newCount]; ViewerOps.PaintViewer[walnut, menu]; END; UserConfirmed: PUBLIC ENTRY PROC RETURNS[BOOL] = BEGIN ENABLE UNWIND => NULL; RETURN[InternalConfirm[confirmMenu]]; END; InternalConfirm: PUBLIC INTERNAL PROC[m: Menus.Menu_ NIL] RETURNS[BOOL] = BEGIN InternalChangeMenu[IF m = NIL THEN confirmMenu ELSE m]; IF walnut.iconic THEN ViewerOps.BlinkIcon[walnut, 0] ELSE ViewerOps.BlinkIcon[walnutTS]; UNTIL userHasResponded DO WAIT responseSynch; ENDLOOP; userHasResponded_ FALSE; InternalChangeMenu[workingMenu]; RETURN[userConfirmed]; END; <<------------------------->> walnutMenu: PUBLIC Menus.Menu; workingMenu: PUBLIC Menus.Menu_ Menus.CreateMenu[]; confirmMenu: Menus.Menu_ Menus.CreateMenu[]; blankMenu: Menus.Menu_ Menus.CreateMenu[]; startupConfirmMenu: Menus.Menu_ Menus.CreateMenu[]; forceQuitMenu: PUBLIC Menus.Menu_ Menus.CreateMenu[]; maybeQuitMenu: PUBLIC Menus.Menu_ Menus.CreateMenu[]; scavMenu: PUBLIC Menus.Menu_ Menus.CreateMenu[]; mailDBMenu: PUBLIC Menus.Menu_ Menus.CreateMenu[]; readOnlyDBMenu: PUBLIC Menus.Menu_ Menus.CreateMenu[]; nonMailDBMenu: PUBLIC Menus.Menu_ Menus.CreateMenu[]; BuildWalnutMenu: PROC = { OPEN Menus; <> AppendMenuEntry[mailDBMenu, CreateEntry["NewForm", NewMsgProc]]; AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "NewMail", NewMailProc]]; AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Commit", MyCommitProc]]; AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "CloseAll", CloseAllProc]]; AppendMenuEntry[mailDBMenu, CreateEntry["MessageSetOps", MsgSetOpsProc]]; <> AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[q: walnutQueue, name: "Expunge", proc: MenuExpunge, guarded: TRUE], 1]; AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Delete", DeleteMsgSetsProc], 1]; AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "SizeOf", SizeOfMsgSetsProc], 1]; AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Print", PrintMsgSetsProc], 1]; AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Create", CreateMsgSetButtonProc], 1]; AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Archive", ArchiveButtonProc], 1]; AppendMenuEntry[mailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Append", AppendButtonProc], 1]; Menus.ChangeNumberOfLines[mailDBMenu, 1]; <> AppendMenuEntry[readOnlyDBMenu, CreateEntry["NewForm", NewMsgProc]]; AppendMenuEntry[readOnlyDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "CloseAll", CloseAllProc]]; AppendMenuEntry[readOnlyDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "SizeOf", SizeOfMsgSetsProc]]; AppendMenuEntry[readOnlyDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Print", PrintMsgSetsProc]]; AppendMenuEntry[readOnlyDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Archive", ArchiveButtonProc], 1]; AppendMenuEntry[readOnlyDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Append", AppendButtonProc], 1]; <> AppendMenuEntry[nonMailDBMenu, CreateEntry["NewForm", NewMsgProc]]; AppendMenuEntry[nonMailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Commit", MyCommitProc]]; AppendMenuEntry[nonMailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "CloseAll", CloseAllProc]]; AppendMenuEntry[nonMailDBMenu, CreateEntry["MessageSetOps", MsgSetOpsProc]]; <<>> <> AppendMenuEntry[nonMailDBMenu, WalnutViewer.CreateMenuEntry[q: walnutQueue, name: "Expunge", proc: MenuExpunge, guarded: TRUE], 1]; AppendMenuEntry[nonMailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Delete", DeleteMsgSetsProc], 1]; AppendMenuEntry[nonMailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "SizeOf", SizeOfMsgSetsProc], 1]; AppendMenuEntry[nonMailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Print", PrintMsgSetsProc], 1]; AppendMenuEntry[nonMailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Create", CreateMsgSetButtonProc], 1]; AppendMenuEntry[nonMailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Archive", ArchiveButtonProc], 1]; AppendMenuEntry[nonMailDBMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Append", AppendButtonProc], 1]; Menus.ChangeNumberOfLines[nonMailDBMenu, 1]; AppendMenuEntry[workingMenu, CreateEntry["NewForm", NewMsgProc]]; AppendMenuEntry[confirmMenu, CreateEntry["NewForm", NewMsgProc]]; AppendMenuEntry[confirmMenu, CreateEntry["Confirm", WWConfirmProc]]; AppendMenuEntry[confirmMenu, CreateEntry["Deny", WWDenyProc]]; AppendMenuEntry[startupConfirmMenu, CreateEntry["Continue", WWConfirmProc]]; AppendMenuEntry[startupConfirmMenu, CreateEntry["Quit", WWDenyProc]]; AppendMenuEntry[forceQuitMenu, CreateEntry["Quit", WWConfirmProc]]; AppendMenuEntry[maybeQuitMenu, CreateEntry["Quit", WWConfirmProc]]; AppendMenuEntry[maybeQuitMenu, CreateEntry["Retry", WWDenyProc]]; AppendMenuEntry[scavMenu, CreateEntry["Scavenge", WWConfirmProc]]; AppendMenuEntry[scavMenu, CreateEntry["Quit", WWDenyProc]]; }; ChangeDBParams: TYPE = REF ChangeDBObject; ChangeDBObject: TYPE = MONITORED RECORD[segName, logName: ROPE, logIsAlp, alpNeeded: BOOL, cond: CONDITION]; ChangeDB: PROC[cdb: ChangeDBParams] = { DoWaitCall[ChangeDBProc, cdb] }; ChangeDatabase: PUBLIC ENTRY PROC[ra: REF ANY] = BEGIN ENABLE UNWIND => NULL; cdb: ChangeDBParams_ NARROW[ra]; BEGIN ENABLE UNWIND => { NOTIFY cdb.cond }; walnut.inhibitDestroy_ TRUE; InternalChangeMenu[workingMenu]; Report["\nClosing database on ", walnutSegmentFile]; TakeDownWalnutViewers[]; DestroyAllMsgSetButtons[]; FlushWQueue[]; CloseTransactions[TRUE ! DB.Failure, IO.Error, UNWIND => CONTINUE]; WalnutRetrieve.CloseConnection[]; lastStateReported_ unknown; walnutSegmentFile_ cdb.segName; walnutLogName_ cdb.logName; logIsAlpineFile_ cdb.logIsAlp; WalnutDisplayerOps.msgName _ Rope.Concat[walnutSegmentFile, "!Msg!"]; WalnutDisplayerOps.msgSetName _ Rope.Concat[walnutSegmentFile, "!MsgSet!"]; []_ StartOrRestartWalnut[TRUE]; Report["Database is now ", walnutSegmentFile]; NOTIFY cdb.cond; END; END; NewMsgProc: Menus.MenuProc = { WalnutSendOps.WalnutSendProc[fromExec: FALSE]}; <> MsgSetOpsProc: Menus.MenuProc = BEGIN v: Viewer = NARROW[parent]; count: NAT = Menus.GetNumberOfLines[v.menu]; newCount: NAT = IF count = 2 THEN 1 ELSE 2; Menus.ChangeNumberOfLines[v.menu, newCount]; ViewerBLT.ChangeNumberOfLines[v, newCount]; END; MenuExpunge: Menus.MenuProc = { EntryExpunge: ENTRY PROC = BEGIN ENABLE UNWIND => NULL; DoExpunge[doUpdates: TRUE, tailRewrite: mouseButton=red]; FinishCall[]; END; IF shift THEN { start: INT_ WalnutDBLog.GetStartExpungePos[]; len: INT_ WalnutLog.LogLength[FALSE]; dif: INT; Report[IO.PutFR[" The log file is %g bytes (%g pages) long", IO.int[len], IO.int[(len+511)/512]]]; Report[IO.PutFR[" An expunge would start at pos %g and read %g bytes (%g pages) of the log", IO.int[start], IO.int[dif_ (len - start)], IO.int[(dif+511)/512]]]; } ELSE EntryExpunge[]; }; DoExpunge: PUBLIC INTERNAL PROC[doUpdates, tailRewrite: BOOL] = BEGIN ok: BOOL_ TRUE; tempLogName: ROPE_ "Walnut.TempLog"; InternalChangeMenu[workingMenu]; TakeDownWalnutViewers[]; DestroyAllMsgSetButtons[]; WalnutLog.MarkWalnutTransaction[]; FlushWQueue[]; IF WalnutLogExtras.walnutTempLogName = NIL THEN SetTempLogName[]; Report[IO.PutFR["Dumping message database to %g at %g", IO.rope[WalnutLogExtras.walnutTempLogName], IO.time[]]]; BEGIN ENABLE ABORTED => {ok_ FALSE; CONTINUE}; ok_ WalnutLog.ExpungeMsgs [tempLog: NIL, doUpdates: doUpdates, tailRewrite: (tailRewrite AND enableTailRewrite)]; END; IF ~ok THEN { DB.AbortTransaction[ DB.TransactionOf[segmentName ! DB.Aborted, DB.Failure => CONTINUE]]; Report["Expunge failed or aborted; changes to database have been aborted"]; } ELSE Report[" Expunge complete"]; InternalChangeMenu[walnutMenu]; END; MyCommitProc: Menus.MenuProc = { ENABLE UNWIND => ChangeWalnutMenu[walnutMenu]; ChangeWalnutMenu[workingMenu]; WalnutLog.MarkWalnutTransaction[]; ChangeWalnutMenu[walnutMenu] }; NewMailProc: Menus.MenuProc = { ENABLE UNWIND => ChangeWalnutMenu[walnutMenu]; ChangeWalnutMenu[workingMenu]; RetrieveNewMail[]; ChangeWalnutMenu[walnutMenu] }; AutoNewMailProc: PROC = BEGIN ENABLE UNWIND => tryForNewMail_ TRUE; DoWaitCall[RetrieveNewMailProc]; tryForNewMail_ TRUE; END; RetrieveNewMail: PUBLIC ENTRY PROC = BEGIN ENABLE UNWIND => NULL; IF doingCheckpoint THEN WAIT rollbackFinished; InternalNewMail[]; END; InternalNewMail: PUBLIC PROC = BEGIN ENABLE UNWIND => tryForNewMail_ TRUE; thisUser: ROPE_ UserCredentials.Get[].name; IF ~Rope.Equal[thisUser, previousUser, FALSE] THEN { r: ROPE_ "User has changed"; Report[IO.PutFR ["******%g may not retrieve %g's new mail", IO.rope[thisUser], IO.rope[previousUser]]]; Report["You will be forced to quit out of Walnut; you should then Login to the exec"]; FlushWQueue[]; MBQueue.QueueClientAction[walnutQueue, QuitWalnut, r]; } ELSE { tryForNewMail_ FALSE; DoNewMail[]; tryForNewMail_ TRUE }; END; CloseAllProc: Menus.MenuProc = BEGIN msgSetList, msgList, queryList: LIST OF Viewer; [msgSetList, msgList, queryList]_ EnumWalnutViewers[TRUE]; FOR vL: LIST OF Viewer_ msgList, vL.rest UNTIL vL=NIL DO ViewerOps.DestroyViewer[vL.first]; ENDLOOP; FOR vL: LIST OF Viewer_ queryList, vL.rest UNTIL vL=NIL DO ViewerOps.CloseViewer[vL.first]; ENDLOOP; FOR vL: LIST OF Viewer_ msgSetList, vL.rest UNTIL vL=NIL DO ViewerOps.CloseViewer[vL.first]; ENDLOOP; MyCommitProc[parent]; ViewerOps.CloseViewer[walnut]; END; QuitProc: ENTRY ViewerEvents.EventProc = BEGIN IF walnutEventReg = NIL THEN RETURN[FALSE]; MBQueue.QueueClientAction[walnutQueue, QuitWalnut, NIL]; RETURN[TRUE]; END; WWDenyProc: ENTRY Menus.MenuProc = { ENABLE UNWIND => NULL; userConfirmed_ FALSE; userHasResponded_ TRUE; BROADCAST responseSynch }; WWConfirmProc: ENTRY Menus.MenuProc = { ENABLE UNWIND => NULL; userConfirmed_ TRUE; userHasResponded_ TRUE; BROADCAST responseSynch }; <<**********************************************************************>> <> SetWalnutUpdatesPending: PUBLIC PROC[newVersion: BOOL] = BEGIN old: BOOL; IF walnut = NIL OR walnut.destroyed OR doAutoCommit THEN RETURN; old_ walnut.newVersion; IF newVersion#old THEN {walnut.newVersion_ newVersion; ViewerOps.PaintViewer[walnut, caption]}; END; CheckForAutoCommit: PUBLIC INTERNAL PROC = BEGIN IF doAutoCommit THEN WalnutLogExtras.QuietlyMarkTransaction[]; END; UncomittedUpdates: PUBLIC ENTRY PROC RETURNS[updatesPending: BOOL] = { ENABLE UNWIND => NULL; RETURN[IF walnut#NIL THEN walnut.newVersion ELSE FALSE]; }; <<**********************************************************************>> Report: PUBLIC PROC[msg1, msg2, msg3, msg4: ROPE_ NIL] = { IF wtsOut = NIL THEN { IF msg1#NIL THEN MessageWindow.Append[msg1]; IF msg2#NIL THEN MessageWindow.Append[msg2]; IF msg3#NIL THEN MessageWindow.Append[msg3]; IF msg4#NIL THEN MessageWindow.Append[msg4]; RETURN }; IF msg1#NIL THEN wtsOut.PutRope[msg1]; IF msg2#NIL THEN wtsOut.PutRope[msg2]; IF msg3#NIL THEN wtsOut.PutRope[msg3]; IF msg4#NIL THEN wtsOut.PutRope[msg4]; wtsOut.PutChar[IO.CR]; }; ReportRope: PUBLIC PROC[msg1: ROPE] = { IF wtsOut # NIL THEN wtsOut.PutRope[msg1] ELSE MessageWindow.Append[msg1]; }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> WatchMailBox: PUBLIC PROC[newState: GVRetrieve.MBXState] = <> BEGIN showTime: BOOL_ TRUE; status: ROPE; icon: Icons.IconFlavor_ walnutIcon; IF walnut = NIL THEN RETURN; IF walnut.destroyed THEN RETURN; IF newState = unknown THEN RETURN; IF (lastStateReported = notEmpty) AND (newState = someEmpty OR newState = allEmpty) THEN status_ "New mail was read" ELSE { SELECT newState FROM badName => {status_ "Your user name is invalid, please log in"; showTime_ FALSE}; badPwd => {status_ "Your password is invalid"; showTime_ FALSE}; cantAuth => {status_ "Can't check your credentials at this time"; showTime_ FALSE}; userOK => {status_ "Your credentials are OK"; showTime_ FALSE}; allDown => status_ "All of the mail servers are down"; someEmpty => status_ "All of the mail servers checked are empty"; allEmpty => status_ "There is no new mail"; notEmpty => {status_ "You have new mail"; icon_ newMailIcon}; ENDCASE => status_ "Bad State!"; }; lastStateReported _ newState; IF walnut.icon # icon THEN { walnut.icon_ icon; IF walnut.iconic THEN ViewerOps.PaintViewer[walnut, all]}; Labels.Set[mailNotifyLabel, IF showTime THEN Rope.Concat[status, IO.PutFR[" at %g", IO.time[]]] ELSE status]; IF newState = notEmpty AND autoNewMail AND tryForNewMail THEN { tryForNewMail_ FALSE; TRUSTED {Process.Detach[FORK AutoNewMailProc]}; }; END; SetAutoNewMailVars: ENTRY UserProfile.ProfileChangedProc = CHECKED BEGIN ENABLE UNWIND => NULL; autoNewMail_ UserProfile.Boolean[key: "Walnut.AutoNewMail", default: FALSE]; tryForNewMail_ autoNewMail; SetTempLogName[]; END; SetTempLogName: PROC = { WalnutLogExtras.walnutTempLogName_ Rope.Cat["///Walnut/", WalnutSendOps.userRName, "/Walnut.TempLog"]; }; <<-------------------------->> walnutQueue_ MBQueue.Create[pushModel: FALSE]; -- queue for menu/button pushes BuildWalnutMenu[]; Commander.Register["Walnut", BuildWalnut, "For retrieving and sending mail"]; Commander.Register["WalnutScavenge", WalnutScavenge, "Rebuilds Mail database from log file"]; Commander.Register["WalnutOldMailReader", WalnutReadMailFile, "Reads Laurel, Hardy or Archive files into Mail database"]; Commander.Register["WalnutExpunge", WalnutExpunge, "Expunges deleted messages"]; Commander.Register["WalnutDump", WalnutDump, "Writes an expunged log file on Walnut.TempLog; does not change database"]; Commander.Register["WalnutNewMail", WalnutNewMail, "Retrieves new mail"]; UserProfile.CallWhenProfileChanges[SetAutoNewMailVars]; END.