分享
 
 
 

qmail源代码分析之qmail-pop3d

王朝other·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

PRogrammer:夜未眠

Comefrom: ChongQing Gearbox co.,ltd

关键数据结构

队列: prioq

这个数据结构在很多qmail很多程式中都有用到,最好记下来

struct prioq_elt {

datetime_sec dt;//时间戳,优先级

unsigned long id;//邮件唯一id,你可以把它同qmail-queue分析中介绍中pid文件inode联系起来

} ;

prioq在prioq.h中prioq是这样定义的

GEN_ALLOC_typedef(prioq,struct prioq_elt,p,len,a)

展开后实际上定义为

typedef struct prioq

{

struct prioq_elt *p; // 指针

unsigned int len; //队列的长度

unsigned int a;

}prioq;

[/code:1:c3936d2617]

消息块: message 我把它叫作消息块是因为他并不包含消息内容,也许这样称呼它并不确切 [code:1:c3936d2617]

struct message {

int flagdeleted; //删除标记,在qmail-pop3d程式退出时进行实际删除动作

unsigned long size; //消息文件大小

char *fn; //消息文件名

} *m;

[/code:1:c3936d2617]

主要功能:

qmail-pop3d是则vchkpw或checkpassWord之类的程式启动的。这些程式(vchkpw)会更改环境变量USER,

HOME,SHELL等等,并在启动qmail-pop3d前将工作目录改变到$HOME下.

qmail-pop3d在启动时首先检查./Maildir/tmp(./Maildir是在argv中指定的)下最后访问时间超过36小

时的文件,如果存在就将其删除。也正是由于qmail-pop3d在启动时就有chdir的动作,所以qmail-pop3d

不支持mailbox形式的pop.

扫描Maildir/cur及Maildir/new目录构造一个消息块数组 m(首先是构造一个临时队列pq,然后根据这个队列

来构造消息块数组),输出+OK,进入命令循环,等待用户输入pop命令进行相应的处理.具体见代码分析.

[code:1:c3936d2617]

void die() { _exit(0); }

//超时读,超时时间为20分钟,正常返回读到的字节数,否则程式失败die()

int saferead(fd,buf,len) int fd; char *buf; int len;

{

int r;

r = timeoutread(1200,fd,buf,len);

if (r

return r;

}

//超时写,超时时间为20分钟,正常返回写的字节数,否则程式失败die()

int safewrite(fd,buf,len) int fd; char *buf; int len;

{

int r;

r = timeoutwrite(1200,fd,buf,len);

if (r

return r;

}

/*定义ssout为向fd1写,超时时间为20分钟

定义ssin为从fd0读,超时时间为20分钟

由于tcpserver或inetd已经重定向了fd1,fd0到网络,所以这就

等同于向网络读写*/

char ssoutbuf[1024];

substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);

char ssinbuf[128];

substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);

void put(buf,len) char *buf; int len;

{

substdio_put(%26amp;ssout,buf,len);//将buf缓存中的内容向网络写

}

void puts(s) char *s;

{

substdio_puts(%26amp;ssout,s);//将s的内容向网络写,这个函数实际上是调用的substdio_put

}

void flush() //确保输出缓存中已经没有内容。

{

substdio_flush(%26amp;ssout);

}

void err(s) char *s;

{

puts("-ERR ");

puts(s);

puts("\r\n");

flush();

}

//错误处理函数

void die_nomem() { err("out of memory"); die(); }

void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); }

void die_scan() { err("unable to scan $HOME/Maildir"); die(); }

void err_syntax() { err("syntax error"); }

void err_unimpl() { err("unimplemented"); }

void err_deleted() { err("already deleted"); }

void err_nozero() { err("messages are counted from 1"); }

void err_toobig() { err("not that many messages"); }

void err_nosuch() { err("unable to open that message"); }

void err_nounlink() { err("unable to unlink all deleted messages"); }

void okay() { puts("+OK \r\n"); flush(); }

void printfn(fn) char *fn;

{

fn += 4;

put(fn,str_chr(fn,':'));

}

char strnum[FMT_ULONG];

stralloc line = {0};

void blast(ssfrom,limit)//从ssfrom读数据输出到fd1,一次一行(用全局缓存line)

substdio *ssfrom;

unsigned long limit;//除开消息头部信息,最多读limit行,limit为0将全部读完

