前几天突然想做一个信件分析并自动分发的程序。先给服务器设置了域名MX记录,开放了端口,设置了收信专用账户。然后准备开始分析邮件。

发现邮件在服务器上是在一个文件中连续存储的。打开/var/spool/mail/username可以发现里面顺序存储了很多邮件。那么把这些邮件拆开就成了问题。Mail命令可以分析并分拆邮件,但是mail命令有自己的命令行,用shell的方法对其操作很麻烦。

然后决定看一下邮件的协议,看看邮件的规范是什么,从中找出特征值来分隔。后来发现各个邮件服务器发送的邮件形状各异,很难找到一些共性。而邮件规范RFC822并没有定义太严格的邮件格式,只是定义邮件头和邮件正文由换行隔开。(
)。qq,163的邮件都是中规中矩的,很好识别。但是学校的邮件服务器发送的邮件就惨不忍睹。想了一天,也没有想出比较好的方法。

上面只是问题一,还有一个问题就是文件同步互斥问题。如何保证读到的文件的完整性、sendmail在写入文件的策略是什么,都不太清楚。

由于有了上面的问题,想到系统内置的mail命令应该是比较完美解决这些问题的,于是决定看一下mail的实现方法。
首先查到mail命令从属的软件包是mailx。版本是:mailx-8.1.1-44.2.2。从网上找到mailx-8.1.1-44.2.2.src.rpm 下载下来。运行:rpm -ivh mailx-8.1.1-44.2.2.src.rpm。这样,源码就被解压到了/usr/src/redhat/下。该文件夹下有两个目录,一个是SOURCE,一个是SPECS。其中SOURCE下就是补丁和源码。SPECS里是打包rpm的一个工具文件。

到SOURCE目录下,发现有一个mailx-8.1.1.tar.gz文件,一堆补丁,和一个.c文件。注意要先打补丁,然后再编译、查看源码。我刚开始的时候直接解开tar.gz开始弄,发现里面的Makefile都没法通过编译。自己写Makefile编译后还有bug。这是因为没有打补丁的缘故。

下面说一下打补丁。这个时候要借助SPECS里面的文件。首先把tar.gz解开。解出的mailx-8.1.1文件夹和补丁们放在同一个目录下。然后去mailx.spec里看,有一句:Source1: flock.c。说明SOURCE下的flock.c文件是后来补的源文件,需要放到mailx-8.1.1里面。下面接着是:
Patch0: mailx-8.1.1.debian.patch
Patch1: mailx-8.1.1.security.patch
Patch2: mailx-8.1.1.nolock.patch
Patch3: mailx-8.1.1.debian2.patch
Patch4: mailx-noroot.patch
Patch5: mailx-nopanic.patch
Patch6: mailx-nullchar.patch
Patch7: mailx-8.1.1-fhs.patch
Patch8: mailx-8.1.1-environ.patch
Patch9: mailx-8.1.1-siglj.patch
Patch10: mailx-8.1.1-bug15728.patch
Patch11: mailx-8.1.1-bug10074.patch
Patch12: mailx-8.1.1-uidcheck.patch
Patch13: mailx-8.1.1-flock.patch
Patch14: mailx-8.1.1-nostrip.patch
Patch15: mailx-8.1.1-ctime.patch
Patch16: mailx-8.1.1-bug134837.patch
Patch17: mailx-8.1.1-manpage-fix.patch
Patch18: mailx-8.1.1-manfix.patch
Patch19: mailx-8.1.1-display.patch
Patch20: mailx-8.1.1-bug44798.patch
Patch21: mailx-8.1.1-bug58672.patch
Patch22: mailx-8.1.1-reproblem.patch
Patch23: mailx-8.1.1-mbproblem.patch
Patch24: mailx-8.1.1-unread.patch
这个是打补丁的顺序。需要用:patch -p0 < *****.patch 挨个执行一遍。(-p0具体含义可查patch用法,和目录层级有关系。假如把补丁文件和源文件放到一起就是-p1)
这么多补丁要是手动去打的话很累。我直接写了个脚本,按列表挨个执行一遍打补丁操作。
打完补丁后再去Makefile里看,会发现现在Makefile已经完整了,直接make就可以编译。这时要注意,通过打补丁,有的源文件已经不再被需要。dotlock.c被no_dot_lock.c替换了。在读源码的时候要注意这一点。假如想使用dotlock.c的话可以修改Makefile,不过还要注意dotlock.c在包含头文件的时候#include "extern.h"和#include "rcv.h"这两句反了,要对调一下才能通过编译。

现在开始读源码,从main.c开始,先是进行一些校验活动,临时文件的创建,参数读取。在265行if (setfile(ef) < 0)调用setfile开始对系统邮箱进行处理。

去lex.c找到setfile,也是一些初始化、校验过程后。156行setptr(ibuf);调用setptr函数,这个函数是分析系统邮箱的过程。

去fio.c,找到setptr函数。115到151行是重点之所在。判断逻辑是:假如一行的开头是From并且后面是一个空格,那么这是一封邮件的开始。向下移动到第一个空行。到达邮件正文段。直到遇到下一封邮件头。

