歡迎您光臨本站 註冊首頁

實現qmail下郵件過濾和郵件到達簡訊通知

←手機掃碼閱讀     火星人 @ 2014-03-05 , reply:0

實現qmail下郵件過濾和郵件到達簡訊通知

     簡訊這麼火熱,如果自己的郵件伺服器也能和簡訊結合起來,那多麼好啊?在qmail下,我用qmail_queue來解決郵件過濾和簡訊到達通知。
     本郵件系統在freebsd 4.10上實施成功。
     1、對於郵件系統的安裝,沒有太多說的。建議大家看HD的http://bsd.huangdong.com/server/mail/qmail.html來安裝。如果已經安裝了有qmail+vpopmail+qmail_queue補丁的,請直接跳到4繼續安裝。在新裝郵件伺服器時有幾點需要注意的是,安裝qmail的時候應該將步驟改為:
 cd /usr/ports/mail/qmail
 make WITH_QMAILQUEUE_PATCH=yes WITH_BIG_TODO_PATCH=yes  WITH_OUTGOINGIP_PATCH=yes WITH_PRESERVE_CONFIG_FILES=yes  extract
 cd /usr/ports/mail/qmail/work/qmail-1.03
 fetch http://www.nimh.org/dl/qmail-smtpd.c
 cd /usr/ports/mail/qmail
 make WITH_QMAILQUEUE_PATCH=yes WITH_BIG_TODO_PATCH=yes  WITH_OUTGOINGIP_PATCH=yes WITH_PRESERVE_CONFIG_FILES=yes  install
 make disable-sendmail
 make enable-qmail
 make clean
 
 在安裝vpopmail的時候可能因為port更新的原因讓使用vpopmail的時候出現奇怪問題,最好是安裝webmin將建立的vpopmail資料庫刪除后,重新建立,並指定資料庫的用戶許可權。由於webmin的安裝不是本文重點,這裡將忽略。
 
 2、關於文章裡面的殺毒部分因為系統效率問題,我沒有安裝。各位請自行決定是否安裝。
 
 3、反垃圾郵件當然需要,我用的是http://anti-spam.org.cn的cbl+服務。使用的時候只需要將smtpd的腳本改為:
 #!/bin/sh
 QMAILDUID=`/usr/bin/id -u qmaild`
 NOFILESGID=`/usr/bin/id -g qmaild`
 exec /usr/local/bin/tcpserver -H -R -l 0 -p -x \
         /usr/local/vpopmail/etc/tcp.smtp.cdb -u"$QMAILDUID" \
         -g"$NOFILESGID" -v -c100 0 smtp rblsmtpd \
         -r cblplus.anti-spam.org.cn \
         -r relays.ordb.org \
         /var/qmail/bin/qmail-smtpd /usr/local/vpopmail/bin/vchkpw-smtpd /usr/bin/true 2>;&1
 
 從http://anti-spam.org.cn/cgi-bin/rblclient/(你的郵件伺服器的dns伺服器IP地址)看到你的郵件伺服器的流量和使用cbl+服務的情況。
 
 4、這裡是重點了。過濾和簡訊到達是需要qmail_queue來完成的,一定要打這個包。
 使用python來實現這個功能,當然需要安裝python啦。
 cd /usr/ports/lang/python
 make;make install;make clean
 一般來講這個安裝是非常順利的。
 安裝結束后。
 cd /var/qmail/bin
 編輯qmfilt.py,內容如下:
 #!/usr/local/bin/python --
 
 import os, sys, string, time, traceback, re, socket
 
 Version  = '1.1'
 PyVersion  = '1.0'
 Logging  = 1
 Debug    = 0
 QmailD   = 82#這裡需要和你/etc/password裡面qmaild用戶的一樣
 LogFile  = None
 TestMode = None
 Filters  = []
 MailEnvelope = ''
 MailData     = ''
 ValidActions = { 'trap': '', 'drop' : '', 'block' :'' }
 
 #這裡是你通過ucp協議將消息發送到哪個伺服器的哪個埠
 def mail_sms(msg):
     host = "xxx.xxx.114.xx"
     port = 9999
     buf = 500
     addr = (host,port)
 
     # Create socket
     UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
 
     def_msg = msg;
     UDPSock.sendto(def_msg,addr)
 
     # Close socket
     UDPSock.close()
 
 def openlog():
   global LogFile
 
   if not Logging:
      return
   try:
     LogFile = os.open('/var/log/qmfilt', os.O_APPEND|os.O_WRONLY|os.O_CREAT, 0744)
   except:
     if TestMode:
       print "Can't create /var/log/qmfilt"
     else:
       # Indicate a write error
       sys.exit(53)
 
 def log(message):
   message = time.asctime(time.localtime(time.time())) + " - " + message + "\n"
   if Logging:
     os.write(LogFile, message)
     # Indicate a write error
     #sys.exit(53)
   if TestMode:
     print message
 
 
 def showFilters():
   
   if len(Filters) == 0:
     print "No filters"
   for f in Filters:
     print "Filter -  %s - Action: %s  Regex: %s" % (f, f, f)
 
 def readFilters():
   global Filters
 
   try:
     file = open('/var/qmail/control/qmfilt', 'r')
     configLines = file.readlines()
     file.close()
   except:
     log("Can't read /var/qmail/control/qmfilt")
     if not TestMode:
       # Indicate a 'cannot read config file error'
       sys.exit(53)
 
   reg = re.compile('(.*)::(.+)::(.+)::')
   for line in configLines:
     if line == '#':
       continue
     m = reg.match(line)
     if m != None:
       action = string.lower(m.group(2))
       if not ValidActions.has_key(action):
          log("Invalid action in config file [%s] - SKIPPED" %(action))
          continue
       Filters.append(  )
    
 def readDescriptor(desc):
   result = ''
   while 1:
     data = os.read(desc, 16384)
     if data == '':
       break
     result = result + data
 
   return result
 
 def writeDescriptor(desc, data):
   while data:
     num  = os.write(desc, data)
     data = data
 
   os.close(desc)
 
 
 def filterHits():
   
   for regEx in Filters:
     reg = re.compile(regEx, re.M|re.I)
     match = reg.search(MailData)
     if match:
       log("Matched [%s]" % (regEx))
       return regEx, regEx
 
   return None,None
 
 
 def storeInTrap(hit):
   try:
     pid = os.getpid()
     mailFile = os.open('/var/qmail/qmfilt/qmfilt.%s' %(pid), os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0744)
   except:
     log("Can't create /var/qmail/qmfilt/qmfilt.%s" %(pid))
     # Indicate a write error
     sys.exit(53)
 
   try:
     """(None, inode, None, None, None, None, size, None, None, None) = os.fstat(mailFile)"""
     os.rename('/var/qmail/qmfilt/qmfilt.%s' %(pid), '/var/qmail/qmfilt/%s.mail' %(inode))
   except:
     log("Can't rename /var/qmail/qmfilt/qmfilt.%s ->; /var/qmail/qmfilt/%s.mail" %(pid, inode))
     # Indicate a write error
     sys.exit(53)
 
   try:
     envFile = os.open('/var/qmail/qmfilt/%s.envelope' %(inode), os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0744)
     mesgFile = os.open('/var/qmail/qmfilt/%s.qmfilt' %(inode), os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0744)
     writeDescriptor(mailFile, MailData)
     writeDescriptor(envFile,  MailEnvelope)
     writeDescriptor(mesgFile, "Matched filter [ %s] to file %s" %(hit, inode))
     log("Matched filter [ %s] to file %s" %(hit, inode))
   except:
     log("Can't create/write files into trap")
     # Indicate a write error
     sys.exit(53)
 
   return 0
 
 
 def sendToQmailQueue():
   
   # Open a pipe to qmail queue
   fd0 = os.pipe()
   fd1 = os.pipe()
   pid = os.fork()
   if pid < 0:
     log("Error couldn't fork")
     sys.exit(81)
   if pid == 0:
     # This is the child
     os.dup2(fd0, 0)
     os.dup2(fd1, 1)
     i = 2
     while (i < 64):
       try:
         os.close(i)
       except:
         pass
       i = i + 1
     os.chdir('/var/qmail')
     #time.sleep(10)
     os.execv("bin/qmail-queue", ('bin/qmail-queue',))
     log("Something went wrong")
     sys.exit(127)  # This is only reached on error
   else:
     # This is the parent
     # close the readable descriptors
     os.close(fd0)
     os.close(fd1)
 
 
   # Send the data
   recvHdr = "Received: (QMFILT: %s); " %(Version)
   recvHdr = recvHdr + time.strftime("%d %b %Y %H:%M:%S", time.gmtime(time.time()))
   recvHdr = recvHdr + " -0000\n"
   os.write(fd0, recvHdr)
   writeDescriptor(fd0, MailData)
   writeDescriptor(fd1, MailEnvelope)
 
   # Catch the exit code to return
   result = os.waitpid(pid, 0)
   if PyVersion >; '1.5.1':
     if os.WIFEXITED(result):
       return os.WEXITSTATUS(result)
     else:
       log("Didn't exit normally")
       sys.exit(81)
   else:
     return result
 
 def conver(msg):
   msg = msg.replace(chr(00),' ')
   msg = msg.replace('F','From:',1)
   msg = msg.replace(' T',' To:')
   return msg
    
 
 def main(argv, stdout, environ):
 
   global TestMode
   global MailData
   global MailEnvelope
   global PyVersion
 
   PyVersion = string.split(sys.version)
 
   if len(argv) >; 1 and argv == '--test':
     TestMode = 1
 
   os.setuid(QmailD)
 
   # Insure Environment is OK
 
   # Try to open log
   openlog()
 
   if not os.path.exists('/var/qmail/qmfilt'):
     # Indicate a problem with the queue directory
     log("Directory /var/qmail/qmfilt doesn't exist")
     if not TestMode:
       sys.exit(61)
 
 #  Python 1.5.2 feature
 #  if not os.access('/var/qmail/qmfilt', os.W_OK):
 #    # Indicate a problem with the queue directory
 #    log("Directory /var/qmail/qmfilt doesn't have write permission")
 #    if not TestMode:
 #      sys.exit(61)
 
   if os.path.exists('/var/qmail/control/qmfilt'):
     readFilters()
   else:
     if TestMode:
       print "No filter file /var/qmail/control/qmfilt - no filters applied"
 
   if TestMode:
     showFilters()
 
   # Go no further if in test mode
   if TestMode:
     sys.exit(0)
 
 
 
   # Get the data
 
   # Read the data
   MailData     = readDescriptor(0)
 
   # Read the envelope
   MailEnvelope = readDescriptor(1)
   if Debug:
     log(MailData)
     log(MailEnvelope)
 
   action,hit = filterHits()
 
   if action == 'trap':
     storeInTrap(hit)
   if action == 'block':
     log("Matched filter [ %s] and email was BLOCKED/Refused delivery" %(hit))
     sys.exit(31)
   if action == 'drop':
     log("Matched filter [ %s] and email was DROPPED" %(hit))
   if action == None:
     sendToQmailQueue()
   #Log
   log(conver(MailEnvelope))
   #send sms
   mail_sms(conver(MailEnvelope))
 
   if Debug:
     log("qmailqueue returned [%d]" %(result))
   sys.exit(0)
 
 if __name__ == "__main__":
   try:
     main(sys.argv, sys.stdout, os.environ)
 
   # Catch the sys.exit() errors
   except SystemExit, val:
     sys.exit(val)
   except:
     # return a fatal error for the unknown error
     if TestMode:
       traceback.print_exc()
     sys.exit(81)
 然後在cd /var/qmail/control
 編輯qmfilt內容如下:
 #
 # This is the qmfilt control file
 # If any email comes in that matches this
 # filter, the mail will be redirected
 # to the filter directory
 #
 # A filter regular expression must be on a single
 # line and in between a pair of '::'
 #
 # Valid actions:
 #     trap  - store in the trap directory
 #     block - tell the smtp sender that we won't take the mail
 #     drop  - accept the mail, but don't queue it
 #
 
 # This will match any executable Visual Basic Scripts
 VisualBasic::block::^Content-Type: application/octet-stream;\s+name="(.*\.vbs)"::
 SHS::block::^Content-Type: application/octet-stream;\s+name="(.*\.shs)"::
 SHB::block::^Content-Type: application/octet-stream;\s+name="(.*\.shb)"::
 COM::block::^Content-Type: application/octet-stream;\s+name="(.*\.com)"::
 EXE::block::^Content-Type: application/octet-stream;\s+name="(.*\.exe)"::
 SCR::block::^Content-Type: application/octet-stream;\s+name="(.*\.scr)"::
 PIF::block::^Content-Type: application/octet-stream;\s+name="(.*\.pif)"::
 BAT::block::^Content-Type: application/octet-stream;\s+name="(.*\.bat)"::
 
 # This is the Outlook date overflow bug
 DATE::block::^Date:.{160,}::
 
 在/var/log下建立qmfilt文件,內容為空
 chown qmaild qmfilt
 在/var/qmail下建立qmfilt目錄。
 chown -R qmaild qmfilt
 好了,來讓我們的程序啟用吧
 先測試一把
 /var/qmail/bin/qmfilt.py --test
 如果返回了先定義的qmfilt裡面的內容,恭喜成功了一半了。內容應該和下面的相似:
 Filter -  VisualBasic - Action: block  Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.vbs)"
 Filter -  SHS - Action: block  Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.shs)"
 Filter -  SHB - Action: block  Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.shb)"
 Filter -  COM - Action: block  Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.com)"
 Filter -  EXE - Action: block  Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.exe)"
 Filter -  SCR - Action: block  Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.scr)"
 Filter -  PIF - Action: block  Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.pif)"
 Filter -  BAT - Action: block  Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.bat)"
 Filter -  DATE - Action: block  Regex: ^Date:.{160,}
 好了,讓我們的過濾器用起來吧
 cd /usr/local/vpopmail/etc
 編譯tcp.smtp內容如下
 127.:allow,RELAYCLIENT="",QMAILQUEUE="bin/qmfilt.py"
 :allow,QMAILQUEUE="bin/qmfilt.py"
 然後生成tcp.smtp.cdb文件
 tcprules tcp.smtp.cdb tcp.smtp.tmp < tcp.smtp
 ,發個帶exe為附件的郵件看看。如果成功過濾,應該在/var/log/qmfilt裡面看到如下信息:
 Fri Sep 10 14:37:01 2004 - Matched filter [ EXE] and email was BLOCKED/Refused delivery
 Fri Sep 10 14:38:09 2004 - Matched
 如果你是用foxmail等客戶端發送帶exe為結尾的郵件的話,伺服器會提示你拒絕接收的。
 到這裡,你的郵件過濾已經成功了。
 你可以在/var/qmail/control的qmfilt文件裡面定義你需要過濾的指定代碼,然後測試規則是否生效了。
 
 關於簡訊到達通知,因為安全和商業上的問題。不能將伺服器端的代碼貼出來。我將機制給大家講一下。
 qmfilt.py這個程序裡面mail_sms用udp將類似From:info@xxx.com.cn To:weilx@xxx.com發送到指定的伺服器上。伺服器接收到這個數據包后,將這個數據包的內容用簡訊發送出去。
 當然可以將郵件主題等信息一起發送到手機上,大家根據自己的情況更改吧。
 
 這個程序是根據http://sourceforge.net/projects/qmfilt/這個項目更改的,但是直接用他的代碼會造成不能收信,大家可以在這個項目的cvs關注他的代碼修改情況。
 


[火星人 ] 實現qmail下郵件過濾和郵件到達簡訊通知已經有391次圍觀

http://coctec.com/docs/service/show-post-38251.html