首页 | 邮件资讯 | 技术教程 | 解决方案 | 产品评测 | 邮件人才 | 邮件博客 | 邮件系统论坛 | 软件下载 | 邮件周刊 | 热点专题 | 工具
网络技术 | 操作系统 | 邮件系统 | 客户端 | 电子邮箱 | 反垃圾邮件 | 邮件安全 | 邮件营销 | 移动电邮 | 邮件软件下载 | 电子书下载

邮件服务器

技术前沿 | Qmail | IMail | MDaemon | Exchange | Domino | 其它 | Foxmail | James | Kerio | JavaMail | WinMail | Sendmail | Postfix | Winwebmail | Merak | CMailServer | 邮件与开发 | 金笛 |
首页 > 邮件服务器 > Qmail > qmail源代码分析之qmail-pop3d > 正文

qmail源代码分析之qmail-pop3d

出处:夜未眠 作者:夜未眠 时间:2005-9-1 13:44:00
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 <= 0) die();
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 <= 0) die();
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(&ssout,buf,len);//将buf缓存中的内容向网络写
}
void puts(s) char *s;
{
substdio_puts(&ssout,s);//将s的内容向网络写,这个函数实际上是调用的substdio_put
}
void flush() //确保输出缓存中已经没有内容。
{
substdio_flush(&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,&line,&match,'\n') != 0) die();
if (!match && !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(&line);//清除Maildir/tmp/目录下最后访问时间超过 36小时的文件
if (maildir_scan(&pq,&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 < numm;++i) {
if (!prioq_min(&pq,&pe)) { numm = i; break; }
prioq_delmin(&pq);
m[i].fn = filenames.s + pe.id;
m[i].flagdeleted = 0;
if (stat(m[i].fn,&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 < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
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 < numm;++i) m[i].flagdeleted = 0;
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 < numm;++i)
if (m[i].flagdeleted) {
if (unlink(m[i].fn) == -1) err_nounlink();
}
else
if (str_start(m[i].fn,"new/")) {
if (!stralloc_copys(&line,"cur/")) die_nomem();
if (!stralloc_cats(&line,m[i].fn + 4)) die_nomem();
if (!stralloc_cats(&line,":2,")) die_nomem();
if (!stralloc_0(&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,&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 < numm;++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,&limit);//显示几行,如果未指定那么limit为0(balst函数打印全部内容)
while (*arg == ' ') ++arg;
if (scan_ulong(arg,&limit)) ++limit; else limit = 0;

fd = open_read(m[i].fn);
if (fd == -1) { err_nosuch(); return; }
okay();
//关系ssmsg为从指定的消息文件中读
substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
//从ssmsg中读到fd1,如果limit大于0将只读取除消息头外的limit行,如果等于0读全部邮件
blast(&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(&ssin,pop3commands);
die();
}
,
相关文章 热门文章
  • 程序员眼中的qmail(qmail源代码分析)
  • qmail源代码分析之qmail-start.c
  • qmail源代码分析之qmail-popup.c
  • qmail源代码分析之qmail-queue
  • qmail源代码分析之qmail-smtpd.c
  • Linux邮件服务器软件比较
  • 域名和邮件服务器FAQ
  • Qmail自动安装包Qmail_setup-v1.5.3发布
  • freebsd+qmail+mysql+vpopmail之完全ports安装
  • qmail+vpopmail+MySQL+igenus+RedHat 7下建立邮件系统
  • QMAIL终极安装指南
  • 配置你的第一台e-mail服务器
  • qmail+webmail on Linux9 安装全过程
  • 分布式的Qmail邮件系统
  • qmail+vpopmail+mysql+qmailadmin+ezmlm+igenus构建企..
  • qmail+webmail on Linux9 安装全过程
  • Qmail Server Howto
  • 自由广告区
     
    最新软件下载
  • SharePoint Server 2010 部署文档
  • Exchange 2010 RTM升级至SP1 教程
  • Exchange 2010 OWA下RBAC实现的组功能...
  • Lync Server 2010 Standard Edition 标..
  • Lync Server 2010 Enterprise Edition...
  • Forefront Endpoint Protection 2010 ...
  • Lync Server 2010 Edge 服务器部署文档
  • 《Exchange 2003专家指南》
  • Mastering Hyper-V Deployment
  • Windows Server 2008 R2 Hyper-V
  • Microsoft Lync Server 2010 Unleashed
  • Windows Server 2008 R2 Unleashed
  • 今日邮件技术文章
  • 腾讯,在创新中演绎互联网“进化论”
  • 华科人 张小龙 (中国第二代程序员 QQ...
  • 微软推出新功能 提高Hotmail密码安全性
  • 快压技巧分享:秒传邮件超大附件
  • 不容忽视的邮件营销数据分析过程中的算..
  • 国内手机邮箱的现状与未来发展——访尚..
  • 易观数据:2011Q2中国手机邮箱市场收入..
  • 穿越时空的爱恋 QQ邮箱音视频及贺卡邮件
  • Hotmail新功能:“我的朋友可能被黑了”
  • 入侵邻居网络发骚扰邮件 美国男子被重..
  • 网易邮箱莫子睿:《非你莫属》招聘多过..
  • 中国电信推广189邮箱绿色账单
  • 最新专题
  • 鸟哥的Linux私房菜之Mail服务器
  • Exchange Server 2010技术专题
  • Windows 7 技术专题
  • Sendmail 邮件系统配置
  • 组建Exchange 2003邮件系统
  • Windows Server 2008 专题
  • ORF 反垃圾邮件系统
  • Exchange Server 2007 专题
  • ISA Server 2006 教程专题
  • Windows Vista 技术专题
  • “黑莓”(BlackBerry)专题
  • Apache James 专题
  • 分类导航
    邮件新闻资讯:
    IT业界 | 邮件服务器 | 邮件趣闻 | 移动电邮
    电子邮箱 | 反垃圾邮件|邮件客户端|网络安全
    行业数据 | 邮件人物 | 网站公告 | 行业法规
    网络技术:
    邮件原理 | 网络协议 | 网络管理 | 传输介质
    线路接入 | 路由接口 | 邮件存储 | 华为3Com
    CISCO技术 | 网络与服务器硬件
    操作系统:
    Windows 9X | Linux&Uinx | Windows NT
    Windows Vista | FreeBSD | 其它操作系统
    邮件服务器:
    程序与开发 | Exchange | Qmail | Postfix
    Sendmail | MDaemon | Domino | Foxmail
    KerioMail | JavaMail | Winwebmail |James
    Merak&VisNetic | CMailServer | WinMail
    金笛邮件系统 | 其它 |
    反垃圾邮件:
    综述| 客户端反垃圾邮件|服务器端反垃圾邮件
    邮件客户端软件:
    Outlook | Foxmail | DreamMail| KooMail
    The bat | 雷鸟 | Eudora |Becky! |Pegasus
    IncrediMail |其它
    电子邮箱: 个人邮箱 | 企业邮箱 |Gmail
    移动电子邮件:服务器 | 客户端 | 技术前沿
    邮件网络安全:
    软件漏洞 | 安全知识 | 病毒公告 |防火墙
    攻防技术 | 病毒查杀| ISA | 数字签名
    邮件营销:
    Email营销 | 网络营销 | 营销技巧 |营销案例
    邮件人才:招聘 | 职场 | 培训 | 指南 | 职场
    解决方案:
    邮件系统|反垃圾邮件 |安全 |移动电邮 |招标
    产品评测:
    邮件系统 |反垃圾邮件 |邮箱 |安全 |客户端
    广告联系 | 合作联系 | 关于我们 | 联系我们 | 繁體中文
    版权所有:邮件技术资讯网©2003-2010 www.5dmail.net, All Rights Reserved
    www.5Dmail.net Web Team   粤ICP备05009143号