/* ** $VER: CfgSortMail.thor 3.0 (24.5.96) ** by Eirik Nicolai Synnes ** ** See SortMail.guide for documentation ** */ options results options failat 31 /*signal on error*/ signal on syntax signal on break_c signal on halt parse arg arguments /* ** Initialize some variables */ globals = 'version mvhelp thorport thorpath cfgpath cfgfile data. head. text. textread globalcfg. trigger. temp. atemp. stemp. menulist. conflist. progwin currentmsg. BBSREAD.LASTERROR THOR.LASTERROR globals' version = subword(sourceline(2), 4) cfgfile = 'SortMail.cfg' cfgpath = 'ENV:Thor/' /* ** Check for Thor's ARexx port */ thorport = address() if left(thorport, 5) ~= 'THOR.' then do say 'CfgSortMail.thor can only be run from within Thor.' signal cleanup end /* ** Find/open BBSREAD ARexx port */ if ~show('P', 'BBSREAD') then do address(command) 'Run >NIL: `GetEnv THOR/THORPath`bin/LoadBBSRead' 'WaitForPort BBSREAD' if rc ~= 0 then call displayerror(30, 'Couldn''t open BBSREAD''s ARexx port.') end /* ** See if another copy of SortMail is already running */ address(thorport) if getclip('CSM_Active') ~= '' then if ~reqnotify('Another copy of CfgSortMail is probably running.\nDo you want to continue?') then signal cleanup call setclip('CSM_Active', 'True') /* ** Find the configuration file (SortMail.cfg) */ call open(pn, 'ENV:Thor/THORPATH', 'R') thorpath = readln(pn) call close(pn) /* ** Check Workbench version */ address command 'Version >NIL: workbench.library 40' if rc = 0 then mvhelp = 1; else mvhelp = 0 /* ** Read configuration */ call readcfg() /* ** Display main menu */ mainmenu: menulist.1 = 'Email system: 'globalcfg.SYSTEM menulist.2 = 'Email conference: 'globalcfg.CONFERENCE menulist.3 = 'Create statistics: '; if globalcfg.STATISTICS then menulist.3 = menulist.3 || 'YES'; else menulist.3 = menulist.3 || 'NO' menulist.4 = 'Display warnings: '; if ~globalcfg.NOWARN then menulist.4 = menulist.4 || 'YES'; else menulist.4 = menulist.4 || 'NO' menulist.5 = 'Use messages'' login state: '; if globalcfg.LOGINSTATE then menulist.5 = menulist.5 || 'YES'; else menulist.5 = menulist.5 || 'NO' menulist.6 = '' menulist.7 = 'Add new trigger' menulist.8 = 'Edit a trigger' menulist.9 = 'Delete a trigger' menulist.10 = '' menulist.11 = 'Save and exit' menulist.count = 11 choice = showmenu('CfgSortMail 'version, 4) select when choice = 0 then if reqnotify('Are you sure you want\nto exit without saving?') then signal cleanup when choice = 1 then do address(bbsread) GETBBSLIST STEM bbslist if rc > 0 then call displayerror(rc, 'GETBBSLIST:\n'BBSREAD.LASTERROR) address(thorport) REQUESTLIST INSTEM bbslist TITLE '"Select system"' SIZEGADGET if rc > 5 then call displayerror(rc, 'REQUESTLIST:\n'THOR.LASTERROR) else if rc ~= 5 then globalcfg.SYSTEM = result drop bbslist. end when choice = 2 then do address(bbsread) GETCONFLIST BBSNAME '"'globalcfg.SYSTEM'"' STEM conflist if rc > 0 then call displayerror(rc, 'GETCONFLIST:\n'BBSREAD.LASTERROR) address(thorport) REQUESTLIST INSTEM conflist TITLE '"Select conference"' SIZEGADGET if rc > 5 then call displayerror(rc, 'REQUESTLIST:\n'THOR.LASTERROR) else if rc ~= 5 then globalcfg.CONFERENCE = result drop conflist. end when choice = 3 then globalcfg.STATISTICS = reqnotify('Create statistics?\nIf you select *"Yes*" a message will be written in\nthe Email conference containing statistics on\nSortMail''s doings every time SortMail is run.') when choice = 4 then if reqnotify('Display warnings?\nDisplaying of non-fatal warnings can be\nsupressed by selecting *"No*". The warnings\nwill still be reported in SortMail''s log\nmessage.') then globalcfg.NOWARN = 0; else globalcfg.NOWARN = 1 when choice = 5 then globalcfg.LOGINSTATE = reqnotify('Use messages'' login state?\nIf you select *"Yes*" SortMail will process\nall messages that was marked as unread\nwhen the user entered the current system\nin Thor. Otherwise only the messages currently\nmarked as unread will be processed.') when choice = 7 then do call cfgtrigger(1) end when choice = 8 then do call cfgtrigger(2) end when choice = 9 then do do i = 1 to trigger.count; menulist.i = trigger.i.name; end menulist.count = trigger.count deltrig = showmenu('Select trigger to delete') if deltrig = 0 then break if ~reqnotify('Are you sure you want to\nwant to delete this trigger:\n'trigger.deltrig.name'.') then break tcnt = 0 do i = 1 to trigger.count if i ~= deltrig then do; tcnt = tcnt + 1; if tcnt ~= i then call copystem(20, i, tcnt); end end trigger.count = trigger.count - 1; i = i - 1 drop trigger.i. deltrig end when choice = 11 then do if globalcfg.SYSTEM = 'Change me!' | globalcfg.CONFERENCE = 'Change me!' then do if reqnotify('You must specify a system and a\nconference name. Do you want to\nexit without saving the configuration?') then signal cleanup end else do call writecfg() signal cleanup end end otherwise nop end signal mainmenu error: syntax: select when symbol('BBSREAD.LASTERROR') = 'VAR' then call displayerror(rc, 'Line 'sigl': 'BBSREAD.LASTERROR) when symbol('THOR.LASTERROR') = 'VAR' then call displayerror(rc, 'Line 'sigl': 'THOR.LASTERROR) otherwise call displayerror(30, 'Error 'rc' in line 'sigl': 'errortext(rc)) end break_c: halt: cleanup: /* ** Close progress window if open */ if symbol('progwin') = 'VAR' then do address(thorport) CLOSEPROGRESS REQ progwin drop progwin end /* ** Have a nice day */ call setclip('CSM_Active') exit(0) /**************************************************************************** ****************************** Configure trigger ****************************** ****************************************************************************/ cfgtrigger: interpret 'procedure expose 'globals parse arg cfgtype if cfgtype = 1 then do /* Add trigger */ temp.name = 'Change me' temp.delmsg = 0 temp.deluser = 0 temp.action.count = 0 temp.search.count = 0 org = trigger.count + 1 end if cfgtype = 2 then do /* Edit trigger */ if trigger.count = 0 then do call reqnotify('There are no triggers to configure.', 'Bummer') return(0) end do i = 1 to trigger.count; menulist.i = trigger.i.name; end menulist.count = trigger.count org = showmenu('Select trigger to edit') if org = 0 then return(0) call copystem(1,org) end triggermenu: menulist.1 = 'Name: 'left(temp.name, 56) menulist.2 = 'Delete message: '; if temp.delmsg then menulist.2 = menulist.2 || 'YES'; else menulist.2 = menulist.2 || 'NO' menulist.3 = 'Delete user: '; if temp.deluser then menulist.3 = menulist.3 || 'YES'; else menulist.3 = menulist.3 || 'NO' menulist.4 = '' menulist.5 = 'Add search entry' menulist.6 = 'Add action entry' menulist.7 = '' menulist.8 = 'Search entries:'; sline = 8 if temp.search.count > 0 then do i = 1 to temp.search.count lno = sline + i select when temp.search.i.type = 'TOADDR' then menulist.lno = ' To address: 'left(temp.search.i.criteria, 56) when temp.search.i.type = 'TONAME' then menulist.lno = ' To name: 'left(temp.search.i.criteria, 56) when temp.search.i.type = 'FROMADDR' then menulist.lno = ' From address: 'left(temp.search.i.criteria, 56) when temp.search.i.type = 'FROMNAME' then menulist.lno = ' From name: 'left(temp.search.i.criteria, 56) when temp.search.i.type = 'SUBJECT' then menulist.lno = ' Subject: 'left(temp.search.i.criteria, 56) when temp.search.i.type = 'HEADER' then do; menulist.lno = ' Header field: 'left('"'temp.search.i.keyword'"-line: 'temp.search.i.criteria, 56); end otherwise menulist.lno = ' Unknown: 'left(temp.search.i.criteria, 30) end end else do; lno = sline + 1; menulist.lno = ' None'; end lno = lno + 1; menulist.lno = '' lno = lno + 1; menulist.lno = 'Action entries:'; aline = lno if temp.action.count > 0 then do i = 1 to temp.action.count lno = aline + i select when temp.action.i.type = 'COPY' then menulist.lno = ' Copy message: 'left('To "'temp.action.i.destconf'"', 56) when temp.action.i.type = 'RECENT' then do; menulist.lno = ' AmiNet RECENT: '; if temp.action.i.checkdupes then menulist.lno = menulist.lno'Check for dupes'; else menulist.lno = menulist.lno'Don''t check for dupes'; end when temp.action.i.type = 'SAVEMESSAGE' then do; menulist.lno = ' Save message: '; if symbol('temp.action.i.filename') = 'VAR' then menulist.lno = menulist.lno'Filename "'temp.action.i.filename'"'; else menulist.lno = menulist.lno'Directory "'temp.action.i.directory'"'; end when temp.action.i.type = 'SPLITDIGEST' then menulist.lno = ' Split digest: 'left('To "'temp.action.i.destconf'"', 56) when temp.action.i.type = 'EXTERNAL' then menulist.lno = ' Ext. script: 'left('Script name "'temp.action.i.scriptname'"', 56) otherwise menulist.lno = ' Unknown: 'left(temp.action.i.criteria, 30) end end else do; lno = aline + 1; menulist.lno = ' None'; end lno = lno + 1; menulist.lno = '' lno = lno + 1; menulist.lno = 'Accept and return' menulist.count = lno choice = showmenu('Trigger configuration', 5) select when choice = 0 then do if reqnotify('Are you sure you want to abort\nconfiguration of this trigger?') then do; drop temp.; return(0); end end when choice = 1 then temp.name = reqstring('Trigger name', 'Enter a name for the trigger.', addasterix(temp.name), 40) when choice = 2 then temp.DELMSG = reqnotify('Delete message?\nSelect yes if you want the original\nmessage to be deleted from the EMail\nconference. Otherwise select no.') when choice = 3 then temp.DELUSER = reqnotify('Delete user?\nThor always adds the name of EMail authors\nto the user database, no matter what you have\nconfigured it to do. SortMail can delete\nthese users provided they have written a\nmessage processed by this trigger. Do you\nwant SortMail to do so?') when choice = 5 then call cfgsearch(1) when choice = 6 then call cfgaction(1) when (choice = sline) | (choice = aline) then call reqnotify('Select an entry to\nedit or delete it.', 'I''ll do that') when (choice > sline) & (choice < (aline - 1)) & (temp.search.count > 0) then do call reqnotify('Do you want to edit or\ndelete this search entry?', 'Edit|_Delete|_Abort') if result = 1 then call cfgsearch(2, (choice - sline)) else if result = 2 then call delsearch(choice - sline) end when (choice > aline) & (choice < (lno - 1)) & (temp.action.count > 0) then do call reqnotify('Do you want to edit or\n delete this action entry?', 'Edit|_Delete|_Abort') if result = 1 then call cfgaction(2, (choice - aline)) else if result = 2 then call delaction(choice - aline) end when choice = lno then do if cfgtype = 1 then do; trigger.count = trigger.count + 1; org = trigger.count; end call copystem(10, , org); drop temp. return(0) end otherwise nop end signal triggermenu return(0) /**************************************************************************** **************************** Configure action entry *************************** ****************************************************************************/ cfgaction: interpret 'procedure expose 'globals parse arg atype, ano if atype = 1 then do menulist.1 = 'Copy message to another conference' menulist.2 = 'Parse AmiNet RECENT message' menulist.3 = 'Save message to disk' menulist.4 = 'Split digest' menulist.5 = 'Run an external ARexx script' menulist.count = 5 act = showmenu('Select action type', 7) select when act = 0 then return(0) when act = 1 then do; atemp.type = 'COPY'; atemp.destconf = ''; end when act = 2 then do; atemp.type = 'RECENT'; atemp.checkdupes = 0; end when act = 3 then do; atemp.type = 'SAVEMESSAGE'; atemp.header = 0; atemp.append = 0; atemp.nobin = 0; end when act = 4 then do; atemp.type = 'SPLITDIGEST'; atemp.destconf = ''; end when act = 5 then do; atemp.type = 'EXTERNAL'; atemp.scriptname = ''; end otherwise nop end drop act end if atype = 2 then call copystem(2, ano) select when atemp.type = 'COPY' then do address(bbsread) GETCONFLIST BBSNAME '"'globalcfg.SYSTEM'"' STEM conflist if rc > 0 then call displayerror(rc, 'GETCONFLIST:\n'BBSREAD.LASTERROR) address(thorport) menulist.1 = 'Create new conference ' menulist.2 = '' do i = 1 to conflist.count mcnt = i + 2; menulist.mcnt = conflist.i end menulist.count = i + 1 confchoice = showmenu('Select destination conference') if confchoice = 1 then atemp.destconf = reqstring('Destination conference', 'Enter new conference name.\nThe new conference will be created\nby SortMail when the first message\nis copied to it.', addasterix(atemp.destconf), 40) else if confchoice > 2 then do cno = confchoice - 2; atemp.destconf = conflist.cno end if confchoice ~= 0 then do if symbol('atemp.replyaddr') ~= 'VAR' then atemp.replyaddr = '' atemp.replyaddr = reqstring('Reply address', 'Enter an address replies should be addressed to.\nIf no reply address is given then replies will\nbe directed to the author or the address given\nin the Reply-To: header field. To do so, cancel\nthis requester.', addasterix(atemp.replyaddr), 40, 1, 'replyaddr') if atemp.replyaddr = '' then drop atemp.replyaddr end drop conflist. menulist. confchoice cno mcnt end when atemp.type = 'RECENT' then atemp.CHECKDUPES = reqnotify('Do you want to check for duplicates\nwhen adding files to the file database?\nThis will slow down operation.') when atemp.type = 'SAVEMESSAGE' then do if reqnotify('Do you want to save to a specific\nfile or to a directory using the\nsubject as the file name?', 'File|_Directory') then do; drop atemp.directory; if symbol('atemp.filename') ~= 'VAR' then atemp.filename = ''; end else do; drop atemp.filename; if symbol('atemp.directory') ~= 'VAR' then atemp.directory = ''; end if symbol('atemp.filename') = 'VAR' then do pathpart = getpathpart(atemp.filename) filepart = getfilepart(atemp.filename) pattern = '' end else do pathpart = getpathpart(atemp.directory) filepart = '' pattern = '~#?' end call reqfile('Select path', pathpart, filepart, pattern) if symbol('atemp.filename') = 'VAR' then atemp.filename = result else atemp.directory = getpathpart(result) if symbol('atemp.directory') = 'VAR' then do if reqnotify('Do you want to substitute part of the\nsubject with another string? This can\nbe done to shorten the filename of\nsaved messages in order to fit into\nAmigaDOS''s 35 character filename limit.') then do if symbol('atemp.substitute') ~= 'VAR' then atemp.substitute = '' atemp.substitute = reqstring('Substitute string', 'Enter the string you want to substitute.', addasterix(atemp.substitute), 200, 1) if atemp.substitute = '' then drop atemp.substitute else do if symbol('atemp.with') ~= 'VAR' then atemp.with = '' atemp.with = reqstring('Substitute with', 'Enter the string you want to substitute with.', addasterix(atemp.with), 200, 1) if atemp.with = '' then drop atemp.substitute atemp.with end end else drop atemp.substitute atemp.with end else drop atemp.substitute atemp.with atemp.HEADER = reqnotify('Include header?\nDo you want to include\nheaders in the saved messages?') atemp.APPEND = reqnotify('Append to file?\nDo you want to append messages if the\ndestination file already exists, then\nselect *"YES*". Otherwise the file\nwill be overwritten.') if reqnotify('Save binaries?\nDo you want binary file parts to be copied\nto the download directory, then select *"YES*".\nOtherwise no action will be taken when a file\npart is found.\nNote: uuencoded files are not file parts, but\nfiles embedded in a MIME message are!') then atemp.nobin = 0; else atemp.nobin = 1 end when atemp.type = 'SPLITDIGEST' then do address(bbsread) GETCONFLIST BBSNAME '"'globalcfg.SYSTEM'"' STEM conflist if rc > 0 then call displayerror(rc, 'GETCONFLIST:\n'BBSREAD.LASTERROR) address(thorport) menulist.1 = 'Create new conference ' menulist.2 = '' do i = 1 to conflist.count mcnt = i + 2; menulist.mcnt = conflist.i end menulist.count = i + 1 confchoice = showmenu('Select destination conference') if confchoice = 1 then atemp.destconf = reqstring('Destination conference', 'Enter new conference name.\nThe new conference will be created\nby SortMail when the first message\nis copied to it.', addasterix(atemp.destconf), 40) else if confchoice > 2 then do cno = confchoice - 2; atemp.destconf = conflist.cno end if confchoice ~= 0 then do if symbol('atemp.replyaddr') ~= 'VAR' then atemp.replyaddr = '' atemp.replyaddr = reqstring('Reply address', 'Enter an address replies should be addressed to.\nIf no reply address is given then replies will\nbe directed to the author or the address given\nin the Reply-To: header field. To do so, cancel\nthis requester.', addasterix(atemp.replyaddr), 40, 1, 'replyaddr') if atemp.replyaddr = '' then drop atemp.replyaddr end drop conflist. menulist. confchoice cno mcnt end when atemp.type = 'EXTERNAL' then do if symbol('atemp.scriptname') ~= 'VAR' then atemp.scriptname = '' filepart = getfilepart(atemp.scriptname) pathpart = getpathpart(atemp.scriptname); if pathpart = '' then pathpart = thorpath'rexx' atemp.scriptname = reqfile('Select script', pathpart, filepart, '(#?.br|#?.thor|#?.rexx)') if symbol('atemp.scriptopts') ~= 'VAR' then atemp.scriptopts = '' atemp.scriptopts = reqstring('Script options', 'Enter the command line options that should be\npassed to the script. The following keywords\ncan be used:\n\n %%s returns the system name\n %%c returns the email conference name\n %%n returns the number of the message\n %%p returns the name of Thor''s ARexx port\n\nSortMail will add double quotes (*") around %%s,\n%%c and %%p, so don''t do that yourself.\nIf you don''t want to pass any options then\ncancel this requester.', addasterix(atemp.scriptopts), 256, 1) if atemp.scriptopts = '' then drop atemp.scriptopts end otherwise nop end /* Update action stem */ if atype = 1 then do; temp.action.count = temp.action.count + 1; ano = temp.action.count; end call copystem(11,, ano) drop atemp. ano pathpart filepart return(0) /**************************************************************************** **************************** Configure search entry *************************** ****************************************************************************/ cfgsearch: interpret 'procedure expose 'globals parse arg stype, sno if stype = 1 then do menulist.1 = 'From address ' menulist.2 = 'From name' menulist.3 = 'To address' menulist.4 = 'To name' menulist.5 = 'Subject' menulist.6 = 'Header' menulist.count = 6 searchin = showmenu('Select search type', 6) select when searchin = 0 then return(0) when searchin = 1 then stemp.type = 'FROMADDR' when searchin = 2 then stemp.type = 'FROMNAME' when searchin = 3 then stemp.type = 'TOADDR' when searchin = 4 then stemp.type = 'TONAME' when searchin = 5 then stemp.type = 'SUBJECT' when searchin = 6 then stemp.type = 'HEADER' otherwise nop end stemp.criteria = '' if stemp.type = 'HEADER' then stemp.keyword = '' drop searchin end if stype = 2 then call copystem(3, sno) select when stemp.type = 'FROMADDR' then where = 'from address' when stemp.type = 'FROMNAME' then where = 'from name' when stemp.type = 'TOADDR' then where = 'to address' when stemp.type = 'TONAME' then where = 'to name' when stemp.type = 'SUBJECT' then where = 'subject' when stemp.type = 'HEADER' then where = 'header' otherwise stemp.type = '!unknown!' end if stemp.type = 'HEADER' then do stemp.keyword = reqstring('Search keyword', 'Enter the first word on the\nheader line you want to search in.', addasterix(stemp.keyword), 60, 1, 'headline') if stemp.keyword = '' then signal bailout stemp.criteria = reqstring('Search criteria', 'Enter string to search for\nin the header line.', addasterix(stemp.criteria), 60, 1) if stemp.criteria = '' then signal bailout end else do stemp.criteria = reqstring('Search criteria', 'Enter string to search for\nin the 'where'.', addasterix(stemp.criteria), 60, 1, stemp.TYPE) if stemp.criteria = '' then signal bailout end /* Update search stem */ if stype = 1 then do; temp.search.count = temp.search.count + 1; sno = temp.search.count; end call copystem(12, , sno) bailout: drop stemp. return(0) /**************************************************************************** ***************************** Delete action entry ***************************** ****************************************************************************/ delaction: interpret 'procedure expose 'globals parse arg ano if reqnotify('Are you sure you want\nto delete this entry?') then do acnt = 0 do i = 1 to temp.action.count if i ~= ano then do; acnt = acnt + 1; call copystem(21, i, acnt); end end temp.action.count = temp.action.count - 1; i = i - 1 drop acnt temp.action.i. end return(0) /**************************************************************************** ***************************** Delete search entry ***************************** ****************************************************************************/ delsearch: interpret 'procedure expose 'globals parse arg sno if reqnotify('Are you sure you want\nto delete this entry?') then do scnt = 0 do i = 1 to temp.search.count if i ~= sno then do; scnt = scnt + 1; call copystem(22, i, scnt); end end temp.search.count = temp.search.count - 1; i = i - 1 drop scnt temp.search.i. end return(0) /**************************************************************************** ************************* Show menu and wait for input ************************ ****************************************************************************/ showmenu: interpret 'procedure expose 'globals parse arg menutitle, helpnode if helpnode ~= '' & mvhelp then do rcnt = menulist.count rcnt = rcnt + 1; menulist.rcnt = '' rcnt = rcnt + 1; menulist.rcnt = 'HELP' menulist.count = rcnt end doit: address(thorport) REQUESTLIST INSTEM menulist TITLE '"'menutitle'"' SIZEGADGET if rc = 5 then i = 0 else if rc ~= 0 then displayerror(rc, 'REQUESTLIST:\n'THOR.LASTERROR) else do choice = result if choice = '' then signal doit if choice = 'HELP' then do; call help(helpnode); signal doit; end do i = 1 to menulist.count if choice = menulist.i then break end end drop menulist. return(i) /**************************************************************************** ************************* Write configuration to disk ************************* ****************************************************************************/ writecfg: interpret 'procedure expose 'globals cfgopen = open(cf, cfgpath || cfgfile, 'W') if cfgopen then do address(thorport) OPENPROGRESS TITLE '"CfgSortMail 'version'"' TOTAL trigger.count PT '"Writing configuration..."' if rc ~= 0 then displayerror(rc, 'OPENPROGRESS:\n'THOR.LASTERROR) else progwin = result entry = 'GLOBAL SYSTEM "'addasterix(globalcfg.SYSTEM)'" CONFERENCE "'addasterix(globalcfg.CONFERENCE)'"' if globalcfg.STATISTICS then entry = entry' STATISTICS' if globalcfg.NOWARN then entry = entry' NOWARN' if globalcfg.LOGINSTATE then entry = entry' LOGINSTATE' call writeln(cf, entry) if rc ~= 0 then call displayerror(30, 'Error while writing configuration file.') call writeln(cf, '') if rc ~= 0 then call displayerror(30, 'Error while writing configuration file.') do i = 1 to trigger.count UPDATEPROGRESS progwin CURRENT i if rc ~= 0 then displayerror(rc, 'UPDATEPROGRESS:\n'THOR.LASTERROR) entry = 'TRIGGER NAME "'addasterix(trigger.i.NAME)'"' if trigger.i.delmsg then entry = entry' DELMSG' if trigger.i.deluser then entry = entry' DELUSER' call writeln(cf, entry) if rc ~= 0 then call displayerror(30, 'Error while writing configuration file.') if trigger.i.search.count > 0 then do j = 1 to trigger.i.search.count entry = 'SEARCH 'trigger.i.search.j.type' CRITERIA "'addasterix(trigger.i.search.j.criteria)'"' if symbol('trigger.i.search.j.keyword') = 'VAR' then entry = entry' KEYWORD "'addasterix(trigger.i.search.j.keyword)'"' call writeln(cf, entry) if rc ~= 0 then call displayerror(30, 'Error while writing configuration file.') end if trigger.i.action.count > 0 then do j = 1 to trigger.i.action.count entry = 'ACTION 'trigger.i.action.j.type if symbol('trigger.i.action.j.destconf') = 'VAR' then entry = entry' DESTCONF "'addasterix(trigger.i.action.j.destconf)'"' if symbol('trigger.i.action.j.replyaddr') = 'VAR' then entry = entry' REPLYADDR "'addasterix(trigger.i.action.j.replyaddr)'"' if symbol('trigger.i.action.j.scriptname') = 'VAR' then entry = entry' SCRIPTNAME "'addasterix(trigger.i.action.j.scriptname)'"' if symbol('trigger.i.action.j.scriptopts') = 'VAR' then entry = entry' SCRIPTOPTS "'addasterix(trigger.i.action.j.scriptopts)'"' if symbol('trigger.i.action.j.filename') = 'VAR' then entry = entry' FILENAME "'addasterix(trigger.i.action.j.filename)'"' if symbol('trigger.i.action.j.directory') = 'VAR' then entry = entry' DIRECTORY "'addasterix(trigger.i.action.j.directory)'"' if symbol('trigger.i.action.j.substitute') = 'VAR' then entry = entry' SUBSTITUTE "'addasterix(trigger.i.action.j.substitute)'"' if symbol('trigger.i.action.j.with') = 'VAR' then entry = entry' WITH "'addasterix(trigger.i.action.j.with)'"' if trigger.i.action.j.header = 1 then entry = entry' HEADER' if trigger.i.action.j.append = 1 then entry = entry' APPEND' if trigger.i.action.j.nobin = 1 then entry = entry' NOBIN' if trigger.i.action.j.checkdupes = 1 then entry = entry' CHECKDUPES' call writeln(cf, entry) if rc ~= 0 then call displayerror(30, 'Error while writing configuration file.') end call writeln(cf, 'ENDTRIGGER') if rc ~= 0 then call displayerror(30, 'Error while writing configuration file.') call writeln(cf, '') if rc ~= 0 then call displayerror(30, 'Error while writing configuration file.') end end call close(cf) address command 'Copy "'cfgpath || cfgfile'" "ENVARC:Thor/" QUIET' if rc ~= 0 then call reqnotify('Error while updating\nconfiguration file in ENVARC:!', 'Ok') if symbol('progwin') = 'VAR' then do CLOSEPROGRESS REQ progwin if rc ~= 0 then displayerror(rc, 'CLOSEPROGRESS:\n'THOR.LASTERROR) drop progwin end return(0) /**************************************************************************** *********************** Open and read configuration file ********************** ****************************************************************************/ readcfg: interpret 'procedure expose 'globals triggers = 0 cfgopen = open(cf, cfgpath || cfgfile, 'R') if cfgopen then do cfglength = seek(cf, 0, 'E'); call seek(cf, 0, 'B') address(thorport) OPENPROGRESS TITLE '"CfgSortMail 'version'"' TOTAL cfglength PT '"Reading configuration..."' if rc ~= 0 then displayerror(rc, 'OPENPROGRESS:\n'THOR.LASTERROR) else progwin = result cfgline = 0 address(bbsread) do until seek(cf, 0) = cfglength entry = readln(cf); cfgline = cfgline + 1 if symbol('progwin') = 'VAR' then do address(thorport) UPDATEPROGRESS progwin CURRENT seek(cf, 0) if rc ~= 0 then displayerror(rc, 'UPDATEPROGRESS:\n'THOR.LASTERROR) address(bbsread) end select when upper(subword(entry, 1, 1)) = "SYSTEM" then do call reqnotify('Found old 2.x configuration file. SortMail has\nchanged the configuration file format in version\n3.0. A new one will have to be created from scratch.', 'Ok') globalcfg.SYSTEM = 'Change me!'; globalcfg.CONFERENCE = 'Change me!' globalcfg.STATISTICS = 0; globalcfg.NOWARN = 0; globalcfg.LOGINSTATE = 0 trigger.count = 0; signal fini end when upper(subword(entry, 1, 1)) = "GLOBAL" then do globalcfg.STATISTICS = 0; globalcfg.NOWARN = 0; globalcfg.LOGINSTATE = 0 READARGS TEMPLATE '"SYSTEM/A,CONFERENCE/A,STATISTICS/S,NOWARN/S,LOGINSTATE/S"' STEM globalcfg CMDLINE subword(entry, 2) if rc ~= 0 then call displayerror(30, 'Error in 'cfgfile' line 'cfgline':\n'BBSREAD.LASTERROR) end when upper(subword(entry, 1, 1)) = "TRIGGER" then do triggers = triggers + 1 trigger.triggers.cmdline = subword(entry, 2) trigger.triggers.DELMSG = 0; trigger.triggers.DELUSER = 0; trigger.triggers.hitcount = 0; trigger.triggers.failcount = 0 READARGS TEMPLATE '"NAME/A,DELMSG/S,DELUSER/S"' STEM trigger.triggers CMDLINE trigger.triggers.cmdline if rc ~= 0 then call displayerror(30, 'Error in 'cfgfile' line 'cfgline':\n'BBSREAD.LASTERROR) do searches = 0; actions = 0 do until upper(subentry) = 'ENDTRIGGER' subentry = readln(cf); cfgline = cfgline + 1 select when upper(subword(subentry, 1, 1)) = 'ACTION' then do actions = actions + 1 trigger.triggers.action.actions.checkdupes = 0; trigger.triggers.action.actions.header = 0; trigger.triggers.action.actions.append = 0; trigger.triggers.action.actions.nobin = 0; trigger.triggers.action.actions.hitcount = 0; trigger.triggers.action.actions.failcount = 0 READARGS TEMPLATE '"TYPE/A,DESTCONF/K,REPLYADDR/K,SCRIPTNAME/K,SCRIPTOPTS/K,FILENAME/K,DIRECTORY/K,SUBSTITUTE/K,WITH/K,HEADER/S,APPEND/S,NOBIN/S,CHECKDUPES/S"' STEM trigger.triggers.action.actions CMDLINE subword(subentry, 2) if rc ~= 0 then call displayerror(30, 'Error in 'cfgfile' line 'cfgline':\n'BBSREAD.LASTERROR) trigger.triggers.action.actions.type = upper(trigger.triggers.action.actions.type) end when upper(subword(subentry, 1, 1)) = 'SEARCH' then do searches = searches + 1 READARGS TEMPLATE '"TYPE/A,CRITERIA/A,KEYWORD/K"' STEM trigger.triggers.search.searches CMDLINE subword(subentry, 2) if rc ~= 0 then call displayerror(30, 'Error in 'cfgfile' line 'cfgline':\n'BBSREAD.LASTERROR) trigger.triggers.search.searches.type = upper(trigger.triggers.search.searches.type) end otherwise nop end end end trigger.triggers.action.count = actions trigger.triggers.search.count = searches end otherwise nop end trigger.count = triggers end end else do call reqnotify('No configuration file found. SortMail has changed\nit''s configuration format, so if you are upgrading\nfrom SortMail 2.x a new one will have to be created.\nPlease see SortMail.guide for more information.', 'Ok') globalcfg.SYSTEM = 'Change me!'; globalcfg.CONFERENCE = 'Change me!' globalcfg.STATISTICS = 0; globalcfg.NOWARN = 0; globalcfg.LOGINSTATE = 0 trigger.count = 0; end fini: if cfgopen then do call close(cf) address(thorport) if symbol('progwin') = 'VAR' then do CLOSEPROGRESS REQ progwin if rc ~= 0 then displayerror(rc, 'CLOSEPROGRESS:\n'THOR.LASTERROR) drop progwin end end return(0) /**************************************************************************** *********************** Display error and return or quit ********************** ****************************************************************************/ displayerror: interpret 'procedure expose 'globals parse arg returned, errmsg address(thorport) select when returned > 0 & returned < 20 then do REQUESTNOTIFY '"CfgSortMail returned 'returned':\n' || errmsg'"' '"Continue|Abort"' if result = 0 then signal cleanup end when returned > 19 then do REQUESTNOTIFY '"CfgSortMail returned 'returned':\n' || errmsg'"' '"Abort"' signal cleanup end otherwise nop end drop THOR.LASTERROR BBSREAD.LASTERROR address(thorport) return(0) /**************************************************************************** ****** Insert asterix (*) before double quotes (") and existing asterixes ***** ****************************************************************************/ addasterix: interpret 'procedure expose 'globals parse arg str if str = '' then return(str) lastfound = 0; found = index(str, '*') do while found > lastfound secondpart = substr(str, found + length('*')) firstpart = substr(str, 1, length(str) - length(substr(str, found))) str = firstpart || '**' || secondpart lastfound = found + length('**') found = index(str, '*', lastfound) end lastfound = 0; found = index(str, '"') do while found > lastfound secondpart = substr(str, found + length('"')) firstpart = substr(str, 1, length(str) - length(substr(str, found))) str = firstpart || '*"' || secondpart lastfound = found + length('*"') found = index(str, '"', lastfound) end return(str) /**************************************************************************** ******************** Extract filename from a complete path ******************** ****************************************************************************/ getfilepart: procedure parse arg path filepart = path if (index(path, '/') > 0) & (lastpos('/', path) ~= length(path)) then filepart = substr(path, (lastpos('/', path) + 1)) else (if index(path, ':') > 0) & (lastpos(':', path) ~= length(path)) then filepart = substr(path, (lastpos(':', path) + 1)) return(filepart) /**************************************************************************** ******************** Extract pathname from a complete path ******************** ****************************************************************************/ getpathpart: procedure expose thorpath parse arg path if index(path, '/') > 0 then path = substr(path, 1, lastpos('/', path)) else if index(path, ':') > 0 then path = substr(path, 1, lastpos(':', path)) return(path) /**************************************************************************** ******************** Copy the contents of a stem to another ******************* ****************************************************************************/ copystem: interpret 'procedure expose 'globals parse arg stemtype, fromnr, tonr if stemtype = 1 then do /* Copy contents of original trigger stem to temporary trigger stem Used when entering trigger configuration */ temp.name = trigger.fromnr.name temp.delmsg = trigger.fromnr.delmsg temp.deluser = trigger.fromnr.deluser temp.action.count = trigger.fromnr.action.count temp.search.count = trigger.fromnr.search.count if trigger.fromnr.search.count > 0 then do i = 1 to trigger.fromnr.search.count temp.search.i.type = trigger.fromnr.search.i.type temp.search.i.criteria = trigger.fromnr.search.i.criteria if symbol('trigger.fromnr.search.i.keyword') = 'VAR' then temp.search.i.keyword = trigger.fromnr.search.i.keyword; else drop temp.search.i.keyword end if trigger.fromnr.action.count > 0 then do i = 1 to trigger.fromnr.action.count temp.action.i.type = trigger.fromnr.action.i.type if symbol('trigger.fromnr.action.i.destconf') = 'VAR' then temp.action.i.destconf = trigger.fromnr.action.i.destconf; else drop temp.action.i.destconf if symbol('trigger.fromnr.action.i.replyaddr') = 'VAR' then temp.action.i.replyaddr = trigger.fromnr.action.i.replyaddr; else drop temp.action.i.replyaddr if symbol('trigger.fromnr.action.i.scriptname') = 'VAR' then temp.action.i.scriptname = trigger.fromnr.action.i.scriptname; else drop temp.action.i.scriptname if symbol('trigger.fromnr.action.i.scriptopts') = 'VAR' then temp.action.i.scriptopts = trigger.fromnr.action.i.scriptopts; else drop temp.action.i.scriptopts if symbol('trigger.fromnr.action.i.filename') = 'VAR' then temp.action.i.filename = trigger.fromnr.action.i.filename; else drop temp.action.i.filename if symbol('trigger.fromnr.action.i.directory') = 'VAR' then temp.action.i.directory = trigger.fromnr.action.i.directory; else drop temp.action.i.directory if symbol('trigger.fromnr.action.i.substitute') = 'VAR' then temp.action.i.substitute = trigger.fromnr.action.i.substitute; else drop temp.action.i.substitute if symbol('trigger.fromnr.action.i.with') = 'VAR' then temp.action.i.with = trigger.fromnr.action.i.with; else drop temp.action.i.with if symbol('trigger.fromnr.action.i.header') = 'VAR' then temp.action.i.header = trigger.fromnr.action.i.header; else drop temp.action.i.header if symbol('trigger.fromnr.action.i.append') = 'VAR' then temp.action.i.append = trigger.fromnr.action.i.append; else drop temp.action.i.append if symbol('trigger.fromnr.action.i.nobin') = 'VAR' then temp.action.i.nobin = trigger.fromnr.action.i.nobin; else drop temp.action.i.nobin if symbol('trigger.fromnr.action.i.checkdupes') = 'VAR' then temp.action.i.checkdupes = trigger.fromnr.action.i.checkdupes; else drop temp.action.i.checkdupes end end if stemtype = 2 then do /* Copy contents of action stem in temporary trigger stem to another temporary action stem Used when entering action configuration */ atemp.type = temp.action.fromnr.type if symbol('temp.action.fromnr.destconf') = 'VAR' then atemp.destconf = temp.action.fromnr.destconf; else drop atemp.destconf if symbol('temp.action.fromnr.replyaddr') = 'VAR' then atemp.replyaddr = temp.action.fromnr.replyaddr; else drop atemp.replyaddr if symbol('temp.action.fromnr.scriptname') = 'VAR' then atemp.scriptname = temp.action.fromnr.scriptname; else drop atemp.scriptname if symbol('temp.action.fromnr.scriptopts') = 'VAR' then atemp.scriptopts = temp.action.fromnr.scriptopts; else drop atemp.scriptopts if symbol('temp.action.fromnr.filename') = 'VAR' then atemp.filename = temp.action.fromnr.filename; else drop atemp.filename if symbol('temp.action.fromnr.directory') = 'VAR' then atemp.directory = temp.action.fromnr.directory; else drop atemp.directory if symbol('temp.action.fromnr.substitute') = 'VAR' then atemp.substitute = temp.action.fromnr.substitute; else drop atemp.substitute if symbol('temp.action.fromnr.with') = 'VAR' then atemp.with = temp.action.fromnr.with; else drop atemp.with if symbol('temp.action.fromnr.header') = 'VAR' then atemp.header = temp.action.fromnr.header; else drop atemp.header if symbol('temp.action.fromnr.append') = 'VAR' then atemp.append = temp.action.fromnr.append; else drop atemp.append if symbol('temp.action.fromnr.nobin') = 'VAR' then atemp.nobin = temp.action.fromnr.nobin; else drop atemp.nobin if symbol('temp.action.fromnr.checkdupes') = 'VAR' then atemp.checkdupes = temp.action.fromnr.checkdupes; else drop atemp.checkdupes end if stemtype = 3 then do /* Copy contents of search stem in temporary trigger stem to another temporary search stem Used when entering search configuration */ stemp.type = temp.search.fromnr.type stemp.criteria = temp.search.fromnr.criteria if symbol('temp.search.fromnr.type') = 'VAR' then stemp.keyword = temp.search.fromnr.keyword; else drop stemp.keyword end if stemtype = 10 then do /* Copy contents of temporary trigger stem to original trigger stem Used when exiting trigger configuration */ trigger.tonr.name = temp.name trigger.tonr.delmsg = temp.delmsg trigger.tonr.deluser = temp.deluser trigger.tonr.action.count = temp.action.count trigger.tonr.search.count = temp.search.count if temp.search.count > 0 then do i = 1 to temp.search.count trigger.tonr.search.i.type = temp.search.i.type trigger.tonr.search.i.criteria = temp.search.i.criteria if symbol('temp.search.i.keyword') = 'VAR' then trigger.tonr.search.i.keyword = temp.search.i.keyword; else drop trigger.tonr.search.i.keyword end if temp.action.count > 0 then do i = 1 to temp.action.count trigger.tonr.action.i.type = temp.action.i.type if symbol('temp.action.i.destconf') = 'VAR' then trigger.tonr.action.i.destconf = temp.action.i.destconf; else drop trigger.tonr.action.i.destconf if symbol('temp.action.i.replyaddr') = 'VAR' then trigger.tonr.action.i.replyaddr = temp.action.i.replyaddr; else drop trigger.tonr.action.i.replyaddr if symbol('temp.action.i.scriptname') = 'VAR' then trigger.tonr.action.i.scriptname = temp.action.i.scriptname; else drop trigger.tonr.action.i.scriptname if symbol('temp.action.i.scriptopts') = 'VAR' then trigger.tonr.action.i.scriptopts = temp.action.i.scriptopts; else drop trigger.tonr.action.i.scriptopts if symbol('temp.action.i.filename') = 'VAR' then trigger.tonr.action.i.filename = temp.action.i.filename; else drop trigger.tonr.action.i.filename if symbol('temp.action.i.directory') = 'VAR' then trigger.tonr.action.i.directory = temp.action.i.directory; else drop trigger.tonr.action.i.directory if symbol('temp.action.i.substitute') = 'VAR' then trigger.tonr.action.i.substitute = temp.action.i.substitute; else drop trigger.tonr.action.i.substitute if symbol('temp.action.i.with') = 'VAR' then trigger.tonr.action.i.with = temp.action.i.with; else drop trigger.tonr.action.i.with if symbol('temp.action.i.header') = 'VAR' then trigger.tonr.action.i.header = temp.action.i.header; else drop trigger.tonr.action.i.header if symbol('temp.action.i.append') = 'VAR' then trigger.tonr.action.i.append = temp.action.i.append; else drop trigger.tonr.action.i.append if symbol('temp.action.i.nobin') = 'VAR' then trigger.tonr.action.i.nobin = temp.action.i.nobin; else drop trigger.tonr.action.i.nobin if symbol('temp.action.i.checkdupes') = 'VAR' then trigger.tonr.action.i.checkdupes = temp.action.i.checkdupes; else drop trigger.tonr.action.i.checkdupes end end if stemtype = 11 then do /* Copy contents of temporary action stem to temporary trigger stem Used when exiting action configuration */ temp.action.tonr.type = atemp.type if symbol('atemp.destconf') = 'VAR' then temp.action.tonr.destconf = atemp.destconf; else drop temp.action.tonr.destconf if symbol('atemp.replyaddr') = 'VAR' then temp.action.tonr.replyaddr = atemp.replyaddr; else drop temp.action.tonr.replyaddr if symbol('atemp.scriptname') = 'VAR' then temp.action.tonr.scriptname = atemp.scriptname; else drop temp.action.tonr.scriptname if symbol('atemp.scriptopts') = 'VAR' then temp.action.tonr.scriptopts = atemp.scriptopts; else drop temp.action.tonr.scriptopts if symbol('atemp.filename') = 'VAR' then temp.action.tonr.filename = atemp.filename; else drop temp.action.tonr.filename if symbol('atemp.directory') = 'VAR' then temp.action.tonr.directory = atemp.directory; else drop temp.action.tonr.directory if symbol('atemp.substitute') = 'VAR' then temp.action.tonr.substitute = atemp.substitute; else drop temp.action.tonr.substitute if symbol('atemp.with') = 'VAR' then temp.action.tonr.with = atemp.with; else drop temp.action.tonr.with if symbol('atemp.header') = 'VAR' then temp.action.tonr.header = atemp.header; else drop temp.action.tonr.header if symbol('atemp.append') = 'VAR' then temp.action.tonr.append = atemp.append; else drop temp.action.tonr.append if symbol('atemp.nobin') = 'VAR' then temp.action.tonr.nobin = atemp.nobin; else drop temp.action.tonr.nobin if symbol('atemp.checkdupes') = 'VAR' then temp.action.tonr.checkdupes = atemp.checkdupes; else drop temp.action.tonr.checkdupes end if stemtype = 12 then do /* Copy contents of temporary search stem to temporary trigger stem Used when exiting action configuration */ temp.search.tonr.type = stemp.type temp.search.tonr.criteria = stemp.criteria if symbol('stemp.keyword') = 'VAR' then temp.search.tonr.keyword = stemp.keyword; else drop temp.search.tonr.keyword end if stemtype = 20 then do /* Copy contents of original trigger stem to a new stem number Used when deleting of trigger, main menu */ trigger.tonr.name = trigger.fromnr.name trigger.tonr.delmsg = trigger.fromnr.delmsg trigger.tonr.deluser = trigger.fromnr.deluser trigger.tonr.action.count = trigger.fromnr.action.count trigger.tonr.search.count = trigger.fromnr.search.count if trigger.fromnr.search.count > 0 then do j = 1 to trigger.fromnr.search.count trigger.tonr.search.j.type = trigger.fromnr.search.j.type trigger.tonr.search.j.criteria = trigger.fromnr.search.j.criteria if symbol('trigger.fromnr.search.j.keyword') = 'VAR' then trigger.tonr.search.j.keyword = trigger.fromnr.search.j.keyword; else drop trigger.tonr.search.j.keyword end if trigger.fromnr.action.count > 0 then do j = 1 to trigger.fromnr.action.count trigger.tonr.action.j.type = trigger.fromnr.action.j.type if symbol('trigger.fromnr.action.j.destconf') = 'VAR' then trigger.tonr.action.j.destconf = trigger.fromnr.action.j.destconf; else drop trigger.tonr.action.j.destconf if symbol('trigger.fromnr.action.j.replyaddr') = 'VAR' then trigger.tonr.action.j.replyaddr = trigger.fromnr.action.j.replyaddr; else drop trigger.tonr.action.j.replyaddr if symbol('trigger.fromnr.action.j.scriptname') = 'VAR' then trigger.tonr.action.j.scriptname = trigger.fromnr.action.j.scriptname; else drop trigger.tonr.action.j.scriptname if symbol('trigger.fromnr.action.j.scriptopts') = 'VAR' then trigger.tonr.action.j.scriptopts = trigger.fromnr.action.j.scriptopts; else drop trigger.tonr.action.j.scriptopts if symbol('trigger.fromnr.action.j.filename') = 'VAR' then trigger.tonr.action.j.filename = trigger.fromnr.action.j.filename; else drop trigger.tonr.action.j.filename if symbol('trigger.fromnr.action.j.directory') = 'VAR' then trigger.tonr.action.j.directory = trigger.fromnr.action.j.directory; else drop trigger.tonr.action.j.directory if symbol('trigger.fromnr.action.j.substitute') = 'VAR' then trigger.tonr.action.j.substitute = trigger.fromnr.action.j.substitute; else drop trigger.tonr.action.j.substitute if symbol('trigger.fromnr.action.j.with') = 'VAR' then trigger.tonr.action.j.with = trigger.fromnr.action.j.with; else drop trigger.tonr.action.j.with if symbol('trigger.fromnr.action.j.header') = 'VAR' then trigger.tonr.action.j.header = trigger.fromnr.action.j.header; else drop trigger.tonr.action.j.header if symbol('trigger.fromnr.action.j.append') = 'VAR' then trigger.tonr.action.j.append = trigger.fromnr.action.j.append; else drop trigger.tonr.action.j.append if symbol('trigger.fromnr.action.j.nobin') = 'VAR' then trigger.tonr.action.j.nobin = trigger.fromnr.action.j.nobin; else drop trigger.tonr.action.j.nobin if symbol('trigger.fromnr.action.j.checkdupes') = 'VAR' then trigger.tonr.action.j.checkdupes = trigger.fromnr.action.j.checkdupes; else drop trigger.tonr.action.j.checkdupes end end if stemtype = 21 then do /* Copy contents of temporary action stem to a new action stem number Used when deleting action stem */ temp.action.tonr.type = temp.action.fromnr.type if symbol('temp.action.fromnr.destconf') = 'VAR' then temp.action.tonr.destconf = temp.action.fromnr.destconf; else drop temp.action.tonr.destconf if symbol('temp.action.fromnr.replyaddr') = 'VAR' then temp.action.tonr.replyaddr = temp.action.fromnr.replyaddr; else drop temp.action.tonr.replyaddr if symbol('temp.action.fromnr.scriptname') = 'VAR' then temp.action.tonr.scriptname = temp.action.fromnr.scriptname; else drop temp.action.tonr.scriptname if symbol('temp.action.fromnr.scriptopts') = 'VAR' then temp.action.tonr.scriptopts = temp.action.fromnr.scriptopts; else drop temp.action.tonr.scriptopts if symbol('temp.action.fromnr.filename') = 'VAR' then temp.action.tonr.filename = temp.action.fromnr.filename; else drop temp.action.tonr.filename if symbol('temp.action.fromnr.directory') = 'VAR' then temp.action.tonr.directory = temp.action.fromnr.directory; else drop temp.action.tonr.directory if symbol('temp.action.fromnr.substitute') = 'VAR' then temp.action.tonr.substitute = temp.action.fromnr.substitute; else drop temp.action.tonr.substitute if symbol('temp.action.fromnr.with') = 'VAR' then temp.action.tonr.with = temp.action.fromnr.with; else drop temp.action.tonr.with if symbol('temp.action.fromnr.header') = 'VAR' then temp.action.tonr.header = temp.action.fromnr.header; else drop temp.action.tonr.header if symbol('temp.action.fromnr.append') = 'VAR' then temp.action.tonr.append = temp.action.fromnr.append; else drop temp.action.tonr.append if symbol('temp.action.fromnr.nobin') = 'VAR' then temp.action.tonr.nobin = temp.action.fromnr.nobin; else drop temp.action.tonr.nobin if symbol('temp.action.fromnr.checkdupes') = 'VAR' then temp.action.tonr.checkdupes = temp.action.fromnr.checkdupes; else drop temp.action.tonr.checkdupes end if stemtype = 22 then do /* Copy contents of temporary search stem to a new search stem number Used when deleting search stem */ temp.search.tonr.type = temp.search.fromnr.type temp.search.tonr.criteria = temp.search.fromnr.criteria if symbol('temp.search.fromnr.keyword') = 'VAR' then temp.search.tonr.keyword = temp.search.fromnr.keyword; else drop temp.search.tonr.keyword end return(0) /**************************************************************************** *********************** Pop up a notification requester *********************** ****************************************************************************/ reqnotify: interpret 'procedure expose 'globals parse arg body, choices if choices = '' then choices = 'Yes|No' address(thorport) REQUESTNOTIFY '"'body'"' '"'choices'"' if rc ~= 0 then call displayerror(rc, 'REQUESTNOTIFY:\n'THOR.LASTERROR) return(result) /**************************************************************************** ************************** Display a string requester ************************* ****************************************************************************/ reqstring: interpret 'procedure expose 'globals parse arg reqtitle, reqbody, reqid, maxchar, reqmode, pickup orgreqid = reqid showreq: if pickup ~= '' then choices = 'Ok|_Pickup|Cancel'; else choices = 'Ok|Cancel' if symbol('msginfo') = 'VAR' then reqid = msginfo address(thorport) REQUESTSTRING '"'reqtitle'"' BODY '"'reqbody'"' ID '"'reqid'"' BT '"'choices'"' MAXCHARS maxchar select when rc = 5 & (reqmode = 0 | reqmode = '') then return(orgreqid) when rc = 5 & reqmode = 1 then return('') when rc > 5 then call displayerror(rc, 'REQUESTSTRING:\n'THOR.LASTERROR) otherwise if thorrc ~= 2 then return(result) end msginfo = readmsg(pickup) if msginfo = 0 then drop msginfo signal showreq /**************************************************************************** *************************** Display a file requester ************************** ****************************************************************************/ reqfile: interpret 'procedure expose 'globals parse arg reqtitle, pathpart, filepart, reqpattern, pickup address(thorport) REQUESTFILE '"'reqtitle'"' ID '"'pathpart'"' IF '"'filepart'"' PAT '"'reqpattern'"' FP select when rc = 5 then return(pathpart || filepart) when rc > 5 then call displayerror(rc, 'REQUESTFILE:\n'THOR.LASTERROR) otherwise return(result) end /**************************************************************************** ************** Read data from message currently displayed in Thor ************* ****************************************************************************/ readmsg: interpret 'procedure expose 'globals parse arg field address(thorport) CURRENTMSG currentmsg if rc ~= 0 then do call reqnotify('Couldn''t read message:\n'THOR.LASTERROR, 'Ok') return(0) end select when field = 'replyaddr' then do address(bbsread) READBRMESSAGE '"'currentmsg.BBSNAME'"' '"'currentmsg.CONFNAME'"' currentmsg.MSGNR TEXTSTEM text if rc ~= 0 then call displayerror(rc, 'READBRMESSAGE:\n'BBSREAD.LASTERROR) retfield = value('text.'field) if retfield = upper('text.'field) then return(0) end when field = 'headline' then do address(bbsread) READBRMESSAGE '"'currentmsg.BBSNAME'"' '"'currentmsg.CONFNAME'"' currentmsg.MSGNR TEXTSTEM text if rc ~= 0 then call displayerror(rc, 'READBRMESSAGE:\n'BBSREAD.LASTERROR) if symbol('text.comment.count') ~= 'VAR' | text.comment.count = 0 then return(0) address(thorport) REQUESTLIST INSTEM text.comment TITLE '"Select header line"' SIZEGADGET if rc = 5 then return(0) if rc > 0 then do; call displayerror(rc, 'REQUESTLIST:\n'THOR.LASTERROR); return(0); end retfield = subword(result, 1, 1) end otherwise do address(bbsread) READBRMESSAGE '"'currentmsg.BBSNAME'"' '"'currentmsg.CONFNAME'"' currentmsg.MSGNR HEADSTEM head if rc ~= 0 then call displayerror(rc, 'READBRMESSAGE:\n'BBSREAD.LASTERROR) retfield = value('head.'field) if retfield = upper('head.'field) then return(0) end end return(retfield) /**************************************************************************** ********************** Display help using OS3.1+ MultiView ******************** ****************************************************************************/ help: procedure expose thorport thorpath parse arg node if index(show('P',,), 'MV_SORTMAIL') = 0 then do address(thorport) GETGLOBALCONFIG STEM GLOBCFG /* Check version of MultiView? */ address(command) 'Run >NIL: MultiView FILE 'thorpath'Docs/SortMail.guide PORTNAME MV_SORTMAIL PUBSCREEN 'GLOBCFG.PUBSCREENNAME 'WaitForPort MV_SORTMAIL' if rc ~= 0 then do address(thorport) REQUESTNOTIFY '"Couldn''t start MultiView."' '"Sigh"' return end end address(MV_SORTMAIL) DOTRIGGERMETHOD CONTENTS do i = 1 to node; DOTRIGGERMETHOD NEXT; end address(thorport) return