(server端使用hylafax)
作者:cschen33.tw@yahoo.com.tw
最後更新日期:2003/05/09

目的:
利用e-mail來傳送傳真
硬體要求:
個人電腦,數據機,一個電話線路
作業系統:
linux
程式語言:
python
相關軟體(必須安裝或設定好):
hylafax(傳真伺服器)
sendmail(或相關郵件伺服器)
python(script語言)
bg5ps(中文文字檔轉為ps檔)

說明:
根據使用者的要求要使用e-mail來傳送傳真,我開始尋找hylafax傳真伺服器軟體套件中可用的工具或是軟體,首先在其套件中有個叫 faxmail,經由參考其manual內容發覺並不符 合我的要求,其內文也說明了最好是使用sendfax這個軟體來傳送傳真,因此我決定自己寫一個程式來取出mail的夾帶檔,然後使用 sendfax來傳送傳真,由於sendfax可以直接傳送文字檔,ps檔,pdf檔,因此檔案的格式比較不用操心,其中比較有問題的是含有中文的純文字 傳真(由於 sendfax無法傳送中文文字檔,因此我使用bg5ps將文字轉為含有圖形的ps檔),由於我們可能將subject當成傳真號碼,因此持續修改,以下 是我們 詳細說明:
  1. 下載本人的程式:(版權:free,你可以任意修改,測試版,一定會有錯誤)

  2. csc_fax.py
    然後將其放在某一個目錄,一般是放在/usr/bin/csc_fax.py
    確定其可以執行chmod 755 /usrbin/csc_fax.py
  3. 如何將一封mail轉給程式處理
  4. 方法:在/etc/aliases內加入一行,郵件帳號: "|程式路徑 及參數"
    實例:
    mail2fax: "|/usr/bin/csc_fax.py  -d 12345678"
    然後執行newaliases,更新設定,由於之前我使用別名中來指定電話號碼-d 123456789是固定目的的傳真號碼,也就是說傳到mail2fax固定都傳到這個號碼,之後我更改了可以在subject(主旨)內指定
  5. 在較新版的sendmail由於安全的因素會限制執行的程式
  6. 其設定檔在/etc/smrsh,
    要讓csc_fax有權限可以執行,
    ln -s /usr/bin/csc_fax.py csc_fax.py