在mail处理系统邮箱的时候会用flock/fcntl把文件加锁,解决了互斥问题。

这里要注意,邮件开头是From 空格 其他值,而不是邮件头里的From字段,两者相差一个冒号。我刚开始以为mail规定From字段必须在第一行,跑去查RFC822,结果那里说不强制次序。分析收到的邮件发现确实都是From开头的。过了好一会才发现并不是From字段。。。

下面又会疑惑了,这个From开头的行是哪里来的?邮件的发送方并没有发送这个字段。况且规范并没有规定这个,即使有的邮件服务器这样发送,一定会有不太规范的邮件服务器不这样发送。这样会出问题。经过分析有可能是sendmail在收邮件的时候给加上的。

下载sendmail的源码。阅读README,里面讲到mail.local文件夹下是负责向用户系统邮箱投递邮件的模块。进去后发现mail.local.c文件。在784行,(void) fprintf(fp, "From %s %s", from, ctime(&tval)); 显然,sendmail在投递邮件的时候会先写一行,结构是:From 空格 寄信人 空格 时间。这就保证了每封邮件都是From 空格开始了。我们可以放心的用这一特征来分析了。

mail.local.c里还可以发现,在写入文件前也是加了锁的。所以我们在读邮箱时也要加锁,假如加锁成功,那么可以保证我们分析文件与sendmail投递互斥进行。

不过需要注意一点。假如分析邮件的过程比较长,那么需要把邮件先移动到一个临时文件里然后分析。避免长时间加锁邮箱导致sendmail发信队列积压。并且移动完后一定不要忘了给邮箱解锁。

以上就是这几天阅读代码的一个小总结,欢迎拍砖。网上关于sendmail和mail的原理说的很少,大部分都是讲配置的。期待有深度分析的文章出现~~
由于邮件是串行放在文件里的,如何把一个一个的邮件分拆开就成了一个学问,想了一天,好多方案,都不大好。无语了,下了mailx的源码,研究一下,看看它是怎么分析出邮件数的。
今天搞到了个qq企业邮箱的邀请码,于是把我的域名邮箱迁移到了企业邮箱上,突发奇想,想让我的vps也当当邮件服务器,设一个二级域名的mx记录上去。

然后突然疑惑了,smtp是用来发邮件给其他服务器的,那接受其他服务器发过来的邮件怎么办呢?貌似pop3是接邮件的,但是一查又怪怪的。

后来终于搞懂了,smtp是服务器间传送邮件用的。pop3/imap是用户从服务器上取邮件用的。我只是想收邮件,并不用Foxmail等软件从服务器上取,所以不用装pop3服务器。

所以系统自带的sendmail就可以了。

sendmail默认是只监听本地端口的,这时只能向外网发邮件,不能接收外网发过来的邮件,我们需要修改配置文件。

在CentOS下/etc/mail/sendmail.mc,修改此文件,把DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA') 注释掉。

然后运行:m4 /etc/mail/sendmail.mc < sendmail.cf

这个时候可能会提示找不到*****cf文件。原因是没有装sendmail-cf这个软件包,装上即可

然后重启sendmail。

这个时候美滋滋的试着发邮件给vps,结果被退信了:said: 550 5.7.1 ... Relaying denied (in reply to RCPT TO command)

查了下,是由于/etc/mail/local-host-names 里没有配置服务器的域名,比如我想接收**@abc.com,那么在这个里面就要写上:abc.com

然后重启服务器。

再发,还是不行,告知User unknown 。这个问题是由于收件人不存在造成的,以收件人的名字新建一个用户即可。

再试,成功了。

还有一个地方,就是/etc/mail/access,这个文件是规定哪些ip可以使用这个smtp服务器发邮件,我并不想远程使用smtp发邮件,并且为了安全性考虑,这个文件保持不动,这样就只能localhost账户发送邮件出去了。

最近几天晚上都搞到两三点睡,编译python的ssl模块,按网上的方法有64位兼容问题。自己编译python却不知道编译出来的模块放到哪里了,并且是32位程序。算了,不搞了,上传的时候切到2.7上传。那个带ssl

同租小伙和p2p终结者

[| 2010/09/11 16:09]
网费我交了一半,每周六天半不用网,这两天看了两集红楼梦而已,就开p2p终结者阴我,阴我也就阴了,你给我限速50,100也行啊,完全把我的网断掉了。一抓包发现有arp攻击,可以认定局域网内有arp病毒,再查看一下网页错误页面是转到p2p终结者的一个url。可以排除是他不小心中毒。

刚开始嗅探出路由器mac后绑定,发现还是不行。于是直接改ip,改的和他同一个ip。。能上网了,不过貌似这个解决方法也不怎么好。

上网查了下,原来p2p终结者还能欺骗路由器,于是登录路由器,嘿嘿,小伙的路由器密码都是默认值。然后在路由器给我的mac绑定了个固定ip,再在我的电脑上绑定路由器mac,并且设置ip为那个固定ip。

应该可以了,继续看红楼梦。新版红楼梦不错。

gae学习及使用