{

int match;

int inheaders = 1;

for (;

{

if (getln(ssfrom,%26amp;line,%26amp;match,'\n') != 0) die();

if (!match %26amp;%26amp; !line.len) break;

if (match) --line.len; /* no way to pass this info over POP */

if (limit) if (!inheaders) if (!--limit) break;

if (!line.len)

inheaders = 0;

else

if (line.s[0] == '.')

put(".",1);

put(line.s,line.len);

put("\r\n",2);

if (!match) break;

}

put("\r\n.\r\n",5);

flush();

}

stralloc filenames = {0};

prioq pq = {0};

struct message {

int flagdeleted; //删除标记,在程式退出时进行实际删除动作

unsigned long size; //文件大小

char *fn; //文件名

} *m;

int numm;//全局变量记录队列长度

int last = 0;

void getlist()

{

struct prioq_elt pe;

struct stat st;

int i;

maildir_clean(%26amp;line);//清除Maildir/tmp/目录下最后访问时间超过 36小时的文件

if (maildir_scan(%26amp;pq,%26amp;filenames,1,1) == -1) die_scan();

numm = pq.p ? pq.len : 0; //记录下队列长度

//通过队列pq构造消息块数组,构建结束后队列pq删除

m = (struct message *) alloc(numm * sizeof(struct message));//分配消息块

if (!m) die_nomem();

for (i = 0;i

if (!prioq_min(%26amp;pq,%26amp;pe)) { numm = i; break; }

prioq_delmin(%26amp;pq);

m[i].fn = filenames.s + pe.id;

m[i].flagdeleted = 0;

if (stat(m[i].fn,%26amp;st) == -1)

m[i].size = 0;

else

m[i].size = st.st_size;

}

}

void pop3_stat() //打印类似 +OK

{ //如 +OK 3 3555表示总共有3条消息,占用空间3555(通过stat取得的)

int i;

unsigned long total;

total = 0;

for (i = 0;i

puts("+OK ");

put(strnum,fmt_uint(strnum,numm));

puts(" ");

put(strnum,fmt_ulong(strnum,total));

puts("\r\n");

flush();

}

void pop3_rset()//重置pop对话,清除所有删除标记

{

int i;

for (i = 0;i

last = 0;

okay();

}

void pop3_last()//显示最后一个消息块

{

puts("+OK ");

put(strnum,fmt_uint(strnum,last));

puts("\r\n");

flush();

}

void pop3_quit()//结束一次pop对话,删除所有删除标记设置的消息,将new下的消息移到cur下

{

int i;

for (i = 0;i

if (m[i].flagdeleted) {

if (unlink(m[i].fn) == -1) err_nounlink();

}

else

if (str_start(m[i].fn,"new/")) {

if (!stralloc_copys(%26amp;line,"cur/")) die_nomem();

if (!stralloc_cats(%26amp;line,m[i].fn + 4)) die_nomem();

if (!stralloc_cats(%26amp;line,":2,")) die_nomem();

if (!stralloc_0(%26amp;line)) die_nomem();

rename(m[i].fn,line.s); /* if it fails, bummer */

}

okay();

die();

}

//检查消息块是否存在。或消息块的删除标记是否已经设置了

//成功返回消息块的位置int型

//失败返回-1

int msgno(arg) char *arg;

{

unsigned long u;

if (!scan_ulong(arg,%26amp;u)) { err_syntax(); return -1; }

if (!u) { err_nozero(); return -1; }

--u;

if (u = numm) { err_toobig(); return -1; }

if (m[u].flagdeleted) { err_deleted(); return -1; }

return u;

}

void pop3_dele(arg) char *arg;//将arg指定消息块设置删除标记,实际删除动作将在pop3退出时进行

{

int i;

i = msgno(arg);

if (i == -1) return;

m[i].flagdeleted = 1;

if (i + 1 last) last = i + 1;

okay();

}

void list(i,flaguidl)

int i;

int flaguidl;

{//显示消息块的内容,如果flaguidl设置,输出消息文件名,否则消息大小

put(strnum,fmt_uint(strnum,i + 1));

puts(" ");

if (flaguidl) printfn(m[i].fn);

else put(strnum,fmt_ulong(strnum,m[i].size));

puts("\r\n");

}

//如果指定了参数arg那么列出arg指定的消息块的内容,否则列出全部消息

void dolisting(arg,flaguidl) char *arg; int flaguidl;

{

unsigned int i;

if (*arg) {

i = msgno(arg);

if (i == -1) return;

puts("+OK ");

list(i,flaguidl);

}

else {

okay();

for (i = 0;i

if (!m[i].flagdeleted)

list(i,flaguidl);

puts(".\r\n");

}

flush();

}

void pop3_uidl(arg) char *arg; { dolisting(arg,1); }

void pop3_list(arg) char *arg; { dolisting(arg,0); }

substdio ssmsg; char ssmsgbuf[1024];

void pop3_top(arg) char *arg;//显示指定消息的内容

{

int i;

unsigned long limit;

int fd;

i = msgno(arg);//邮件号

if (i == -1) return;

arg += scan_ulong(arg,%26amp;limit);//显示几行,如果未指定那么limit为0(balst函数打印全部内容)

while (*arg == ' ') ++arg;

if (scan_ulong(arg,%26amp;limit)) ++limit; else limit = 0;

fd = open_read(m[i].fn);

if (fd == -1) { err_nosuch(); return; }

okay();

//关系ssmsg为从指定的消息文件中读

substdio_fdbuf(%26amp;ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));

//从ssmsg中读到fd1,如果limit大于0将只读取除消息头外的limit行,如果等于0读全部邮件

blast(%26amp;ssmsg,limit);

close(fd);

}

struct commands pop3commands[] = { //pop3命令及处理函数表

{ "quit", pop3_quit, 0 }

, { "stat", pop3_stat, 0 }

, { "list", pop3_list, 0 }//显示消息大小

, { "uidl", pop3_uidl, 0 }//显示消息文件名

, { "dele", pop3_dele, 0 }

, { "retr", pop3_top, 0 }//取一条消息的内容,与top实现是一样的

, { "rset", pop3_rset, 0 }//重置pop对话,清除所有删除标记

, { "last", pop3_last, 0 }

, { "top", pop3_top, 0 }

, { "noop", okay, 0 }

, { 0, err_unimpl, 0 }

} ;

/*qmail-pop3d由vchkpw或checkpassword之类的程式起动,只有认证通过后才能

执行本程式提供各种pop3命令

*/

void main(argc,argv)

int argc;

char **argv;

{

sig_alarmcatch(die);

sig_pipeignore();

if (!argv[1]) die_nomaildir();

//由于vchkpw或checkpassword之类的程式在启动pop3之前已经将工作目录改变到HOME下了.

//所以这里直接进入arg指定的Maildir目录.也是由于这个改变目录原因。qamil-pop3d不支持Mailbox.

if (chdir(argv[1]) == -1) die_nomaildir();

getlist(); //这里构造了我们前面提到了消息块数组*m

okay();

//进入命令循环

commands(%26amp;ssin,pop3commands);

die();

}

,

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有