使用說明:
  1. 夾帶檔ps,pdf,tif
  2. 使用一般的mail軟體(如outlook express),目前一次只能傳送一個夾帶檔,收件者為mail2fax@你的網域
    這個程式會略過文字本文部份(由於判別比較難,因此我還沒有想到),只會傳送附件檔
  3. 英文的純文字檔
  4. 寫一封mail,以outlook express為例,寫完一封信之後,設定( 格式->純文字,格式->編碼->西歐語系)
    然後傳送,如果有訊息(請按直接傳送)
  5. 中文的純文字檔
  6. 同上寫一封信,設定(格式->純文字,格式->編碼->繁體中文(big5)
    然後傳送,你會發覺傳送的傳真右下角有一個printed by bg5ps ....,如果你不喜歡的話,可以直接修改bg5ps程式尋找上述字,ow(printed by bg5ps.......),改成你要的
  7. 傳真號碼
  8. 你可以在subject指定它可以接受的字
    0-9 , "+" , "-" , "," , "(" , ")"
    然而你如果需要用到標題,使用一般文字,則它會使用/etc/aliase中-d 設定的電話號碼
  9. 如果程式執行有錯會return一封信給你
  10. 如果傳真失敗成功等也會使用hylafax內定的通知信給寄信者
以下是程式內容
#!/usr/bin/python
import mimetools,multifile,sys,os
import getopt,sys,commands,tempfile
from time import localtime,time,strftime
from string import *
proc_time=strftime('%m月%d日-%H%M%S',localtime(time()))
mimetype=["image/tiff","image/tif","application/postscript","application/pdf"]
phone_limit=["0","1","2","3","4","5","6","7","8","9","-",",","(",")","+"]
logfile="/var/log/faxmail.log"
log_f=open(logfile,"a+")
log_f.write("\n========================================\n")
log_f.write(proc_time+"\n")

fax_prg=commands.getoutput('which sendfax')
bg5ps=commands.getoutput('which bg5ps')
args=sys.argv[1:]
opts,args=getopt.getopt(args,"d:")
faxnum=[]
log_f.write(join(faxnum))
default_dest=""
for test in opts:
    faxnum.append(test[1])
    default_dest=default_dest+" -d "+test[1]+" "
 
pipfile=sys.stdin

def extract_attach(stream,mimetype):
    mf=multifile.MultiFile(stream,0)
    mainheaders=mimetools.Message(mf)
    type=mainheaders.getmaintype()
    sub_type=mainheaders.getsubtype()
    subject=mainheaders["subject"]
    char_set=mainheaders.getparam("charset")
    sender=mainheaders["from"]
    if sender.find("<") and sender.find(">"):
       sender=sender[sender.find("<")+1:sender.find(">")]
    if type=='multipart':
        fax_file=tempfile.mktemp(".tmp")
        h_fax=open(fax_file,"wb+")
        mf.push(mainheaders.getparam("boundary"))
        while mf.next():
            submsg=mimetools.Message(mf)
            try:
                mimetools.decode(mf,h_fax,submsg.getencoding())
            except ValueError:
                continue

            if mimetype.count(lower(submsg.gettype()))==1:
               break
            else:
               h_fax.close()
               os.unlink(fax_file)              
               h_fax=open(fax_file,"wb+")
              
        mf.pop()
        h_fax.close()
    elif type=="text" and sub_type=="plain":
        text_body=join(mf.readlines())
        if char_set=="big5":
           bg5ps_s=tempfile.mktemp(".txt")
           fax_file=tempfile.mktemp(".ps")
           h_source=open(bg5ps_s,"w+")
           h_source.write(text_body)
           h_source.close()
           exit_code,exit_status=commands.getstatusoutput( bg5ps+" -s 10 -en big5 -cf /etc/bg5ps.conf -lm 12 -rm 12 -bm 12 -tm 12 -ls 6 -cs 2 -if "+bg5ps_s+" -of "+fax_file )
           os.unlink(bg5ps_s)
           log_f.write("==============big5ps============\n")
           log_f.write("exit_code:"+str(exit_code)+"\n")
        else:
           fax_file=tempfile.mktemp(".txt")
           h_fax=open(fax_file,"w+")
           h_fax.write(text_body)
           h_fax.close()
    return fax_file,subject,sender   
            
fax_file,subject,sender=extract_attach(pipfile,mimetype)
#fax number pority is subject , next is set in  aliase
#we need to test if subject = faxnumber
#otherwise we use then default set in aliases
if len(strip(subject))==0:
   faxstring=default_dest
else:
   list_subject=list(subject)
   dynic_dest=" -d "+subject+" "
   faxstring=dynic_dest
   for test_str in list_subject:
       if phone_limit.count(test_str)==0:
          faxstring=default_dest
          break
 
log_f.write("number:"+faxstring+"\n")
h_fax=open(fax_file,"r")
data=h_fax.readlines()
h_fax.close()
if len(data)==0:
   log_f.write("no attach can be send !\n")
   log_f.write("fax abort!\n")
   sys.exit(1)
   raise RuntimeError," not attach file can be send to fax !"


log_f.write("fax_file="+fax_file+"\n")
command_fax=fax_prg+" -n -D -f "+sender+" "+ faxstring+fax_file
send_exit,send_status=commands.getstatusoutput(command_fax)

log_f.write("mail from:"+sender+"\n")
log_f.write("fax cmd="+command_fax+"\n")
log_f.write("sendfax status:"+send_status+"\n")
log_f.write("exit code:"+str(send_exit))
log_f.close()
os.unlink(fax_file)

#新的idea ,我想到一個好方法,來使用mail2fax ,首先列印到某一台印表機,然後產生ps檔,然後會啟動你的e-mail軟體,並插入這個ps檔,並自動指定傳真的email-address,然後使用 者再打入傳真號碼,如此,使用者可以幾乎全自動的傳真了:
預計使用redmon列表機連接埠
難題如何得知預設mail軟體,如何執行及插入附件,及指定mail-address?

針對Outlook Express而言,經過幾天的查詢資料,可以在登錄檔中利用outlook express程式的名稱msimn.exe,找到了/mailurl:%1,但是卻不知如何使用它,後來想到網頁使用超鏈結mailto:,本來以為是 只能用在網頁,經查是是可以使用在command line,只是mailto:是使用預設的mail client,而mailurl則是要指定執行程式所在,如此我現在可以指定收件者,主旨及本文內容,但唯一的附件無法指定?
有一個別人寫好的命令列的程式mapimail.exe,可以呼叫你的mail client並夾帶附件檔,但可惜的是無法指定主旨
命令列傳送郵件
mailto:
c:\Program files\Outlook Express\MSIMN.exe" /mailurl:

主  旨
用法
Subject
Subject=主旨
Body
Body=內文
CC
CC=email address
BCC
BCC=email address
Attach
Attach="檔名及路徑"
注意:分隔符號有兩二個?及&,而空格則是%20
例:mailto:aaa@xxx.xxx.xxx?Subject=this%20is%20test&Body=abcd&CC= aa@xxx.xxx.xxx
而Attach經在Outlook Express中測試無效

在python下如何執行windows的外部程式呢?
以前在linux下有使用commands.getstatusoutput這個函式
但是在windows下無法使用,經查可以使用os.system()這個函式
os.system("c:\\temp\\mapimail.exe  c:\\autoexec.bat")