[| 2010/09/10 09:27]
昨天看了看GAE,在公司装,老卡住,xp虚拟机装,也是卡。回去后在笔记本上装,不卡了,估计是公司网络问题。

上传项目试了试,可以传,但是报没有ssl库,心想还是装个python ssl库吧,安全性高一点。下下来库的源码,一编译,告诉要linux编译器,还好我又dev-cpp,再编译,还不行。说要几个库的developer tool,下下来两个代码包,还不行,有说缺少libpython25.a,需要自己编译。

编译libpython25.a,需要从python25.dll中导出符号表然后********,结果导出符号表的那个工具在64bit系统下崩溃。自己编译也不行。

搞到三点多,搞不定。睡觉。


今天早晨走的时候发现运行中的空调不能用遥控器关掉了。。。蛋疼,只好拔插头,晚上回去看看是什么毛病。

open flash chart学习

[| 2010/09/09 00:50]
最近学学open flash chart。下了个2.0版,结果一运行发现自带的例子大多不能正常使用。。网上的资料都是一大抄,不靠谱。

先慢慢研究了一下,软件包里的文件放的非常乱,并且没有显著的文档告诉哪个文件夹是干什么的。摸索了半天,终于入了点门。后来发现例子里的路径都是错的,改了一通,有一个例子可以了,剩下的还是不行。总是报SWFObject未定义。大为蛋疼,上网搜了下,不知为何。

后来发现原来是因为新版SWFObject库不支持new SWFObject这种用法了。。open flash chart 自带的SWFObject库是新版的,而例子都是用的旧版的用法。。。。。。怪不得不好使。

现在总算弄懂一点了。对于flash怎么获取数据还是晕晕乎乎的,官方例子一运行就报这个错那个错。。都找不到一个可以完美运行的例子。蛋疼

心情大坏

[| 2010/09/08 20:51]
rt
假如一段gbk编码的文字在插入数据库的时候set names latin1了,那么要在utf8格式网页上显示,应该怎么弄?

在这里数据库的编码是不重要的,只需set names latin1,然后读出来再用iconv从gbk转到utf8即可。

在期间其实数据库里存储的是“乱码”,只不过显示的时候编码正确而已。

假如gbk文字以utf8方式插进去了呢?那就需要以utf8方式读出来,再从gbk转到utf8。假如页面是big5,那就从gbk转到big5。

假如gbk文字以gbk方式插进去了呢?那就utf8方式读出来,直接显示。假如页面是big5,那就big5方式读出来,直接显示。这个期间所有的地方都不是乱码。

现在难度提升。

插入的时候不变。读出的时候set names big5。显示的时候想显示成utf8,怎么办?

答案是从big5转到latin1,再从gbk转到utf8显示。

注:从**转到**指的是把字符串当成**编码,将其转成**编码。

这个不好理解了吧,解释一下。

当插入的时候,我们其实在欺骗服务器,我们告诉服务器我们这个是latin1字符串,于是服务器就按latin1的方式理解了我们的字符串。理解后,将其转换成同含义的数据库字符集的串存起来。当我们取的时候,我们说:“请把数据库里的数据发一个big5编码的版本给我。”服务器就把自己存的那个含义转换成big5版本给我们。

这个时候,我们从big5转换成latin1,句子的含义不变。也就是说这个串就是我们当初给服务器的那个串。现在把这个串当成gbk来理解才会有正确的意思。所以我们把它当成gbk,转换成utf8,期间含义不变,我们得到了正确的字符串。


上面的讲解可能比较抽象,下面以小故事的形式来讲一下:小明会说法语,小丽会各种语言,小兰会说英语,浏览器只会说葡萄牙语。

小明用法语写了“我爱吃饭”,然后给了小丽,小丽以为是个荷兰语写的条子,于是按荷兰语的方式理解,她以为条子上写的是“天气不错”,于是在自己的本子上用意大利语写了“天气不错”。小兰很八卦,她跑过来问小丽:刚才小明写什么了?小丽用英语给她一个条子:“天气不错”。现在我们要想让浏览器正确理解小明说的什么,怎么办?

小兰现在知道小明说的那句话用荷兰语来理解的话是“天气不错”的意思。她又知道小明其实说的是法语。于是她认为第一步要先把小明写的那个条子还原。于是,她通过查字典,用荷兰语写了个条子。现在这个条子上写的东西和当初小明写的是一模一样的了。然后,她找了语言老师,请老师把这个法语条子改写成葡萄牙语的版本,并让老师转交给浏览器。浏览器拿到条子一看,写的是“我爱吃饭”。

在这里面,小明是写入时的客户端,小丽是数据库,小兰是读出时的客户端。浏览器是浏览器。

虽然浏览器正确的理解了小明说的话,但小丽和小兰并不知道小明到底说了什么。
刚开始按网上的方法弄的时候总是在运行Python的时候显示没有Python运行时库和pips库,后来搜了下,要装上s60_open_c插件才行。装了后可以使用了。
分页: 20/31 第一页 上页 15 16 17 18 19 20 21 22 23 24 下页 最后页 [ 显示模式: 摘要 | 列表 ]