Contents
freebsd
qmail下使用python编写脚本来过滤邮件和发送短信
实现qmail下邮件过滤和邮件到达短信通知
作者:梅劲松
时间:2004年9月10日
感谢:HD、刘鑫
短信这么火热,如果自己的邮件服务器也能和短信结合起来,那多么好啊?在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的脚本改为:
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,内容如下:
1 #!/usr/local/bin/python --
2
3 import os, sys, string, time, traceback, re, socket
4
5 Version = '1.1'
6 PyVersion = '1.0'
7 Logging = 1
8 Debug = 0
9 QmailD = 82#这里需要和你/etc/password里面qmaild用户的一样
10 LogFile = None
11 TestMode = None
12 Filters = []
13 MailEnvelope = ''
14 MailData = ''
15 ValidActions = { 'trap': '', 'drop' : '', 'block' :'' }
16
17 #这里是你通过ucp协议将消息发送到哪个服务器的哪个端口
18 def mail_sms(msg):
19 host = "xxx.xxx.114.2"
20 port = 9999
21 buf = 500
22 addr = (host,port)
23
24 # Create socket
25 UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
26
27 def_msg = msg;
28 UDPSock.sendto(def_msg,addr)
29
30 # Close socket
31 UDPSock.close()
32
33 def openlog():
34 global LogFile
35
36 if not Logging:
37 return
38 try:
39 LogFile = os.open('/var/log/qmfilt', os.O_APPEND|os.O_WRONLY|os.O_CREAT, 0744)
40 except:
41 if TestMode:
42 print "Can't create /var/log/qmfilt"
43 else:
44 # Indicate a write error
45 sys.exit(53)
46
47 def log(message):
48 message = time.asctime(time.localtime(time.time())) + " - " + message + "\n"
49 if Logging:
50 os.write(LogFile, message)
51 # Indicate a write error
52 #sys.exit(53)
53 if TestMode:
54 print message
55
56
57 def showFilters():
58
59 if len(Filters) == 0:
60 print "No filters"
61 for f in Filters:
62 print "Filter - %s - Action: %s Regex: %s" % (f[0], f[1], f[2])
63
64 def readFilters():
65 global Filters
66
67 try:
68 file = open('/var/qmail/control/qmfilt', 'r')
69 configLines = file.readlines()
70 file.close()
71 except:
72 log("Can't read /var/qmail/control/qmfilt")
73 if not TestMode:
74 # Indicate a 'cannot read config file error'
75 sys.exit(53)
76
77 reg = re.compile('(.*)::(.+)::(.+)::')
78 for line in configLines:
79 if line[0] == '#':
80 continue
81 m = reg.match(line)
82 if m != None:
83 action = string.lower(m.group(2))
84 if not ValidActions.has_key(action):
85 log("Invalid action in config file [%s] - SKIPPED" %(action))
86 continue
87 Filters.append( [m.group(1), string.lower(m.group(2)), m.group(3)] )
88
89 def readDescriptor(desc):
90 result = ''
91 while 1:
92 data = os.read(desc, 16384)
93 if data == '':
94 break
95 result = result + data
96
97 return result
98
99 def writeDescriptor(desc, data):
100 while data:
101 num = os.write(desc, data)
102 data = data[num:]
103
104 os.close(desc)
105
106
107 def filterHits():
108
109 for regEx in Filters:
110 reg = re.compile(regEx[2], re.M|re.I)
111 match = reg.search(MailData)
112 if match:
113 log("Matched [%s]" % (regEx[0]))
114 return regEx[1], regEx[0]
115
116 return None,None
117
118
119 def storeInTrap(hit):
120 try:
121 pid = os.getpid()
122 mailFile = os.open('/var/qmail/qmfilt/qmfilt.%s' %(pid), os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0744)
123 except:
124 log("Can't create /var/qmail/qmfilt/qmfilt.%s" %(pid))
125 # Indicate a write error
126 sys.exit(53)
127
128 try:
129 #如果不屏蔽会出现很多问题,至于为什么我还没弄明白,如果你找到问题所在,请告诉我。
130 """(None, inode, None, None, None, None, size, None, None, None) = os.fstat(mailFile)"""
131 os.rename('/var/qmail/qmfilt/qmfilt.%s' %(pid), '/var/qmail/qmfilt/%s.mail' %(inode))
132 except:
133 log("Can't rename /var/qmail/qmfilt/qmfilt.%s -> /var/qmail/qmfilt/%s.mail" %(pid, inode))
134 # Indicate a write error
135 sys.exit(53)
136
137 try:
138 envFile = os.open('/var/qmail/qmfilt/%s.envelope' %(inode), os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0744)
139 mesgFile = os.open('/var/qmail/qmfilt/%s.qmfilt' %(inode), os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0744)
140 writeDescriptor(mailFile, MailData)
141 writeDescriptor(envFile, MailEnvelope)
142 writeDescriptor(mesgFile, "Matched filter [ %s] to file %s" %(hit, inode))
143 log("Matched filter [ %s] to file %s" %(hit, inode))
144 except:
145 log("Can't create/write files into trap")
146 # Indicate a write error
147 sys.exit(53)
148
149 return 0
150
151
152 def sendToQmailQueue():
153
154 # Open a pipe to qmail queue
155 fd0 = os.pipe()
156 fd1 = os.pipe()
157 pid = os.fork()
158 if pid < 0:
159 log("Error couldn't fork")
160 sys.exit(81)
161 if pid == 0:
162 # This is the child
163 os.dup2(fd0[0], 0)
164 os.dup2(fd1[0], 1)
165 i = 2
166 while (i < 64):
167 try:
168 os.close(i)
169 except:
170 pass
171 i = i + 1
172 os.chdir('/var/qmail')
173 #time.sleep(10)
174 os.execv("bin/qmail-queue", ('bin/qmail-queue',))
175 log("Something went wrong")
176 sys.exit(127) # This is only reached on error
177 else:
178 # This is the parent
179 # close the readable descriptors
180 os.close(fd0[0])
181 os.close(fd1[0])
182
183
184 # Send the data
185 recvHdr = "Received: (QMFILT: %s); " %(Version)
186 recvHdr = recvHdr + time.strftime("%d %b %Y %H:%M:%S", time.gmtime(time.time()))
187 recvHdr = recvHdr + " -0000\n"
188 os.write(fd0[1], recvHdr)
189 writeDescriptor(fd0[1], MailData)
190 writeDescriptor(fd1[1], MailEnvelope)
191
192 # Catch the exit code to return
193 result = os.waitpid(pid, 0)[1]
194 if PyVersion > '1.5.1':
195 if os.WIFEXITED(result):
196 return os.WEXITSTATUS(result)
197 else:
198 log("Didn't exit normally")
199 sys.exit(81)
200 else:
201 return result
202
203 def conver(msg):
204 msg = msg.replace(chr(00),' ')
205 msg = msg.replace('F','From:',1)
206 msg = msg.replace(' T',' To:')
207 return msg
208
209
210 def main(argv, stdout, environ):
211
212 global TestMode
213 global MailData
214 global MailEnvelope
215 global PyVersion
216
217 PyVersion = string.split(sys.version)[0]
218
219 if len(argv) > 1 and argv[1] == '--test':
220 TestMode = 1
221
222 os.setuid(QmailD)
223
224 # Insure Environment is OK
225
226 # Try to open log
227 openlog()
228
229 if not os.path.exists('/var/qmail/qmfilt'):
230 # Indicate a problem with the queue directory
231 log("Directory /var/qmail/qmfilt doesn't exist")
232 if not TestMode:
233 sys.exit(61)
234
235 if os.path.exists('/var/qmail/control/qmfilt'):
236 readFilters()
237 else:
238 if TestMode:
239 print "No filter file /var/qmail/control/qmfilt - no filters applied"
240
241 if TestMode:
242 showFilters()
243
244 # Go no further if in test mode
245 if TestMode:
246 sys.exit(0)
247
248
249
250 # Get the data
251
252 # Read the data
253 MailData = readDescriptor(0)
254
255 # Read the envelope
256 MailEnvelope = readDescriptor(1)
257 if Debug:
258 log(MailData)
259 log(MailEnvelope)
260
261 action,hit = filterHits()
262
263 if action == 'trap':
264 storeInTrap(hit)
265 if action == 'block':
266 log("Matched filter [ %s] and email was BLOCKED/Refused delivery" %(hit))
267 sys.exit(31)
268 if action == 'drop':
269 log("Matched filter [ %s] and email was DROPPED" %(hit))
270 if action == None:
271 sendToQmailQueue()
272 #Log
273 log(conver(MailEnvelope))
274 #send sms
275 mail_sms(conver(MailEnvelope))
276
277 if Debug:
278 log("qmailqueue returned [%d]" %(result))
279 sys.exit(0)
280
281 if __name__ == "__main__":
282 try:
283 main(sys.argv, sys.stdout, os.environ)
284
285 # Catch the sys.exit() errors
286 except SystemExit, val:
287 sys.exit(val)
288 except:
289 # return a fatal error for the unknown error
290 if TestMode:
291 traceback.print_exc()
292 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 [SCR]
如果你是用foxmail等客户端发送带exe为结尾的邮件的话,服务器会提示你拒绝接收的。到这里,你的邮件过滤已经成功了。
你可以在/var/qmail/control的qmfilt文件里面定义你需要过滤的指定代码,然后测试规则是否生效了。
关于短信到达通知,因为安全和商业上的问题。不能将服务器端的代码贴出来。我将机制给大家讲一下。
qmfilt.py这个程序里面mail_sms用udp将类似 From:[email protected] To:[email protected] 发送到指定的服务器上。服务器接收到这个数据包后,将这个数据包的内容用短信发送出去。
当然可以将邮件主题等信息一起发送到手机上,大家根据自己的情况更改吧。
这个程序是根据http://sourceforge.net/projects/qmfilt/这个项目更改的,但是直接用他的代码会造成不能收信,大家可以在这个项目的cvs关注他的代码修改情况。
结束,谢谢!
FreeBSD 5.3的使用技巧
- FreeBSD5.3直接安装后不像4x样默认启动了sshd服务,你需要在安装的时候选择使用sshd服务。或者在/etc/rc.conf中加入sshd_enable="YES" 如果你使用SecureCRT用ssh2连接时提示unable to authenticate using any of the configured authentication methods !错误,请在连接属性中的Authentication的Primary选项中,选择Keyboard Interactive。
FreeBSD 5.3下安装Jail
感谢:黄冬
- 按照黄冬的文章在FB5.3下安装Jail未果,他提醒我FB5.3的Jail已经发生了变更,在他的帮助下成功在FB5.3下安装了Jail,整理成资料大家分享一下。
首先安装系统时,最好划分一个单独的分区来存放你的vhost,如果不想变更你的分区设置。也可以将vhost安装在你的/usr或者/home中。如果你有了cvsup,请到/usr/share/examples/cvsup中修改你的stable-supfile文件,设置*default host=cvsup.FreeBSD.org。然后将他丢到后台开始下载最新的源码吧。cvsup -g -L 2 stable-supfile & 由于FB5.3已经不能直接make world ,更新完代码后,到/usr/src下按步骤编译。如果你初装系统,建议你将整个系统都编译和优化一下。如果只想安装jail,就没有必要完整编译整个系统了。
在/usr/src下make buildworld&在后台编译,这个过程可能需要2小时或者4小时。你可以做点别的事情去。结束后,我们来安装我们的jail吧。我做了个新建jail的脚本new_jail.sh,贴到这里
if [ -z $1 ]; then echo "specify dest dir such as $0 /some/dir" exit fi D=$1 echo $D mkdir -p $D cd /usr/src make installworld DESTDIR=$D cd etc make distribution DESTDIR=$D cd $D ln -sf dev/null kernel
先给它加上执行权限chmod +x new_jail.sh
然后
mkdir -p /vhost/jail/179
./new_jail.sh /vhost/jail/179
来创建你的新vhost,这里/vhost/jail/179替换为你要安装的虚机路径。最好是绝对地址,防止出错。
当一切结束后,用单用户来启动我们的vhost吧
ifconfig_eth0_alias0="inet 10.0.0.179 netmask 255.255.255.255"
替换eh0为你的实际网卡设备名,如果你不知道可以用ifconfig命令查看
jail /vhost/jail/179 dns 10.0.0.79 /bin/sh
现在设置你的root密码吧passwd 输入新密码
vhost没有tty供你操作,你需要运行sysinstall,在用户管理中建立一个属于wheel组的用户,用来ssh登陆上去。然后设置一下时区是非常有必要的。
然后我们还需要为ssh准备一个key
/etc/rc.d/sshd start
然后回车,或者输入点什么东西再回车。
我们还需要将这个vhost中设置它的文件
/etc/hosts设置主机名
/etc/rc.conf 中加入
sshd_enable="YES"
sendmail_enable="NONE"
/etc/crontab中删除和adjkerntz 相关的内容。
/etc/resolv.conf 中设置你的dns服务器地址
格式为:nameserver IP地址
好了,输入exit退出单用户模式。回到主系统后修改主系统的rc.conf,加上以下信息
ifconfig_eth0_alias0="inet 10.0.0.179 netmask 255.255.255.255" jail_enable="YES" jail_list="dns" jail_dns_hostname="dns.test.com" jail_dns_ip="10.0.0.179" jail_dns_rootdir="/vhost/jail/179" jail_dns_exec="/bin/sh /etc/rc" jail_dns_devfs_enable="YES" jail_dns_devfs_ruleset="devfsrules_jail"
重新启动你的机器吧,当然如果你想继续你的uptime时间,你可以先输入init 1后,在提示行下输入exit来重新回到多用户模式。
dmesg -a | more来看看你的启动信息,如果你的jail和下面的信息相似,恭喜你。可以用ssh连接10.0.0.179来登陆你的jail了。
Starting jails:
- dns.test.com
.
Local package initialization:
还有点小技巧
1、 /etc/rc.d/jail这个命令可以用来开始,结束,重新启动你的jail,输入这个命令看看帮助吧
jls这个命令可以看现在正在运行的 jail的列表。试一下?
vhost1# jls
- JID IP Address Hostname Path
- 3 10.0.0.179 dns.test.com /vhost/jail/179
2、 删除jail
/etc/rc.d/jail stop dns
chflags -R noschg 179
rm -R 179就可以删除了
3、 在jail中使用ports
先在jail中建立ports目录,比如mkdir /usr/ports
在再主系统中执行mount_nullfs /usr/ports /vhost/jail/179/usr/ports
4、 如果需要同时运行多个jail,你的rc.conf应该这样配置
ifconfig_eth0_alias0="inet 10.0.0.179 netmask 255.255.255.255" ifconfig_eth0_alias0="inet 10.0.0.180 netmask 255.255.255.255" jail_enable="YES" jail_list="dns mail" jail_dns_hostname="dns.test.com" jail_dns_ip="10.0.0.179" jail_dns_rootdir="/vhost/jail/179" jail_dns_exec="/bin/sh /etc/rc" jail_dns_devfs_enable="YES" jail_dns_devfs_ruleset="devfsrules_jail" jail_mail_hostname="mail.test.com" jail_mail_ip="10.0.0.180" jail_mail_rootdir="/vhost/jail/180" jail_mail_exec="/bin/sh /etc/rc" jail_mail_devfs_enable="YES" jail_mail_devfs_ruleset="devfsrules_jail"
启动或者停止其中一个jail可以/etc/rc.d/jail start mail或者/etc/rc.d/jail stop dns来操作。
python
python调用com以及com事件
python调用有事件发生的com时,需要一些技巧。
我们以ie这个com为例来讲解一下我的经验。
首先我们需要pywin32这个模块的支持,它提供了我们调用com便利直接方法。你可以www.sf.net搜索并下载它。
先运行如下代码:
1 import win32gui
2 import win32com
3 import win32com.client
4 import pythoncom
5 import time
6
7
8 class EventHandler:
9
10 def OnVisible(self, visible):
11 global bVisibleEventFired
12 bVisibleEventFired = 1
13 def OnDownloadBegin(self):
14 print "DownloadBegin"
15 def OnDownloadComplete(self):
16 print "DownloadComplete"
17 def OnDocumentComplete(self, pDisp = pythoncom.Missing , URL = pythoncom.Missing):
18 print "documentComplete of %s" % URL
19
20 #这里用EventHandler类来处理ie中发生的事件,这里的函数名必须和事件名称一致。
21 ie = win32com.client.DispatchWithEvents("InternetExplorer.Application", EventHandler)
22 ie.Visible = 1
23 ie.Navigate("www.aawns.com")
24 #这里是等待事件的发生
25 pythoncom.PumpMessages()
26 ie.Quit()
我们看到,程序运行正常,能打开我们指定的站点,并各事件被触发后都能作出正确的反映。
但是假如我们希望在事件发生后,能调用我们继承下来的一些方法和属性。你会发现无法使用。
如下代码将展示这个例子
1 # -*- coding: cp936 -*-
2 import win32gui
3 import win32com
4 import win32com.client
5 import pythoncom
6 import time
7
8 class test:
9 def runtest(self):
10 print 'tuntest'
11
12 class EventHandler:
13
14 def OnVisible(self, visible):
15 global bVisibleEventFired
16 bVisibleEventFired = 1
17 def OnDownloadBegin(self):
18 print "DownloadBegin"
19 def OnDownloadComplete(self):
20 print "DownloadComplete"
21 def OnDocumentComplete(self, pDisp = pythoncom.Missing , URL = pythoncom.Missing):
22 print "documentComplete of %s" % URL
23 #在这里我们再调用test的runtest方法,看是否继承成功。
24 self.runtest()
25
26 class runcom(test):
27 def __init__(self):
28 ie = win32com.client.DispatchWithEvents("InternetExplorer.Application", EventHandler)
29 ie.Visible = 1
30 ie.Navigate("www.aawns.com")
31 #这里调用test的runtest方法,看是否继承成功。
32 self.runtest()
33 pythoncom.PumpMessages()
34 ie.Quit()
35 a=runcom()
运行结果是错误的。
tuntest DownloadBegin DownloadComplete DownloadBegin DownloadComplete documentComplete of http://www.aawns.com/ pythoncom error: Python error invoking COM method. Traceback (most recent call last): File "C:\Python23\Lib\site-packages\win32com\server\policy.py", line 283, in _ Invoke_ return self._invoke_(dispid, lcid, wFlags, args) File "C:\Python23\Lib\site-packages\win32com\server\policy.py", line 288, in _ invoke_ return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None) File "C:\Python23\Lib\site-packages\win32com\server\policy.py", line 550, in _ invokeex_ return func(*args) File "D:\python\test.py", line 24, in OnDocumentComplete self.runtest() File "C:\Python23\Lib\site-packages\win32com\client\__init__.py", line 454, in __getattr__ raise AttributeError, "'%s' object has no attribute '%s'" % (repr(self), att r) exceptions.AttributeError: '<win32com.client.COMEventClass instance at 0x1554295 2>' object has no attribute 'runtest'
我们看到test类的runtest方法并没有被继承进去,为什么呢?我的理解是因为com的事件模式让你无法继承python中的self,因为在调用ie的时候并不是用EventHandler的实例而是将这个类作为了事件处理的方法(不知道这里理解是否正确,如果有更好的理解。我们交流)
经过查找了很多资料和试探了很多方法,只有采用全局变量的方式才能在事件和各个类之间传递数据。代码变更成了这样
1 # -*- coding: cp936 -*-
2 import win32gui
3 import win32com
4 import win32com.client
5 import pythoncom
6 import time
7
8
9 class EventHandler:
10
11 def OnVisible(self, visible):
12 global bVisibleEventFired
13 bVisibleEventFired = 1
14 def OnDownloadBegin(self):
15 print "DownloadBegin"
16 #先继承全局变量增加一个字符串
17 global testlist
18 testlist.append("DownloadBegin")
19 def OnDownloadComplete(self):
20 print "DownloadComplete"
21 #先继承全局变量增加一个字符串
22 global testlist
23 testlist.append("DownloadComplete")
24 def OnDocumentComplete(self, pDisp = pythoncom.Missing , URL = pythoncom.Missing):
25 print "documentComplete of %s" % URL
26 #先继承全局变量再打印
27 global testlist
28 print testlist
29
30 class runcom:
31 def __init__(self):
32 global testlist
33 ie = win32com.client.DispatchWithEvents("InternetExplorer.Application", EventHandler)
34 ie.Visible = 1
35 ie.Navigate("www.aawns.com")
36 #打印全局变量
37 print testlist
38 pythoncom.PumpMessages()
39 ie.Quit()
40 testlist=[]
41 a=runcom()
可以看到,用全局变量的方式解决了于事件内传输数据的问题。
没有解决的问题:使用Twisted的时候也有相应的事件,如何保证Twisted 和com中的事件都被触发?
使用python写nt服务
如果我们想让系统启动的时候就执行某个程序,windows系统和unix系统是不一样的,对于unix只需要将要执行的命令放到rc.local中,系统重新启动的时候就可以加载了。windows就麻烦多了,如果你将程序放到启动组中,只有输入了密码后,程序才被执行,如果想在系统一启动的时候就执行程序,必须使用nt服务。 python下如何使用nt服务,其实很简单。 下载python的win32支持。我使用的是:pywin32-202.win32-py2.3.exe安装好后就可以来写我们的服务了。 我们先来建立一个空的服务,建立test1.py这个文件,并写入如下代码:
1 # -*- coding: cp936 -*-
2 import win32serviceutil
3 import win32service
4 import win32event
5
6 class test1(win32serviceutil.ServiceFramework):
7 _svc_name_ = "test_python"
8 _svc_display_name_ = "test_python"
9 def __init__(self, args):
10 win32serviceutil.ServiceFramework.__init__(self, args)
11 self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
12
13 def SvcStop(self):
14 # 先告诉SCM停止这个过程
15 self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
16 # 设置事件
17 win32event.SetEvent(self.hWaitStop)
18
19 def SvcDoRun(self):
20 # 等待服务被停止
21 win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
22
23 if __name__=='__main__':
24 win32serviceutil.HandleCommandLine(test1)
这里注意,如果你需要更改文件名,比如将win32serviceutil.HandleCommandLine(test1)中的test1更改为你的文件名,同时class也需要和你的文件名一致,否则会出现服务不能启动的问题。 在命令窗口执行,test1.py可以看到帮助提示
C:\>test1.py Usage: 'test1.py [options] install|update|remove|start [...]|stop|restart [...]| debug [...]' Options for 'install' and 'update' commands only: --username domain\username : The Username the service is to run under --password password : The password for the username --startup [manual|auto|disabled] : How the service starts, default = manual --interactive : Allow the service to interact with the desktop. C:\>
安装我们的服务
C:\>test1.py install Installing service test_python to Python class C:\test1.test1 Service installed C:\>
我们就可以用命令或者在控制面板-》管理工具-》服务中管理我们的服务了。在服务里面可以看到test_python这个服务,虽然这个服务什么都不做,但能启动和停止他。 下面我们来写个复杂点的服务。 改天写,要下班了。
使用python来发扑克牌
在QQ群里面出了个题目,发52张扑克牌到4个人,要随机,速度足够快。我写了一个,请大家指正。
1 #!/usr/local/bin/python --
2 # -*- coding: cp936 -*-
3 import random,time
4
5 class timer:
6 def __init__(self):
7 self.start= time.time()
8 def stop(self):
9 self.end= time.time()
10 return "\n 本次运行已用时 %s秒"% (self.end-self.start)
11
12 class people:
13 #定义四个玩牌的人
14 jack = []
15 python = []
16 java = []
17 stephen = []
18 def print_jack(self):
19 i=0
20 tmp = ''
21 while i < 13:
22 tmp += self.jack[i]+' '
23 i += 1
24 return tmp
25 def print_python(self):
26 i=0
27 tmp = ''
28 while i < 13:
29 tmp += self.python[i]+' '
30 i += 1
31 return tmp
32 def print_java(self):
33 i=0
34 tmp = ''
35 while i < 13:
36 tmp += self.java[i]+' '
37 i += 1
38 return tmp
39 def print_stephen(self):
40 i=0
41 tmp = ''
42 while i < 13:
43 tmp += self.stephen[i]+' '
44 i += 1
45 return tmp
46
47 class agent(people):
48 def __init__(self):
49 #定义一个空的列表存放牌
50 self.card = []
51 #定义洗牌的次数,并不是越大牌就越乱
52 self.count = 52
53 #定义一个基本的牌序
54 self.base = ['A','2','3','4','5','6','7','8','9','10','J','Q','K']
55
56 def arrangement(self):
57 #重新排列纸牌
58 i = 0
59 while i < 13:
60 self.card.insert(0,'黑桃'+self.base[i])
61 i += 1
62 i = 0
63 while i < 13:
64 self.card.insert(0,'红桃'+self.base[i])
65 i += 1
66 i = 0
67 while i < 13:
68 self.card.insert(0,'草花'+self.base[i])
69 i += 1
70 i = 0
71 while i < 13:
72 self.card.insert(0,'方块'+self.base[i])
73 i += 1
74
75 def shuffle(self):
76 #洗牌
77 i = 1
78 while i < self.count:
79 #产生一个随机数来确定要交换牌的位置
80 position = random.randint(0,51)
81 #临时取出一张牌准备用来交换位置
82 tmp = self.card[position]
83 #交换随机位置的牌和第一张牌
84 self.card[position] = self.card[0]
85 self.card[0] = tmp
86 i += 1
87
88 def outcards(self):
89 #发牌
90 i = 0
91 while i < 52:
92 if i <= 12:
93 self.jack.insert(0,self.card[i])
94 elif i >= 12 and i <= 25 :
95 self.python.insert(0,self.card[i])
96 elif i >= 25 and i <= 38 :
97 self.java.insert(0,self.card[i])
98 elif i >= 38 and i <= 52 :
99 self.stephen.insert(0,self.card[i])
100 i += 1
101
102 start=timer()
103 a=agent()
104 a.arrangement()
105 a.shuffle()
106 a.outcards()
107 print a.print_jack()
108 print a.print_python()
109 print a.print_java()
110 print a.print_stephen()
111 print start.stop()
另外,0.00999999046326好象是最小计数了,再小就计算成0秒了。
如何打印对象的方法和属性
有时候我们需要让对象可以打印来方便调试,可以重载对象的str方法来实现。
class header: """dns消息头抽象类""" def __init__(self, qid = 1): #初始化消息头 self.qid = qid #DNS 查询封包编号,作为确认依据。长度为16 byte self.qr = 0 #查询封包为 0 ﹔回应为 1 。长度为 1 byte self.opcode = 0 #封包类别(QUERY, IQUERY, STATUS, Reserved)。长度为 4 bytes。 self.aa = 0 #Flags共 4 bytes ,各表示:AA(Authoritative Answer)、TC(Truncation)、RD(Recursion Desired)、RA(Recursion Avalable)。 self.tc = 0 self.rd = 1 self.ra = 0 self.reserved = 0 #保留未用。 self.rcode = 0 #回应讯息,长 4 bytes ,除 0 及 6-15 保留未用外,1-5 分别为:Format Error、Server Failure、Name self.qsection = 1 #问题部分,只支持1。 self.ansection = 0 #答案部分。 self.ausection = 0 #权力部分。 self.arsection = 0 #另外的部分。 def __str__(self): """字符串化""" if len(self.__dict__) > 0: plist = [] for field in self.__dict__: plist.append(str(field) + ":" + str(self.__dict__[field])) return reduce(lambda x,y: x + "\n" + y, plist) else: return "" test=header() print test
使用Twisted实现一个简单Web服务器
作者:梅劲松 版权:本文档为MIT授权 运行环境:Python 2.3+Twisted的py-23安装版本
自己实现Web服务器的优点就不用说太多了,主要是能控制具体的实现。也能按照自己的习惯实现互动方式。
而Twisted在tcp以下是C写的,ip和udp部分应该是C和Python的混合产物,而http smtp等则是Python的,自己能很好的扩充。
下面来看个具体的例子:
首先你需要编辑一个html为结尾的文件名放到你的htm目录下。
然后在htm的上一级目录建立一个文件,文件名为web.py,内容如下:
代码:
PORT = 80#这个是80,如果你的端口被占用了,换成其他的 from twisted.web.resource import Resource from twisted.web import server from twisted.web import static from twisted.internet import reactor class ReStructured( Resource ): def __init__( self, filename, *a ): self.rst = open( filename ).read( ) def render( self, request ): return self.rst resource = static.File('./htm/') resource.processors = { '.html' : ReStructured } resource.indexNames = [ 'index.html'] reactor.listenTCP( PORT, server.Site( resource ) ) reactor.run( )
在控制台下进入目录输入 python web.py,然后打开浏览器,输入http://127.0.0.1,看到你的站点了吗?
sip
用sip服务器实现msn企业内部交流
winxp里面的windows messager可以登陆企业自己的sip服务器,实现企业内部交流。
你可以在这里下载berkeke OnDO SIP Server 1.2
http://www.brekeke-sip.com/download/oss/dl/oss1_2_7_4.exe
如果不能下载,请自己想办法。这个软件确实可下,只不过被国际出口上锁住了。
配置berkeke server的方法在QQ群6390838里的朋友苏醒的帮助下,终于成功了。
在服务里启动server后,进入http://localhost:18080/oss/ 页面,用sa,sa登录后,选择Authentication栏目,在右边的edit框里填写用户的名称
User
Password
(Confirm)
Name
Email Address
Description
接着按"add"按钮,有Update succeeded. 字样。然后在windows messenger里的服务器ip写你的服务器ip,比如192.168.3.135,可以不加端口号5060。还要把连接方式改为udp(你连接不上,可能是你没改协议的原因)。可以不写用户名。然后登录,在登录的时候写你的User名。
这段时间准备用python写一个sip服务器提供给windows messager用,有兴趣的朋友可以和我联系。
反馈
- 看起来很不错,不过有没有对于sip服务更详细的东西?还有你的nt服务的下一章还没开始啊。 -- Dreamingk