home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / steward8.zip / Admin.cmd next >
OS/2 REXX Batch file  |  1996-06-27  |  37KB  |  1,246 lines

  1. /* Steward Version 1.1 Build 8 */
  2. /* Administration Module */
  3. /*
  4.  * A mailing list processor in Rexx by Paul Hethmon
  5.  *
  6.  */
  7.  
  8. /* variable declarations */
  9.  
  10. Steward = 'Steward'
  11. StewardVersion = 'Version 1.1 Build 8'
  12. StewardDate = '26 June 1996'
  13. uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  14. lowercase = 'abcdefghijklmnopqrstuvwxyz'
  15. Env = 'OS2ENVIRONMENT'
  16. FALSE = 0
  17. TRUE = 1
  18.  
  19. /* Set to 1 to enable debug output */
  20. Debug = TRUE
  21. /* Set to 1 to enable logging */
  22. Log = TRUE
  23. LogFile = ''
  24. ETime1 = 0
  25. ETime2 = 0
  26. Author = ''
  27. AdminFile = ''
  28. AdminSubject = ''
  29. AdminTo = ''
  30. ListName = ''
  31.  
  32. /* Variables normally read from the configuration file */
  33. /* These values are provided as defaults only */
  34. HomeDir = 'c:'
  35. LogDir = 'c:'
  36. ListDir = 'c:'
  37. Mailer = 'hmailer'
  38. WhereAmI = 'hostname'
  39. WhoAmI = Steward
  40. WhoAmIOwner = 'postmaster@'WhereAmI
  41. MasterPassword = 'steward'
  42.  
  43. /* The following are set on a per list basis */
  44. AdminPassword = 'steward-list'
  45. Administrivia = 0
  46. ListOwner = WhoAmIOwner
  47. Advertise = '*'
  48. ApprovePassword = 'steward-pass'
  49. DoArchive = 0
  50. Moderated = 0
  51. NoList = 0
  52. Precedence = 1
  53. ListHeader = 1
  54. DoDigest = 0
  55. DigestVolume = 0
  56. DigestIssue = 0
  57. DigestName = ''
  58. DigestRmHeader = 1
  59. DigestFronter = ''
  60. DigestFooter = ''
  61. DigestSubs = TRUE
  62. SubscribePolicy = 'open'
  63. ReplyTo = ''
  64. SubjectPrefix = 'Steward-List: '
  65. OpenPosting = FALSE
  66. WelcomeFile = ''
  67. CaseInsensitive = FALSE
  68.  
  69. /* Some other global variables */
  70. HeadFrom = ''
  71. HeadTo = ''
  72. HeadReplyTo = ''
  73. HeadSubject = ''
  74. HeadDate = ''
  75. HeadCc = ''
  76. HeadSender = ''
  77. HeadEmail = ''
  78. Email = ''
  79. Approved = FALSE
  80. PassWord = ''
  81.  
  82. /* The following addresses are always rejected from admin requests */
  83. BadAddrs = 'postmaster' 'mailer-daemon' 'listserv',
  84.            'majordomo'
  85.  
  86. /* The external functions we need */
  87. call RxFuncAdd 'SysTempFileName', 'RexxUtil', 'SysTempFileName'
  88. call RxFuncAdd 'SysFileDelete', 'RexxUtil', 'SysFileDelete'
  89. call RxFuncAdd 'SysFileTree', 'RexxUtil', 'SysFileTree'
  90. call RxFuncAdd 'SysSleep', 'RexxUtil', 'SysSleep'
  91.  
  92. /* start main function */
  93. /* The first arg is who the message was sent to.
  94.  * The second is the filename. We're responsible
  95.  * for cleaning up the file if needed.
  96.  */
  97. parse arg ListName MsgFile
  98.  
  99. if Debug = TRUE then say 'ListName =' ListName 'MsgFile =' MsgFile
  100.  
  101. call on error name ErrHandler
  102.  
  103. say 'Reading Master Configuration File Now.'
  104.  
  105. /* Read the master configuration file now */
  106. rc = ReadMasterCf()
  107. if rc = FALSE then
  108.   do
  109.   say 'Unable to read master configuration file. Failing.'
  110.   ErrFile = SysTempFileName('?????.err', '?')
  111.   rc = stream(ErrFile, 'C', 'OPEN WRITE')
  112.   rc = lineout(ErrFile, 'Steward Error File', )
  113.   rc = lineout(ErrFile, 'You must rerun Steward with the recipient name and', )
  114.   rc = lineout(ErrFile, 'message file name listed below in order to process', )
  115.   rc = lineout(ErrFile, 'this message.', )
  116.   rc = lineout(ErrFile, 'Rcpt =' Rcpt, )
  117.   rc = lineout(ErrFile, 'MsgFile =', MsgFile, )
  118.   rc = stream(ErrFile, 'C', 'CLOSE')
  119.   exit
  120.   end
  121.  
  122. if Debug = TRUE then
  123.   do
  124.   say 'LogDir =' LogDir
  125.   say 'HomeDir = ' HomeDir
  126.   say 'ListDir =' ListDir
  127.   end
  128.  
  129. if Log = TRUE then do
  130.   ETime1 = time('E')
  131.   call StartLog
  132.   call WriteLog('Rcpt =' Rcpt)
  133.   call WriteLog('MsgFile =' MsgFile)
  134.   end
  135.  
  136. call DoAdmin
  137.  
  138. if Log = TRUE then do
  139.   ETime2 = time('E')
  140.   call StopLog
  141.   end
  142.  
  143. /* Make sure the tmp file is deleted */
  144. rc = SysFileDelete(MsgFile)
  145.  
  146. exit
  147.  
  148. /* ------------------------------------------------------------------ */
  149.  
  150. DoAdmin:
  151.  
  152. NoRequests = TRUE
  153.  
  154. rc = stream(MsgFile, 'C', 'OPEN READ')  /* open the file for reading */
  155. if rc <> 'READY:' then do
  156.   call WriteLog('no message')
  157.   end
  158.  
  159. call ParseHeaders  /* first get the header info */
  160.  
  161. /* Figure out who to send mail back to */
  162. if HeadReplyTo <> '' then
  163.   HeadEmail = HeadReplyTo
  164. else
  165.   HeadEmail = HeadFrom
  166. /* now clean up the email address */
  167. HeadEmail = NormalizeEmail(HeadEmail)
  168. HeadEmail = translate(HeadEmail, lowercase, uppercase)
  169.  
  170. if Debug = TRUE then say 'HeadEmail =' HeadEmail
  171. if Log = TRUE then call WriteLog('Email from' HeadEmail)
  172.  
  173. /* Look for bad addresses such as postmaster, majordomo, etc. */
  174. parse var HeadEmail User '@' Domain
  175. User = translate(User, lowercase, uppercase)
  176. do i = 1 to words(BadAddrs)
  177.   if User = word(BadAddrs, i) then do
  178.     rc = stream(MsgFile, 'c', 'close')
  179.     say 'Calling errors. BadAddrs found.'
  180.     call Errors(ListName MsgFile)
  181.     exit
  182.     end
  183.   end
  184.  
  185. /* create a temp file for the outgoing message */
  186. OutFile = SysTempFileName('f?????.tmp', '?');
  187. rc = stream(OutFile, 'C', 'OPEN WRITE')  /* open the file for writing */
  188. if rc <> 'READY:' then do
  189.   call WriteLog('no tempfile')
  190.   end
  191.  
  192. /* write the headers to the outfile */
  193. AdminSubject = WhoAmI 'Results'
  194. if HeadReplyTo <> '' then
  195.   AdminTo = HeadReplyTo
  196. else if HeadFrom <> '' then
  197.   AdminTo = HeadFrom
  198. AdminFile = OutFile
  199. call WriteAdminHeaders
  200.  
  201. if Debug = TRUE then say 'Admin Headers written'
  202.  
  203. /* now look for admin requests */
  204. do while lines(MsgFile) <> 0         /* until end of file */
  205.   Line = linein(MsgFile)             /* get a line of the file */
  206.   parse var Line Cmd Rest            /* look for a command */
  207.   if Cmd <> '' then do               /* if not null */
  208.     Cmd = strip(Cmd, 'B', ' ')       /* remove any blanks */
  209.     Cmd = translate(Cmd, lowercase, uppercase)
  210.     select
  211.       when Cmd = 'approved:' then do
  212.         /* Save the approved password in case the subscribe request */
  213.         /* needs it because of a closed list subscription policy */
  214.         PassWord = strip(Rest, 'B', ' ')
  215.         end
  216.       when Cmd = 'subscribe' | Cmd = 'sub' then do
  217.         call DoSubscribe
  218.         rc = lineout(OutFile, '', )
  219.         NoRequests = FALSE
  220.         end
  221.       when Cmd = 'end' then do
  222.         rc = lineout(OutFile, '>>>'Cmd, )
  223.         rc = lineout(OutFile, 'Ending command processing.', )
  224.         leave
  225.         end
  226.       when Cmd = 'unsubscribe' | Cmd = 'unsub' then do
  227.         call DoUnSubscribe
  228.         rc = lineout(OutFile, '', )
  229.         NoRequests = FALSE
  230.         end
  231.       when Cmd = 'help' then do
  232.         call DoHelp
  233.         rc = lineout(OutFile, '', )
  234.         NoRequests = FALSE
  235.         end
  236.       when Cmd = 'lists' | Cmd = 'list' then do
  237.         call DoLists
  238.         rc = lineout(OutFile, '', )
  239.         NoRequests = FALSE
  240.         end
  241.       otherwise do
  242.         rc = lineout(OutFile, '>>>'Cmd Rest, )
  243.         rc = lineout(OutFile, 'Unknown command ignored.', )
  244.         rc = lineout(OutFile, '', )
  245.         end
  246.     end /* select */
  247.   end   /* if Cmd <> '' */
  248. end     /* do while lines */
  249.  
  250. if NoRequests = TRUE then do
  251.   /* send them help since we didn't find a valid command */
  252.   rc = lineout(OutFile, 'No valid commands found in your message. Sending help instead.', )
  253.   call DoHelp
  254.   end
  255.  
  256. rc = stream(MsgFile, 'C', 'CLOSE')
  257. rc = stream(OutFile, 'C', 'CLOSE')
  258.  
  259. if Debug = TRUE then say 'Preparing to send msg back.'
  260.  
  261. /* the admin requests are processed, now send the message back */
  262. /* first create a file with the email address in it */
  263. EmailFile = SysTempFileName('e?????.tmp', '?')
  264. rc = stream(EmailFile, 'C', 'OPEN WRITE')  /* open the file for writing */
  265. if rc <> 'READY:' then do
  266.   call WriteLog('no emailfile')
  267.   end
  268.  
  269. if HeadEmail <> '' then
  270.   rc = lineout(EmailFile, HeadEmail, )
  271. else
  272.   rc = lineout(EmailFile, WhoAmIOwner, )
  273. rc = stream(EmailFile, 'C', 'CLOSE')
  274.  
  275. if Debug = TRUE then say 'Sending mail now.'
  276.  
  277. /* now start the mailer */
  278. Mailer WhoAmI'-owner@'WhereAmI EmailFile OutFile
  279.  
  280. return
  281.  
  282. /* ------------------------------------------------------------------ */
  283. /*
  284.  * Process a request to show what lists are available.
  285.  *
  286.  */
  287.  
  288. DoLists:
  289.  
  290. if Log = TRUE then call WriteLog('Returning available lists.')
  291. if Debug = TRUE then say 'Returning available lists.'
  292.  
  293. Email = HeadEmail
  294.  
  295. rc = lineout(OutFile, '>>>' Cmd Rest, )
  296. rc = lineout(OutFile, '', )
  297. rc = lineout(OutFile, 'Lists available here include:', )
  298. rc = lineout(OutFile, '', )
  299.  
  300. ListNames = ListDir'\*'
  301.  
  302. rc = SysFileTree(ListNames, s., 'DO')   /* only directories */
  303.  
  304. if rc = 0 then do i = 1 to s.0
  305.   ListName = s.i                       /* we only need the name */
  306.   ListName = strip(ListName, 'B', )    /* eliminate extra spaces */
  307.   x = lastpos('\', ListName)           /* pull off the directory alone */
  308.   List = substr(ListName, x + 1)
  309.   call ReadListCf(List)                /* read the config file */
  310.   if NoList = FALSE then               /* if it's ok to show it */
  311.     do
  312.     if Debug = TRUE then say 'List Name:' List
  313.     rc = lineout(OutFile, 'List Name:' List, )
  314.     ListInfo = ListName'\'List'.info'
  315.     rc = SysFileTree(ListInfo, lc., 'F')
  316.     if rc = 0 & lc.0 = 1 then
  317.       do
  318.       /* send back the info file also */
  319.       rc = LockOpen(ListInfo 'READ')
  320.       if rc = TRUE then
  321.         do
  322.           do while lines(ListInfo) <> 0
  323.           Line = linein(ListInfo)
  324.           rc = lineout(OutFile, Line, )
  325.           end
  326.         rc = LockClose(ListInfo)
  327.         end
  328.       end
  329.     rc = lineout(OutFile, '', )  /* put a blank line between lists */
  330.     end
  331. end
  332.  
  333. return
  334.  
  335. /* ------------------------------------------------------------------ */
  336. /* Find out the listname and email address for this request */
  337.  
  338. WhoAndWhat:
  339.  
  340. parse arg Part1 Part2
  341.  
  342. say 'Part1 =' Part1
  343. say 'Part2 =' Part2
  344. /* Figure out the listname and email address first */
  345. i = words(Rest)
  346. select
  347.   when i = 2 then  /* we have a listname and email address */
  348.     do
  349.     ListName = Part1
  350.     Email = Part2
  351.     say 'Assigning ListName and Email'
  352.     return TRUE
  353.     end
  354.   when i = 1 then  /* we have either a listname or email address */
  355.     do
  356.     rc = IsList(Part1)
  357.     if rc = TRUE then 
  358.       do
  359.       ListName = Part1
  360.       Email = HeadEmail
  361.       end
  362.     else 
  363.       do
  364.       Email = Part1
  365.       end
  366.     return TRUE
  367.     end
  368.   when i = 0 then  /* use listname and heademail */
  369.     do
  370.     Email = HeadEmail
  371.     return TRUE
  372.     end
  373.   otherwise        /* bad number of arguments */
  374.     do
  375.     rc = lineout(OutFile, 'I do not understand your command.', )
  376.     return FALSE
  377.     end
  378. end
  379.  
  380. return
  381.  
  382. /* ------------------------------------------------------------------ */
  383. /*
  384.  * Process a unsubscribe request
  385.  *
  386.  */
  387.  
  388. DoUnSubscribe:
  389.  
  390. rc = lineout(OutFile, '>>>' Cmd Rest, )
  391.  
  392. if Debug = TRUE then say 'Processing unsubscribe request.'
  393. if Log = TRUE then call WriteLog('Processing unsubscribe request.')
  394.  
  395. rc = WhoAndWhat(Rest)      /* find out the listname and email address */
  396. if rc = FALSE then return  /* bad command line */
  397.  
  398. /* First, make sure the list is valid */
  399. rc = IsList(ListName)
  400. if rc = FALSE then do
  401.   rc = lineout(OutFile, 'Sorry, the list' ListName 'does not exist.', )
  402.   return
  403.   end
  404. call ReadListCf(ListName)
  405. if PassWord = ApprovePassword then 
  406.   Approved = TRUE
  407. else
  408.   Approved = FALSE
  409.  
  410. /* if email doesn't match and not approved, do nothing */
  411. if Email <> HeadEmail & Approved = FALSE then do
  412.   rc = lineout(OutFile, 'Your email address given does not match the email address', )
  413.   rc = lineout(OutFile, 'in the message headers. Request refused.', )
  414.   return
  415.   end
  416.  
  417. /* Everything has been approved, take them off the list */
  418. rc = IsMember(ListName Email)
  419. if rc = TRUE then do
  420.   rc = UnSubscribe(ListName Email)
  421.   if rc = TRUE then do
  422.     rc = lineout(OutFile, 'You have been unsubscribed to list' ListName'.', )
  423.     if Email <> HeadEmail then call SpecialReply('unsub')
  424.     end
  425.   else
  426.     rc = lineout(OutFile, 'Sorry, unable to unsubscribe you to the list' ListName'.', )
  427.   end
  428. else
  429.   rc = lineout(OutFile, 'You are not subscribed to the list' ListName'.', )
  430.  
  431. return
  432.  
  433. /* ------------------------------------------------------------------ */
  434. /*
  435.  * Process a subscribe request
  436.  *
  437.  */
  438.  
  439. DoSubscribe:
  440.  
  441. rc = lineout(OutFile, '>>>' Cmd Rest, )
  442.  
  443. if Debug = TRUE then say 'Processing subscribe request.'
  444. if Log = TRUE then call WriteLog('Processing subscribe request.')
  445.  
  446. rc = WhoAndWhat(Rest)      /* find out the listname and email address */
  447. if rc = FALSE then return  /* bad command line */
  448.  
  449. /* First, make sure the list is valid */
  450. rc = IsList(ListName)
  451. if rc = FALSE then do
  452.   rc = lineout(OutFile, 'Sorry, the list' ListName 'does not exist.', )
  453.   say 'The list' ListName 'does not exist.'
  454.   return
  455.   end
  456. call ReadListCf(ListName)
  457. if PassWord = ApprovePassword then
  458.   Approved = TRUE
  459. else
  460.   Approved = FALSE
  461.  
  462. /* if emails don't match and not approved, do nothing for them */
  463. if Email <> HeadEmail & Approved = FALSE then do
  464.   say 'Email mismatch. Subscribe denied.'
  465.   rc = lineout(OutFile, 'Your email address given does not match the email address', )
  466.   rc = lineout(OutFile, 'in the message headers. Request refused.', )
  467.   return
  468.   end
  469.  
  470. say 'Checking if' Email 'is a member.'
  471. /* We have a good ListName and Email now */
  472. rc = IsMember(ListName Email)
  473. if rc = FALSE then do
  474.   /* Check and see if we can subscribe them now */
  475.   if SubscribePolicy = 'open' | Approved = TRUE then do
  476.     rc = Subscribe(ListName Email)
  477.     if rc = TRUE then do
  478.       rc = lineout(OutFile, 'You have been subscribed to list' ListName'.', )
  479.       rc = lineout(OutFile, 'Messages will be sent to the email address:' Email, )
  480.       if Email <> HeadEmail then call SpecialReply('sub')
  481.       if WelcomeFile <> '' then do
  482.         rc = SendWelcome(ListName Email)
  483.         end
  484.       end
  485.     else
  486.       rc = lineout(OutFile, 'Sorry, unable to subscribe you to the list' ListName'.', )
  487.     end
  488.   else do /* closed list and no approval yet */
  489.     rc = lineout(OutFile, 'Your request for a subscription will be forwarded to the moderator for approval.', )
  490.     call SubscribeApproval
  491.     end  /* end of subscribepolicy not open */
  492.   end    /* end of if rc = FALSE */
  493. else
  494.   rc = lineout(OutFile, 'You are already subscribed to the list' ListName'.', )
  495.  
  496. return
  497.  
  498. /* ------------------------------------------------------------------ */
  499. /* Someone was either subscribed/unsubscribed to the list by the list */
  500. /* owner. Send that person a message telling them their request was   */
  501. /* approved. */
  502.  
  503. SpecialReply:
  504.  
  505. parse arg Type
  506.  
  507. /* create a temp file for the outgoing message */
  508. ReplyFile = SysTempFileName('f?????.tmp', '?');
  509. rc = stream(ReplyFile, 'C', 'OPEN WRITE')  /* open the file for writing */
  510. if rc <> 'READY:' then do
  511.   call WriteLog('no tempfile')
  512.   end
  513.  
  514. /* Create the proper headers */
  515. AdminTo = Email
  516. if Type = 'sub' then
  517.   AdminSubject = ListName 'Subscription Approved'
  518. else
  519.   AdminSubject = ListName 'Unsubscription Approved'
  520. AdminFile = ReplyFile
  521. call WriteAdminHeaders
  522.  
  523. /* Now insert the message */
  524. if Type = 'sub' then do
  525.   rc = lineout(ReplyFile, 'Your request to subscribe to the list "'ListName'" has', )
  526.   rc = lineout(ReplyFile, 'been approved by the list owner.', )
  527.   rc = lineout(ReplyFile, '', )
  528.   rc = lineout(ReplyFile, 'You will receive list messages at the following email address:', )
  529.   rc = lineout(ReplyFile, '    >>' Email '<<', )
  530.   rc = lineout(ReplyFile, '', )
  531.   end
  532. else do
  533.   rc = lineout(ReplyFile, 'Your request to unsubscribe to the list "'ListName'" has', )
  534.   rc = lineout(ReplyFile, 'been approved by the list owner.', )
  535.   rc = lineout(ReplyFile, '', )
  536.   rc = lineout(ReplyFile, 'You will no longer receive list messages at the following email address:', )
  537.   rc = lineout(ReplyFile, '    >>' Email '<<', )
  538.   rc = lineout(ReplyFile, '', )
  539.   end
  540.  
  541. rc = stream(ReplyFile, 'C', 'CLOSE')
  542.  
  543. EmailFile = SysTempFileName('e?????.tmp', '?')
  544. rc = stream(EmailFile, 'C', 'OPEN WRITE')  /* open the file for writing */
  545. if rc <> 'READY:' then do
  546.   call WriteLog('no emailfile')
  547.   end
  548. rc = lineout(EmailFile, Email, )
  549. rc = stream(EmailFile, 'C', 'CLOSE')
  550.  
  551. if Debug = TRUE then say 'Sending special reply mail now.'
  552.  
  553. /* now start the mailer */
  554. Mailer WhoAmI'-owner@'WhereAmI EmailFile ReplyFile
  555.  
  556. return
  557.  
  558. /* ------------------------------------------------------------------ */
  559.  
  560. UnSubscribe: procedure expose ListDir TRUE FALSE Debug Log LogFile CaseInsensitive ,
  561.              lowercase uppercase
  562.  
  563. parse arg ListName Email
  564.  
  565. if Debug = TRUE then say 'Now doing the unsubcribe operation.'
  566. if Log = TRUE then call WriteLog('Now doing the unsubcribe operation.')
  567.  
  568. /* First check to see if this is a digest request */
  569. parse var ListName List '-' Digest
  570. Digest = translate(Digest, lowercase, uppercase)
  571. if Digest = 'digest' then
  572.   FileName = ListDir'\'List'\'List'.digest'
  573. else  
  574.   FileName = ListDir'\'ListName'\'ListName
  575.  
  576. rc = LockOpen(FileName 'READ')  /* open the file locking it */
  577. if rc = FALSE then
  578.   return FALSE           /* return FALSE if cannot open */
  579.  
  580. TmpFile = SysTempFileName('u????.tmp', '?')
  581. rc = stream(TmpFile, 'C', 'OPEN WRITE')  /* open the file for writing */
  582. if rc <> 'READY:' then do
  583.   call WriteLog('no tempfile')
  584.   end
  585. /* always lowercase */
  586. Email = translate(Email, lowercase, uppercase)
  587.  
  588. NoNames = TRUE
  589.  
  590. do while lines(FileName) <> 0         /* until end of file */
  591.   Line = linein(FileName)             /* get a line of the file */
  592.   Line = translate(Line, lowercase, uppercase)
  593.   if Line <> Email & Line <> '' then do
  594.     NoNames = FALSE
  595.     rc = lineout(TmpFile, Line, )     /* save this name */
  596.     end
  597. end
  598.  
  599. if NoNames = TRUE then 
  600.   rc = lineout(TmpFile, '', )           /* make sure at least one byte file */
  601.  
  602. rc = LockClose(FileName)
  603. rc = stream(TmpFile, 'C', 'CLOSE')
  604.  
  605. /* replace the old members list with the new one */
  606. rc = CopyLock(TmpFile FileName)
  607. /* Delete the temporary */
  608. rc = SysFileDelete(TmpFile)
  609.  
  610. return TRUE
  611.  
  612. /* ------------------------------------------------------------------ */
  613.  
  614. SubscribeApproval:
  615.  
  616. if Log = TRUE then call WriteLog('Sending SubscribeApproval request to moderator')
  617. if Debug = TRUE then say 'Doing SubscribeApproval.'
  618.  
  619. TmpFile = SysTempFileName('f?????.tmp', '?')
  620. rc = stream(TmpFile, 'C', 'OPEN WRITE')
  621. parse var rc rc ':'
  622. if rc <> 'READY' then do
  623.   if Debug = TRUE then say 'Unable to create temp file.'
  624.   if Log = TRUE then call WriteLog('Unable to create temporary file in SubscribeApproval.')
  625.   return
  626.   end
  627.  
  628. /* write the headers first */
  629. AdminSubject = 'Approval Request for' ListName
  630. AdminTo = ListOwner
  631. AdminFile = TmpFile
  632. call WriteAdminHeaders
  633. rc = lineout(TmpFile, 'Approved: ', )
  634. rc = lineout(TmpFile, '', )
  635. rc = lineout(TmpFile, 'subscribe' ListName Email, )
  636. rc = lineout(TmpFile, 'end', )
  637. rc = lineout(TmpFile, '', )
  638. rc = lineout(TmpFile, '--------------------------------------------------', )
  639. rc = lineout(TmpFile, 'From:' HeadFrom, )
  640. rc = lineout(TmpFile, 'To:' HeadTo, )
  641. rc = lineout(TmpFile, 'Subject:' HeadSubject, )
  642. rc = lineout(TmpFile, '--------------------------------------------------', )
  643.  
  644. rc = LockClose(TmpFile)
  645.  
  646. EmailFile = SysTempFileName('e?????.tmp', '?')
  647. rc = stream(EmailFile, 'C', 'OPEN WRITE')  /* open the file for writing */
  648. if rc <> 'READY:' then do
  649.   call WriteLog('no emailfile')
  650.   end
  651. rc = lineout(EmailFile, ListOwner, )
  652. rc = stream(EmailFile, 'C', 'CLOSE')
  653.  
  654. if Debug = TRUE then say 'Mailing request to moderator.'
  655. /* now mail it to the moderator */
  656. Mailer WhoAmI'-owner@'WhereAmI EmailFile TmpFile
  657.  
  658. return
  659.  
  660. /* ------------------------------------------------------------------ */
  661.  
  662. IsList: procedure expose ListDir TRUE FALSE Debug Log LogFile lowercase uppercase
  663.  
  664. parse arg ListName
  665.  
  666. if Debug = TRUE then say 'Checking for list' ListName
  667. if Log = TRUE then call WriteLog('Checking for list' ListName)
  668.  
  669. /* First check to see if this is a digest request */
  670. parse var ListName List '-' Digest
  671. Digest = translate(Digest, lowercase, uppercase)
  672. if Digest = 'digest' then
  673.   DirName = ListDir'\'List'\Digests'
  674. else  
  675.   DirName = ListDir'\'ListName
  676.  
  677. rc = SysFileTree(DirName, s., 'D')
  678. if rc = 0 & s.0 = 1 then
  679.   return TRUE
  680. else
  681.   return FALSE
  682.  
  683. return FALSE  /* safety net */
  684.  
  685. /* ------------------------------------------------------------------ */
  686.  
  687. IsMember: procedure expose ListDir TRUE FALSE Debug Log LogFile ,
  688.           CaseInsensitive lowercase uppercase
  689.  
  690. parse arg ListName Email
  691.  
  692. if Debug = TRUE then say 'Checking if' Email 'is a list member of' ListName
  693. if Log = TRUE then call WriteLog('Checking if' Email 'is a list member of' ListName)
  694.  
  695. /* First check to see if this is a digest request */
  696. parse var ListName List '-' Digest
  697. Digest = translate(Digest, lowercase, uppercase)
  698. if Digest = 'digest' then
  699.   FileName = ListDir'\'List'\'List'.digest'
  700. else  
  701.   FileName = ListDir'\'ListName'\'ListName
  702.  
  703. Sub = FALSE
  704.  
  705. rc = LockOpen(FileName 'READ')  /* open the file locking it */
  706. if rc = FALSE then
  707.   return FALSE                   /* return FALSE if cannot open */
  708.  
  709. Email = translate(Email, lowercase, uppercase)
  710.  
  711. do while lines(FileName) <> 0         /* until end of file */
  712.   Line = linein(FileName)             /* get a line of the file */
  713.   Line = translate(Line, lowercase, uppercase)
  714.   if Line = Email then do
  715.     Sub = TRUE
  716.     say 'Found user already as member.'
  717.     leave
  718.     end
  719. end
  720.  
  721. rc = LockClose(FileName)
  722.  
  723. if Sub = TRUE then return TRUE
  724.  
  725. return FALSE
  726.  
  727. /* ------------------------------------------------------------------ */
  728.  
  729. Subscribe: procedure expose ListDir TRUE FALSE Debug Log LogFile lowercase ,
  730.            uppercase
  731.  
  732. parse arg ListName Email
  733.  
  734. if Debug = TRUE then say 'Doing subscribe operation.'
  735. if Log = TRUE then call WriteLog('Doing subscribe operation.')
  736. if Log = TRUE then call WriteLog('New member =' Email)
  737.  
  738. /* First check to see if this is a digest request */
  739. parse var ListName List '-' Digest
  740. Digest = translate(Digest, lowercase, uppercase)
  741. if Digest = 'digest' then
  742.   FileName = ListDir'\'List'\'List'.digest'
  743. else  
  744.   FileName = ListDir'\'ListName'\'ListName
  745.  
  746. if Log = TRUE then call WriteLog('Updating file' FileName 'with new member.')
  747.  
  748. rc = LockOpen(FileName 'WRITE')  /* open the file locking it */
  749. if rc = FALSE then do
  750.   if Log = TRUE then call WriteLog('Cannot open' FileName 'for updating. File Locked.')
  751.   return FALSE                    /* return FALSE if cannot open */
  752.   end
  753.  
  754. rc = stream(FileName, 'C', 'SEEK <0')      /* go to end of file */
  755. Email = translate(Email, lowercase, uppercase)
  756. rc2 = lineout(FileName, Email, )           /* save the new email address */
  757.  
  758. rc = LockClose(FileName)
  759.  
  760. if rc2 <> 0 then do
  761.   if Log = TRUE then call WriteLog('Failure to write to' FileName '.')
  762.   return FALSE
  763.   end
  764.  
  765. if Log = TRUE then call WriteLog(FileName 'updated with new member.')
  766.   
  767. return TRUE
  768.  
  769. /* ------------------------------------------------------------------ */
  770. /*
  771.  * Normalize the email address into a SMTP form
  772.  *
  773.  */
  774.  
  775. NormalizeEmail: procedure expose Author
  776.  
  777. parse arg All
  778.  
  779. rc = pos('<', All, )
  780. if rc = 0 then
  781.   do
  782.   /* in case some mailers use () instead of <> */
  783.   All = translate(All, '<', '(')
  784.   All = translate(All, '>', ')')
  785.   end
  786.  
  787. parse var All Part1 '<' Part2 '>' Part3
  788.  
  789. rc = pos('@', Part1, )
  790. if rc <> 0 then 
  791.   do
  792.   Part1 = strip(Part1, 'B', )  /* we must strip any blanks leftover */
  793.   if Part2 <> '' then Author = Part2
  794.   else if Part3 <> '' then Author = Part3
  795.   else Author = Part1
  796.   return Part1
  797.   end
  798.  
  799. rc = pos('@', Part2, )
  800. if rc <> 0 then
  801.   do
  802.   Part2 = strip(Part2, 'B', )
  803.   if Part1 <> '' then Author = Part1
  804.   else if Part3 <> '' then Author = Part3
  805.   else Author = Part2
  806.   return Part2
  807.   end
  808.  
  809. rc = pos('@', Part3, )
  810. if rc <> 0 then
  811.   do
  812.   Part3 = strip(Part3, 'B', )
  813.   if Part2 <> '' then Author = Part2
  814.   else if Part1 <> '' then Author = Part1
  815.   else Author = Part3
  816.   return Part3
  817.   end
  818.  
  819. return ''  /* error finding SMTP email address */
  820.  
  821. /* ------------------------------------------------------------------ */
  822. /*
  823.  * Write out our standard headers for an admin message
  824.  *
  825.  */
  826.  
  827. WriteAdminHeaders: procedure expose AdminTo WhoAmI WhereAmI AdminSubject AdminFile ,
  828.                    Env lowercase uppercase
  829.  
  830. TimeZone = value( 'TZ', , Env)
  831. TmpTime = time('N')
  832. DayOfWeek = date('W')
  833. DayOfWeek = left(DayOfWeek, 3)
  834. TmpDate = date('N')
  835. rc = lineout(AdminFile, 'Date:' DayOfWeek',' TmpDate TmpTime TimeZone, )
  836. rc = lineout(AdminFile, 'Sender:' WhoAmI'-owner <'WhoAmI'-owner@'WhereAmI'>', )
  837. rc = lineout(AdminFile, 'From:' WhoAmI'-owner <'WhoAmI'-owner@'WhereAmI'>', )
  838. rc = lineout(AdminFile, 'Reply-To:' WhoAmI '<'WhoAmI'@'WhereAmI'>', )
  839. rc = lineout(AdminFile, 'To:' AdminTo, )
  840. rc = lineout(AdminFile, 'Subject:' AdminSubject, )
  841. rc = lineout(AdminFile, '', )
  842.  
  843. return
  844.  
  845. /* ------------------------------------------------------------------ */
  846. /*
  847.  * Parse RFC822 headers
  848.  *
  849.  */
  850.  
  851. ParseHeaders: procedure expose HeadTo HeadFrom HeadReplyTo MsgFile HeadSubject ,
  852.               lowercase uppercase HeadDate HeadCc HeadSender Log FALSE TRUE LogFile
  853.  
  854. say 'ParseHeaders starting'
  855.  
  856. Line = linein(MsgFile)                /* get a line of the file */
  857. do while Line <> ''                   /* until end of headers */
  858.   parse var Line Key ':' Val          /* separate out the components */
  859.   Key = translate(Key, lowercase, uppercase)
  860.   select
  861.     when Key = 'to' then
  862.       HeadTo = Val
  863.     when Key = 'reply-to' then
  864.       HeadReplyTo = Val
  865.     when Key = 'from' then
  866.       HeadFrom = Val
  867.     when Key = 'subject' then
  868.       HeadSubject = Val
  869.     when Key = 'date' then
  870.       HeadDate = Val
  871.     when Key = 'cc' then
  872.       HeadCc = Val
  873.     when Key = 'sender' then
  874.       HeadSender = Val
  875.     otherwise nop
  876.     end   /* select */
  877.   Line = linein(MsgFile)
  878. end       /* do while */
  879.  
  880. if Log = TRUE then
  881.   do
  882.   say 'Writing headers info to log file'
  883.   call WriteLog('ParseHeaders Info:')
  884.   call WriteLog(' To:' HeadTo)
  885.   call WriteLog(' From:' HeadFrom)
  886.   call WriteLog(' Reply-to:' HeadReplyTo)
  887.   call WriteLog(' Subject:' HeadSubject)
  888.   end
  889.  
  890. return
  891.  
  892. /* ------------------------------------------------------------------ */
  893. /*
  894.  * Send the person a help message
  895.  *
  896.  */
  897.  
  898. DoHelp:
  899.  
  900. if Debug = TRUE then say 'Sending help to sender.'
  901. if Log = TRUE then call WriteLog('Sending help to sender.')
  902.  
  903. if Email == '' then do          /* find their email address */
  904.   Email = HeadEmail
  905. end
  906.  
  907. rc = lineout(OutFile, '>>>' Cmd Rest, )
  908. rc = lineout(OutFile, '', )
  909. rc = lineout(OutFile, 'This is the' Steward 'mailing list software,' StewardVersion, )
  910. rc = lineout(OutFile, 'of' StewardDate'.', )
  911. rc = lineout(OutFile, '', )
  912. rc = lineout(OutFile, Steward 'understands the following commands:', )
  913. rc = lineout(OutFile, '', )
  914. rc = lineout(OutFile, 'subscribe <listname> [<address>]', )
  915. rc = lineout(OutFile, '  Subscribe yourself to the named <listname>.', )
  916. rc = lineout(OutFile, '  <address> is optional.', )
  917. rc = lineout(OutFile, '', )
  918. rc = lineout(OutFile, 'unsubscribe <listname> [<address>]', )
  919. rc = lineout(OutFile, '  Unsubscribe yourself to the named <listname>.', )
  920. rc = lineout(OutFile, '  <address> is optional.', )
  921. rc = lineout(OutFile, '', )
  922. rc = lineout(OutFile, 'help', )
  923. rc = lineout(OutFile, '  Send this message.', )
  924. rc = lineout(OutFile, '', )
  925. rc = lineout(OutFile, 'lists', )
  926. rc = lineout(OutFile, '  Show the lists available from this server.', )
  927. rc = lineout(OutFile, '', )
  928. rc = lineout(OutFile, 'end', )
  929. rc = lineout(OutFile, '  Stop processing commands (useful if your mailer adds a signature).', )
  930. rc = lineout(OutFile, '', )
  931. rc = lineout(OutFile, 'Commands should be sent in the body of the email message to', )
  932. rc = lineout(OutFile, WhoAmI'@'WhereAmI'. Multiple commands may be included in one', )
  933. rc = lineout(OutFile, 'message provided each is on its own line.', )
  934. rc = lineout(OutFile, '', )
  935. rc = lineout(OutFile, 'Commands in the "Subject" field are ignored.', )
  936. rc = lineout(OutFile, '', )
  937. rc = lineout(OutFile, 'Questions should be sent to' WhoAmIOwner'.', )
  938. rc = lineout(OutFile, '', )
  939.  
  940. return
  941.  
  942. /* ------------------------------------------------------------------ */
  943. /*
  944.  * Read the master configuration file
  945.  *
  946.  */
  947.  
  948. ReadMasterCf: procedure expose HomeDir LogDir ListDir Mailer WhereAmI WhoAmI ,
  949.               WhoAmIOwner MasterPassword Env TRUE FALSE Debug lowercase uppercase
  950.  
  951. if Debug = TRUE then say 'Reading Steward configuration file.'
  952.  
  953. /* Find out where the configuration file should be */
  954. StewardCf = value('steward_cf',,Env)
  955. /* StewardCf = value('steward_cf_test',,Env) */
  956.  
  957. /* If its not defined then assume wherever we are */
  958. if StewardCf = '' then do
  959.   StewardCf = '.'
  960.   end
  961.  
  962. FileName = StewardCf'\steward.cf'
  963.  
  964. rc = LockOpen(FileName 'READ')  /* open the file locking it */
  965. if rc = FALSE then
  966.   return FALSE                   /* return FALSE if cannot open */
  967.  
  968. /* now read the configuration file */
  969. do while lines(FileName) <> 0         /* until end of file */
  970.   Line = linein(FileName)             /* get a line of the file */
  971.   parse var Line Line '#' Comment     /* separate out any comments */
  972.   if Line <> '' then do               /* if not null */
  973.     parse var Line Key '=' Val        /* find the key and value */
  974.     if Key <> '' then do
  975.       Val = strip(Val, 'B', ' ')      /* remove any blanks */
  976.       Key = strip(Key, 'B', ' ')
  977.       select
  978.         when Key = 'HomeDir' then
  979.           HomeDir = Val
  980.         when Key = 'LogDir' then
  981.           LogDir = Val
  982.         when Key = 'ListDir' then
  983.           ListDir = Val
  984.         when Key = 'Mailer' then
  985.           Mailer = Val
  986.         when Key = 'WhereAmI' then
  987.           WhereAmI = Val
  988.         when Key = 'WhoAmI' then
  989.           WhoAmI = Val
  990.         when Key = 'WhoAmIOwner' then
  991.           WhoAmIOwner = Val
  992.         when Key = 'MasterPassword' then
  993.           MasterPassword = Val
  994.         otherwise nop
  995.         end   /* select */
  996.       end     /* if Key <> '' */
  997.     end       /* if Line <> '' */
  998.  
  999.   Key = ''
  1000.  
  1001. end /* end do while */
  1002.  
  1003. rc = LockClose(FileName)
  1004.  
  1005. if Debug = TRUE then say 'Steward.cf file read.'
  1006.  
  1007. return TRUE
  1008.  
  1009. /* ------------------------------------------------------------------ */
  1010. /*
  1011.  * Read the per list configuration file
  1012.  *
  1013.  */
  1014.  
  1015. ReadListCf: procedure expose ListDir AdminPassword ListOwner Administrivia,
  1016.             Advertise ApprovePassword DoArchive Moderated NoList Precedence,
  1017.             ListHeader SubscribePolicy ReplyTo SubjectPrefix TRUE FALSE,
  1018.             DoDigest DigestRmHeader DigestVolume DigestIssue DigestFronter,
  1019.             DigestFooter DigestName Debug Log LogFile OpenPosting WelcomeFile,
  1020.             DigestSubs CaseInsensitive lowercase uppercase
  1021.  
  1022. parse arg ListName
  1023.  
  1024. if Debug = TRUE then say 'Reading list configuration file for' ListName
  1025.  
  1026. /* First check to see if this is a digest request */
  1027. parse var ListName List '-' Digest
  1028. Digest = translate(Digest, lowercase, uppercase)
  1029. if Digest = 'digest' then
  1030.   FileName = ListDir'\'List'\'List'.cf'
  1031. else  
  1032.   FileName = ListDir'\'ListName'\'ListName'.cf'
  1033.  
  1034. if Debug = TRUE then say 'Reading filename "'FileName'"'
  1035.  
  1036. rc = LockOpen(FileName 'READ')  /* open the file locking it */
  1037. if rc = FALSE then
  1038.   return FALSE                   /* return FALSE if cannot open */
  1039.  
  1040. /* now read the configuration file */
  1041. do while lines(FileName) <> 0         /* until end of file */
  1042.   Line = linein(FileName)             /* get a line of the file */
  1043.   parse var Line Line '#' Comment     /* separate out any comments */
  1044.   if Line <> '' then do               /* if not null */
  1045.     parse var Line Key '=' Val        /* find the key and value */
  1046.     if Key <> '' then do
  1047.       Val = strip(Val, 'B', ' ')      /* remove any blanks */
  1048.       Key = strip(Key, 'B', ' ')
  1049. /*      say Key '=' Val */
  1050.       select
  1051.         when Key = 'AdminPassword' then
  1052.           AdminPassword = Val
  1053.         when Key = 'ListOwner' then
  1054.           ListOwner = Val
  1055.         when Key = 'Administrivia' then
  1056.           Administrivia = Val
  1057.         when Key = 'Advertise' then
  1058.           Advertise = Val
  1059.         when Key = 'ApprovePassword' then
  1060.           ApprovePassword = Val
  1061.         when Key = 'DoArchive' then
  1062.           DoArchive = Val
  1063.         when Key = 'Moderated' then
  1064.           Moderated = Val
  1065.         when Key = 'NoList' then
  1066.           NoList = Val
  1067.         when Key = 'Precedence' then
  1068.           Precedence = Val
  1069.         when Key = 'ListHeader' then
  1070.           ListHeader = Val
  1071.         when Key = 'SubscribePolicy' then
  1072.           SubscribePolicy = Val
  1073.         when Key = 'ReplyTo' then
  1074.           ReplyTo = Val
  1075.         when Key = 'SubjectPrefix' then
  1076.           SubjectPrefix = Val
  1077.         when Key = 'DoDigest' then
  1078.           DoDigest = Val
  1079.         when Key = 'DigestRmHeader' then
  1080.           DigestRmHeader = Val
  1081.         when Key = 'DigestVolume' then
  1082.           DigestVolume = Val
  1083.         when Key = 'DigestIssue' then
  1084.           DigestIssue = Val
  1085.         when Key = 'DigestName' then
  1086.           DigestName = Val
  1087.         when Key = 'DigestFronter' then
  1088.           DigestFronter = Val
  1089.         when Key = 'DigestFooter' then
  1090.           DigestFooter = Val
  1091.         when Key = 'OpenPosting' then
  1092.           OpenPosting = Val
  1093.         when Key = 'WelcomeFile' then
  1094.           WelcomeFile = Val
  1095.         when Key = 'DigestSubs' then
  1096.           DigestSubs = Val
  1097.         when Key = 'CaseInsensitive' then
  1098.           CaseInsensitive = Val
  1099.         otherwise nop
  1100.         end   /* select */
  1101.       end     /* if Key <> '' */
  1102.     end       /* if Line <> '' */
  1103.  
  1104.   Key = ''
  1105.  
  1106. end /* end do while */
  1107.  
  1108. rc = LockClose(FileName)
  1109.  
  1110. return TRUE
  1111.  
  1112. /* ------------------------------------------------------------------ */
  1113.  
  1114. StartLog: procedure expose LogDir LogFile ETime1 ETime2 Debug FALSE TRUE
  1115.  
  1116. FileName = LogDir'\?????.log'
  1117. LogFile = SysTempFileName(FileName, '?')
  1118.  
  1119. if LogFile = '' then
  1120.   do
  1121.   say 'Cannot create temporary file.'
  1122.   say 'Setting logfile to NUL'
  1123.   LogFile = 'NUL'
  1124.   Log = FALSE
  1125.   return
  1126.   end
  1127.  
  1128. if Debug = TRUE then say 'LogFile =' LogFile
  1129.  
  1130. rc = stream(LogFile, 'C', 'OPEN WRITE')
  1131.  
  1132. TmpTime = time('N')
  1133. TmpDate = date('N')
  1134.  
  1135. rc = lineout(LogFile, 'Date:' TmpDate, )
  1136. rc = lineout(LogFile, 'Time:' TmpTime, )
  1137.  
  1138. return
  1139.  
  1140. /* ------------------------------------------------------------------ */
  1141.  
  1142. StopLog: procedure expose LogFile LogDir ETime1 ETime2 Debug FALSE TRUE
  1143.  
  1144. ETime = ETime2 - Etime1
  1145.  
  1146. if Debug= TRUE then say 'Elapsed Time =' ETime
  1147.  
  1148. call WriteLog('Elapsed Time:' ETime)
  1149. call WriteLog('')
  1150. call WriteLog('=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=')
  1151. call WriteLog('')
  1152.  
  1153. rc = stream(LogFile, 'C', 'CLOSE')
  1154.  
  1155. PermLog = LogDir'\steward.log'
  1156.  
  1157. call AppendLock(LogFile PermLog)
  1158.  
  1159. rc = SysFileDelete(LogFile)
  1160.  
  1161. return
  1162.  
  1163. /* ------------------------------------------------------------------ */
  1164.  
  1165. WriteLog: procedure expose LogFile
  1166.  
  1167. parse arg String
  1168.  
  1169. rc = lineout(LogFile, String, )
  1170.  
  1171. return
  1172.   
  1173. /* ------------------------------------------------------------------ */
  1174.  
  1175. ErrHandler:
  1176.  
  1177. SIGerrCode = RC
  1178. StewardErrLog = 'Steward.err'
  1179.  
  1180. if Debug = TRUE then say 'Identified error while executing line #'Sigl'   RC = ['SIGerrCode']'
  1181. if Debug = TRUE then say '['SourceLine(Sigl)']'
  1182. rc = lineout( StewardErrLog, '     -----', )
  1183. rc = lineout( StewardErrLog, 'Error ['SIGerrCode'] while executing line #'Sigl, )
  1184. rc = lineout( StewardErrLog, '['SourceLine(Sigl)']')
  1185.  
  1186. return
  1187.  
  1188. /* ------------------------------------------------------------------ */
  1189.  
  1190. SendWelcome: procedure expose ListDir lowercase uppercase WelcomeFile,
  1191.              AdminSubject AdminTo AdminFile Mailer WhoAmI WhereAmI TRUE FALSE,
  1192.              Env
  1193.  
  1194. parse arg List1 Email1
  1195.  
  1196. /* First check to see if this is a digest request */
  1197. parse var List1 List2 '-' Digest
  1198. Digest = translate(Digest, lowercase, uppercase)
  1199. if Digest = 'digest' then
  1200.   FileName = ListDir'\'List2'\'WelcomeFile
  1201. else  
  1202.   FileName = ListDir'\'List1'\'WelcomeFile
  1203.  
  1204. /* Create and open a temporary file for the message */
  1205. MsgFile = SysTempFileName('msg?????.tmp', '?')
  1206. if MsgFile = '' then return
  1207.  
  1208. rc = stream(MsgFile, 'C', 'OPEN WRITE')
  1209. if rc <> 'READY:' then return
  1210.  
  1211. AdminSubject = 'Welcome to' List1
  1212. AdminTo = Email1
  1213. AdminFile = MsgFile
  1214. call WriteAdminHeaders
  1215. rc = stream(MsgFile, 'C', 'CLOSE')
  1216.  
  1217. call AppendLock(FileName MsgFile)
  1218.  
  1219. /* Create the email file */
  1220. EmailFile = SysTempFileName('email????.tmp', '?')
  1221. if EmailFile = '' then
  1222.   do
  1223.   rc = SysFileDelete(MsgFile)
  1224.   return
  1225.   end
  1226.  
  1227. rc = stream(EmailFile, 'C', 'OPEN WRITE')
  1228. if rc <> 'READY:' then
  1229.   do
  1230.   rc = SysFileDelete(MsgFile)
  1231.   return
  1232.   end
  1233.  
  1234. rc = lineout(EmailFile, Email1, )
  1235. rc = stream(EmailFile, 'C', 'CLOSE')
  1236.  
  1237. /* now mail it to them */
  1238. Mailer WhoAmI'-owner@'WhereAmI EmailFile MsgFile
  1239.  
  1240. return TRUE
  1241.  
  1242. /* ------------------------------------------------------------------ */
  1243. /* ------------------------------------------------------------------ */
  1244.  
  1245.  
  1246.