断断续续的学习C++也有三两个月了,(2006年8月下旬起)。很感谢C++,可以说它是
一种催化剂,让我自身的修养和人生观都有很大的提高。(当然这也离不开个人自身
的基础)
在C++大门前,我只能算是刚刚看到这个大门的台阶,但这段小小的距离让我受益颇
浅,现将其中心得奉献出来,欢迎老鸟指正,以供刚接触的新人参考。心得中,尽量减少对语言技术上的评论,以另一种心理来分析指正学习。
注:别人的心得和经验你可以学习,但是不可以完全照而搬之。因为每个人的心理,生活方式,看待事物及个人志向立场等等其他都不同,我的方法不一定适合你,但是可以学习借鉴。
在写之前,想先写一句话,千万不要以为你是天才,所谓天才即胜别人N倍的人,如果你真认为你是天才,请以天才的标准来要求自己,将自己付出和别人的比例以N:1来进行。
学习编程无外乎跟三者打交道,第一自己,第二编程,第三计算机。
第一自己:
心态:很多朋友都是在刚步入大学校门开始接触计算机或者其他。大学是一个很让人迷茫颓废的地方,可能你在高中踌躇满志,但在大学的管理机制下却心态渐糜。能够在大学(大一)里找到自己人生之志的人才是通过了大学的考验,能够真正迈上社会的人。
目标要明确,不要得过且过,不要追逐热门的东西。不管在什么时候,心里都要有自己的信念和理想,不要被其他东西左右了自己。
要从兴趣和竞争出发,因为这才是你产生动力源的地方。学东西时,要让自己先喜欢上它,要学会享受学习它的时间,喜欢乔丹的都知道,乔老爷子最常说的一句就是要享受比赛的时间。因为只有你喜欢上了它,享受了它,才不会被它所产生的压力压倒自己,不要产生一种厌恶的心理。而且心态要调整,不要一味的蛮学,死学,在没有什么兴趣学的时候可以先玩几天,在回过头来,或者用其他的方式来转换。
要抱着一种十年方能磨一剑的心理,扎扎实实从基础开始,不要为高速开发的一些现象所迷惑,那些都只是软件的功劳。我个人认为搞编程的英语和数学很重要,大家一定要时时的抓,至于怎么抓那就是另外一回事了,当然其他的知识也要汲取,可以多接触点天文,地理,人文方面的东西来补充自己。
转换的生活方式。要学会生活,在人的一生中,都会遇到一些很不开心的事,或者大大小小的意外,孙子兵法里面一句大意就是说,战场上要会将劣势转成优势,即最大的劣势很可能就是你的优势所在。一个打击你可以把他变为前进的动力,不必为了一点挫折和捶胸顿足,不必为了一点小事争吵不休。空闲的时候可以躺下来看看天,你就会明白自强不息,厚德载物之理。(看到一些为了失恋或者其他而堕落的人真的很难受和可惜)
要给人以帮助。别人有困难时或者一些举手之劳在自己解决范围内的尽力帮之,这对自己不会损失什么,相反在对方心里能够树立你的形象和增加好感。记住,给人之恩时也是给你自己铺路,这个世界不是靠你一个人可改变的。
第二编程:
语言的争论。几个月来,看到最有争议的帖子即关于某种语言好坏得,往往能顶个几百几千的人气。试问,中国牌子有几个不被骂的,试问,世界上哪部影片没人说他坏话的?术业有专攻,各司其职,每个语言的出现肯定都有自己价值和领域的范围,不同的只是价值和领域的高级和大小。就像高,矮,胖,瘦组成一个“型”。但是技术是进步的,这个进步也是在旧的基础上。放心,技术再飞跃也不会今天奔I,明天扣肉的迅速,在学习的同时也要时时关注整个语言界的走势(貌似哪个伟人说的与时俱进),保持一种同步曲线的前进。
语言能干什么?能问这句话,很欣慰。表明你是从基础学起才会问出这样的话来。但换而言之你一开始对这门语言调查的信息不足。庙算者胜,对自己身边的事物了解是非常重要的,这也能说明说你的教材或者教导你的人不够充分。如果是我对新手交流,我会花足时间让他明白这门语言是什么,能干什么,整个体系等等。这样在学的时候,对同一条语句上的理解你可能跟别人就不一样了, 因为你知道这门语言的体系架构,你可以从更多的特性角度去挖掘使用它。
提高自己对语言阅读能力。如何快速的进步?可以告诉你没有一步登天的办法,但是也可以告诉你如何让你快速掌握你所学的技术。多读他人代码,多领悟他人思维,多给自己出难题,多几种方法来解问题,多将语言的思想和身边的事物联系起来。想想你是怎么掌握普通话的,方法就不具体说了。
编程的思维。计算机是充满诱惑的东西,很多人都说热爱计算机,那只是它的表面,你热爱的只是他的一种扩展性的东西。能学计算机我很高兴,因为计算机从某种意义上来说将空间的距离给消除掉了。这里没有流血,没有牺牲。你可以以一种艺术家的思维来塑造它,可以以侦探的逻辑思维来破解它,可以以一种战争狂人的思维来摧毁它。你可以将你的这种思维体现在代码和整个布局里,不要惊异别人代码思维的异风,你也有自己的思维风格,你也可以,你也可以用自己代码让别人惊异。记住你学东西时更多的要体会他的思维,比如数学,微积分,离散,无数的定理,你学完以后可能永远都用不到,但是你可能有这种感觉,在你碰到某一问题时,这一问题的性质和你学到的东西很类似,但是中间就是差了一点东西,对了,这就是它思维上的一种运用,一种抽象,一种转换。
第三计算机
前面从说了很多,但是基本都是从理论上来说。因为我也没走到技术的高端(刚窥门槛还不到),说技术只是班门弄斧,但是我想我一些入门的方法还是可以和大家共享的。
前面提过,学一个东西首先要了解它,这样你才能知道你学的东西特性这个词。
不管你是什么目的学它的,你都要让自己喜欢上它。
建议新手们先从微机原理入手,弄懂后,再在自己脑中重绘一遍计算机的布局,学的时候,尽量将自己模拟的布局和语言给联系起来
接着学习英语,和数学,切记,学习数学是锻炼自己的逻辑,抽象和转换。
搞定这三个后再重新定位一下自己对计算机哪部分感兴趣,再从前辈中那里获取一些信息,这里教材和入门的准备很重要。一开始我也走了很多弯路,现在只能狂补英语和数学。
遇到问题时先不要问他人(常谈的问题了),应先以自己的所掌握的知识和思维来推敲问题,做个大胆的假设。
一种催化剂,让我自身的修养和人生观都有很大的提高。(当然这也离不开个人自身
的基础)
在C++大门前,我只能算是刚刚看到这个大门的台阶,但这段小小的距离让我受益颇
浅,现将其中心得奉献出来,欢迎老鸟指正,以供刚接触的新人参考。心得中,尽量减少对语言技术上的评论,以另一种心理来分析指正学习。
注:别人的心得和经验你可以学习,但是不可以完全照而搬之。因为每个人的心理,生活方式,看待事物及个人志向立场等等其他都不同,我的方法不一定适合你,但是可以学习借鉴。
在写之前,想先写一句话,千万不要以为你是天才,所谓天才即胜别人N倍的人,如果你真认为你是天才,请以天才的标准来要求自己,将自己付出和别人的比例以N:1来进行。
学习编程无外乎跟三者打交道,第一自己,第二编程,第三计算机。
第一自己:
心态:很多朋友都是在刚步入大学校门开始接触计算机或者其他。大学是一个很让人迷茫颓废的地方,可能你在高中踌躇满志,但在大学的管理机制下却心态渐糜。能够在大学(大一)里找到自己人生之志的人才是通过了大学的考验,能够真正迈上社会的人。
目标要明确,不要得过且过,不要追逐热门的东西。不管在什么时候,心里都要有自己的信念和理想,不要被其他东西左右了自己。
要从兴趣和竞争出发,因为这才是你产生动力源的地方。学东西时,要让自己先喜欢上它,要学会享受学习它的时间,喜欢乔丹的都知道,乔老爷子最常说的一句就是要享受比赛的时间。因为只有你喜欢上了它,享受了它,才不会被它所产生的压力压倒自己,不要产生一种厌恶的心理。而且心态要调整,不要一味的蛮学,死学,在没有什么兴趣学的时候可以先玩几天,在回过头来,或者用其他的方式来转换。
要抱着一种十年方能磨一剑的心理,扎扎实实从基础开始,不要为高速开发的一些现象所迷惑,那些都只是软件的功劳。我个人认为搞编程的英语和数学很重要,大家一定要时时的抓,至于怎么抓那就是另外一回事了,当然其他的知识也要汲取,可以多接触点天文,地理,人文方面的东西来补充自己。
转换的生活方式。要学会生活,在人的一生中,都会遇到一些很不开心的事,或者大大小小的意外,孙子兵法里面一句大意就是说,战场上要会将劣势转成优势,即最大的劣势很可能就是你的优势所在。一个打击你可以把他变为前进的动力,不必为了一点挫折和捶胸顿足,不必为了一点小事争吵不休。空闲的时候可以躺下来看看天,你就会明白自强不息,厚德载物之理。(看到一些为了失恋或者其他而堕落的人真的很难受和可惜)
要给人以帮助。别人有困难时或者一些举手之劳在自己解决范围内的尽力帮之,这对自己不会损失什么,相反在对方心里能够树立你的形象和增加好感。记住,给人之恩时也是给你自己铺路,这个世界不是靠你一个人可改变的。
第二编程:
语言的争论。几个月来,看到最有争议的帖子即关于某种语言好坏得,往往能顶个几百几千的人气。试问,中国牌子有几个不被骂的,试问,世界上哪部影片没人说他坏话的?术业有专攻,各司其职,每个语言的出现肯定都有自己价值和领域的范围,不同的只是价值和领域的高级和大小。就像高,矮,胖,瘦组成一个“型”。但是技术是进步的,这个进步也是在旧的基础上。放心,技术再飞跃也不会今天奔I,明天扣肉的迅速,在学习的同时也要时时关注整个语言界的走势(貌似哪个伟人说的与时俱进),保持一种同步曲线的前进。
语言能干什么?能问这句话,很欣慰。表明你是从基础学起才会问出这样的话来。但换而言之你一开始对这门语言调查的信息不足。庙算者胜,对自己身边的事物了解是非常重要的,这也能说明说你的教材或者教导你的人不够充分。如果是我对新手交流,我会花足时间让他明白这门语言是什么,能干什么,整个体系等等。这样在学的时候,对同一条语句上的理解你可能跟别人就不一样了, 因为你知道这门语言的体系架构,你可以从更多的特性角度去挖掘使用它。
提高自己对语言阅读能力。如何快速的进步?可以告诉你没有一步登天的办法,但是也可以告诉你如何让你快速掌握你所学的技术。多读他人代码,多领悟他人思维,多给自己出难题,多几种方法来解问题,多将语言的思想和身边的事物联系起来。想想你是怎么掌握普通话的,方法就不具体说了。
编程的思维。计算机是充满诱惑的东西,很多人都说热爱计算机,那只是它的表面,你热爱的只是他的一种扩展性的东西。能学计算机我很高兴,因为计算机从某种意义上来说将空间的距离给消除掉了。这里没有流血,没有牺牲。你可以以一种艺术家的思维来塑造它,可以以侦探的逻辑思维来破解它,可以以一种战争狂人的思维来摧毁它。你可以将你的这种思维体现在代码和整个布局里,不要惊异别人代码思维的异风,你也有自己的思维风格,你也可以,你也可以用自己代码让别人惊异。记住你学东西时更多的要体会他的思维,比如数学,微积分,离散,无数的定理,你学完以后可能永远都用不到,但是你可能有这种感觉,在你碰到某一问题时,这一问题的性质和你学到的东西很类似,但是中间就是差了一点东西,对了,这就是它思维上的一种运用,一种抽象,一种转换。
第三计算机
前面从说了很多,但是基本都是从理论上来说。因为我也没走到技术的高端(刚窥门槛还不到),说技术只是班门弄斧,但是我想我一些入门的方法还是可以和大家共享的。
前面提过,学一个东西首先要了解它,这样你才能知道你学的东西特性这个词。
不管你是什么目的学它的,你都要让自己喜欢上它。
建议新手们先从微机原理入手,弄懂后,再在自己脑中重绘一遍计算机的布局,学的时候,尽量将自己模拟的布局和语言给联系起来
接着学习英语,和数学,切记,学习数学是锻炼自己的逻辑,抽象和转换。
搞定这三个后再重新定位一下自己对计算机哪部分感兴趣,再从前辈中那里获取一些信息,这里教材和入门的准备很重要。一开始我也走了很多弯路,现在只能狂补英语和数学。
遇到问题时先不要问他人(常谈的问题了),应先以自己的所掌握的知识和思维来推敲问题,做个大胆的假设。
C++学习重点分析
一、#include “filename.h”和#include 的区别
#include “filename.h”是指编译器将从当前工作目录上开始查找此文件
#include 是指编译器将从标准库目录中开始查找此文件
二、头文件的作用
加强安全检测
通过头文件可能方便地调用库功能,而不必关心其实现方式
三、* , &修饰符的位置
对于*和&修饰符,为了避免误解,最好将修饰符紧靠变量名
四、if语句
不要将布尔变量与任何值进行比较,那会很容易出错的。
整形变量必须要有类型相同的值进行比较
浮点变量最好少比点,就算要比也要有值进行限制
指针变量要和NULL进行比较,不要和布尔型和整形比较
五、const和#define的比较
const有数据类型,#define没有数据类型
个别编译器中const可以进行调试,#define不可以进行调试
在类中定义常量有两种方式
1、 在类在声明常量,但不赋值,在构造函数初始化表中进行赋值;
2、 用枚举代替const常量。
六、C++函数中值的传递方式
有三种方式:值传递(Pass by value)、指针传递(Pass by pointer)、引用传递(Pass by reference)
void fun(char c) //pass by value
void fun(char *str) //pass by pointer
void fun(char &str) //pass by reference
如果输入参数是以值传递的话,最好使用引用传递代替,因为引用传递省去了临时对象的构造和析构
函数的类型不能省略,就算没有也要加个void
七、函数体中的指针或引用常量不能被返回
Char *func(void)
{
char str[]=”Hello Word”;
//这个是不能被返回的,因为str是个指定变量,不是一般的值,函数结束后会被注销掉
return str;
}
函数体内的指针变量并不会随着函数的消亡而自动释放
八、一个内存拷贝函数的实现体
void *memcpy(void *pvTo,const void *pvFrom,size_t size)
{
assert((pvTo!=NULL)&&(pvFrom!=NULL));
byte *pbTo=(byte*)pvTo; //防止地址被改变
byte *pbFrom=(byte*)pvFrom;
while (size-- >0)
pbTo++ = pbForm++;
return pvTo;
}
九、内存的分配方式
分配方式有三种,请记住,说不定那天去面试的时候就会有人问你这问题
1、 静态存储区,是在程序编译时就已经分配好的,在整个运行期间都存在,如全局变量、常量。
2、 栈上分配,函数内的局部变量就是从这分配的,但分配的内存容易有限。
3、 堆上分配,也称动态分配,如我们用new,malloc分配内存,用delete,free来释放的内存。
十、内存分配的注意事项
用new或malloc分配内存时,必须要对此指针赋初值。
用delete 或free释放内存后,必须要将指针指向NULL
不能修改指向常量的指针数据
十一、内容复制与比较
//数组……
char a[]=”Hello Word!”;
char b[10];
strcpy(b,a);
if (strcmp(a,b)==0)
{}
//指针……
char a[]=”Hello Word!”;
char *p;
p=new char[strlen(a)+1];
strcpy(p,a);
if (strcmp(p,a)==0)
{}
十二、sizeof的问题
记住一点,C++无法知道指针所指对象的大小,指针的大小永远为4字节
char a[]=”Hello World!”
char *p=a;
count<
count<
而且,在函数中,数组参数退化为指针,所以下面的内容永远输出为4
void fun(char a[1000])
{
count<
}
十三、关于指针
1、 指针创建时必须被初始化
2、 指针在free 或delete后必须置为NULL
3、 指针的长度都为4字节
4、释放内存时,如果是数组指针,必须要释放掉所有的内存,如
char *p=new char[100];
strcpy(p,”Hello World”);
delete []p; //注意前面的[]号
p=NULL;
5、数组指针的内容不能超过数组指针的最大容易。
如:
char *p=new char[5];
strcpy(p,”Hello World”); //报错 目标容易不够大
delete []p; //注意前面的[]号
p=NULL;
十四、关于malloc/free 和new /delete
l malloc/free 是C/C+的内存分配符,new /delete是C++的内存分配符。
l 注意:malloc/free是库函数,new/delete是运算符
l malloc/free不能执行构造函数与析构函数,而new/delete可以
l new/delete不能在C上运行,所以malloc/free不能被淘汰
l 两者都必须要成对使用
l C++中可以使用_set_new_hander函数来定义内存分配异常的处理
十五、C++的特性
C++新增加有重载(overload),内联(inline),Const,Virtual四种机制
重载和内联:即可用于全局函数,也可用于类的成员函数;
Const和Virtual:只可用于类的成员函数;
重载:在同一类中,函数名相同的函数。由不同的参数决定调用那个函数。函数可要不可要Virtual关键字。和全局函数同名的函数不叫重载。如果在类中调用同名的全局函数,必须用全局引用符号::引用。
覆盖是指派生类函数覆盖基类函数
函数名相同;
参数相同;
基类函数必须有Virtual关键字;
不同的范围(派生类和基类)。
隐藏是指派生类屏蔽了基类的同名函数相同
1、 函数名相同,但参数不同,此时不论基类有无Virtual关键字,基类函数将被隐藏。
2、 函数名相同,参数也相同,但基类无Virtual关键字(有就是覆盖),基类函数将被隐藏。
内联:inline关键字必须与定义体放在一起,而不是单单放在声明中。
Const:const是constant的缩写,“恒定不变”的意思。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
1、 参数做输入用的指针型参数,加上const可防止被意外改动。
2、 按值引用的用户类型做输入参数时,最好将按值传递的改为引用传递,并加上const关键字,目的是为了提高效率。数据类型为内部类型的就没必要做这件事情;如:
将void Func(A a) 改为void Func(const A &a)。
而void func(int a)就没必要改成void func(const int &a);
3、 给返回值为指针类型的函数加上const,会使函数返回值不能被修改,赋给的变量也只能是const型变量。如:函数const char*GetString(void); char *str=GetString()将会出错。而const char *str=GetString()将是正确的。
4、 Const成员函数是指此函数体内只能调用Const成员变量,提高程序的键壮性。如声明函数 int GetCount(void) const;此函数体内就只能调用Const成员变量。
Virtual:虚函数:派生类可以覆盖掉的函数,纯虚函数:只是个空函数,没有函数实现体;
十六、extern“C”有什么作用?
Extern “C”是由C++提供的一个连接交换指定符号,用于告诉C++这段代码是C函数。这是因为C++编译后库中函数名会变得很长,与C生成的不一致,造成C++不能直接调用C函数,加上extren “c”后,C++就能直接调用C函数了。
Extern “C”主要使用正规DLL函数的引用和导出 和 在C++包含C函数或C头文件时使用。使用时在前面加上extern “c” 关键字即可。
十七、构造函数与析构函数
派生类的构造函数应在初始化表里调用基类的构造函数;
派生类和基类的析构函数应加Virtual关键字。
不要小看构造函数和析构函数,其实编起来还是不容易。
#include
class Base
{
public:
virtual ~Base() { cout<< "~Base" << endl ; }
};
class Derived : public Base
{
public:
virtual ~Derived() { cout<< "~Derived" << endl ; }
};
void main(void)
{
Base * pB = new Derived; // upcast
delete pB;
}
输出结果为:
~Derived
~Base
如果析构函数不为虚,那么输出结果为
~Base
十八、#IFNDEF/#DEFINE/#ENDIF有什么作用
仿止该头文件被重复引用
一、#include “filename.h”和#include 的区别
#include “filename.h”是指编译器将从当前工作目录上开始查找此文件
#include 是指编译器将从标准库目录中开始查找此文件
二、头文件的作用
加强安全检测
通过头文件可能方便地调用库功能,而不必关心其实现方式
三、* , &修饰符的位置
对于*和&修饰符,为了避免误解,最好将修饰符紧靠变量名
四、if语句
不要将布尔变量与任何值进行比较,那会很容易出错的。
整形变量必须要有类型相同的值进行比较
浮点变量最好少比点,就算要比也要有值进行限制
指针变量要和NULL进行比较,不要和布尔型和整形比较
五、const和#define的比较
const有数据类型,#define没有数据类型
个别编译器中const可以进行调试,#define不可以进行调试
在类中定义常量有两种方式
1、 在类在声明常量,但不赋值,在构造函数初始化表中进行赋值;
2、 用枚举代替const常量。
六、C++函数中值的传递方式
有三种方式:值传递(Pass by value)、指针传递(Pass by pointer)、引用传递(Pass by reference)
void fun(char c) //pass by value
void fun(char *str) //pass by pointer
void fun(char &str) //pass by reference
如果输入参数是以值传递的话,最好使用引用传递代替,因为引用传递省去了临时对象的构造和析构
函数的类型不能省略,就算没有也要加个void
七、函数体中的指针或引用常量不能被返回
Char *func(void)
{
char str[]=”Hello Word”;
//这个是不能被返回的,因为str是个指定变量,不是一般的值,函数结束后会被注销掉
return str;
}
函数体内的指针变量并不会随着函数的消亡而自动释放
八、一个内存拷贝函数的实现体
void *memcpy(void *pvTo,const void *pvFrom,size_t size)
{
assert((pvTo!=NULL)&&(pvFrom!=NULL));
byte *pbTo=(byte*)pvTo; //防止地址被改变
byte *pbFrom=(byte*)pvFrom;
while (size-- >0)
pbTo++ = pbForm++;
return pvTo;
}
九、内存的分配方式
分配方式有三种,请记住,说不定那天去面试的时候就会有人问你这问题
1、 静态存储区,是在程序编译时就已经分配好的,在整个运行期间都存在,如全局变量、常量。
2、 栈上分配,函数内的局部变量就是从这分配的,但分配的内存容易有限。
3、 堆上分配,也称动态分配,如我们用new,malloc分配内存,用delete,free来释放的内存。
十、内存分配的注意事项
用new或malloc分配内存时,必须要对此指针赋初值。
用delete 或free释放内存后,必须要将指针指向NULL
不能修改指向常量的指针数据
十一、内容复制与比较
//数组……
char a[]=”Hello Word!”;
char b[10];
strcpy(b,a);
if (strcmp(a,b)==0)
{}
//指针……
char a[]=”Hello Word!”;
char *p;
p=new char[strlen(a)+1];
strcpy(p,a);
if (strcmp(p,a)==0)
{}
十二、sizeof的问题
记住一点,C++无法知道指针所指对象的大小,指针的大小永远为4字节
char a[]=”Hello World!”
char *p=a;
count<
count<
而且,在函数中,数组参数退化为指针,所以下面的内容永远输出为4
void fun(char a[1000])
{
count<
}
十三、关于指针
1、 指针创建时必须被初始化
2、 指针在free 或delete后必须置为NULL
3、 指针的长度都为4字节
4、释放内存时,如果是数组指针,必须要释放掉所有的内存,如
char *p=new char[100];
strcpy(p,”Hello World”);
delete []p; //注意前面的[]号
p=NULL;
5、数组指针的内容不能超过数组指针的最大容易。
如:
char *p=new char[5];
strcpy(p,”Hello World”); //报错 目标容易不够大
delete []p; //注意前面的[]号
p=NULL;
十四、关于malloc/free 和new /delete
l malloc/free 是C/C+的内存分配符,new /delete是C++的内存分配符。
l 注意:malloc/free是库函数,new/delete是运算符
l malloc/free不能执行构造函数与析构函数,而new/delete可以
l new/delete不能在C上运行,所以malloc/free不能被淘汰
l 两者都必须要成对使用
l C++中可以使用_set_new_hander函数来定义内存分配异常的处理
十五、C++的特性
C++新增加有重载(overload),内联(inline),Const,Virtual四种机制
重载和内联:即可用于全局函数,也可用于类的成员函数;
Const和Virtual:只可用于类的成员函数;
重载:在同一类中,函数名相同的函数。由不同的参数决定调用那个函数。函数可要不可要Virtual关键字。和全局函数同名的函数不叫重载。如果在类中调用同名的全局函数,必须用全局引用符号::引用。
覆盖是指派生类函数覆盖基类函数
函数名相同;
参数相同;
基类函数必须有Virtual关键字;
不同的范围(派生类和基类)。
隐藏是指派生类屏蔽了基类的同名函数相同
1、 函数名相同,但参数不同,此时不论基类有无Virtual关键字,基类函数将被隐藏。
2、 函数名相同,参数也相同,但基类无Virtual关键字(有就是覆盖),基类函数将被隐藏。
内联:inline关键字必须与定义体放在一起,而不是单单放在声明中。
Const:const是constant的缩写,“恒定不变”的意思。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
1、 参数做输入用的指针型参数,加上const可防止被意外改动。
2、 按值引用的用户类型做输入参数时,最好将按值传递的改为引用传递,并加上const关键字,目的是为了提高效率。数据类型为内部类型的就没必要做这件事情;如:
将void Func(A a) 改为void Func(const A &a)。
而void func(int a)就没必要改成void func(const int &a);
3、 给返回值为指针类型的函数加上const,会使函数返回值不能被修改,赋给的变量也只能是const型变量。如:函数const char*GetString(void); char *str=GetString()将会出错。而const char *str=GetString()将是正确的。
4、 Const成员函数是指此函数体内只能调用Const成员变量,提高程序的键壮性。如声明函数 int GetCount(void) const;此函数体内就只能调用Const成员变量。
Virtual:虚函数:派生类可以覆盖掉的函数,纯虚函数:只是个空函数,没有函数实现体;
十六、extern“C”有什么作用?
Extern “C”是由C++提供的一个连接交换指定符号,用于告诉C++这段代码是C函数。这是因为C++编译后库中函数名会变得很长,与C生成的不一致,造成C++不能直接调用C函数,加上extren “c”后,C++就能直接调用C函数了。
Extern “C”主要使用正规DLL函数的引用和导出 和 在C++包含C函数或C头文件时使用。使用时在前面加上extern “c” 关键字即可。
十七、构造函数与析构函数
派生类的构造函数应在初始化表里调用基类的构造函数;
派生类和基类的析构函数应加Virtual关键字。
不要小看构造函数和析构函数,其实编起来还是不容易。
#include
class Base
{
public:
virtual ~Base() { cout<< "~Base" << endl ; }
};
class Derived : public Base
{
public:
virtual ~Derived() { cout<< "~Derived" << endl ; }
};
void main(void)
{
Base * pB = new Derived; // upcast
delete pB;
}
输出结果为:
~Derived
~Base
如果析构函数不为虚,那么输出结果为
~Base
十八、#IFNDEF/#DEFINE/#ENDIF有什么作用
仿止该头文件被重复引用
控制台一闪一闪的小光标从当前位置后退一格= =、
比如printf("1234");结果就是1234
而printf("123\b4");结果是124,因为退一格再打印4就把先前打印的3洗掉了
猎鹰(905607625) 15:47:51
\b 退格。将当前的输出位置退回前一列处,即消除前一个已输出的字符。
比如printf("1234");结果就是1234
而printf("123\b4");结果是124,因为退一格再打印4就把先前打印的3洗掉了
=============================================
printf可能是许多程序员在开始学习C语言时接触到的第二个函数(我猜第一个是main),说起来,自然是老朋友了,可是,你对这个老朋友了解多吗?你对它的那个孪生兄弟sprintf了解多吗?在将各种类型的数据构造成字符串时,sprintf的强大功能很少会让你失望。
由于sprintf跟printf在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。这也导致sprintf比printf有用得多。所以本文着重介绍sprintf,有时也穿插着用用pritnf。
sprintf是个变参函数,定义如下:
int sprintf( char *buffer, const char *format [, argument] ... );
除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:格式化字符串上。
printf和sprintf都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要的字符串。
1. 格式化数字字符串
sprintf最常见的应用之一莫过于把整数打印到字符串中,所以,spritnf在大多数场合可以替代itoa。如:
//把整数123打印成一个字符串保存在s中。
sprintf(s, "%d", 123); //产生"123"
可以指定宽度,不足的左边补空格:
sprintf(s, "%8d%8d", 123, 4567); //产生:" 123 4567"
当然也可以左对齐:
sprintf(s, "%-8d%8d", 123, 4567); //产生:"123 4567"
也可以按照16进制打印:
sprintf(s, "%8x", 4567); //小写16进制,宽度占8个位置,右对齐
sprintf(s, "%-8X", 4568); //大写16进制,宽度占8个位置,左对齐
这样,一个整数的16进制字符串就很容易得到,但我们在打印16进制内容时,通常想要一种左边补0的等宽格式,那该怎么做呢?很简单,在表示宽度的数字前面加个0就可以了。
sprintf(s, "%08X", 4567); //产生:"000011D7"
上面以”%d”进行的10进制打印同样也可以使用这种左边补0的方式。
这里要注意一个符号扩展的问题:比如,假如我们想打印短整数(short)-1的内存16进制表示形式,在Win32平台上,一个short型占2个字节,所以我们自然希望用4个16进制数字来打印它:
short si = -1;
sprintf(s, "%04X", si);
产生“FFFFFFFF”,怎么回事?因为spritnf是个变参函数,除了前面两个参数之外,后面的参数都不是类型安全的,函数更没有办法仅仅通过一个“%X”就能得知当初函数调用前参数压栈时被压进来的到底是个4字节的整数还是个2字节的短整数,所以采取了统一4字节的处理方式,导致参数压栈时做了符号扩展,扩展成了32位的整数-1,打印时4个位置不够了,就把32位整数-1的8位16进制都打印出来了。如果你想看si的本来面目,那么就应该让编译器做0扩展而不是符号扩展(扩展时二进制左边补0而不是补符号位):
sprintf(s, "%04X", (unsigned short)si);
就可以了。或者:
unsigned short si = -1;
sprintf(s, "%04X", si);
sprintf和printf还可以按8进制打印整数字符串,使用”%o”。注意8进制和16进制都不会打印出负数,都是无符号的,实际上也就是变量的内部编码的直接的16进制或8进制表示。
2. 控制浮点数打印格式
浮点数的打印和格式控制是sprintf的又一大常用功能,浮点数使用格式符”%f”控制,默认保留小数点后6位数字,比如:
sprintf(s, "%f", 3.1415926); //产生"3.141593"
但有时我们希望自己控制打印的宽度和小数位数,这时就应该使用:”%m.nf”格式,其中m表示打印的宽度,n表示小数点后的位数。比如:
sprintf(s, "%10.3f", 3.1415626); //产生:" 3.142"
sprintf(s, "%-10.3f", 3.1415626); //产生:"3.142 "
sprintf(s, "%.3f", 3.1415626); //不指定总宽度,产生:"3.142"
注意一个问题,你猜
int i = 100;
sprintf(s, "%.2f", i);
会打出什么东东来?“100.00”?对吗?自己试试就知道了,同时也试试下面这个:
sprintf(s, "%.2f", (double)i);
第一个打出来的肯定不是正确结果,原因跟前面提到的一样,参数压栈时调用者并不知道跟i相对应的格式控制符是个”%f”。而函数执行时函数本身则并不知道当年被压入栈里的是个整数,于是可怜的保存整数i的那4个字节就被不由分说地强行作为浮点数格式来解释了,整个乱套了。
不过,如果有人有兴趣使用手工编码一个浮点数,那么倒可以使用这种方法来检验一下你手工编排的结果是否正确。J
字符/Ascii码对照
我们知道,在C/C++语言中,char也是一种普通的scalable类型,除了字长之外,它与short,int,long这些类型没有本质区别,只不过被大家习惯用来表示字符和字符串而已。(或许当年该把这个类型叫做“byte”,然后现在就可以根据实际情况,使用byte或short来把char通过typedef定义出来,这样更合适些)
于是,使用”%d”或者”%x”打印一个字符,便能得出它的10进制或16进制的ASCII码;反过来,使用”%c”打印一个整数,便可以看到它所对应的ASCII字符。以下程序段把所有可见字符的ASCII码对照表打印到屏幕上(这里采用printf,注意”#”与”%X”合用时自动为16进制数增加”0X”前缀):
for(int i = 32; i < 127; i++) {
printf("[ %c ]: %3d 0x%#04X\n", i, i, i);
}
3. 连接字符串
sprintf的格式控制串中既然可以插入各种东西,并最终把它们“连成一串”,自然也就能够连接字符串,从而在许多场合可以替代strcat,但sprintf能够一次连接多个字符串(自然也可以同时在它们中间插入别的内容,总之非常灵活)。比如:
char* who = "I";
char* whom = "CSDN";
sprintf(s, "%s love %s.", who, whom); //产生:"I love CSDN. "
strcat只能连接字符串(一段以’\0’结尾的字符数组或叫做字符缓冲,null-terminated-string),但有时我们有两段字符缓冲区,他们并不是以’\0’结尾。比如许多从第三方库函数中返回的字符数组,从硬件或者网络传输中读进来的字符流,它们未必每一段字符序列后面都有个相应的’\0’来结尾。如果直接连接,不管是sprintf还是strcat肯定会导致非法内存操作,而strncat也至少要求第一个参数是个null-terminated-string,那该怎么办呢?我们自然会想起前面介绍打印整数和浮点数时可以指定宽度,字符串也一样的。比如:
char a1[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
char a2[] = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
如果:
sprintf(s, "%s%s", a1, a2); //Don't do that!
十有八九要出问题了。是否可以改成:
sprintf(s, "%7s%7s", a1, a2);
也没好到哪儿去,正确的应该是:
sprintf(s, "%.7s%.7s", a1, a2);//产生:"ABCDEFGHIJKLMN"
这可以类比打印浮点数的”%m.nf”,在”%m.ns”中,m表示占用宽度(字符串长度不足时补空格,超出了则按照实际宽度打印),n才表示从相应的字符串中最多取用的字符数。通常在打印字符串时m没什么大用,还是点号后面的n用的多。自然,也可以前后都只取部分字符:
sprintf(s, "%.6s%.5s", a1, a2);//产生:"ABCDEFHIJKL"
在许多时候,我们或许还希望这些格式控制符中用以指定长度信息的数字是动态的,而不是静态指定的,因为许多时候,程序要到运行时才会清楚到底需要取字符数组中的几个字符,这种动态的宽度/精度设置功能在sprintf的实现中也被考虑到了,sprintf采用”*”来占用一个本来需要一个指定宽度或精度的常数数字的位置,同样,而实际的宽度或精度就可以和其它被打印的变量一样被提供出来,于是,上面的例子可以变成:
sprintf(s, "%.*s%.*s", 7, a1, 7, a2);
或者:
sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2);
实际上,前面介绍的打印字符、整数、浮点数等都可以动态指定那些常量值,比如:
sprintf(s, "%-*d", 4, 'A'); //产生"65 "
sprintf(s, "%#0*X", 8, 128); //产生"0X000080","#"产生0X
sprintf(s, "%*.*f", 10, 2, 3.1415926); //产生" 3.14"
4. 打印地址信息
有时调试程序时,我们可能想查看某些变量或者成员的地址,由于地址或者指针也不过是个32位的数,你完全可以使用打印无符号整数的”%u”把他们打印出来:
sprintf(s, "%u", &i);
不过通常人们还是喜欢使用16进制而不是10进制来显示一个地址:
sprintf(s, "%08X", &i);
然而,这些都是间接的方法,对于地址打印,sprintf 提供了专门的”%p”:
sprintf(s, "%p", &i);
我觉得它实际上就相当于:
sprintf(s, "%0*x", 2 * sizeof(void *), &i);
5. 利用sprintf的返回值
较少有人注意printf/sprintf函数的返回值,但有时它却是有用的,spritnf返回了本次函数调用最终打印到字符缓冲区中的字符数目。也就是说每当一次sprinf调用结束以后,你无须再调用一次strlen便已经知道了结果字符串的长度。如:
int len = sprintf(s, "%d", i);
对于正整数来说,len便等于整数i的10进制位数。
下面的是个完整的例子,产生10个[0, 100)之间的随机数,并将他们打印到一个字符数组s中,以逗号分隔开。
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main() {
srand(time(0));
char s[64];
int offset = 0;
for(int i = 0; i < 10; i++) {
offset += sprintf(s + offset, "%d,", rand() % 100);
}
s[offset - 1] = '\n';//将最后一个逗号换成换行符。
printf(s);
return 0;
}
设想当你从数据库中取出一条记录,然后希望把他们的各个字段按照某种规则连接成一个字符串时,就可以使用这种方法,从理论上讲,他应该比不断的strcat效率高,因为strcat每次调用都需要先找到最后的那个’\0’的位置,而在上面给出的例子中,我们每次都利用sprintf返回值把这个位置直接记下来了。
6. 使用sprintf的常见问题
sprintf是个变参函数,使用时经常出问题,而且只要出问题通常就是能导致程序崩溃的内存访问错误,但好在由sprintf误用导致的问题虽然严重,却很容易找出,无非就是那么几种情况,通常用眼睛再把出错的代码多看几眼就看出来了。
Ø 缓冲区溢出
第一个参数的长度太短了,没的说,给个大点的地方吧。当然也可能是后面的参数的问题,建议变参对应一定要细心,而打印字符串时,尽量使用”%.ns”的形式指定最大字符数。
Ø 忘记了第一个参数
低级得不能再低级问题,用printf用得太惯了。//偶就常犯。:。(
Ø 变参对应出问题
通常是忘记了提供对应某个格式符的变参,导致以后的参数统统错位,检查检查吧。尤其是对应”*”的那些参数,都提供了吗?不要把一个整数对应一个”%s”,编译器会觉得你欺她太甚了(编译器是obj和exe的妈妈,应该是个女的,:P)。
7. strftime
sprintf还有个不错的表妹:strftime,专门用于格式化时间字符串的,用法跟她表哥很像,也是一大堆格式控制符,只是毕竟小姑娘家心细,她还要调用者指定缓冲区的最大长度,可能是为了在出现问题时可以推卸责任吧。这里举个例子:
time_t t = time(0);
//产生"YYYY-MM-DD hh:mm:ss"格式的字符串。
char s[32];
strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t));
sprintf在MFC中也能找到他的知音:CString::Format,strftime在MFC中自然也有她的同道:CTime::Format,这一对由于从面向对象哪里得到了赞助,用以写出的代码更觉优雅。
8. 后记
本文介绍的所有这些功能,在MSDN中都可以很容易地查到,笔者只是根据自己的使用经验,结合一些例子,把一些常用的,有用的,而可能为许多初学者所不知的用法介绍了一点,希望大家不要笑话,也希望大家批评指正。
有人认为这种带变参的函数会引起各种问题,因而不提倡使用。但笔者本人每每还是抵挡不了它们强大功能的诱惑,在实际工作中一直在使用。实际上,C#.NET从开始就支持变参,刚发布不久的Java5.0也支持变参了。
感谢ericzhangali(另一个空间)仔细审阅了全稿,纠正了很多小错误,并提出了一些建议。也感谢laomai(老迈)阅读了全稿并给出了增删一些内容的建议。
①获取System时间: void GetSystemTime(LPSYSTEMTIME lpSystemTime); 下面是例子:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
void main() {
SYSTEMTIME st; //定义存放时间的结构体
char strTime[256];
int n=0;
GetSystemTime(&st);
n = sprintf(strTime,"Year:\t%d\n",st.wYear);
n += sprintf(strTime+n,"Month:\t%d\n",st.wMonth);
n += sprintf(strTime+n,"Day:\t%d\n",st.wDay);
n += sprintf(strTime+n,"Date:\t%d\n",st.wDayOfWeek);
n += sprintf(strTime+n,"Hour:\t%d\n",st.wHour);
n += sprintf(strTime+n,"Minute:\t%d\n",st.wMinute);
n += sprintf(strTime+n,"Second:\t%d\n",st.wSecond);
n += sprintf(strTime+n,"MilliSecond:\t%d\n",st.wMilliseconds);
printf("%s",strTime);
system("pause");
}
比如printf("1234");结果就是1234
而printf("123\b4");结果是124,因为退一格再打印4就把先前打印的3洗掉了
猎鹰(905607625) 15:47:51
\b 退格。将当前的输出位置退回前一列处,即消除前一个已输出的字符。
比如printf("1234");结果就是1234
而printf("123\b4");结果是124,因为退一格再打印4就把先前打印的3洗掉了
=============================================
printf可能是许多程序员在开始学习C语言时接触到的第二个函数(我猜第一个是main),说起来,自然是老朋友了,可是,你对这个老朋友了解多吗?你对它的那个孪生兄弟sprintf了解多吗?在将各种类型的数据构造成字符串时,sprintf的强大功能很少会让你失望。
由于sprintf跟printf在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。这也导致sprintf比printf有用得多。所以本文着重介绍sprintf,有时也穿插着用用pritnf。
sprintf是个变参函数,定义如下:
int sprintf( char *buffer, const char *format [, argument] ... );
除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:格式化字符串上。
printf和sprintf都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要的字符串。
1. 格式化数字字符串
sprintf最常见的应用之一莫过于把整数打印到字符串中,所以,spritnf在大多数场合可以替代itoa。如:
//把整数123打印成一个字符串保存在s中。
sprintf(s, "%d", 123); //产生"123"
可以指定宽度,不足的左边补空格:
sprintf(s, "%8d%8d", 123, 4567); //产生:" 123 4567"
当然也可以左对齐:
sprintf(s, "%-8d%8d", 123, 4567); //产生:"123 4567"
也可以按照16进制打印:
sprintf(s, "%8x", 4567); //小写16进制,宽度占8个位置,右对齐
sprintf(s, "%-8X", 4568); //大写16进制,宽度占8个位置,左对齐
这样,一个整数的16进制字符串就很容易得到,但我们在打印16进制内容时,通常想要一种左边补0的等宽格式,那该怎么做呢?很简单,在表示宽度的数字前面加个0就可以了。
sprintf(s, "%08X", 4567); //产生:"000011D7"
上面以”%d”进行的10进制打印同样也可以使用这种左边补0的方式。
这里要注意一个符号扩展的问题:比如,假如我们想打印短整数(short)-1的内存16进制表示形式,在Win32平台上,一个short型占2个字节,所以我们自然希望用4个16进制数字来打印它:
short si = -1;
sprintf(s, "%04X", si);
产生“FFFFFFFF”,怎么回事?因为spritnf是个变参函数,除了前面两个参数之外,后面的参数都不是类型安全的,函数更没有办法仅仅通过一个“%X”就能得知当初函数调用前参数压栈时被压进来的到底是个4字节的整数还是个2字节的短整数,所以采取了统一4字节的处理方式,导致参数压栈时做了符号扩展,扩展成了32位的整数-1,打印时4个位置不够了,就把32位整数-1的8位16进制都打印出来了。如果你想看si的本来面目,那么就应该让编译器做0扩展而不是符号扩展(扩展时二进制左边补0而不是补符号位):
sprintf(s, "%04X", (unsigned short)si);
就可以了。或者:
unsigned short si = -1;
sprintf(s, "%04X", si);
sprintf和printf还可以按8进制打印整数字符串,使用”%o”。注意8进制和16进制都不会打印出负数,都是无符号的,实际上也就是变量的内部编码的直接的16进制或8进制表示。
2. 控制浮点数打印格式
浮点数的打印和格式控制是sprintf的又一大常用功能,浮点数使用格式符”%f”控制,默认保留小数点后6位数字,比如:
sprintf(s, "%f", 3.1415926); //产生"3.141593"
但有时我们希望自己控制打印的宽度和小数位数,这时就应该使用:”%m.nf”格式,其中m表示打印的宽度,n表示小数点后的位数。比如:
sprintf(s, "%10.3f", 3.1415626); //产生:" 3.142"
sprintf(s, "%-10.3f", 3.1415626); //产生:"3.142 "
sprintf(s, "%.3f", 3.1415626); //不指定总宽度,产生:"3.142"
注意一个问题,你猜
int i = 100;
sprintf(s, "%.2f", i);
会打出什么东东来?“100.00”?对吗?自己试试就知道了,同时也试试下面这个:
sprintf(s, "%.2f", (double)i);
第一个打出来的肯定不是正确结果,原因跟前面提到的一样,参数压栈时调用者并不知道跟i相对应的格式控制符是个”%f”。而函数执行时函数本身则并不知道当年被压入栈里的是个整数,于是可怜的保存整数i的那4个字节就被不由分说地强行作为浮点数格式来解释了,整个乱套了。
不过,如果有人有兴趣使用手工编码一个浮点数,那么倒可以使用这种方法来检验一下你手工编排的结果是否正确。J
字符/Ascii码对照
我们知道,在C/C++语言中,char也是一种普通的scalable类型,除了字长之外,它与short,int,long这些类型没有本质区别,只不过被大家习惯用来表示字符和字符串而已。(或许当年该把这个类型叫做“byte”,然后现在就可以根据实际情况,使用byte或short来把char通过typedef定义出来,这样更合适些)
于是,使用”%d”或者”%x”打印一个字符,便能得出它的10进制或16进制的ASCII码;反过来,使用”%c”打印一个整数,便可以看到它所对应的ASCII字符。以下程序段把所有可见字符的ASCII码对照表打印到屏幕上(这里采用printf,注意”#”与”%X”合用时自动为16进制数增加”0X”前缀):
for(int i = 32; i < 127; i++) {
printf("[ %c ]: %3d 0x%#04X\n", i, i, i);
}
3. 连接字符串
sprintf的格式控制串中既然可以插入各种东西,并最终把它们“连成一串”,自然也就能够连接字符串,从而在许多场合可以替代strcat,但sprintf能够一次连接多个字符串(自然也可以同时在它们中间插入别的内容,总之非常灵活)。比如:
char* who = "I";
char* whom = "CSDN";
sprintf(s, "%s love %s.", who, whom); //产生:"I love CSDN. "
strcat只能连接字符串(一段以’\0’结尾的字符数组或叫做字符缓冲,null-terminated-string),但有时我们有两段字符缓冲区,他们并不是以’\0’结尾。比如许多从第三方库函数中返回的字符数组,从硬件或者网络传输中读进来的字符流,它们未必每一段字符序列后面都有个相应的’\0’来结尾。如果直接连接,不管是sprintf还是strcat肯定会导致非法内存操作,而strncat也至少要求第一个参数是个null-terminated-string,那该怎么办呢?我们自然会想起前面介绍打印整数和浮点数时可以指定宽度,字符串也一样的。比如:
char a1[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
char a2[] = {'H', 'I', 'J', 'K', 'L', 'M', 'N'};
如果:
sprintf(s, "%s%s", a1, a2); //Don't do that!
十有八九要出问题了。是否可以改成:
sprintf(s, "%7s%7s", a1, a2);
也没好到哪儿去,正确的应该是:
sprintf(s, "%.7s%.7s", a1, a2);//产生:"ABCDEFGHIJKLMN"
这可以类比打印浮点数的”%m.nf”,在”%m.ns”中,m表示占用宽度(字符串长度不足时补空格,超出了则按照实际宽度打印),n才表示从相应的字符串中最多取用的字符数。通常在打印字符串时m没什么大用,还是点号后面的n用的多。自然,也可以前后都只取部分字符:
sprintf(s, "%.6s%.5s", a1, a2);//产生:"ABCDEFHIJKL"
在许多时候,我们或许还希望这些格式控制符中用以指定长度信息的数字是动态的,而不是静态指定的,因为许多时候,程序要到运行时才会清楚到底需要取字符数组中的几个字符,这种动态的宽度/精度设置功能在sprintf的实现中也被考虑到了,sprintf采用”*”来占用一个本来需要一个指定宽度或精度的常数数字的位置,同样,而实际的宽度或精度就可以和其它被打印的变量一样被提供出来,于是,上面的例子可以变成:
sprintf(s, "%.*s%.*s", 7, a1, 7, a2);
或者:
sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2);
实际上,前面介绍的打印字符、整数、浮点数等都可以动态指定那些常量值,比如:
sprintf(s, "%-*d", 4, 'A'); //产生"65 "
sprintf(s, "%#0*X", 8, 128); //产生"0X000080","#"产生0X
sprintf(s, "%*.*f", 10, 2, 3.1415926); //产生" 3.14"
4. 打印地址信息
有时调试程序时,我们可能想查看某些变量或者成员的地址,由于地址或者指针也不过是个32位的数,你完全可以使用打印无符号整数的”%u”把他们打印出来:
sprintf(s, "%u", &i);
不过通常人们还是喜欢使用16进制而不是10进制来显示一个地址:
sprintf(s, "%08X", &i);
然而,这些都是间接的方法,对于地址打印,sprintf 提供了专门的”%p”:
sprintf(s, "%p", &i);
我觉得它实际上就相当于:
sprintf(s, "%0*x", 2 * sizeof(void *), &i);
5. 利用sprintf的返回值
较少有人注意printf/sprintf函数的返回值,但有时它却是有用的,spritnf返回了本次函数调用最终打印到字符缓冲区中的字符数目。也就是说每当一次sprinf调用结束以后,你无须再调用一次strlen便已经知道了结果字符串的长度。如:
int len = sprintf(s, "%d", i);
对于正整数来说,len便等于整数i的10进制位数。
下面的是个完整的例子,产生10个[0, 100)之间的随机数,并将他们打印到一个字符数组s中,以逗号分隔开。
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main() {
srand(time(0));
char s[64];
int offset = 0;
for(int i = 0; i < 10; i++) {
offset += sprintf(s + offset, "%d,", rand() % 100);
}
s[offset - 1] = '\n';//将最后一个逗号换成换行符。
printf(s);
return 0;
}
设想当你从数据库中取出一条记录,然后希望把他们的各个字段按照某种规则连接成一个字符串时,就可以使用这种方法,从理论上讲,他应该比不断的strcat效率高,因为strcat每次调用都需要先找到最后的那个’\0’的位置,而在上面给出的例子中,我们每次都利用sprintf返回值把这个位置直接记下来了。
6. 使用sprintf的常见问题
sprintf是个变参函数,使用时经常出问题,而且只要出问题通常就是能导致程序崩溃的内存访问错误,但好在由sprintf误用导致的问题虽然严重,却很容易找出,无非就是那么几种情况,通常用眼睛再把出错的代码多看几眼就看出来了。
Ø 缓冲区溢出
第一个参数的长度太短了,没的说,给个大点的地方吧。当然也可能是后面的参数的问题,建议变参对应一定要细心,而打印字符串时,尽量使用”%.ns”的形式指定最大字符数。
Ø 忘记了第一个参数
低级得不能再低级问题,用printf用得太惯了。//偶就常犯。:。(
Ø 变参对应出问题
通常是忘记了提供对应某个格式符的变参,导致以后的参数统统错位,检查检查吧。尤其是对应”*”的那些参数,都提供了吗?不要把一个整数对应一个”%s”,编译器会觉得你欺她太甚了(编译器是obj和exe的妈妈,应该是个女的,:P)。
7. strftime
sprintf还有个不错的表妹:strftime,专门用于格式化时间字符串的,用法跟她表哥很像,也是一大堆格式控制符,只是毕竟小姑娘家心细,她还要调用者指定缓冲区的最大长度,可能是为了在出现问题时可以推卸责任吧。这里举个例子:
time_t t = time(0);
//产生"YYYY-MM-DD hh:mm:ss"格式的字符串。
char s[32];
strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t));
sprintf在MFC中也能找到他的知音:CString::Format,strftime在MFC中自然也有她的同道:CTime::Format,这一对由于从面向对象哪里得到了赞助,用以写出的代码更觉优雅。
8. 后记
本文介绍的所有这些功能,在MSDN中都可以很容易地查到,笔者只是根据自己的使用经验,结合一些例子,把一些常用的,有用的,而可能为许多初学者所不知的用法介绍了一点,希望大家不要笑话,也希望大家批评指正。
有人认为这种带变参的函数会引起各种问题,因而不提倡使用。但笔者本人每每还是抵挡不了它们强大功能的诱惑,在实际工作中一直在使用。实际上,C#.NET从开始就支持变参,刚发布不久的Java5.0也支持变参了。
感谢ericzhangali(另一个空间)仔细审阅了全稿,纠正了很多小错误,并提出了一些建议。也感谢laomai(老迈)阅读了全稿并给出了增删一些内容的建议。
①获取System时间: void GetSystemTime(LPSYSTEMTIME lpSystemTime); 下面是例子:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
void main() {
SYSTEMTIME st; //定义存放时间的结构体
char strTime[256];
int n=0;
GetSystemTime(&st);
n = sprintf(strTime,"Year:\t%d\n",st.wYear);
n += sprintf(strTime+n,"Month:\t%d\n",st.wMonth);
n += sprintf(strTime+n,"Day:\t%d\n",st.wDay);
n += sprintf(strTime+n,"Date:\t%d\n",st.wDayOfWeek);
n += sprintf(strTime+n,"Hour:\t%d\n",st.wHour);
n += sprintf(strTime+n,"Minute:\t%d\n",st.wMinute);
n += sprintf(strTime+n,"Second:\t%d\n",st.wSecond);
n += sprintf(strTime+n,"MilliSecond:\t%d\n",st.wMilliseconds);
printf("%s",strTime);
system("pause");
}
#include "iostream"
#include "../../lib/sinalib/StrLib/str.h"
#include "StrLib.h"
using namespace std;
int main()
{
string strinfo="Please input your name:";
cout << strinfo ;
cin >> strinfo;
if( strinfo == "winter" )
cout << "you are winter!"<<endl;
strinfo += " , Welcome to China!";
cout << strinfo ;
string strtmp = "How are you? " + strinfo;
for(int i = 0 ; i < strtmp.size(); i ++)
cout<<strtmp<<strtmp.size();
}
前言: string 的角色
1 string 使用
1.1 充分使用string 操作符
1.2 眼花缭乱的string find 函数
1.3 string insert, replace, erase 2 string 和 C风格字符串
3 string 和 Charactor Traits
4 string 建议
5 小结
6 附录前言: string 的角色
C++ 语言是个十分优秀的语言,但优秀并不表示完美。还是有许多人不愿意使用C或者C++,为什么?原因众多,其中之一就是C/C++的文本处理功能太麻烦,用起来很不方便。以前没有接触过其他语言时,每当别人这么说,我总是不屑一顾,认为他们根本就没有领会C++的精华,或者不太懂C++,现在我接触perl, php, 和Shell脚本以后,开始理解了以前为什么有人说C++文本处理不方便了。
举例来说,如果文本格式是:用户名 电话号码,文件名name.txt
Tom 23245332
Jenny 22231231
Heny 22183942
Tom 23245332
...
现在我们需要对用户名排序,且只输出不同的姓名。
那么在shell 编程中,可以这样用:
awk '{print $1}' name.txt | sort | uniq
简单吧?
如果使用C/C++ 就麻烦了,他需要做以下工作:
先打开文件,检测文件是否打开,如果失败,则退出。
声明一个足够大得二维字符数组或者一个字符指针数组
读入一行到字符空间
然后分析一行的结构,找到空格,存入字符数组中。
关闭文件
写一个排序函数,或者使用写一个比较函数,使用qsort排序
遍历数组,比较是否有相同的,如果有,则要删除,copy...
输出信息
你可以用C++或者C语言去实现这个流程。如果一个人的主要工作就是处理这种类似的文本(例如做apache的日志统计和分析),你说他会喜欢C/C++么?
当然,有了STL,这些处理会得到很大的简化。我们可以使用 fstream来代替麻烦的fopen fread fclose, 用vector 来代替数组。最重要的是用 string来代替char * 数组,使用sort排序算法来排序,用unique 函数来去重。听起来好像很不错 。看看下面代码(例程1):
#i nclude <string>
#i nclude <iostream>
#i nclude <algorithm>
#i nclude <vector>
#i nclude <fstream>
using namespace std;
int main(){
ifstream in("name.txt");
string strtmp;
vector<string> vect;
while(getline(in, strtmp, '\n'))
vect.push_back(strtmp.substr(0, strtmp.find(' ')));
sort(vect.begin(), vect.end());
vector<string>::iterator it=unique(vect.begin(), vect.end());
copy(vect.begin(), it, ostream_iterator<string>(cout, "\n"));
return 0;
}
也还不错吧,至少会比想象得要简单得多!(代码里面没有对错误进行处理,只是为了说明问题,不要效仿).
当然,在这个文本格式中,不用vector而使用map会更有扩充性,例如,还可通过人名找电话号码等等,但是使用了map就不那么好用sort了。你可以用map试一试。
这里string的作用不只是可以存储字符串,还可以提供字符串的比较,查找等。在sort和unique函数中就默认使用了less 和equal_to函数, 上面的一段代码,其实使用了string的以下功能:
存储功能,在getline() 函数中
查找功能,在find() 函数中
子串功能,在substr() 函数中
string operator < , 默认在sort() 函数中调用
string operator == , 默认在unique() 函数中调用
总之,有了string 后,C++的字符文本处理功能总算得到了一定补充,加上配合STL其他容器使用,其在文本处理上的功能已经与perl, shell, php的距离缩小很多了。 因此掌握string 会让你的工作事半功倍。
1 string 使用
其实,string并不是一个单独的容器,只是basic_string 模板类的一个typedef 而已,相对应的还有wstring, 你在string 头文件中你会发现下面的代码:
extern "C++" {
typedef basic_string <char> string;
typedef basic_string <wchar_t> wstring;
} // extern "C++"
由于只是解释string的用法,如果没有特殊的说明,本文并不区分string 和 basic_string的区别。
string 其实相当于一个保存字符的序列容器,因此除了有字符串的一些常用操作以外,还有包含了所有的序列容器的操作。字符串的常用操作包括:增加、删除、修改、查找比较、链接、输入、输出等。详细函数列表参看附录。不要害怕这么多函数,其实有许多是序列容器带有的,平时不一定用的上。
如果你要想了解所有函数的详细用法,你需要查看basic_string,或者下载STL编程手册。这里通过实例介绍一些常用函数。
1.1 充分使用string 操作符
string 重载了许多操作符,包括 +, +=, <, =, , [], <<, >>等,正式这些操作符,对字符串操作非常方便。先看看下面这个例子:tt.cpp(例程2)
#i nclude <string>
#i nclude <iostream>
using namespace std;
int main(){
string strinfo="Please input your name:";
cout << strinfo ;
cin >> strinfo;
if( strinfo == "winter" )
cout << "you are winter!"<<endl;
else if( strinfo != "wende" )
cout << "you are not wende!"<<endl;
else if( strinfo < "winter")
cout << "your name should be ahead of winter"<<endl;
else
cout << "your name should be after of winter"<<endl;
strinfo += " , Welcome to China!";
cout << strinfo<<endl;
cout <<"Your name is :"<<endl;
string strtmp = "How are you? " + strinfo;
for(int i = 0 ; i < strtmp.size(); i ++)
cout<<strtmp;
return 0;
}
下面是程序的输出
-bash-2.05b$ make tt
c++ -O -pipe -march=pentiumpro tt.cpp -o tt
-bash-2.05b$ ./tt
Please input your name:Hero
you are not wende!
Hero , Welcome to China!
How are you? Hero , Welcome to China!
有了这些操作符,在STL中仿函数都可以直接使用string作为参数,例如 less, great, equal_to 等,因此在把string作为参数传递的时候,它的使用和int 或者float等已经没有什么区别了。例如,你可以使用:
map<string, int> mymap;
//以上默认使用了 less<string>
有了 operator + 以后,你可以直接连加,例如:
string strinfo="Winter";
string strlast="Hello " + strinfo + "!";
//你还可以这样:
string strtest="Hello " + strinfo + " Welcome" + " to China" + " !";
看见其中的特点了吗?只要你的等式里面有一个 string 对象,你就可以一直连续"+",但有一点需要保证的是,在开始的两项中,必须有一项是 string 对象。其原理很简单:
系统遇到"+"号,发现有一项是string 对象。
系统把另一项转化为一个临时 string 对象。
执行 operator + 操作,返回新的临时string 对象。
如果又发现"+"号,继续第一步操作。
由于这个等式是由左到右开始检测执行,如果开始两项都是const char* ,程序自己并没有定义两个const char* 的加法,编译的时候肯定就有问题了。
有了操作符以后,assign(), append(), compare(), at()等函数,除非有一些特殊的需求时,一般是用不上。当然at()函数还有一个功能,那就是检查下标是否合法,如果是使用:
string str="winter";
//下面一行有可能会引起程序中断错误
str[100]='!';
//下面会抛出异常:throws: out_of_range
cout<<str.at(100)<<endl;
了解了吗?如果你希望效率高,还是使用[]来访问,如果你希望稳定性好,最好使用at()来访问。
1.2 眼花缭乱的string find 函数
由于查找是使用最为频繁的功能之一,string 提供了非常丰富的查找函数。其列表如下:
函数名 描述 find 查找 rfind 反向查找 find_first_of 查找包含子串中的任何字符,返回第一个位置 find_first_not_of 查找不包含子串中的任何字符,返回第一个位置 find_last_of 查找包含子串中的任何字符,返回最后一个位置 find_last_not_of 查找不包含子串中的任何字符,返回最后一个位置 以上函数都是被重载了4次,以下是以find_first_of 函数为例说明他们的参数,其他函数和其参数一样,也就是说总共有24个函数 :
size_type find_first_of(const basic_string& s, size_type pos = 0)
size_type find_first_of(const charT* s, size_type pos, size_type n)
size_type find_first_of(const charT* s, size_type pos = 0)
size_type find_first_of(charT c, size_type pos = 0)
所有的查找函数都返回一个size_type类型,这个返回值一般都是所找到字符串的位置,如果没有找到,则返回string::npos。有一点需要特别注意,所有和string::npos的比较一定要用string::size_type来使用,不要直接使用int 或者unsigned int等类型。其实string::npos表示的是-1, 看看头文件:
template <class _CharT, class _Traits, class _Alloc>
const basic_string<_CharT,_Traits,_Alloc>::size_type
basic_string<_CharT,_Traits,_Alloc>::npos
= basic_string<_CharT,_Traits,_Alloc>::size_type) -1;
find 和 rfind 都还比较容易理解,一个是正向匹配,一个是逆向匹配,后面的参数pos都是用来指定起始查找位置。对于find_first_of 和find_last_of 就不是那么好理解。
find_first_of 是给定一个要查找的字符集,找到这个字符集中任何一个字符所在字符串中第一个位置。或许看一个例子更容易明白。
有这样一个需求:过滤一行开头和结尾的所有非英文字符。看看用string 如何实现:
#i nclude <string>
#i nclude <iostream>
using namespace std;
int main(){
string strinfo=" //*---Hello Word!......------";
string strset="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int first = strinfo.find_first_of(strset);
if(first == string::npos) {
cout<<"not find any characters"<<endl;
return -1;
}
int last = strinfo.find_last_of(strset);
if(last == string::npos) {
cout<<"not find any characters"<<endl;
return -1;
}
cout << strinfo.substr(first, last - first + 1)<<endl;
return 0;
}
这里把所有的英文字母大小写作为了需要查找的字符集,先查找第一个英文字母的位置,然后查找最后一个英文字母的位置,然后用substr 来的到中间的一部分,用于输出结果。下面就是其结果:
Hello Word
前面的符号和后面的符号都没有了。像这种用法可以用来查找分隔符,从而把一个连续的字符串分割成为几部分,达到 shell 命令中的 awk 的用法。特别是当分隔符有多个的时候,可以一次指定。例如有这样的需求:
张三|3456123, 湖南
李四,4564234| 湖北
王小二, 4433253|北京
...
我们需要以 "|" ","为分隔符,同时又要过滤空格,把每行分成相应的字段。可以作为你的一个家庭作业来试试,要求代码简洁。
1.3 string insert, replace, erase
了解了string 的操作符,查找函数和substr,其实就已经了解了string的80%的操作了。insert函数, replace函数和erase函数在使用起来相对简单。下面以一个例子来说明其应用。
string只是提供了按照位置和区间的replace函数,而不能用一个string字串来替换指定string中的另一个字串。这里写一个函数来实现这个功能:
void string_replace(string & strBig, const string & strsrc, const string &strdst) {
string::size_type pos=0;
string::size_type srclen=strsrc.size();
string::size_type dstlen=strdst.size();
while( (pos=strBig.find(strsrc, pos)) != string::npos){
strBig.replace(pos, srclen, strdst);
pos += dstlen;
}
}看看如何调用:
#i nclude <string>
#i nclude <iostream>
using namespace std;
int main() {
string strinfo="This is Winter, Winter is a programmer. Do you know Winter?";
cout<<"Orign string is :\n"<<strinfo<<endl;
string_replace(strinfo, "Winter", "wende");
cout<<"After replace Winter with wende, the string is :\n"<<strinfo<<endl;
return 0;
}其输出结果:
Orign string is :
This is Winter, Winter is a programmer. Do you know Winter?
After replace Winter with wende, the string is :
This is wende, wende is a programmer. Do you know wende?如果不用replace函数,则可以使用erase和insert来替换,也能实现string_replace函数的功能:
void string_replace(string & strBig, const string & strsrc, const string &strdst) {
string::size_type pos=0;
string::size_type srclen=strsrc.size();
string::size_type dstlen=strdst.size();
while( (pos=strBig.find(strsrc, pos)) != string::npos){
strBig.erase(pos, srclen);
strBig.insert(pos, strdst);
pos += dstlen;
}
}当然,这种方法没有使用replace来得直接。
2 string 和 C风格字符串
现在看了这么多例子,发现const char* 可以和string 直接转换,例如我们在上面的例子中,使用
string_replace(strinfo, "Winter", "wende");来代用
void string_replace(string & strBig, const string & strsrc, const string &strdst) 在C语言中只有char* 和 const char*,为了使用起来方便,string提供了三个函数满足其要求:
const charT* c_str() const
const charT* data() const
size_type copy(charT* buf, size_type n, size_type pos = 0) const 其中:
c_str 直接返回一个以\0结尾的字符串。
data 直接以数组方式返回string的内容,其大小为size()的返回值,结尾并没有\0字符。
copy 把string的内容拷贝到buf空间中。
你或许会问,c_str()的功能包含data(),那还需要data()函数干什么?看看源码:
const charT* c_str () const
{ if (length () == 0) return ""; terminate (); return data (); }原来c_str()的流程是:先调用terminate(),然后在返回data()。因此如果你对效率要求比较高,而且你的处理又不一定需要以\0的方式结束,你最好选择data()。但是对于一般的C函数中,需要以const char*为输入参数,你就要使用c_str()函数。
对于c_str() data()函数,返回的数组都是由string本身拥有,千万不可修改其内容。其原因是许多string实现的时候采用了引用机制,也就是说,有可能几个string使用同一个字符存储空间。而且你不能使用sizeof(string)来查看其大小。详细的解释和实现查看Effective STL的条款15:小心string实现的多样性。
另外在你的程序中,只在需要时才使用c_str()或者data()得到字符串,每调用一次,下次再使用就会失效,如:
string strinfo("this is Winter");
...
//最好的方式是:
foo(strinfo.c_str());
//也可以这么用:
const char* pstr=strinfo.c_str();
foo(pstr);
//不要再使用了pstr了, 下面的操作已经使pstr无效了。
strinfo += " Hello!";
foo(pstr);//错误!会遇到什么错误?当你幸运的时候pstr可能只是指向"this is Winter Hello!"的字符串,如果不幸运,就会导致程序出现其他问题,总会有一些不可遇见的错误。总之不会是你预期的那个结果。
3 string 和 Charactor Traits
了解了string的用法,该详细看看string的真相了。前面提到string 只是basic_string的一个typedef。看看basic_string 的参数:
template <class charT, class traits = char_traits<charT>,
class Allocator = allocator<charT> >
class basic_string
{
//...
}char_traits不仅是在basic_string 中有用,在basic_istream 和 basic_ostream中也需要用到。
就像Steve Donovan在过度使用C++模板中提到的,这些确实有些过头了,要不是系统自己定义了相关的一些属性,而且用了个typedef,否则还真不知道如何使用。
但复杂总有复杂道理。有了char_traits,你可以定义自己的字符串类型。当然,有了char_traits < char > 和char_traits < wchar_t > 你的需求使用已经足够了,为了更好的理解string ,咱们来看看char_traits都有哪些要求。
如果你希望使用你自己定义的字符,你必须定义包含下列成员的结构: 表达式 描述
char_type 字符类型
int_type int 类型
pos_type 位置类型
off_type 表示位置之间距离的类型
state_type 表示状态的类型
assign(c1,c2) 把字符c2赋值给c1
eq(c1,c2) 判断c1,c2 是否相等
lt(c1,c2) 判断c1是否小于c2
length(str) 判断str的长度
compare(s1,s2,n) 比较s1和s2的前n个字符
copy(s1,s2, n) 把s2的前n个字符拷贝到s1中
move(s1,s2, n) 把s2中的前n个字符移动到s1中
assign(s,n,c) 把s中的前n个字符赋值为c
find(s,n,c) 在s的前n个字符内查找c
eof() 返回end-of-file
to_int_type(c) 将c转换成int_type
to_char_type(i) 将i转换成char_type
not_eof(i) 判断i是否为EOF
eq_int_type(i1,i2) 判断i1和i2是否相等
想看看实际的例子,你可以看看sgi STL的char_traits结构源码.
现在默认的string版本中,并不支持忽略大小写的比较函数和查找函数,如果你想练练手,你可以试试改写一个char_traits , 然后生成一个case_string类, 也可以在string 上做继承,然后派生一个新的类,例如:ext_string,提供一些常用的功能,例如:
定义分隔符。给定分隔符,把string分为几个字段。
提供替换功能。例如,用winter, 替换字符串中的wende
大小写处理。例如,忽略大小写比较,转换等
整形转换。例如把"123"字符串转换为123数字。
这些都是常用的功能,如果你有兴趣可以试试。其实有人已经实现了,看看Extended STL string。如果你想偷懒,下载一个头文件就可以用,有了它确实方便了很多。要是有人能提供一个支持正则表达式的string,我会非常乐意用。
4 string 建议
使用string 的方便性就不用再说了,这里要重点强调的是string的安全性。
string并不是万能的,如果你在一个大工程中需要频繁处理字符串,而且有可能是多线程,那么你一定要慎重(当然,在多线程下你使用任何STL容器都要慎重)。
string的实现和效率并不一定是你想象的那样,如果你对大量的字符串操作,而且特别关心其效率,那么你有两个选择,首先,你可以看看你使用的STL版本中string实现的源码;另一选择是你自己写一个只提供你需要的功能的类。
string的c_str()函数是用来得到C语言风格的字符串,其返回的指针不能修改其空间。而且在下一次使用时重新调用获得新的指针。
string的data()函数返回的字符串指针不会以'\0'结束,千万不可忽视。
尽量去使用操作符,这样可以让程序更加易懂(特别是那些脚本程序员也可以看懂)
5 小结
难怪有人说:
string 使用方便功能强,我们一直用它!
6 附录
string 函数列表 函数名 描述
begin 得到指向字符串开头的Iterator
end 得到指向字符串结尾的Iterator
rbegin 得到指向反向字符串开头的Iterator
rend 得到指向反向字符串结尾的Iterator
size 得到字符串的大小
length 和size函数功能相同
max_size 字符串可能的最大大小
capacity 在不重新分配内存的情况下,字符串可能的大小
empty 判断是否为空
operator[] 取第几个元素,相当于数组
c_str 取得C风格的const char* 字符串
data 取得字符串内容地址
operator= 赋值操作符
reserve 预留空间
swap 交换函数
insert 插入字符
append 追加字符
push_back 追加字符
operator+= += 操作符
erase 删除字符串
clear 清空字符容器中所有内容
resize 重新分配空间
assign 和赋值操作符一样
replace 替代
copy 字符串到空间
find 查找
rfind 反向查找
find_first_of 查找包含子串中的任何字符,返回第一个位置
find_first_not_of 查找不包含子串中的任何字符,返回第一个位置
find_last_of 查找包含子串中的任何字符,返回最后一个位置
find_last_not_of 查找不包含子串中的任何字符,返回最后一个位置
substr 得到字串
compare 比较字符串
operator+ 字符串链接
operator== 判断是否相等
operator!= 判断是否不等于
operator< 判断是否小于
operator>> 从输入流中读入字符串
operator<< 字符串写入输出流
getline 从输入流中读入一行
#include "../../lib/sinalib/StrLib/str.h"
#include "StrLib.h"
using namespace std;
int main()
{
string strinfo="Please input your name:";
cout << strinfo ;
cin >> strinfo;
if( strinfo == "winter" )
cout << "you are winter!"<<endl;
strinfo += " , Welcome to China!";
cout << strinfo ;
string strtmp = "How are you? " + strinfo;
for(int i = 0 ; i < strtmp.size(); i ++)
cout<<strtmp<<strtmp.size();
}
前言: string 的角色
1 string 使用
1.1 充分使用string 操作符
1.2 眼花缭乱的string find 函数
1.3 string insert, replace, erase 2 string 和 C风格字符串
3 string 和 Charactor Traits
4 string 建议
5 小结
6 附录前言: string 的角色
C++ 语言是个十分优秀的语言,但优秀并不表示完美。还是有许多人不愿意使用C或者C++,为什么?原因众多,其中之一就是C/C++的文本处理功能太麻烦,用起来很不方便。以前没有接触过其他语言时,每当别人这么说,我总是不屑一顾,认为他们根本就没有领会C++的精华,或者不太懂C++,现在我接触perl, php, 和Shell脚本以后,开始理解了以前为什么有人说C++文本处理不方便了。
举例来说,如果文本格式是:用户名 电话号码,文件名name.txt
Tom 23245332
Jenny 22231231
Heny 22183942
Tom 23245332
...
现在我们需要对用户名排序,且只输出不同的姓名。
那么在shell 编程中,可以这样用:
awk '{print $1}' name.txt | sort | uniq
简单吧?
如果使用C/C++ 就麻烦了,他需要做以下工作:
先打开文件,检测文件是否打开,如果失败,则退出。
声明一个足够大得二维字符数组或者一个字符指针数组
读入一行到字符空间
然后分析一行的结构,找到空格,存入字符数组中。
关闭文件
写一个排序函数,或者使用写一个比较函数,使用qsort排序
遍历数组,比较是否有相同的,如果有,则要删除,copy...
输出信息
你可以用C++或者C语言去实现这个流程。如果一个人的主要工作就是处理这种类似的文本(例如做apache的日志统计和分析),你说他会喜欢C/C++么?
当然,有了STL,这些处理会得到很大的简化。我们可以使用 fstream来代替麻烦的fopen fread fclose, 用vector 来代替数组。最重要的是用 string来代替char * 数组,使用sort排序算法来排序,用unique 函数来去重。听起来好像很不错 。看看下面代码(例程1):
#i nclude <string>
#i nclude <iostream>
#i nclude <algorithm>
#i nclude <vector>
#i nclude <fstream>
using namespace std;
int main(){
ifstream in("name.txt");
string strtmp;
vector<string> vect;
while(getline(in, strtmp, '\n'))
vect.push_back(strtmp.substr(0, strtmp.find(' ')));
sort(vect.begin(), vect.end());
vector<string>::iterator it=unique(vect.begin(), vect.end());
copy(vect.begin(), it, ostream_iterator<string>(cout, "\n"));
return 0;
}
也还不错吧,至少会比想象得要简单得多!(代码里面没有对错误进行处理,只是为了说明问题,不要效仿).
当然,在这个文本格式中,不用vector而使用map会更有扩充性,例如,还可通过人名找电话号码等等,但是使用了map就不那么好用sort了。你可以用map试一试。
这里string的作用不只是可以存储字符串,还可以提供字符串的比较,查找等。在sort和unique函数中就默认使用了less 和equal_to函数, 上面的一段代码,其实使用了string的以下功能:
存储功能,在getline() 函数中
查找功能,在find() 函数中
子串功能,在substr() 函数中
string operator < , 默认在sort() 函数中调用
string operator == , 默认在unique() 函数中调用
总之,有了string 后,C++的字符文本处理功能总算得到了一定补充,加上配合STL其他容器使用,其在文本处理上的功能已经与perl, shell, php的距离缩小很多了。 因此掌握string 会让你的工作事半功倍。
1 string 使用
其实,string并不是一个单独的容器,只是basic_string 模板类的一个typedef 而已,相对应的还有wstring, 你在string 头文件中你会发现下面的代码:
extern "C++" {
typedef basic_string <char> string;
typedef basic_string <wchar_t> wstring;
} // extern "C++"
由于只是解释string的用法,如果没有特殊的说明,本文并不区分string 和 basic_string的区别。
string 其实相当于一个保存字符的序列容器,因此除了有字符串的一些常用操作以外,还有包含了所有的序列容器的操作。字符串的常用操作包括:增加、删除、修改、查找比较、链接、输入、输出等。详细函数列表参看附录。不要害怕这么多函数,其实有许多是序列容器带有的,平时不一定用的上。
如果你要想了解所有函数的详细用法,你需要查看basic_string,或者下载STL编程手册。这里通过实例介绍一些常用函数。
1.1 充分使用string 操作符
string 重载了许多操作符,包括 +, +=, <, =, , [], <<, >>等,正式这些操作符,对字符串操作非常方便。先看看下面这个例子:tt.cpp(例程2)
#i nclude <string>
#i nclude <iostream>
using namespace std;
int main(){
string strinfo="Please input your name:";
cout << strinfo ;
cin >> strinfo;
if( strinfo == "winter" )
cout << "you are winter!"<<endl;
else if( strinfo != "wende" )
cout << "you are not wende!"<<endl;
else if( strinfo < "winter")
cout << "your name should be ahead of winter"<<endl;
else
cout << "your name should be after of winter"<<endl;
strinfo += " , Welcome to China!";
cout << strinfo<<endl;
cout <<"Your name is :"<<endl;
string strtmp = "How are you? " + strinfo;
for(int i = 0 ; i < strtmp.size(); i ++)
cout<<strtmp;
return 0;
}
下面是程序的输出
-bash-2.05b$ make tt
c++ -O -pipe -march=pentiumpro tt.cpp -o tt
-bash-2.05b$ ./tt
Please input your name:Hero
you are not wende!
Hero , Welcome to China!
How are you? Hero , Welcome to China!
有了这些操作符,在STL中仿函数都可以直接使用string作为参数,例如 less, great, equal_to 等,因此在把string作为参数传递的时候,它的使用和int 或者float等已经没有什么区别了。例如,你可以使用:
map<string, int> mymap;
//以上默认使用了 less<string>
有了 operator + 以后,你可以直接连加,例如:
string strinfo="Winter";
string strlast="Hello " + strinfo + "!";
//你还可以这样:
string strtest="Hello " + strinfo + " Welcome" + " to China" + " !";
看见其中的特点了吗?只要你的等式里面有一个 string 对象,你就可以一直连续"+",但有一点需要保证的是,在开始的两项中,必须有一项是 string 对象。其原理很简单:
系统遇到"+"号,发现有一项是string 对象。
系统把另一项转化为一个临时 string 对象。
执行 operator + 操作,返回新的临时string 对象。
如果又发现"+"号,继续第一步操作。
由于这个等式是由左到右开始检测执行,如果开始两项都是const char* ,程序自己并没有定义两个const char* 的加法,编译的时候肯定就有问题了。
有了操作符以后,assign(), append(), compare(), at()等函数,除非有一些特殊的需求时,一般是用不上。当然at()函数还有一个功能,那就是检查下标是否合法,如果是使用:
string str="winter";
//下面一行有可能会引起程序中断错误
str[100]='!';
//下面会抛出异常:throws: out_of_range
cout<<str.at(100)<<endl;
了解了吗?如果你希望效率高,还是使用[]来访问,如果你希望稳定性好,最好使用at()来访问。
1.2 眼花缭乱的string find 函数
由于查找是使用最为频繁的功能之一,string 提供了非常丰富的查找函数。其列表如下:
函数名 描述 find 查找 rfind 反向查找 find_first_of 查找包含子串中的任何字符,返回第一个位置 find_first_not_of 查找不包含子串中的任何字符,返回第一个位置 find_last_of 查找包含子串中的任何字符,返回最后一个位置 find_last_not_of 查找不包含子串中的任何字符,返回最后一个位置 以上函数都是被重载了4次,以下是以find_first_of 函数为例说明他们的参数,其他函数和其参数一样,也就是说总共有24个函数 :
size_type find_first_of(const basic_string& s, size_type pos = 0)
size_type find_first_of(const charT* s, size_type pos, size_type n)
size_type find_first_of(const charT* s, size_type pos = 0)
size_type find_first_of(charT c, size_type pos = 0)
所有的查找函数都返回一个size_type类型,这个返回值一般都是所找到字符串的位置,如果没有找到,则返回string::npos。有一点需要特别注意,所有和string::npos的比较一定要用string::size_type来使用,不要直接使用int 或者unsigned int等类型。其实string::npos表示的是-1, 看看头文件:
template <class _CharT, class _Traits, class _Alloc>
const basic_string<_CharT,_Traits,_Alloc>::size_type
basic_string<_CharT,_Traits,_Alloc>::npos
= basic_string<_CharT,_Traits,_Alloc>::size_type) -1;
find 和 rfind 都还比较容易理解,一个是正向匹配,一个是逆向匹配,后面的参数pos都是用来指定起始查找位置。对于find_first_of 和find_last_of 就不是那么好理解。
find_first_of 是给定一个要查找的字符集,找到这个字符集中任何一个字符所在字符串中第一个位置。或许看一个例子更容易明白。
有这样一个需求:过滤一行开头和结尾的所有非英文字符。看看用string 如何实现:
#i nclude <string>
#i nclude <iostream>
using namespace std;
int main(){
string strinfo=" //*---Hello Word!......------";
string strset="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int first = strinfo.find_first_of(strset);
if(first == string::npos) {
cout<<"not find any characters"<<endl;
return -1;
}
int last = strinfo.find_last_of(strset);
if(last == string::npos) {
cout<<"not find any characters"<<endl;
return -1;
}
cout << strinfo.substr(first, last - first + 1)<<endl;
return 0;
}
这里把所有的英文字母大小写作为了需要查找的字符集,先查找第一个英文字母的位置,然后查找最后一个英文字母的位置,然后用substr 来的到中间的一部分,用于输出结果。下面就是其结果:
Hello Word
前面的符号和后面的符号都没有了。像这种用法可以用来查找分隔符,从而把一个连续的字符串分割成为几部分,达到 shell 命令中的 awk 的用法。特别是当分隔符有多个的时候,可以一次指定。例如有这样的需求:
张三|3456123, 湖南
李四,4564234| 湖北
王小二, 4433253|北京
...
我们需要以 "|" ","为分隔符,同时又要过滤空格,把每行分成相应的字段。可以作为你的一个家庭作业来试试,要求代码简洁。
1.3 string insert, replace, erase
了解了string 的操作符,查找函数和substr,其实就已经了解了string的80%的操作了。insert函数, replace函数和erase函数在使用起来相对简单。下面以一个例子来说明其应用。
string只是提供了按照位置和区间的replace函数,而不能用一个string字串来替换指定string中的另一个字串。这里写一个函数来实现这个功能:
void string_replace(string & strBig, const string & strsrc, const string &strdst) {
string::size_type pos=0;
string::size_type srclen=strsrc.size();
string::size_type dstlen=strdst.size();
while( (pos=strBig.find(strsrc, pos)) != string::npos){
strBig.replace(pos, srclen, strdst);
pos += dstlen;
}
}看看如何调用:
#i nclude <string>
#i nclude <iostream>
using namespace std;
int main() {
string strinfo="This is Winter, Winter is a programmer. Do you know Winter?";
cout<<"Orign string is :\n"<<strinfo<<endl;
string_replace(strinfo, "Winter", "wende");
cout<<"After replace Winter with wende, the string is :\n"<<strinfo<<endl;
return 0;
}其输出结果:
Orign string is :
This is Winter, Winter is a programmer. Do you know Winter?
After replace Winter with wende, the string is :
This is wende, wende is a programmer. Do you know wende?如果不用replace函数,则可以使用erase和insert来替换,也能实现string_replace函数的功能:
void string_replace(string & strBig, const string & strsrc, const string &strdst) {
string::size_type pos=0;
string::size_type srclen=strsrc.size();
string::size_type dstlen=strdst.size();
while( (pos=strBig.find(strsrc, pos)) != string::npos){
strBig.erase(pos, srclen);
strBig.insert(pos, strdst);
pos += dstlen;
}
}当然,这种方法没有使用replace来得直接。
2 string 和 C风格字符串
现在看了这么多例子,发现const char* 可以和string 直接转换,例如我们在上面的例子中,使用
string_replace(strinfo, "Winter", "wende");来代用
void string_replace(string & strBig, const string & strsrc, const string &strdst) 在C语言中只有char* 和 const char*,为了使用起来方便,string提供了三个函数满足其要求:
const charT* c_str() const
const charT* data() const
size_type copy(charT* buf, size_type n, size_type pos = 0) const 其中:
c_str 直接返回一个以\0结尾的字符串。
data 直接以数组方式返回string的内容,其大小为size()的返回值,结尾并没有\0字符。
copy 把string的内容拷贝到buf空间中。
你或许会问,c_str()的功能包含data(),那还需要data()函数干什么?看看源码:
const charT* c_str () const
{ if (length () == 0) return ""; terminate (); return data (); }原来c_str()的流程是:先调用terminate(),然后在返回data()。因此如果你对效率要求比较高,而且你的处理又不一定需要以\0的方式结束,你最好选择data()。但是对于一般的C函数中,需要以const char*为输入参数,你就要使用c_str()函数。
对于c_str() data()函数,返回的数组都是由string本身拥有,千万不可修改其内容。其原因是许多string实现的时候采用了引用机制,也就是说,有可能几个string使用同一个字符存储空间。而且你不能使用sizeof(string)来查看其大小。详细的解释和实现查看Effective STL的条款15:小心string实现的多样性。
另外在你的程序中,只在需要时才使用c_str()或者data()得到字符串,每调用一次,下次再使用就会失效,如:
string strinfo("this is Winter");
...
//最好的方式是:
foo(strinfo.c_str());
//也可以这么用:
const char* pstr=strinfo.c_str();
foo(pstr);
//不要再使用了pstr了, 下面的操作已经使pstr无效了。
strinfo += " Hello!";
foo(pstr);//错误!会遇到什么错误?当你幸运的时候pstr可能只是指向"this is Winter Hello!"的字符串,如果不幸运,就会导致程序出现其他问题,总会有一些不可遇见的错误。总之不会是你预期的那个结果。
3 string 和 Charactor Traits
了解了string的用法,该详细看看string的真相了。前面提到string 只是basic_string的一个typedef。看看basic_string 的参数:
template <class charT, class traits = char_traits<charT>,
class Allocator = allocator<charT> >
class basic_string
{
//...
}char_traits不仅是在basic_string 中有用,在basic_istream 和 basic_ostream中也需要用到。
就像Steve Donovan在过度使用C++模板中提到的,这些确实有些过头了,要不是系统自己定义了相关的一些属性,而且用了个typedef,否则还真不知道如何使用。
但复杂总有复杂道理。有了char_traits,你可以定义自己的字符串类型。当然,有了char_traits < char > 和char_traits < wchar_t > 你的需求使用已经足够了,为了更好的理解string ,咱们来看看char_traits都有哪些要求。
如果你希望使用你自己定义的字符,你必须定义包含下列成员的结构: 表达式 描述
char_type 字符类型
int_type int 类型
pos_type 位置类型
off_type 表示位置之间距离的类型
state_type 表示状态的类型
assign(c1,c2) 把字符c2赋值给c1
eq(c1,c2) 判断c1,c2 是否相等
lt(c1,c2) 判断c1是否小于c2
length(str) 判断str的长度
compare(s1,s2,n) 比较s1和s2的前n个字符
copy(s1,s2, n) 把s2的前n个字符拷贝到s1中
move(s1,s2, n) 把s2中的前n个字符移动到s1中
assign(s,n,c) 把s中的前n个字符赋值为c
find(s,n,c) 在s的前n个字符内查找c
eof() 返回end-of-file
to_int_type(c) 将c转换成int_type
to_char_type(i) 将i转换成char_type
not_eof(i) 判断i是否为EOF
eq_int_type(i1,i2) 判断i1和i2是否相等
想看看实际的例子,你可以看看sgi STL的char_traits结构源码.
现在默认的string版本中,并不支持忽略大小写的比较函数和查找函数,如果你想练练手,你可以试试改写一个char_traits , 然后生成一个case_string类, 也可以在string 上做继承,然后派生一个新的类,例如:ext_string,提供一些常用的功能,例如:
定义分隔符。给定分隔符,把string分为几个字段。
提供替换功能。例如,用winter, 替换字符串中的wende
大小写处理。例如,忽略大小写比较,转换等
整形转换。例如把"123"字符串转换为123数字。
这些都是常用的功能,如果你有兴趣可以试试。其实有人已经实现了,看看Extended STL string。如果你想偷懒,下载一个头文件就可以用,有了它确实方便了很多。要是有人能提供一个支持正则表达式的string,我会非常乐意用。
4 string 建议
使用string 的方便性就不用再说了,这里要重点强调的是string的安全性。
string并不是万能的,如果你在一个大工程中需要频繁处理字符串,而且有可能是多线程,那么你一定要慎重(当然,在多线程下你使用任何STL容器都要慎重)。
string的实现和效率并不一定是你想象的那样,如果你对大量的字符串操作,而且特别关心其效率,那么你有两个选择,首先,你可以看看你使用的STL版本中string实现的源码;另一选择是你自己写一个只提供你需要的功能的类。
string的c_str()函数是用来得到C语言风格的字符串,其返回的指针不能修改其空间。而且在下一次使用时重新调用获得新的指针。
string的data()函数返回的字符串指针不会以'\0'结束,千万不可忽视。
尽量去使用操作符,这样可以让程序更加易懂(特别是那些脚本程序员也可以看懂)
5 小结
难怪有人说:
string 使用方便功能强,我们一直用它!
6 附录
string 函数列表 函数名 描述
begin 得到指向字符串开头的Iterator
end 得到指向字符串结尾的Iterator
rbegin 得到指向反向字符串开头的Iterator
rend 得到指向反向字符串结尾的Iterator
size 得到字符串的大小
length 和size函数功能相同
max_size 字符串可能的最大大小
capacity 在不重新分配内存的情况下,字符串可能的大小
empty 判断是否为空
operator[] 取第几个元素,相当于数组
c_str 取得C风格的const char* 字符串
data 取得字符串内容地址
operator= 赋值操作符
reserve 预留空间
swap 交换函数
insert 插入字符
append 追加字符
push_back 追加字符
operator+= += 操作符
erase 删除字符串
clear 清空字符容器中所有内容
resize 重新分配空间
assign 和赋值操作符一样
replace 替代
copy 字符串到空间
find 查找
rfind 反向查找
find_first_of 查找包含子串中的任何字符,返回第一个位置
find_first_not_of 查找不包含子串中的任何字符,返回第一个位置
find_last_of 查找包含子串中的任何字符,返回最后一个位置
find_last_not_of 查找不包含子串中的任何字符,返回最后一个位置
substr 得到字串
compare 比较字符串
operator+ 字符串链接
operator== 判断是否相等
operator!= 判断是否不等于
operator< 判断是否小于
operator>> 从输入流中读入字符串
operator<< 字符串写入输出流
getline 从输入流中读入一行
http://drivers.mydrivers.com/download/196-78699-NVIDIA-GeForceFX-GeForce6-GeForce7-GeForce8-ForceWare-169.21-WHQL-For-WinXP/
FreeBSD成功对一目录下有多个小文件成功批量删除的写法:find /var/spool/clientmqueue -type f -delete
前置来自:https://jackxiang.com/post/9321/ ,cp filename{,bak} , find /var/www/ -name file -exec cp {}{,.bak} ; 递归备份文件,大括号扩展:cp和mv搭配{,}在shell作用,cp 快速备份 bash{} 扩展。 要查找一个指定后缀文件里有没有一些指定字符串,显示出文件和行号等,还是用 xargs方便,如下:
上面一句顶exec两句,如下:
背景:删除一些vim的临时文件及批量文件修改时间之外的过期文件,有用xargs的也有用exec......但是这个命令的后面这一部分得小心点写,有一个空格。
第一:花括号}后面必须得有空格.
第二:最后斜杠后面必须得有分号。
第三:exec前面有一个中横线,否则也不行,三者必须要注意。
摘自:https://jackxiang.com/post/7859/
(1)删除10天以来没有修改的文件,经常使用的短小精悍又不失效率的命令隆重出场ing:
(2)find /data/***/logs -mtime +30 //查找30天里没有被修改的文件
来自:http://jackxiang.com/post/3557/
————————————————————————————————————————————————
用man find可以看到下面的说明:
-mtime n
Files data was last modified n*24 hours ago.
参考mtime英文帮助来自:http://zhidao.baidu.com/link?url=EaXnp9x5fKWF40ACYNZlx1tA0Pyw_qHd7N7cWQ246XiS8fXSLmetBTxlqFT9HHgSB7g4cSHemtozCjrCIdYVjK
____________________________________________________________________________
使用find和xargs
有时可能需要在系统中查找具有某一特征的文件(例如文件权限、文件属主、文件长度、
文件类型等等)。这样做可能有很多原因。可能出于安全性的考虑,或是一般性的系统管理任
务,或许只是为了找出一个不知保存在什么地方的文件。F i n d是一个非常有效的工具,它可
以遍历当前目录甚至于整个文件系统来查找某些文件或目录。
在本章中,我们介绍以下内容:
* find命令选项。
* 使用f i n d命令不同选项的例子。
* 配合f i n d使用x a rg s命令的例子。
由于f i n d具有如此强大的功能,所以它的选项也很多,其中大部分选项都值得我们花时间
来了解一下。即使系统中含有网络文件系统( N F S ),f i n d命令在该文件系统中同样有效,只要
你具有相应的权限。
在运行一个非常消耗资源的f i n d命令时,很多人都倾向于把它放在后台执行,因为遍历一
个大的文件系统可能会花费很长的时间(这里是指3 0 G字节以上的文件系统)。
F i n d命令的一般形式为:
find pathname -options [-print -exec -ok]
让我们来看看该命令的参数:
pathname find命令所查找的目录路径。例如用.来表示当前目录,用/来表示系统根目录。
-print find命令将匹配的文件输出到标准输出。
-exec find命令对匹配的文件执行该参数所给出的s h e l l命令。相应命令的形式为' c o m m -
and' {} \;,注意{ }和\;之间的空格。
-ok 和- e x e c的作用相同,只不过以一种更为安全的模式来执行该参数所给出的s h e l l命令,
在执行每一个命令之前,都会给出提示,让用户来确定是否执行。
find命令选项
f i n d命令有很多选项或表达式,每一个选项前面跟随一个横杠-。让我们先来看一下该命
令的主要选项,然后再给出一些例子。
-name 按照文件名查找文件。
-perm 按照文件权限来查找文件。
-prune 使用这一选项可以使f i n d命令不在当前指定的目录中查找,如果同时使用了- d e p t h
选项,那么- p r u n e选项将被f i n d命令忽略。
-user 按照文件属主来查找文件。
-group 按照文件所属的组来查找文件。
-mtime -n +n 按照文件的更改时间来查找文件, - n表示文件更改时间距现在n天以内,+ n
表示文件更改时间距现在n天以前。F i n d命令还有- a t i m e和- c t i m e选项,但它们都和- m t i m e选项
相似,所以我们在这里只介绍- m t i m e选项。
-nogroup 查找无有效所属组的文件,即该文件所属的组在/ e t c / g r o u p s中不存在。
-nouser 查找无有效属主的文件,即该文件的属主在/ e t c / p a s s w d中不存在。
-newer file1 ! file2 查找更改时间比文件f i l e 1新但比文件f i l e 2旧的文件。
-type 查找某一类型的文件,诸如:
b - 块设备文件。
d - 目录。
c - 字符设备文件。
p - 管道文件。
l - 符号链接文件。
f - 普通文件。
-size n[c] 查找文件长度为n块的文件,带有c时表示文件长度以字节计。
-depth 在查找文件时,首先查找当前目录中的文件,然后再在其子目录中查找。
-fstype 查找位于某一类型文件系统中的文件,这些文件系统类型通常可以在配置文件
/ e t c / f s t a b中找到,该配置文件中包含了本系统中有关文件系统的信息。
-mount 在查找文件时不跨越文件系统m o u n t点。
-follow 如果f i n d命令遇到符号链接文件,就跟踪至链接所指向的文件。
-cpio 对匹配的文件使用c p i o命令,将这些文件备份到磁带设备中。
使用name选项
文件名选项是f i n d命令最常用的选项,要么单独使用该选项,要么和其他选项一起使用。
可以使用某种文件名模式来匹配文件,记住要用引号将文件名模式引起来。
不管当前路径是什么,如果想要在自己的根目录$ H O M E中查找文件名符合* . t x t的文件,
使用~作为' p a t h n a m e参数,波浪号~代表了你的$ H O M E目录。
$ find ~ -name "*.txt" -print
想要在当前目录及子目录中查找所有的' * . t x t'文件,可以用:
$ find . -name "*.txt" -print
想要的当前目录及子目录中查找文件名以一个大写字母开头的文件,可以用:
$ find . -name "[A-Z]*" -print
想要在/ e t c目录中查找文件名以h o s t开头的文件,可以用:
$ find /etc -name "host*" -print
想要查找$ H O M E目录中的文件,可以用:
$ find ~ -name "*" -print 或find . -print
要想让系统高负荷运行,就从根目录开始查找所有的文件。如果希望在系统管理员那里
保留一个好印象的话,最好在这么做之前考虑清楚!
$ find / -name "*" -print
如果想在当前目录查找文件名以两个小写字母开头,跟着是两个数字,最后是* . t x t的文
件,下面的命令就能够返回名为a x 3 7 . t x t的文件:
$ find . -name "[a-z][a-z][0--9][0--9].txt" -print
使用perm选项
如果希望按照文件权限模式来查找文件的话,可以采用- p e r m选项。你可能需要找到所有
用户都具有执行权限的文件,或是希望查看某个用户目录下的文件权限类型。在使用这一选
项的时候,最好使用八进制的权限表示法。
为了在当前目录下查找文件权限位为7 5 5的文件,即文件属主可以读、写、执行,其他用
户可以读、执行的文件,可以用:
$ find . -perm 755 -print
如果希望在当前目录下查找所有用户都可读、写、执行的文件(要小心这种情况),我们
可以使用f i n d命令的- p e r m选项。在八进制数字前面要加一个横杠-。在下面的命令中- p e r m代
表按照文件权限查找,而' 0 0 7'和你在c h m o d命令的绝对模式中所采用的表示法完全相同。
$ find . -perm -007 -print
忽略某个目录
如果在查找文件时希望忽略某个目录,因为你知道那个目录中没有你所要查找的文件,
那么可以使用- p r u n e选项来指出需要忽略的目录。在使用- p r u n e选项时要当心,因为如果你同
时使用了- d e p t h选项,那么- p r u n e选项就会被f i n d命令忽略。
如果希望在/ a p p s目录下查找文件,但不希望在/ a p p s / b i n目录下查找,可以用:
$ find /apps -name "/apps/bin" -prune -o -print
2.1.4 使用user和nouser选项
如果希望按照文件属主查找文件,可以给出相应的用户名。例如,在$ H O M E目录中查找
文件属主为d a v e的文件,可以用:
$ find ~ -user dave -print
在/ e t c目录下查找文件属主为u u c p的文件:
$ find /etc -user uucp -print
为了查找属主帐户已经被删除的文件,可以使用- n o u s e r选项。这样就能够找到那些属主
在/ e t c / p a s s w d文件中没有有效帐户的文件。在使用- n o u s e r选项时,不必给出用户名; f i n d命令
能够为你完成相应的工作。例如,希望在/ h o m e目录下查找所有的这类文件,可以用:
$ find /home -nouser -print
使用group和nogroup选项
就像u s e r和n o u s e r选项一样,针对文件所属于的用户组, f i n d命令也具有同样的选项,为
了在/ a p p s目录下查找属于a c c t s用户组的文件,可以用:
$ find /apps -group accts -print
要查找没有有效所属用户组的所有文件,可以使用n o g r o u p选项。下面的f i n d命令从文件
系统的根目录处查找这样的文件
$ fine/-nogroup-print
按照更改时间查找文件
如果希望按照更改时间来查找文件,可以使用m t i m e选项。如果系统突然没有可用空间了,
很有可能某一个文件的长度在此期间增长迅速,这时就可以用m t i m e选项来查找这样的文件。
用减号-来限定更改时间在距今n日以内的文件,而用加号+来限定更改时间在距今n日以前的
文件。
希望在系统根目录下查找更改时间在5日以内的文件,可以用:
$ find / -mtime -5 -print
为了在/ v a r / a d m目录下查找更改时间在3日以前的文件,可以用:
$ find /var/adm -mtime +3 -print
查找比某个文件新或旧的文件
如果希望查找更改时间比某个文件新但比另一个文件旧的所有文件,可以使用- n e w e r选
项。它的一般形式为:
newest_file_name ! oldest_file_name
其中,!是逻辑非符号。
这里有两个文件,它们的更改时间大约相差两天。
下面给出的f i n d命令能够查找更改时间比文件a g e . a w k新但比文件b e l t s . a w k旧的文件:
如果想使用f i n d命令的这一选项来查找更改时间在两个小时以内的文件,除非有一个现成
的文件其更改时间恰好在两个小时以前,否则就没有可用来比较更改时间的文件。为了解决
这一问题,可以首先创建一个文件并将其日期和时间戳设置为所需要的时间。这可以用t o u c h
命令来实现。
假设现在的时间是2 3 : 4 0,希望查找更改时间在两个小时以内的文件,可以首先创建这样
一个文件:
一个符合要求的文件已经被创建;这里我们假设今天是五月四日,而该文件的更改时间
是2 1 : 4 0,比现在刚好早两个小时。
现在我们就可以使用f i n d命令的- n e w e r选项在当前目录下查找所有更改时间在两个小时以
内的文件:
$ find . -newer dstamp -print
使用type选项
U N I X或L I N U X系统中有若干种不同的文件类型,这部分内容我们在前面的章节已经做了
介绍,这里就不再赘述。如果要在/ e t c目录下查找所有的目录,可以用:
$ find /etc -type d -print
为了在当前目录下查找除目录以外的所有类型的文件,可以用:
$ find . ! -type d -print
为了在/ e t c目录下查找所有的符号链接文件,可以用:
$ find /etc -type l -print
2.1.9 使用size选项
可以按照文件长度来查找文件,这里所指的文件长度既可以用块( b l o c k)来计量,也可
以用字节来计量。以字节计量文件长度的表达形式为N c;以块计量文件长度只用数字表示即
可。
就我个人而言,我总是使用以字节计的方式,在按照文件长度查找文件时,大多数人都
喜欢使用这种以字节表示的文件长度,而不用块的数目来表示,除非是在查看文件系统的大
小,因为这时使用块来计量更容易转换。
为了在当前目录下查找文件长度大于1 M字节的文件,可以用:
$ find . -size +1000000c -print
为了在/ h o m e / a p a c h e目录下查找文件长度恰好为1 0 0字节的文件,可以用:
$ find /home/apache -size 100c -print
为了在当前目录下查找长度超过1 0块的文件(一块等于5 1 2字节),可以用:
$ find . -size +10 -print
使用depth选项
在使用f i n d命令时,可能希望先匹配所有的文件,再在子目录中查找。使用d e p t h选项就
可以使f i n d命令这样做。这样做的一个原因就是,当在使用f i n d命令向磁带上备份文件系统时,
希望首先备份所有的文件,其次再备份子目录中的文件。
在下面的例子中, f i n d命令从文件系统的根目录开始,查找一个名为C O N . F I L E的文件。
它将首先匹配所有的文件然后再进入子目录中查找。
$ find / -name "CON.FILE" -depth -print
使用mount选项
在当前的文件系统中查找文件(不进入其他文件系统),可以使用f i n d命令的m o u n t选项。
在下面的例子中,我们从当前目录开始查找位于本文件系统中文件名以X C结尾的文件:
$ find . -name "*.XC" -mount -print
使用cpio选项
c p i o命令可以用来向磁带设备备份文件或从中恢复文件。可以使用f i n d命令在整个文件系
统中(更多的情况下是在部分文件系统中)查找文件,然后用c p i o命令将其备份到磁带上。
如果希望使用c p i o命令备份/ e t c、/ h o m e和/ a p p s目录中的文件,可以使用下面所给出的命
令,不过要记住你是在文件系统的根目录下:
(在上面的例子中,第一行末尾的\告诉s h e l l命令还未结束,忽略\后面的回车。)
在上面的例子中,应当注意到路径中缺少/。这叫作相对路径。之所以使用相对路径,是
因为在从磁带中恢复这些文件的时候,可以选择恢复文件的路径。例如,可以将这些文件先
恢复到另外一个目录中,对它们进行某些操作后,再恢复到原始目录中。如果在备份时使用
了绝对路径,例如/ e t c,那么在恢复时,就只能恢复到/ e t c目录中去,别无其他选择。在上面
的例子中,我告诉f i n d命令首先进入/ e t c目录,然后是/ h o m e和/ a p p s目录,先匹配这些目录下
的文件,然后再匹配其子目录中的文件,所有这些结果将通过管道传递给c p i o命令进行备份。
顺便说一下,在上面的例子中c p i o命令使用了C 6 5 5 3 6选项,我本可以使用B选项,不过这
样每块的大小只有5 1 2 字节,而使用了C 6 5 5 3 6 选项后,块的大小变成了6 4 K 字节
(6 5 5 3 6 / 1 0 2 4)。
使用exec或ok来执行shell命令
当匹配到一些文件以后,可能希望对其进行某些操作,这时就可以使用- e x e c选项。一旦
f i n d命令匹配到了相应的文件,就可以用- e x e c选项中的命令对其进行操作(在有些操作系统
中只允许- e x e c选项执行诸如l s或ls -l这样的命令)。大多数用户使用这一选项是为了查找旧文
件并删除它们。这里我强烈地建议你在真正执行r m命令删除文件之前,最好先用l s命令看一
下,确认它们是所要删除的文件。
e x e c选项后面跟随着所要执行的命令,然后是一对儿{ },一个空格和一个\,最后是一个
分号。
为了使用e x e c选项,必须要同时使用p r i n t选项。如果验证一下f i n d命令,会发现该命令只
输出从当前路径起的相对路径及文件名。
为了用ls -l命令列出所匹配到的文件,可以把ls -l命令放在f i n d命令的- e x e c选项中,例如:
上面的例子中,f i n d命令匹配到了当前目录下的所有普通文件,并在- e x e c选项中使用ls -l
命令将它们列出。
为了在/ l o g s目录中查找更改时间在5日以前的文件并删除它们,可以用:
$ find logs -type f -mtime +5 -exec rm {} \;
记住,在s h e l l中用任何方式删除文件之前,应当先查看相应的文件,一定要小心!
当使用诸如m v或r m命令时,可以使用- e x e c选项的安全模式。它将在对每个匹配到的文件
进行操作之前提示你。在下面的例子中, f i n d命令在当前目录中查找所有文件名以. L O G结尾、
更改时间在5日以上的文件,并删除它们,只不过在删除之前先给出提示。
按y键删除文件,按n键不删除。
任何形式的命令都可以在- e x e c选项中使用。在下面的例子中我们使用g r e p命令。f i n d命令
首先匹配所有文件名为" p a s s w d *"的文件,例如p a s s w d、p a s s w d . o l d、p a s s w d . b a k,然后执
行g r e p命令看看在这些文件中是否存在一个r o u n d e r用户。
find命令的例子
我们已经介绍了f i n d命令的基本选项,下面给出f i n d命令的一些其他的例子。
为了匹配$ H O M E目录下的所有文件,下面两种方法都可以使用:
$ find $HOME -print
$ find ~ -print
为了在当前目录中查找s u i d置位,文件属主具有读、写、执行权限,并且文件所属组的用
户和其他用户具有读和执行的权限的文件,可以用:
$ find . -type f -perm 4755 -print
为了查找系统中所有文件长度为0的普通文件,并列出它们的完整路径,可以用:
$ find / -type f -size 0 -exec ls -l {} \;
为了查找/ v a r / l o g s目录中更改时间在7日以前的普通文件,并删除它们,可以用:
$ find /var/logs -type f -mtime +7 -exec rm {} \;
为了查找系统中所有属于a u d i t组的文件,可以用:
$find /-name -group audit -print
我们的一个审计系统每天创建一个审计日志文件。日志文件名的最后含有数字,这样我
们一眼就可以看出哪个文件是最新的,哪个是最旧的。A d m i n . l o g 文件编上了序号:
a d m i n . l o g . 0 0 1、a d m i n . l o g . 0 0 2等等。下面的f i n d命令将删除/ l o g s目录中访问时间在7日以前、
含有数字后缀的a d m i n . l o g文件。该命令只检查三位数字,所以相应日志文件的后缀不要超过
9 9 9。
$ find /logs -name 'admin.log[0-9][0-9][0-9] '-atime +7 -exec rm {} \;
为了查找当前文件系统中的所有目录并排序,可以用:
$ find . -type d -print -local -mount |sort
为了查找系统中所有的r m t磁带设备,可以用:
$ find /dev/rmt -print
xargs
在使用f i n d命令的- e x e c选项处理匹配到的文件时, f i n d命令将所有匹配到的文件一起传递
给e x e c执行。不幸的是,有些系统对能够传递给e x e c的命令长度有限制,这样在f i n d命令运行
几分钟之后,就会出现溢出错误。错误信息通常是"参数列太长"或"参数列溢出"。这就是
x a rg s命令的用处所在,特别是与f i n d命令一起使用。F i n d命令把匹配到的文件传递给x a rg s命
令,而x a rg s命令每次只获取一部分文件而不是全部,不像- e x e c选项那样。这样它可以先处理
最先获取的一部分文件,然后是下一批,并如此继续下去。在有些系统中,使用- e x e c选项会
为处理每一个匹配到的文件而发起一个相应的进程,并非将匹配到的文件全部作为参数一次
执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;而使用
x a rg s命令则只有一个进程。另外,在使用x a rg s命令时,究竟是一次获取所有的参数,还是分
批取得参数,以及每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参
数来确定。
让我们来看看x a rg s命令是如何同f i n d命令一起使用的,并给出一些例子。
下面的例子查找系统中的每一个普通文件,然后使用x a rg s命令来测试它们分别属于哪类
文件:
下面的例子在整个系统中查找内存信息转储文件(core dump) ,然后把结果保存到
/tmp/core.log 文件中:
$ find . -name "core" -print | xargs echo "" >/tmp/core.log
下面的例子在/ a p p s / a u d i t目录下查找所有用户具有读、写和执行权限的文件,并收回相应
的写权限:
$ find /apps/audit -perm -7 -print | xargs chmod o-w
在下面的例子中,我们用g r e p命令在所有的普通文件中搜索d e v i c e这个词:
$ find / -type f -print | xargs grep "device"
在下面的例子中,我们用g r e p命令在当前目录下的所有普通文件中搜索D B O这个词:
$ find . -name \ *-type f -print | xargs grep "DBO"
注意,在上面的例子中, \用来取消f i n d命令中的*在s h e l l中的特殊含义。
小结
f i n d命令是一个非常优秀的工具,它可以按照用户指定的准则来匹配文件。使用e x e c和
x a rg s可以使用户对所匹配到的文件执行几乎所有的命令。
前置来自:https://jackxiang.com/post/9321/ ,cp filename{,bak} , find /var/www/ -name file -exec cp {}{,.bak} ; 递归备份文件,大括号扩展:cp和mv搭配{,}在shell作用,cp 快速备份 bash{} 扩展。 要查找一个指定后缀文件里有没有一些指定字符串,显示出文件和行号等,还是用 xargs方便,如下:
上面一句顶exec两句,如下:
背景:删除一些vim的临时文件及批量文件修改时间之外的过期文件,有用xargs的也有用exec......但是这个命令的后面这一部分得小心点写,有一个空格。
第一:花括号}后面必须得有空格.
第二:最后斜杠后面必须得有分号。
第三:exec前面有一个中横线,否则也不行,三者必须要注意。
摘自:https://jackxiang.com/post/7859/
(1)删除10天以来没有修改的文件,经常使用的短小精悍又不失效率的命令隆重出场ing:
(2)find /data/***/logs -mtime +30 //查找30天里没有被修改的文件
来自:http://jackxiang.com/post/3557/
————————————————————————————————————————————————
用man find可以看到下面的说明:
-mtime n
Files data was last modified n*24 hours ago.
参考mtime英文帮助来自:http://zhidao.baidu.com/link?url=EaXnp9x5fKWF40ACYNZlx1tA0Pyw_qHd7N7cWQ246XiS8fXSLmetBTxlqFT9HHgSB7g4cSHemtozCjrCIdYVjK
____________________________________________________________________________
使用find和xargs
有时可能需要在系统中查找具有某一特征的文件(例如文件权限、文件属主、文件长度、
文件类型等等)。这样做可能有很多原因。可能出于安全性的考虑,或是一般性的系统管理任
务,或许只是为了找出一个不知保存在什么地方的文件。F i n d是一个非常有效的工具,它可
以遍历当前目录甚至于整个文件系统来查找某些文件或目录。
在本章中,我们介绍以下内容:
* find命令选项。
* 使用f i n d命令不同选项的例子。
* 配合f i n d使用x a rg s命令的例子。
由于f i n d具有如此强大的功能,所以它的选项也很多,其中大部分选项都值得我们花时间
来了解一下。即使系统中含有网络文件系统( N F S ),f i n d命令在该文件系统中同样有效,只要
你具有相应的权限。
在运行一个非常消耗资源的f i n d命令时,很多人都倾向于把它放在后台执行,因为遍历一
个大的文件系统可能会花费很长的时间(这里是指3 0 G字节以上的文件系统)。
F i n d命令的一般形式为:
find pathname -options [-print -exec -ok]
让我们来看看该命令的参数:
pathname find命令所查找的目录路径。例如用.来表示当前目录,用/来表示系统根目录。
-print find命令将匹配的文件输出到标准输出。
-exec find命令对匹配的文件执行该参数所给出的s h e l l命令。相应命令的形式为' c o m m -
and' {} \;,注意{ }和\;之间的空格。
-ok 和- e x e c的作用相同,只不过以一种更为安全的模式来执行该参数所给出的s h e l l命令,
在执行每一个命令之前,都会给出提示,让用户来确定是否执行。
find命令选项
f i n d命令有很多选项或表达式,每一个选项前面跟随一个横杠-。让我们先来看一下该命
令的主要选项,然后再给出一些例子。
-name 按照文件名查找文件。
-perm 按照文件权限来查找文件。
-prune 使用这一选项可以使f i n d命令不在当前指定的目录中查找,如果同时使用了- d e p t h
选项,那么- p r u n e选项将被f i n d命令忽略。
-user 按照文件属主来查找文件。
-group 按照文件所属的组来查找文件。
-mtime -n +n 按照文件的更改时间来查找文件, - n表示文件更改时间距现在n天以内,+ n
表示文件更改时间距现在n天以前。F i n d命令还有- a t i m e和- c t i m e选项,但它们都和- m t i m e选项
相似,所以我们在这里只介绍- m t i m e选项。
-nogroup 查找无有效所属组的文件,即该文件所属的组在/ e t c / g r o u p s中不存在。
-nouser 查找无有效属主的文件,即该文件的属主在/ e t c / p a s s w d中不存在。
-newer file1 ! file2 查找更改时间比文件f i l e 1新但比文件f i l e 2旧的文件。
-type 查找某一类型的文件,诸如:
b - 块设备文件。
d - 目录。
c - 字符设备文件。
p - 管道文件。
l - 符号链接文件。
f - 普通文件。
-size n[c] 查找文件长度为n块的文件,带有c时表示文件长度以字节计。
-depth 在查找文件时,首先查找当前目录中的文件,然后再在其子目录中查找。
-fstype 查找位于某一类型文件系统中的文件,这些文件系统类型通常可以在配置文件
/ e t c / f s t a b中找到,该配置文件中包含了本系统中有关文件系统的信息。
-mount 在查找文件时不跨越文件系统m o u n t点。
-follow 如果f i n d命令遇到符号链接文件,就跟踪至链接所指向的文件。
-cpio 对匹配的文件使用c p i o命令,将这些文件备份到磁带设备中。
使用name选项
文件名选项是f i n d命令最常用的选项,要么单独使用该选项,要么和其他选项一起使用。
可以使用某种文件名模式来匹配文件,记住要用引号将文件名模式引起来。
不管当前路径是什么,如果想要在自己的根目录$ H O M E中查找文件名符合* . t x t的文件,
使用~作为' p a t h n a m e参数,波浪号~代表了你的$ H O M E目录。
$ find ~ -name "*.txt" -print
想要在当前目录及子目录中查找所有的' * . t x t'文件,可以用:
$ find . -name "*.txt" -print
想要的当前目录及子目录中查找文件名以一个大写字母开头的文件,可以用:
$ find . -name "[A-Z]*" -print
想要在/ e t c目录中查找文件名以h o s t开头的文件,可以用:
$ find /etc -name "host*" -print
想要查找$ H O M E目录中的文件,可以用:
$ find ~ -name "*" -print 或find . -print
要想让系统高负荷运行,就从根目录开始查找所有的文件。如果希望在系统管理员那里
保留一个好印象的话,最好在这么做之前考虑清楚!
$ find / -name "*" -print
如果想在当前目录查找文件名以两个小写字母开头,跟着是两个数字,最后是* . t x t的文
件,下面的命令就能够返回名为a x 3 7 . t x t的文件:
$ find . -name "[a-z][a-z][0--9][0--9].txt" -print
使用perm选项
如果希望按照文件权限模式来查找文件的话,可以采用- p e r m选项。你可能需要找到所有
用户都具有执行权限的文件,或是希望查看某个用户目录下的文件权限类型。在使用这一选
项的时候,最好使用八进制的权限表示法。
为了在当前目录下查找文件权限位为7 5 5的文件,即文件属主可以读、写、执行,其他用
户可以读、执行的文件,可以用:
$ find . -perm 755 -print
如果希望在当前目录下查找所有用户都可读、写、执行的文件(要小心这种情况),我们
可以使用f i n d命令的- p e r m选项。在八进制数字前面要加一个横杠-。在下面的命令中- p e r m代
表按照文件权限查找,而' 0 0 7'和你在c h m o d命令的绝对模式中所采用的表示法完全相同。
$ find . -perm -007 -print
忽略某个目录
如果在查找文件时希望忽略某个目录,因为你知道那个目录中没有你所要查找的文件,
那么可以使用- p r u n e选项来指出需要忽略的目录。在使用- p r u n e选项时要当心,因为如果你同
时使用了- d e p t h选项,那么- p r u n e选项就会被f i n d命令忽略。
如果希望在/ a p p s目录下查找文件,但不希望在/ a p p s / b i n目录下查找,可以用:
$ find /apps -name "/apps/bin" -prune -o -print
2.1.4 使用user和nouser选项
如果希望按照文件属主查找文件,可以给出相应的用户名。例如,在$ H O M E目录中查找
文件属主为d a v e的文件,可以用:
$ find ~ -user dave -print
在/ e t c目录下查找文件属主为u u c p的文件:
$ find /etc -user uucp -print
为了查找属主帐户已经被删除的文件,可以使用- n o u s e r选项。这样就能够找到那些属主
在/ e t c / p a s s w d文件中没有有效帐户的文件。在使用- n o u s e r选项时,不必给出用户名; f i n d命令
能够为你完成相应的工作。例如,希望在/ h o m e目录下查找所有的这类文件,可以用:
$ find /home -nouser -print
使用group和nogroup选项
就像u s e r和n o u s e r选项一样,针对文件所属于的用户组, f i n d命令也具有同样的选项,为
了在/ a p p s目录下查找属于a c c t s用户组的文件,可以用:
$ find /apps -group accts -print
要查找没有有效所属用户组的所有文件,可以使用n o g r o u p选项。下面的f i n d命令从文件
系统的根目录处查找这样的文件
$ fine/-nogroup-print
按照更改时间查找文件
如果希望按照更改时间来查找文件,可以使用m t i m e选项。如果系统突然没有可用空间了,
很有可能某一个文件的长度在此期间增长迅速,这时就可以用m t i m e选项来查找这样的文件。
用减号-来限定更改时间在距今n日以内的文件,而用加号+来限定更改时间在距今n日以前的
文件。
希望在系统根目录下查找更改时间在5日以内的文件,可以用:
$ find / -mtime -5 -print
为了在/ v a r / a d m目录下查找更改时间在3日以前的文件,可以用:
$ find /var/adm -mtime +3 -print
查找比某个文件新或旧的文件
如果希望查找更改时间比某个文件新但比另一个文件旧的所有文件,可以使用- n e w e r选
项。它的一般形式为:
newest_file_name ! oldest_file_name
其中,!是逻辑非符号。
这里有两个文件,它们的更改时间大约相差两天。
下面给出的f i n d命令能够查找更改时间比文件a g e . a w k新但比文件b e l t s . a w k旧的文件:
如果想使用f i n d命令的这一选项来查找更改时间在两个小时以内的文件,除非有一个现成
的文件其更改时间恰好在两个小时以前,否则就没有可用来比较更改时间的文件。为了解决
这一问题,可以首先创建一个文件并将其日期和时间戳设置为所需要的时间。这可以用t o u c h
命令来实现。
假设现在的时间是2 3 : 4 0,希望查找更改时间在两个小时以内的文件,可以首先创建这样
一个文件:
一个符合要求的文件已经被创建;这里我们假设今天是五月四日,而该文件的更改时间
是2 1 : 4 0,比现在刚好早两个小时。
现在我们就可以使用f i n d命令的- n e w e r选项在当前目录下查找所有更改时间在两个小时以
内的文件:
$ find . -newer dstamp -print
使用type选项
U N I X或L I N U X系统中有若干种不同的文件类型,这部分内容我们在前面的章节已经做了
介绍,这里就不再赘述。如果要在/ e t c目录下查找所有的目录,可以用:
$ find /etc -type d -print
为了在当前目录下查找除目录以外的所有类型的文件,可以用:
$ find . ! -type d -print
为了在/ e t c目录下查找所有的符号链接文件,可以用:
$ find /etc -type l -print
2.1.9 使用size选项
可以按照文件长度来查找文件,这里所指的文件长度既可以用块( b l o c k)来计量,也可
以用字节来计量。以字节计量文件长度的表达形式为N c;以块计量文件长度只用数字表示即
可。
就我个人而言,我总是使用以字节计的方式,在按照文件长度查找文件时,大多数人都
喜欢使用这种以字节表示的文件长度,而不用块的数目来表示,除非是在查看文件系统的大
小,因为这时使用块来计量更容易转换。
为了在当前目录下查找文件长度大于1 M字节的文件,可以用:
$ find . -size +1000000c -print
为了在/ h o m e / a p a c h e目录下查找文件长度恰好为1 0 0字节的文件,可以用:
$ find /home/apache -size 100c -print
为了在当前目录下查找长度超过1 0块的文件(一块等于5 1 2字节),可以用:
$ find . -size +10 -print
使用depth选项
在使用f i n d命令时,可能希望先匹配所有的文件,再在子目录中查找。使用d e p t h选项就
可以使f i n d命令这样做。这样做的一个原因就是,当在使用f i n d命令向磁带上备份文件系统时,
希望首先备份所有的文件,其次再备份子目录中的文件。
在下面的例子中, f i n d命令从文件系统的根目录开始,查找一个名为C O N . F I L E的文件。
它将首先匹配所有的文件然后再进入子目录中查找。
$ find / -name "CON.FILE" -depth -print
使用mount选项
在当前的文件系统中查找文件(不进入其他文件系统),可以使用f i n d命令的m o u n t选项。
在下面的例子中,我们从当前目录开始查找位于本文件系统中文件名以X C结尾的文件:
$ find . -name "*.XC" -mount -print
使用cpio选项
c p i o命令可以用来向磁带设备备份文件或从中恢复文件。可以使用f i n d命令在整个文件系
统中(更多的情况下是在部分文件系统中)查找文件,然后用c p i o命令将其备份到磁带上。
如果希望使用c p i o命令备份/ e t c、/ h o m e和/ a p p s目录中的文件,可以使用下面所给出的命
令,不过要记住你是在文件系统的根目录下:
(在上面的例子中,第一行末尾的\告诉s h e l l命令还未结束,忽略\后面的回车。)
在上面的例子中,应当注意到路径中缺少/。这叫作相对路径。之所以使用相对路径,是
因为在从磁带中恢复这些文件的时候,可以选择恢复文件的路径。例如,可以将这些文件先
恢复到另外一个目录中,对它们进行某些操作后,再恢复到原始目录中。如果在备份时使用
了绝对路径,例如/ e t c,那么在恢复时,就只能恢复到/ e t c目录中去,别无其他选择。在上面
的例子中,我告诉f i n d命令首先进入/ e t c目录,然后是/ h o m e和/ a p p s目录,先匹配这些目录下
的文件,然后再匹配其子目录中的文件,所有这些结果将通过管道传递给c p i o命令进行备份。
顺便说一下,在上面的例子中c p i o命令使用了C 6 5 5 3 6选项,我本可以使用B选项,不过这
样每块的大小只有5 1 2 字节,而使用了C 6 5 5 3 6 选项后,块的大小变成了6 4 K 字节
(6 5 5 3 6 / 1 0 2 4)。
使用exec或ok来执行shell命令
当匹配到一些文件以后,可能希望对其进行某些操作,这时就可以使用- e x e c选项。一旦
f i n d命令匹配到了相应的文件,就可以用- e x e c选项中的命令对其进行操作(在有些操作系统
中只允许- e x e c选项执行诸如l s或ls -l这样的命令)。大多数用户使用这一选项是为了查找旧文
件并删除它们。这里我强烈地建议你在真正执行r m命令删除文件之前,最好先用l s命令看一
下,确认它们是所要删除的文件。
e x e c选项后面跟随着所要执行的命令,然后是一对儿{ },一个空格和一个\,最后是一个
分号。
为了使用e x e c选项,必须要同时使用p r i n t选项。如果验证一下f i n d命令,会发现该命令只
输出从当前路径起的相对路径及文件名。
为了用ls -l命令列出所匹配到的文件,可以把ls -l命令放在f i n d命令的- e x e c选项中,例如:
上面的例子中,f i n d命令匹配到了当前目录下的所有普通文件,并在- e x e c选项中使用ls -l
命令将它们列出。
为了在/ l o g s目录中查找更改时间在5日以前的文件并删除它们,可以用:
$ find logs -type f -mtime +5 -exec rm {} \;
记住,在s h e l l中用任何方式删除文件之前,应当先查看相应的文件,一定要小心!
当使用诸如m v或r m命令时,可以使用- e x e c选项的安全模式。它将在对每个匹配到的文件
进行操作之前提示你。在下面的例子中, f i n d命令在当前目录中查找所有文件名以. L O G结尾、
更改时间在5日以上的文件,并删除它们,只不过在删除之前先给出提示。
按y键删除文件,按n键不删除。
任何形式的命令都可以在- e x e c选项中使用。在下面的例子中我们使用g r e p命令。f i n d命令
首先匹配所有文件名为" p a s s w d *"的文件,例如p a s s w d、p a s s w d . o l d、p a s s w d . b a k,然后执
行g r e p命令看看在这些文件中是否存在一个r o u n d e r用户。
find命令的例子
我们已经介绍了f i n d命令的基本选项,下面给出f i n d命令的一些其他的例子。
为了匹配$ H O M E目录下的所有文件,下面两种方法都可以使用:
$ find $HOME -print
$ find ~ -print
为了在当前目录中查找s u i d置位,文件属主具有读、写、执行权限,并且文件所属组的用
户和其他用户具有读和执行的权限的文件,可以用:
$ find . -type f -perm 4755 -print
为了查找系统中所有文件长度为0的普通文件,并列出它们的完整路径,可以用:
$ find / -type f -size 0 -exec ls -l {} \;
为了查找/ v a r / l o g s目录中更改时间在7日以前的普通文件,并删除它们,可以用:
$ find /var/logs -type f -mtime +7 -exec rm {} \;
为了查找系统中所有属于a u d i t组的文件,可以用:
$find /-name -group audit -print
我们的一个审计系统每天创建一个审计日志文件。日志文件名的最后含有数字,这样我
们一眼就可以看出哪个文件是最新的,哪个是最旧的。A d m i n . l o g 文件编上了序号:
a d m i n . l o g . 0 0 1、a d m i n . l o g . 0 0 2等等。下面的f i n d命令将删除/ l o g s目录中访问时间在7日以前、
含有数字后缀的a d m i n . l o g文件。该命令只检查三位数字,所以相应日志文件的后缀不要超过
9 9 9。
$ find /logs -name 'admin.log[0-9][0-9][0-9] '-atime +7 -exec rm {} \;
为了查找当前文件系统中的所有目录并排序,可以用:
$ find . -type d -print -local -mount |sort
为了查找系统中所有的r m t磁带设备,可以用:
$ find /dev/rmt -print
xargs
在使用f i n d命令的- e x e c选项处理匹配到的文件时, f i n d命令将所有匹配到的文件一起传递
给e x e c执行。不幸的是,有些系统对能够传递给e x e c的命令长度有限制,这样在f i n d命令运行
几分钟之后,就会出现溢出错误。错误信息通常是"参数列太长"或"参数列溢出"。这就是
x a rg s命令的用处所在,特别是与f i n d命令一起使用。F i n d命令把匹配到的文件传递给x a rg s命
令,而x a rg s命令每次只获取一部分文件而不是全部,不像- e x e c选项那样。这样它可以先处理
最先获取的一部分文件,然后是下一批,并如此继续下去。在有些系统中,使用- e x e c选项会
为处理每一个匹配到的文件而发起一个相应的进程,并非将匹配到的文件全部作为参数一次
执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;而使用
x a rg s命令则只有一个进程。另外,在使用x a rg s命令时,究竟是一次获取所有的参数,还是分
批取得参数,以及每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参
数来确定。
让我们来看看x a rg s命令是如何同f i n d命令一起使用的,并给出一些例子。
下面的例子查找系统中的每一个普通文件,然后使用x a rg s命令来测试它们分别属于哪类
文件:
下面的例子在整个系统中查找内存信息转储文件(core dump) ,然后把结果保存到
/tmp/core.log 文件中:
$ find . -name "core" -print | xargs echo "" >/tmp/core.log
下面的例子在/ a p p s / a u d i t目录下查找所有用户具有读、写和执行权限的文件,并收回相应
的写权限:
$ find /apps/audit -perm -7 -print | xargs chmod o-w
在下面的例子中,我们用g r e p命令在所有的普通文件中搜索d e v i c e这个词:
$ find / -type f -print | xargs grep "device"
在下面的例子中,我们用g r e p命令在当前目录下的所有普通文件中搜索D B O这个词:
$ find . -name \ *-type f -print | xargs grep "DBO"
注意,在上面的例子中, \用来取消f i n d命令中的*在s h e l l中的特殊含义。
小结
f i n d命令是一个非常优秀的工具,它可以按照用户指定的准则来匹配文件。使用e x e c和
x a rg s可以使用户对所匹配到的文件执行几乎所有的命令。
32 typedef struct
33 {
34 unsigned enterpriseID;
35 string enterpriseName;
36 string telephone;
37 unsigned long long quota;
38 unsigned long long realQuota;
39 unsigned expiration;
40 unsigned flag;
41 unsigned openlong;
42 string enterpriseStatusName;
43 string regTime;
44 string endDate;
45 string subDomain;//for shenbak to stop and start enterprise
46 }enterpriseVectorNode;
vector <enterpriseVectorNode> _enterpriseVector;
for ( vector<enterpriseVectorNode>::iterator it = _enterpriseVector.begin(); it != _enterpriseVector.end() ; ++it )
{
if ( cnt % 2 == 0 )
bgCorlor = "#f8f8f8";
else
bgCorlor = "#f5f5f5";
osTmp<<"<tr align=center "<<bgCorlor<<">";
osTmp<<"<td><input type=checkbox name=enterpriseID[] value="<<it->enterpriseID<<"></td>";
osTmp<<"<td><a href=\""<<linkUrl<<it->enterpriseID<<"\">"<<it->enterpriseName<<"</a></td>";
osTmp<<"<td>"<<it->subDomain<<"</td>";
osTmp<<"<td>"<<it->telephone<<"</td>";
osTmp<<"<td>"<<it->quota<<"</td>";
osTmp<<"<td>"<<it->realQuota<<"</td>";
osTmp<<"<td>"<<it->enterpriseStatusName<<"</td>";
osTmp<<"<td>"<<it->regTime<<"</td>";
osTmp<<"<td>"<<it->endDate<<"</td>";
osTmp<<"</tr>";
}
for( int i = 0; i < list.GetCount( ); i++ )
{
row.Empty();
sql_get2( list[i], row );
enterpriseVectorNode tmpNode;
tmpNode.enterpriseID = atoi((LPCSTR) *row[ 0 ] );
tmpNode.enterpriseName =(LPCSTR) *row[ 1 ] ;
tmpNode.telephone = (LPCSTR) *row[ 2 ] ;
tmpNode.quota = atol((LPCSTR) * row[ 3 ] );
tmpNode.realQuota = atol( (LPCSTR) *row[ 4 ] );
tmpNode.expiration = atoi( (LPCSTR) *row[ 5 ] );
tmpNode.flag = atoi( (LPCSTR) *row[ 6 ] );
tmpNode.regTime = (LPCSTR) *row[ 7 ];
tmpNode.openlong = atoi( (LPCSTR) *row[ 8 ] );
tmpNode.subDomain = (LPCSTR) *row[ 9 ];
initEnterpriseStatus( tmpNode );
string tmpStr;
dateAdd( tmpNode.regTime, tmpStr, tmpNode.openlong, 'd', errMsg );
tmpNode.endDate = tmpStr;
_enterpriseVector.push_back( tmpNode );
}
33 {
34 unsigned enterpriseID;
35 string enterpriseName;
36 string telephone;
37 unsigned long long quota;
38 unsigned long long realQuota;
39 unsigned expiration;
40 unsigned flag;
41 unsigned openlong;
42 string enterpriseStatusName;
43 string regTime;
44 string endDate;
45 string subDomain;//for shenbak to stop and start enterprise
46 }enterpriseVectorNode;
vector <enterpriseVectorNode> _enterpriseVector;
for ( vector<enterpriseVectorNode>::iterator it = _enterpriseVector.begin(); it != _enterpriseVector.end() ; ++it )
{
if ( cnt % 2 == 0 )
bgCorlor = "#f8f8f8";
else
bgCorlor = "#f5f5f5";
osTmp<<"<tr align=center "<<bgCorlor<<">";
osTmp<<"<td><input type=checkbox name=enterpriseID[] value="<<it->enterpriseID<<"></td>";
osTmp<<"<td><a href=\""<<linkUrl<<it->enterpriseID<<"\">"<<it->enterpriseName<<"</a></td>";
osTmp<<"<td>"<<it->subDomain<<"</td>";
osTmp<<"<td>"<<it->telephone<<"</td>";
osTmp<<"<td>"<<it->quota<<"</td>";
osTmp<<"<td>"<<it->realQuota<<"</td>";
osTmp<<"<td>"<<it->enterpriseStatusName<<"</td>";
osTmp<<"<td>"<<it->regTime<<"</td>";
osTmp<<"<td>"<<it->endDate<<"</td>";
osTmp<<"</tr>";
}
for( int i = 0; i < list.GetCount( ); i++ )
{
row.Empty();
sql_get2( list[i], row );
enterpriseVectorNode tmpNode;
tmpNode.enterpriseID = atoi((LPCSTR) *row[ 0 ] );
tmpNode.enterpriseName =(LPCSTR) *row[ 1 ] ;
tmpNode.telephone = (LPCSTR) *row[ 2 ] ;
tmpNode.quota = atol((LPCSTR) * row[ 3 ] );
tmpNode.realQuota = atol( (LPCSTR) *row[ 4 ] );
tmpNode.expiration = atoi( (LPCSTR) *row[ 5 ] );
tmpNode.flag = atoi( (LPCSTR) *row[ 6 ] );
tmpNode.regTime = (LPCSTR) *row[ 7 ];
tmpNode.openlong = atoi( (LPCSTR) *row[ 8 ] );
tmpNode.subDomain = (LPCSTR) *row[ 9 ];
initEnterpriseStatus( tmpNode );
string tmpStr;
dateAdd( tmpNode.regTime, tmpStr, tmpNode.openlong, 'd', errMsg );
tmpNode.endDate = tmpStr;
_enterpriseVector.push_back( tmpNode );
}
在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用担心,mysql已经为我们提供了这样一个功能。
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数。LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是 0(而不是 1): 为了与 PostgreSQL 兼容,MySQL 也支持句法: LIMIT # OFFSET #。
mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15
//为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
//如果只给定一个参数,它表示返回最大的记录行数目:
mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行
//换句话说,LIMIT n 等价于 LIMIT 0,n。
LIMIT A B => 从A+1--->A+B
每页10条举例:
Limit 0 10 =>1 ------>10 (第一页) (0+10)/10
Limit 10 10 =>11 ---->20 (第二页) (10+10)/10
limit 20 10 =>21 ---->30 (第三页) (20+10)/10
。。。。。。。 (第N页)
$offset=($this->page-1)*$this->limit; //$offset就是0,10,20
page=(totalpage-1)/onepage's Number;
LIMIT (page-1)*onepage's,NO onepage's Nnumber;
示例:
$pos_start = ($page - 1) * $page_size;
$sql = "select * from {$this->table_name} where touid={$uid} and fromuid={$frienduid} order by ctime desc limit {$pos_start}, {$page_size}";
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数。LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是 0(而不是 1): 为了与 PostgreSQL 兼容,MySQL 也支持句法: LIMIT # OFFSET #。
mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15
//为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
//如果只给定一个参数,它表示返回最大的记录行数目:
mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行
//换句话说,LIMIT n 等价于 LIMIT 0,n。
LIMIT A B => 从A+1--->A+B
每页10条举例:
Limit 0 10 =>1 ------>10 (第一页) (0+10)/10
Limit 10 10 =>11 ---->20 (第二页) (10+10)/10
limit 20 10 =>21 ---->30 (第三页) (20+10)/10
。。。。。。。 (第N页)
$offset=($this->page-1)*$this->limit; //$offset就是0,10,20
page=(totalpage-1)/onepage's Number;
LIMIT (page-1)*onepage's,NO onepage's Nnumber;
示例:
$pos_start = ($page - 1) * $page_size;
$sql = "select * from {$this->table_name} where touid={$uid} and fromuid={$frienduid} order by ctime desc limit {$pos_start}, {$page_size}";
public function GetSingleList ($uid, $frienduid, $page, $page_size = 20)
{
//add by zaifeng 2009-1-13
$rearr = array();
$sql1 = "select count(*) as num from {$this->table_name} where touid={$uid} and fromuid={$frienduid} ";
$ret = $this->objPdo->Select($sql1);
if($ret){
$total = $this->objPdo->FetchAll();
$rearr['count'] = $total[0]['num'];
}
//end add
$pos_start = ($page - 1) * $page_size;
$sql = "select * from {$this->table_name} where touid={$uid} and fromuid={$frienduid} order by ctime desc limit {$pos_start}, {$page_size}";
$result = $this->objPdo->Select($sql);
if ($result)
{
$rs_array = $this->objPdo->FetchAll();
$rearr['record'] = $rs_array;
return $rearr;
}
return FALSE;
}
{
//add by zaifeng 2009-1-13
$rearr = array();
$sql1 = "select count(*) as num from {$this->table_name} where touid={$uid} and fromuid={$frienduid} ";
$ret = $this->objPdo->Select($sql1);
if($ret){
$total = $this->objPdo->FetchAll();
$rearr['count'] = $total[0]['num'];
}
//end add
$pos_start = ($page - 1) * $page_size;
$sql = "select * from {$this->table_name} where touid={$uid} and fromuid={$frienduid} order by ctime desc limit {$pos_start}, {$page_size}";
$result = $this->objPdo->Select($sql);
if ($result)
{
$rs_array = $this->objPdo->FetchAll();
$rearr['record'] = $rs_array;
return $rearr;
}
return FALSE;
}
void trim(string& str)
{
str.erase(str.find_last_not_of(' ')+1, string::npos);
str.erase(0, str.find_first_not_of(' '));
}
//去前后空格函数结束
//参数检验
//特殊客户验证
//char *flag_domain = "afeng@afeng.sina.net"; // 这个是查要有的字符串
string flag_domain = strDomain;
ifstream fin("domain.txt");
string str;
while(getline(fin,str))
{
trim(str);//去掉读出的一行前后空格
if(flag_domain==str)
{
//printf("Content-Type:text/html\n\n");
//printf("果然有该域名\n\n");
domain_reg_filter=1;
}
}
//特殊客户验证完毕
{
str.erase(str.find_last_not_of(' ')+1, string::npos);
str.erase(0, str.find_first_not_of(' '));
}
//去前后空格函数结束
//参数检验
//特殊客户验证
//char *flag_domain = "afeng@afeng.sina.net"; // 这个是查要有的字符串
string flag_domain = strDomain;
ifstream fin("domain.txt");
string str;
while(getline(fin,str))
{
trim(str);//去掉读出的一行前后空格
if(flag_domain==str)
{
//printf("Content-Type:text/html\n\n");
//printf("果然有该域名\n\n");
domain_reg_filter=1;
}
}
//特殊客户验证完毕
http://man.ddvip.com/os/freebsd_book_chs/ch22.htm
什么是FreeBSD?
http://man.ddvip.com/os/freebsd_book_chs/ch01.htm
http://man.ddvip.com/os/freebsd_book_chs/ch02.htm
。
。
。
什么是FreeBSD?
http://man.ddvip.com/os/freebsd_book_chs/ch01.htm
http://man.ddvip.com/os/freebsd_book_chs/ch02.htm
。
。
。
本文介绍使用
FreeBSD+Postfix+Cyrus-sasl+Courier-imap+Webmail+spamassassin+Clamav+mailscanner +maildrop来架构一个具有多域名,webmail、防病毒、防垃圾邮件、web管理界面的邮件系统。
Jacky, $Revision: 5.1 bate $Date: 2006-10-31
杨廷勇 QQ:187159779
Copyright © 2004、2005、2006
欢迎转载,保留版权和出处[http://www.thismail.org]
系统主要采用MailScanner+clamav+Spamd来对病毒过滤和垃圾邮件过滤。
----------------------------------
新增加对smtpd,imap,pop3,webmail的登录控制
新增加简单的邮件分组
新增邮件监控
加强对垃圾邮件的过滤
增加系统黑名单
-----------------------------------
本文在4.10、5.3、5.4、5.5、6.0、6.1上安装测试通过,采用MailSanner来对邮件过滤和垃圾邮件过滤,配置更容易,并且降低了 系统开消。让系统更加稳定,经过严格病毒邮件测试成功率达到了98%。垃圾邮件过滤基本上达到了95%的成功率。
本人水平有限,如有错误之处请发邮件到scyz2 at 163.com (at = @),本人不对本文档对你的系统造成的损失负任何连带责任。
――――――――――――――――――――――――――――――――――
第一章 FreeBSD 简介
1.1 什么是FreeBSD?
1.2 postfix简介
第二章 操作系统FreeBSD安装
第三章 更新ports
3.1 安装CVSUP
3.2 更新ports树
第四章 软件安装
4.1 安装perl
4.2 安装mysql
4.3 安装apache22
4.4 安装mod_php5
4.5 安装php5的扩展
4.6 安装openssl
4.7 安装phpMyAdmin
4.8 设置数据库
4.9 安装cyrus-sasl2
4.10 安装postfix2
4.11 安装Courier-imap
4.12 安装MailScanner
4.13 安装maildrop
第五章 软件系统配置
5.1 配置cyrus-sasl
5.2 配置postfix
5.3 配置Courier-imap
5.4 安装Tmail管理工具
5.5 登录测试
5.6 垃圾邮件和病毒邮件过滤设置
第六章 安装webmail
附一、常见问题
附二、MailScanner.conf中文参数说明
――――――――――――――――――――――――――――――――――
第一章 FreeBSD 简介
1.1 什么是FreeBSD?
我 想大家都知道 Microsoft Windows 是一套作业系统,FreeBSD 也是一套作业系统。FreeBSD 是一个可以在 Intel 相容个人计算机、DEC Alpha 或 PC98 架构的计算机上执行的 UNIX 作业系统。大家应该听过另一套UNIX的作业系统 Linux,FreeBSD 也是一套免费的作业系统。它可以让我们的个人计算机变成先进的工作站,更稳定的提供你所需的网络服务。
BSD UNIX 系统可以说是网络作业系统的始祖,FreeBSD 是众多 BSD UNIX 分支中的一个,它继承了 BSD 系统的高性能与可靠性。自从1993 年 FreeBSD 推出 1.0-RELEASE 以来,FreeBSD 开发团队便致力于系统的调校,使其发挥绝佳的效能。在 FreeBSD 团队的统筹努力下,使它比起其它免费的 UNIX 作业系统更有结构。在 FreeBSD 上有许多支援的免费软件,这些软件大都已移植收录于 FreeBSD ports 中,使得我们在安装软件时变得十分轻松。FreeBSD 支援 32 位元、64 位元的许多不同平台,具有高效能核心架构、动态函式库共享、绝佳的网络功能,比起其它商用 UNIX 系统毫不逊色。
FreeBSD 作业系统相当容易取得及安装,除了经由传统的光盘安装外,它也可以经由网络安装、MS-DOS 分割区安装等等。当然,我们也可以在计算机中同时安装多种不同的作业系统,例如 Windwos 98 和 FreeBSD 同时并存也是件十分容易的事。
在 FreeBSD 上的应用软件相当的多,也都可以免费取得,由于 FreeBSD 的稳定性高且功能强大,因此许多大型网站都以它为作业平台,其中最知名的就是 YAHOO!。YAHOO! 是一个流量相当大的入口网站,他们选择以 FreeBSD 为作业平台,由此可知 FreeBSD 的优异性。除此之外,在台湾,FreeBSD 普遍被应用于学术网络上,许多大专院校的服务器都是使用 FreeBSD 来提供网络服务。
但是您别以为 FreeBSD 只能用来做为网络服务器,FreeBSD 也可以是一个良好的个人作业系统。事实上,Mac OS X 就是使用 FreeBSD 做为系统核心。大家都知道 Mac OS 是个人计算机上有名的作业系统,它会使用 FreeBSD 做为系统核心,可见 FreeBSD 的质量亦深获 Apple 的肯定。
在宽带网络逐渐普及的台湾,每个人都可以自行架设一台网路服务器,以 FreeBSD 来提供网络服务(如网页、邮件、档案存取等)。值得一提的是 FreeBSD 并不像 MS-Windows 一样每每要求使用者升级计算机才能使用。FreeBSD 对于硬件的要求很低,你可以用一台 Intel 586-133MHz 的旧计算机来安装 FreeBSD,这也算是癈物利用吧。
1.2 为什么要选择FreeBSD?
现在的个人计算机作业系统市场中,是以 MS-Windows 独大,但在网络服务器市场中,UNIX 系统的使用率可不输 MS-Windows 喔。我个人认为 MS-Windows 之所以会有那么多的使用者主要是因为他的使用者介面对初学者而言较容易操作,再加上许多软件的配合及盗版的助长,安装软件只要一直按下一步就完成。使得使 用者即便它的稳定性不高也得乖乖的接受。
其实初学者没有试过其它的作业系统才会有这样的误解,因为一直用 MS-Windows 才会认为当机是无可避免的事,当机对于 MS-Windows 或许是无可避免,但在其它 UNIX 系统中可不常见。在 UNIX 系列的作业系统中,也可以有像 MS-Windows 的图形介面,几乎所有在 MS-Windows 上可以做的事,在 FreeBSD 上都可以做得到,唯一的不同点是你不必花钱去取得你想要的功能。包括排版、图形处理、MP3、多媒体、网络芳邻等等都可以在 FreeBSD 中做到。
重 点是,对于一台网路服务器而言,图形介面会占用系统资源,而且必须安装一堆有的没的软件。笔者偏好乾干脆净净的系统,而 FreeBSD 正好具有这个优点。不过这并不代表您不能使用图形介面,您还是可以安装类似 Windows 的图形介面,让您使用它来做为平日使用的个人计算机。此外,FreeBSD 把自己定位为最佳的网络服务器,它承袭了 BSD 优良的网络血统,在提供服务时能有绝佳的效能表现。而且,它是免费的。
然而,有这么多的免费 UNIX 作业系统中,为什么要选择 FreeBSD 而不使用其它作业系统(如 Linux )呢?在网络上在讨论这个问题时,每每会引发每个作业系统使用者的激辩。对于要使用何种作业系统,除了使用者偏好外,还有许多指标可以提供我们参考。
FreeBSD 是一套完整的作业系统
我 们平常所说的 Linux 指的是它的核心 (Kernel),Kernel 只是整个作业系统的一部份,除了 Kernel 外,我们还需要一些基本的指令、系统及目录架构、图形介面等。Linux 的 Distribution 就是各个不同的组织或公司自行收集一些系统必备的程序,制作出一个作业系统。Linux 系统有很多的 Distribution,如 Redhat、SUSE、Debian 等。因为每个 Distribution 都有自己的程序或架构,所以每个 Distribution 都长的不一样。如果您使用的是 Linux,在 Linux 三十多种 Distribution 中,每一种之间都有些许差异,在设定上用法都不同。
而 FreeBSD 指的不只是核心而已,它是一套完整的作业系统。从系统核心到使用者介面、各种常用指令都是由 FreeBSD 总部所统一推出。因此,FreeBSD 每一个版本之间有着共同的架构,不同软件之间的整合搭配性较为完整,不管是设定、学习都很容易。
由核心团队统筹开发
FreeBSD 整个系统都是由总部的核心团队所维护,所以整个系统都搭配得很好。而 Linux 只有 Kernel 由 Linus Torvlds 所维护。因为 FreeBSD 整个系统都是由总部所开发,不只系统整体较有规划,各个程序之间搭配起来也比较紧密。如果您使用 Linux,由于 Kernel 和其它的程序是由不同组织所维护,在升级时,比较麻烦。而 FreeBSD 就简单多了,每次一有新的版本,一定是整个系统一起升级。
我 觉得 FreeBSD 总部统筹发展 FreeBSD 是一件很棒的事,所有的问题回报都可以统筹管理并予以更新。FreeBSD 推陈出新的速度相当快,每一次安装都确保这个版本不会有上一版的缺失。所以在 Linux 或 MS-Windows 中「新版本不一定是最好」的定律并不适用于 FreeBSD。但这并不意味着你必须不断重新安装系统,FreeBSD 总部随时会发布最新更新的档案提供下载,而且如果使用 CVSUP 就可以和更新版的 FreeBSD 保持同步更新。
完整的使用手册
FreeBSD 的使用手册非常完整,而且会随着软件一起更新。每次一有新的版本推出,FreeBSD 的文件也会做相对的更新。在 FreeBSD 的官方网站上,我们可以轻松的取得这些高质量的手册及文件。
软件安装的便利性
FreeBSD 支援的软件相当多,它将常用的软件分类管理,当需要安装软件时,使用者不必自行到网络上找寻,只要到 FreeBSD ports 目录中下个指令就安装完成。
条理分明的系统架构
FreeBSD 目录结构非常有组织,结构严谨,对于系统管理者而言比较不会紊乱。FreeBSD 将系统预设的目录和管理者自行安装的程序目录划分得十分清楚,管理者在进行升级、维护时可以一目了然。
完美的程序开发平台
安 装完 FreeBSD 后,系统所有程序原始码非常有条理的存放在特定目录中。对于想要进行程序修改的管理者而言,不必四处寻找某支程序原始码。笔者在 FreeBSD 及 Linux 上开发程序多年,我觉得 FreeBSD 原始码及系统架构十分有组织,是程序开发最好的平台。
而且 FreeBSD 的版权较为宽松,对于想要以 FreeBSD 为基础开发产品的公司而言,它可以保护您的修改不会被其它对手抄袭。
1.3 为什么不选择FreeBSD?
许 多企业选择使用目前大多数人使用的 MS windows 做为一般作业用个人计算机的作业系统当然无可厚非。但以 MS windows 做服务器,除了资讯人员的偏好外(或许因为不会使用其它系统吧),还有部份原因是为了在企业内资讯人员技术不足时,能求助于系统供应商。而 FreeBSD 是免费的,企业也害怕有状况时无人可以支援,这对企业是很重要的一项因素。
然而,FreeBSD 的使用人数其实很多,而且使用者都十分热心,在台湾的 BBS 讨论区上,许多问题都可以获得解决。这当然还不够,现在有很多顾问公司提供 FreeBSD 的顾问服务,可以提供企业这方面的服务。虽然说 FreeBSD 的系统稳定,但在应用上如果没有资讯人员的支援,很难能在企业中存活。但正因市场上了解 FreeBSD 的人较少,若我们能主动学习,必能为自己创造更多价值。
FreeBSD 目前支援的平台不多,若您不是使用 X86 的系统,而是使用 ARM、MIPS 等平台,您就必须寻求其它的解决方案。
1.2 postfix简介
Postfix 是一个非常优秀的MTA,她素以高效、安全的特点而著称。Postfix是作者在UNIX上所见过的MTA中在反垃圾邮件(Anti-Spam或Anti -UCE)方面做得最好的一个,甚至有很多公司在Postfix代码的基础上进行二次开发而推出反垃圾邮件网关产品。MTA的反垃圾邮件功能,实际上就是 在MTA处理过程中对会话进行过滤。这个过滤不但过滤了发往自身的垃圾邮件,而且还防止了自身被恶意利用发送垃圾邮件。Postfix实现了目前所有主要 的MTA过滤技术。postfix是Wietse Venema在IBM的GPL协议之下开发的MTA(邮件传输代理)软件。和Sendmail相比Postfix更快、更容易管理、更灵活、更安全,同时 还与sendmail保持足够的兼容性。
第二章 操作系统FreeBSD安装
FreeBSD6.1系统安装手册
官方安装手册:
QUOTE:
http://www.freebsd.org/doc/zh_CN.GB2312/books/handbook/install.html
注意在系统安装过程中,分区方面一定要注意,最好是一个独立的mail分区来存放用户的邮件数据,因为默认的VAR分区读写频繁,数据存放在这样的分区中是很危险的;在安装系统的过程中,建议最小化安装加ports。
第三章 更新ports
3.1 安装CVSUP
QUOTE:
mail# cd /usr/ports/net/cvsup-without-gui
mail# make install clean
安装过程中由于依赖的关系,它会自动安装gettext,会出现下面的选项。
以下是我的选择。
QUOTE:
Options for gettext 0.14.5_2
[X] EXAMPLES install example files
[X] HTMLMAN install man pages in HTML format
3.2 更新ports树
(更新日期2006-07-09)
编辑ports-supfile
QUOTE:
mail# ee /usr/share/examples/cvsup/ports-supfile
QUOTE:
第51行
*default host=CHANGE_THIS.FreeBSD.org
改为:
*default host=cvsup2.freebsdchina.org
QUOTE:
mail# /usr/local/bin/cvsup -g -L 2 /usr/share/examples/cvsup/ports-supfile
Parsing supfile "ports"
Connecting to Cvsup2.FreeBSD.org.cn
Connected to Cvsup2.FreeBSD.org.cn
Server software version: SNAP_16_1h
Negotiating file attribute support
Exchanging collection information
Establishing multiplexed-mode data connection
Running
Updating collection ports-all/cvs
……
……
Shutting down connection to server
Finished successfully
出现上面的提示,表示更新成功。
第四章 软件安装
4.1 安装perl-5.8.8
QUOTE:
mail# cd /usr/ports/lang/perl5.8
mail# make install clean
4.2 安装mysql-5.0.27
4.2 安装mysql
QUOTE:
mail# cd /usr/ports/databases/mysql50-server
编辑Makefile
mail# ee Makefile
在CONFIGURE_ARGS这段话的
--enable-thread-safe-client前面加上三句参数
BUILD_OPTIMIZED=yes \
BUILD_STATIC=yes \
PTHREAD_LIBS=-lkse \
mail# make WITH_CHARSET=gb2312 WITH_XCHARSET=all BUILD_OPTIMIZED=yes BUILD_STATIC=yes install clean
在rc.conf中加入Mysql的启动参数
QUOTE:
mail# echo 'mysql_enable="YES"' >> /etc/rc.conf
启动mysql
QUOTE:
mail# /usr/local/etc/rc.d/mysql-server start
Starting mysql.
查看mysql是否启动成功
QUOTE:
mail# sockstat -4 | grep 3306
mysql mysqld 1510 3 tcp4 *:3306 *:*
出现上面的端口表示mysql启动成功
4.3 安装httpd-2.2.3
QUOTE:
mail# cd /usr/ports/www/apache22
mail# make install clean
在rc.conf中加入apache的启动参数
QUOTE:
mail# echo 'apache_enable="YES"' >> /etc/rc.conf
启动apache
QUOTE:
mail# /usr/local/etc/rc.d/apache22 start
Starting apache.
查看apache是否启动成功
QUOTE:
mail# sockstat -4 | grep 80
www httpd 10679 16 tcp4 *:80 *:*
www httpd 10678 16 tcp4 *:80 *:*
www httpd 10677 16 tcp4 *:80 *:*
www httpd 10676 16 tcp4 *:80 *:*
www httpd 10675 16 tcp4 *:80 *:*
root httpd 10674 16 tcp4 *:80 *:*
出现上面的80端口表示apache启动成功
4.4 安装php-5.1.6
QUOTE:
mail# cd /usr/ports/lang/php5
mail# make install clean
以下是我的选择
QUOTE:
Options for php5 5.1.6_3
[ ] CLI Build CLI version
[ ] CGI Build CGI version
[X] APACHE Build Apache module
[ ] DEBUG Enable debug
[ ] SUHOSIN Enable Suhosin protection system
[X] MULTIBYTE Enable zend multibyte support
[ ] IPV6 Enable ipv6 support
[ ] REDIRECT Enable force-cgi-redirect support (CGI only)
[ ] DISCARD Enable discard-path support (CGI only)
[ ] FASTCGI Enable fastcgi support (CGI only)
[ ] PATHINFO Enable path-info-check support (CGI only)
QUOTE:
mail# echo 'AddType application/x-httpd-php .php' >> /usr/local/etc/apache/httpd.conf
mail# echo 'AddType application/x-httpd-php-source .phps' >> /usr/local/etc/apache/httpd.conf
编辑httpd.conf加入index.php
QUOTE:
DirectoryIndex index.html index.html.var index.php
#注:在DirectoryIndex这里加入index.php,是为了让apache支持首页为index.php的首页文件
4.5 安装php5的扩展
QUOTE:
mail# cd /usr/ports/lang/php5-extensions
mail# make install clean
QUOTE:
Options for php5-extensions 1.0
[X] BCMATH bc style precision math functions
[X] BZ2 bzip2 library support
[X] CALENDAR calendar conversion support
[X] CTYPE ctype functions
[X] CURL CURL support
[ ] DBA dba support
[ ] DBASE dBase library support
[X] DOM DOM support
[ ] EXIF EXIF support
[ ] FILEINFO fileinfo support
[ ] FILEPRO filePro support
[ ] FRIBIDI FriBidi support
[X] FTP FTP support
[X] GD GD library support
[ ] GETTEXT gettext library support
[ ] GMP GNU MP support
[X] HASH HASH Message Digest Framework
[X] ICONV iconv support
[ ] IMAGICK ImageMagick support
[X] IMAP IMAP support
[ ] INTERBASE Interbase 6 database support (Firebird)
[ ] LDAP OpenLDAP support
[ ] MBSTRING multibyte string support
[X] MCRYPT Encryption support
[X] MHASH Crypto-hashing support
[ ] MING ming shockwave flash support
[ ] MSSQL MS-SQL database support
[X] MYSQL MySQL database support
[ ] MYSQLI MySQLi database support
[ ] NCURSES ncurses support (CLI only)
[ ] ODBC unixODBC support
[ ] OPENSSL OpenSSL support
[ ] PANDA panda support
[ ] PCNTL pcntl support (CLI only)
[X] PCRE Perl Compatible Regular Expression support
[ ] PDF PDFlib support (implies GD)
[X] PDO PHP Data Objects Interface (PDO)
[ ] PGSQL PostgreSQL database support
[X] POSIX POSIX-like functions
[ ] PSPELL pspell support
[ ] READLINE readline support (CLI only)
[ ] RECODE recode support
[X] SESSION session support
[ ] SHMOP shmop support
[X] SIMPLEXML simplexml support
[ ] SNMP SNMP support
[ ] SOAP SOAP support
[ ] SOCKETS sockets support
[X] SQLITE sqlite support
[ ] SYBASE_CT Sybase database support
[ ] SYSVMSG System V message support
[ ] SYSVSEM System V semaphore support
[ ] SYSVSHM System V shared memory support
[ ] TIDY TIDY support
[X] TOKENIZER tokenizer support
[ ] WDDX WDDX support (implies XML)
[X] XML XML support
[X] XMLREADER XMLReader support
[ ] XMLRPC XMLRPC-EPI support
[X] XMLWRITER XMLWriter support
[ ] XSL XSL support (Implies DOM)
[ ] YAZ YAZ support (ANSI/NISO Z39.50)
[X] ZIP ZIP support
[X] ZLIB ZLIB support
重启apache安装完成。
4.6 安装openssl-0.9.7l
QUOTE:
mail# cd /usr/ports/security/openssl
mail# make install clean
4.7 安装phpMyAdmin-2.9.0.2
QUOTE:
mail# cd /usr/ports/databases/phpmyadmin
mail# make fetch
注:(在这里建议直接下载后复制安装)
QUOTE:
mail# cd /usr/ports/distfiles
mail# tar –zxvf phpMyAdmin-2.9.0.2.tar.gz
mail# mv /usr/local/www/phpMyAdmin-2.9.0.2 /usr/local/www/apache22/data/dbadmin
修改/usr/local/www/apache22/data/dbadmin/config.inc.php
QUOTE:
$cfg['PmaAbsoluteUri'] = 'http://192.168.0.2/dbadmin/';
$cfg['Servers'][$i]['auth_type'] = 'http'; // Authentication method (config, http or cookie based)?
注:指定phpmyadmin的认证方式为http方式。
在浏览器输入http://192.168. 0.2/dbadmin/,首次进行登入的用户名为root密码为空,登入后可以修改你的密码。
4.8 设置数据库
建立postfix数据库(注意:数据库名称为postfix):
QUOTE:
mail# /usr/local/bin/mysql –u root –p
mysql# CREATE DATABASE `postfix` ;
mysql# use postfix;
把下面的sql语句导入到postfix数据库中去:
QUOTE:
-- phpMyAdmin SQL Dump
-- version 2.9.0.2
-- http://www.phpmyadmin.net
--
-- 主机: localhost:3306
-- 生成日期: 2006 年 10 月 30 日 22:17
-- 服务器版本: 4.0.26
-- PHP 版本: 5.1.6
--
-- 数据库: `tmail`
--
-- --------------------------------------------------------
--
-- 表的结构 `address`
--
CREATE TABLE `address` (
`id` int(11) unsigned NOT NULL auto_increment,
`pw_id` int(5) NOT NULL default '0',
`name` varchar(64) NOT NULL default '',
`email` varchar(128) NOT NULL default '',
UNIQUE KEY `id` (`id`),
KEY `pw_id` (`pw_id`)
) TYPE=MyISAM PACK_KEYS=1 AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `address`
--
-- --------------------------------------------------------
--
-- 表的结构 `admin`
--
CREATE TABLE `admin` (
`id` int(10) unsigned NOT NULL auto_increment,
`site_id` int(10) unsigned NOT NULL default '0',
`domain` varchar(128) NOT NULL default '',
`quota` smallint(5) unsigned NOT NULL default '0',
`total` smallint(5) unsigned NOT NULL default '0',
`createtime` timestamp(14) NOT NULL,
`login` char(1) NOT NULL default '',
`cur_total` smallint(5) NOT NULL default '0',
`cur_quota` smallint(5) NOT NULL default '0',
`gid` varchar(11) NOT NULL default '',
`expiration_time` timestamp(14) NOT NULL default '00000000000000',
`flag` int(10) unsigned NOT NULL default '0',
`maxmsg` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
UNIQUE KEY `domain` (`domain`)
) TYPE=MyISAM PACK_KEYS=1 AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `admin`
--
-- --------------------------------------------------------
--
-- 表的结构 `card`
--
CREATE TABLE `card` (
`id` int(5) unsigned NOT NULL auto_increment,
`pw_id` int(5) unsigned NOT NULL default '0',
`LinkMan` varchar(64) NOT NULL default '',
`CompanyName` varchar(100) NOT NULL default '',
`Address` varchar(255) NOT NULL default '',
`Position` varchar(32) NOT NULL default '',
`PhoneNumber` varchar(16) NOT NULL default '',
`Mobile` varchar(12) NOT NULL default '',
`Email` varchar(128) NOT NULL default '',
`Partaker` varchar(32) NOT NULL default '',
`Memo` varchar(255) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=27 ;
--
-- 导出表中的数据 `card`
--
-- --------------------------------------------------------
--
-- 表的结构 `lastauth`
--
CREATE TABLE `lastauth` (
`user` char(32) NOT NULL default '',
`domain` char(64) NOT NULL default '',
`remote_ip` char(18) NOT NULL default '',
`timestamp` bigint(20) NOT NULL default '0',
PRIMARY KEY (`user`,`domain`)
) TYPE=MyISAM;
--
-- 导出表中的数据 `lastauth`
--
-- --------------------------------------------------------
--
-- 表的结构 `logs`
--
CREATE TABLE `logs` (
`pw_id` int(5) default '0',
`ip` varchar(15) NOT NULL default '',
`action` varchar(15) NOT NULL default '',
`time` datetime default NULL,
`content` varchar(64) NOT NULL default '',
`email` varchar(128) NOT NULL default ''
) TYPE=MyISAM;
--
-- 导出表中的数据 `logs`
--
-- --------------------------------------------------------
--
-- 表的结构 `message`
--
CREATE TABLE `message` (
`id` int(5) unsigned NOT NULL auto_increment,
`title` varchar(255) NOT NULL default '',
`body` text NOT NULL,
`createtime` datetime NOT NULL default '0000-00-00 00:00:00',
`updatetime` datetime NOT NULL default '0000-00-00 00:00:00',
`pw_domain` varchar(64) NOT NULL default '',
UNIQUE KEY `id` (`id`)
) TYPE=MyISAM AUTO_INCREMENT=2 ;
--
-- 导出表中的数据 `message`
--
-- --------------------------------------------------------
--
-- 表的结构 `personal`
--
CREATE TABLE `personal` (
`id` int(11) unsigned NOT NULL auto_increment,
`pw_id` int(5) NOT NULL default '0',
`truename` varchar(10) NOT NULL default '',
`fax` varchar(20) NOT NULL default '',
`telephone` varchar(15) NOT NULL default '',
`sex` int(1) NOT NULL default '0',
`year` int(4) NOT NULL default '0',
`MONTH` int(2) NOT NULL default '0',
`DAY` int(2) NOT NULL default '0',
`education` varchar(4) NOT NULL default '',
`marital` int(1) NOT NULL default '0',
`occupation` varchar(15) NOT NULL default '',
`companyname` varchar(30) NOT NULL default '',
`province` varchar(6) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM PACK_KEYS=1 AUTO_INCREMENT=12 ;
--
-- 导出表中的数据 `personal`
--
-- --------------------------------------------------------
--
-- 表的结构 `scheduler`
--
CREATE TABLE `scheduler` (
`id` int(11) unsigned NOT NULL auto_increment,
`begin_time` int(11) unsigned default NULL,
`end_time` int(11) unsigned default NULL,
`title` varchar(255) NOT NULL default '',
`body` varchar(255) NOT NULL default '',
`pw_id` int(11) unsigned NOT NULL default '0',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `scheduler`
--
-- --------------------------------------------------------
--
-- 表的结构 `stow`
--
CREATE TABLE `stow` (
`id` int(5) unsigned NOT NULL auto_increment,
`pw_id` int(5) unsigned NOT NULL default '0',
`Name` varchar(128) NOT NULL default '',
`http` varchar(255) NOT NULL default 'http://',
`memo` varchar(255) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `stow`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_access`
--
CREATE TABLE `tmail_access` (
`id` int(11) unsigned NOT NULL auto_increment,
`ctime` int(11) unsigned default NULL,
`source` varchar(128) NOT NULL default '',
`access` varchar(16) NOT NULL default '',
`type` char(1) NOT NULL default 'S',
PRIMARY KEY (`id`),
KEY `source` (`source`,`type`,`access`,`ctime`)
) TYPE=MyISAM AUTO_INCREMENT=65 ;
--
-- 导出表中的数据 `tmail_access`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_alias`
--
CREATE TABLE `tmail_alias` (
`id` int(11) unsigned NOT NULL auto_increment,
`alias` varchar(128) NOT NULL default '',
`destination` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_alias`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_autobbc`
--
CREATE TABLE `tmail_autobbc` (
`id` int(9) NOT NULL auto_increment,
`realname` varchar(128) default NULL,
`email` varchar(128) NOT NULL default '',
`autobbc` varchar(128) NOT NULL default '',
`come` int(1) default NULL,
`out` int(1) default NULL,
`domain` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_autobbc`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_company`
--
CREATE TABLE `tmail_company` (
`id` int(6) NOT NULL auto_increment,
`domain` varchar(25) NOT NULL default '',
`name` varchar(50) NOT NULL default '',
`linkman` varchar(50) NOT NULL default '',
`tel` varchar(15) NOT NULL default '',
`Address` varchar(50) NOT NULL default '',
`zip` varchar(6) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_company`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_domaininfo`
--
CREATE TABLE `tmail_domaininfo` (
`id` tinyint(11) NOT NULL auto_increment,
`domain` varchar(25) NOT NULL default '',
`transport` varchar(128) NOT NULL default '',
`alias` varchar(50) default NULL,
`passwd` varchar(35) NOT NULL default '',
`clean` text NOT NULL,
`admin` tinytext NOT NULL,
`yesno` tinytext NOT NULL,
`usernum` text NOT NULL,
`quota` text NOT NULL,
`filequota` text NOT NULL,
`expire` date NOT NULL default '0000-00-00',
`create_time` datetime default NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `domain` (`domain`),
KEY `domain_id` (`id`)
) TYPE=MyISAM PACK_KEYS=0 COMMENT='domain information' AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_domaininfo`
--
INSERT INTO `tmail_domaininfo` (`id`, `domain`, `transport`, `alias`, `passwd`, `clean`, `admin`, `yesno`, `usernum`, `quota`, `filequota`, `expire`, `create_time`) VALUES
(1, 'admin', 'maildrop', NULL, '$1$t5h6XOby$UP3HJpdrozxEsRlcLF6tI0', 'hing4585', '0', '1', '0', '0', '0', '0000-00-00', '2006-10-06 16:44:13');
-- --------------------------------------------------------
--
-- 表的结构 `tmail_group_name`
--
CREATE TABLE `tmail_group_name` (
`id` int(9) NOT NULL auto_increment,
`group_name` varchar(128) NOT NULL default '',
`domain` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_group_name`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_monitor`
--
CREATE TABLE `tmail_monitor` (
`id` int(11) unsigned NOT NULL auto_increment,
`email` varchar(128) NOT NULL default '',
`cc` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`),
KEY `email` (`email`,`cc`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_monitor`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_pop`
--
CREATE TABLE `tmail_pop` (
`id` int(11) unsigned NOT NULL auto_increment,
`email` varchar(128) NOT NULL default '',
`pop` varchar(128) NOT NULL default '',
`name` varchar(128) NOT NULL default '',
`pass` varchar(32) NOT NULL default '32',
`timeout` int(10) unsigned NOT NULL default '60',
`port` tinyint(3) unsigned NOT NULL default '110',
`keep` char(1) NOT NULL default 'Y',
PRIMARY KEY (`id`),
KEY `email` (`email`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_pop`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_relay_domains`
--
CREATE TABLE `tmail_relay_domains` (
`id` int(11) unsigned NOT NULL auto_increment,
`domain` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`),
UNIQUE KEY `domain` (`domain`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_relay_domains`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_relocated`
--
CREATE TABLE `tmail_relocated` (
`id` int(11) unsigned NOT NULL auto_increment,
`email` varchar(128) NOT NULL default '',
`destination` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_relocated`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_transport`
--
CREATE TABLE `tmail_transport` (
`id` int(11) unsigned NOT NULL auto_increment,
`domain` varchar(128) NOT NULL default '',
`destination` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`),
UNIQUE KEY `domain` (`domain`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_transport`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_userpersonal`
--
CREATE TABLE `tmail_userpersonal` (
`id` int(11) NOT NULL auto_increment,
`address` varchar(50) default NULL,
`fax` varchar(20) NOT NULL default '',
`telephone` varchar(15) NOT NULL default '',
`sex` int(1) NOT NULL default '0',
`year` int(4) NOT NULL default '0',
`month` int(2) NOT NULL default '0',
`day` int(2) NOT NULL default '0',
`education` varchar(4) NOT NULL default '',
`marital` int(1) NOT NULL default '0',
`occupation` varchar(15) NOT NULL default '',
`companyname` varchar(30) NOT NULL default '',
`province` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=InnoDB AUTO_INCREMENT=43 ;
--
-- 导出表中的数据 `tmail_userpersonal`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_users`
--
CREATE TABLE `tmail_users` (
`id` int(11) unsigned NOT NULL auto_increment,
`userid` tinytext NOT NULL,
`domain` varchar(128) NOT NULL default '',
`email` varchar(128) NOT NULL default '',
`clear` varchar(128) NOT NULL default '',
`crypt` varchar(128) NOT NULL default '',
`realname` tinytext NOT NULL,
`uid` int(11) unsigned NOT NULL default '1003',
`gid` int(11) unsigned NOT NULL default '1003',
`homedir` tinytext NOT NULL,
`maildir` tinytext NOT NULL,
`quota` tinytext NOT NULL,
`file_quota` tinytext NOT NULL,
`access` enum('Y','N') NOT NULL default 'Y',
`postfix` enum('Y','N') NOT NULL default 'Y',
`disablepop3` char(1) NOT NULL default '0',
`disableimap` char(1) NOT NULL default '0',
`disablewebmail` char(1) NOT NULL default '0',
`sharedgroup` varchar(128) NOT NULL default '0',
`smtpaccess` enum('Y','N') NOT NULL default 'Y',
`expire` date NOT NULL default '0000-00-00',
`create_time` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_users`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_virtual`
--
CREATE TABLE `tmail_virtual` (
`id` int(11) unsigned NOT NULL auto_increment,
`email` varchar(128) NOT NULL default '',
`destination` varchar(128) NOT NULL default '',
`group_name` varchar(128) NOT NULL default '',
`domain` varchar(128) NOT NULL default '',
`group` int(1) default NULL,
`alias` int(1) default NULL,
`forward` int(1) default NULL,
`forward_bak` int(1) default NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_virtual`
--
-- --------------------------------------------------------
--
-- 表的结构 `vpopmail`
--
CREATE TABLE `vpopmail` (
`pw_id` int(5) unsigned NOT NULL auto_increment,
`pw_name` varchar(32) NOT NULL default '',
`pw_domain` varchar(64) NOT NULL default '',
`pw_passwd` varchar(40) NOT NULL default '',
`pw_uid` int(11) default NULL,
`pw_gid` int(11) default NULL,
`pw_gecos` varchar(48) default NULL,
`pw_dir` varchar(255) default NULL,
`pw_shell` varchar(20) default NULL,
`createtime` timestamp(14) NOT NULL,
PRIMARY KEY (`pw_id`),
KEY `pw_name` (`pw_name`,`pw_domain`)
) TYPE=MyISAM PACK_KEYS=1 AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `vpopmail`
注:对于初学者,建议以上操作都在phpmyadmin中操作更加的简便.
建立数据库用户并授以相应的权限
QUOTE:
mail# /usr/local/bin/mysql –u root –p
mysql# use mysql;
mysql# INSERT INTO user (host,user,password) VALUES('localhost','postfix','');
mysql# update user set password=password('postfix') where User='postfix';
重新启动mysql
mail# /usr/local/bin/mysql –u root –p
mysql# use mysql;
mysql# GRANT ALL ON postfix.* TO postfix@localhost IDENTIFIED BY "postfix";
注:这里加用户名和密码都为:postfix。并授权对postfix数据库进行操作
4.9 安装cyrus-sasl-2.1.22
QUOTE:
mail# cd /usr/ports/security/cyrus-sasl2
mail# make WITH_MYSQL=yes WITH_AUTHDAEMON=yes install clean
WITH_AUTHDAEMON=yes参数是为了让sasl支持authdamond的认证方式
WITH_MYSQL=yes参数是为了让sasl支持mysql认证方式
4.10 安装postfix-2.3.3
QUOTE:
mail# cd /usr/ports/mail/postfix
mail# make install clean
以下是我的选择
QUOTE:
Postfix configuration options
Please select desired options:
[ ] NOPCRE DISABLE Perl Compatible Regular Expressions
[ ] SASL Cyrus SASLv1 (Simple Authentication and Security Layer)
[X] SASL2 Cyrus SASLv2 (Simple Authentication and Security Layer)
[ ] SASLKRB If your SASL requires Kerberos select this option
[ ] SASLKRB5 If your SASL requires Kerberos5 select this option
[ ] SASLKRB5MIT If your SASL requires MIT Kerberos5 select this option
[ ] SPF SPF support
[ ] TLS SSL and TLS
[ ] BDB Berkeley DB (select version using WITH_BDB_VER variable)
[X] MySQL MySQL map lookups (choose version with WITH_MYSQL_VER)
[ ] PgSQL PostgreSQL map lookups (choose with DEFAULT_PGSQL_VER)
[ ] OpenLDAP OpenLDAP map lookups (choose ver. with WITH_OPENLDAP_VER)
[ ] CDB CDB map lookups
[ ] NIS NIS map lookups
[X] VDA VDA (Virtual Delivery Agent)
[ ] Test SMTP/LMTP test server and generator
安装过程中会让你回答两个问题,
QUOTE:
===> Installing for postfix-2.2.10_1,1
===> postfix-2.2.10_1,1 depends on shared library: sasl2.2 - found
===> postfix-2.2.10_1,1 depends on shared library: pcre.0 - found
===> postfix-2.2.10_1,1 depends on shared library: mysqlclient.14 - found
Added group "postfix".
Added group "maildrop".
Added user "postfix".
You need user "postfix" added to group "mail".
Would you like me to add it [y]? y
是否把postfix用户加入到mail用户组。回答Y
Installed HTML documentation in /usr/local/share/doc/postfix
Would you like to activate Postfix in /etc/mail/mailer.conf [n]? n
在rc.conf中加入postfix的启动选项
QUOTE:
mail# echo 'postfix_enable="YES"' >> /etc/rc.conf
mail# echo 'sendmail_enable="NO"' >> /etc/rc.conf
mail# echo 'sendmail_submit_enable="NO"' >> /etc/rc.conf
mail# echo 'sendmail_outbound_enable="NO"' >> /etc/rc.conf
mail# echo 'sendmail_msp_queue_enable="NO"' >> /etc/rc.conf
4.11 安装courier-imap-4.1.1
QUOTE:
mail# cd /usr/ports/mail/courier-imap
mail# make install clean
以下是我的选择
QUOTE:
Options for courier-imap 4.1.1,1
[ ] OPENSSL Build with OpenSSL support
[ ] FAM Build in fam support for IDLE command
[ ] TRASHQUOTA Include deleted mails in the quota
[ ] GDBM Use gdbm db instead of system bdb
[ ] IPV6 Build with IPv6 support
[ ] AUTH_LDAP LDAP support
[X] AUTH_MYSQL MySQL support
[ ] AUTH_PGSQL PostgreSQL support
[ ] AUTH_USERDB Userdb support
[ ] AUTH_VCHKPW Vpopmail/vchkpw support
在rc.conf中加入courier的启动选项
QUOTE:
mail# echo 'courier_authdaemond_enable="YES"' >> /etc/rc.conf
mail# echo 'courier_imap_imapd_enable="YES"' >> /etc/rc.conf
mail# echo 'courier_imap_pop3d_enable="YES"' >> /etc/rc.conf
QUOTE:
mail# /usr/local/etc/rc.d/courier-authdaemond start
Starting courier_authdaemond.
注:此时会在/var/run/authdaemond/下产生socket,如果没有下面这一步下面的认证无法通过。
对/var/run/authdaemond目录加执行权限
QUOTE:
mail# chmod +x /var/run/authdaemond
4.12 安装MailScanner-install-4.55.10-3
QUOTE:
mail# cd /usr/ports/mail/mailscanner
mail# make install clean
在安装MailScanner的过程中会出现以下几个选项。以下是我的选择
QUOTE:
Options for MailScanner 4.55.10
[X] SPAMASSASSIN Install SpamAssassin
[X] CLAMAV Install ClamAV
[X] CLAMAVMODULE Install ClamAV Module
[ ] BDC Install BitDefender
Options for sqlite 3.3.6
[ ] TCLWRAPPER TCL wrapper for SQLITE
[ ] THREADS Enable threads support
[ ] DEBUG Enable debugging & verbose explain
[ ] DOCS Building docs (depends on TCL)
Options for Mail-SpamAssassin-3.1.6
[X] AS_ROOT Run spamd as root (recommended)
[X] DOMAINKEYS DomainKeys support
[X] SSL Build with SSL support for spamd/spamc
[ ] MYSQL Add MySQL support
[ ] PGSQL Add PostreSQL support
[X] RAZOR Add Vipul's Razor support
[X] SPF_QUERY Add SPF query support
[X] RELAY_COUNTRY Relay country support
[X] TOOLS Install SpamAssassin tools
Options for clamav 0.88.5
[X] MILTER Compile the milter interface
[X] CURL Support URL downloading
[X] LIBUNRAR Support for external Unrar library
[X] STDERR Print logs to stderr instead of stdout
生成初始配置文件
QUOTE:
mail# make initial-config
根据提示创建MailScanner运行所需的目录
mail# mkdir -p /var/spool/MailScanner/incoming
mail# mkdir /var/spool/MailScanner/quarantine
mail# chown -R postfix:postfix /var/spool/MailScanner
分别在rc.conf中加入clamav、spamassassin和MailScanner的启动参数
QUOTE:
mail# echo 'clamav_clamd_enable="YES"' >> /etc/rc.conf
mail# echo 'clamav_freshclam_enable="YES"' >> /etc/rc.conf
mail# echo 'clamav_milter_enable="YES"' >> /etc/rc.conf
mail# echo 'mailscanner_enable="YES"' >> /etc/rc.conf
mail# echo 'spamd_enable="YES"' >> /etc/rc.conf
4.13 安装maildrop-2.0.2
QUOTE:
mail# cd /usr/ports/mail/maildrop
mail# make install WITH_AUTHLIB=yes
以下是我的选择
QUOTE:
Options for maildrop 2.0.2
[ ] AUTH_LDAP LDAP support
[X] AUTH_MYSQL MySQL support
[ ] AUTH_PGSQL PostgreSQL support
[ ] AUTH_USERDB Userdb support
[ ] AUTH_VCHKPW Vpopmail/vchkpw support
QUOTE:
mail# /usr/local/bin/maildrop -v
maildrop 2.0.2 Copyright 1998-2005 Double Precision, Inc.
Courier Authentication Library extension enabled.
Maildir quota extension enabled.
This program is distributed under the terms of the GNU General Public
License. See COPYING for additional information.
出现上面红色的部分表示maildrop支持Courier。
第五章 软件系统配置
为了支持maildrop,先加入vmail用户
QUOTE:
mail# pw adduser vmail -u 1003 -s /sbin/nologin -d /dev/null
5.1 配置cyrus-sasl
创建/usr/local/lib/sasl2/smtpd.conf
QUOTE:
mail# ee /usr/local/lib/sasl2/smtpd.conf
pwcheck_method: auxprop
auxprop_plugin: sql
mech_list: plain login
sql_engine: mysql
sql_hostnames: localhost
sql_user: tmail
sql_passwd: tmail
sql_database: tmail
sql_select: select clear from tmail_users where email='%u@%r' and smtpaccess='Y'
5.2 配置postfix
QUOTE:
mail# ln -s /usr/local/sbin/sendmail /usr/sbin/sendmail
注:如果/usr/sbin/sendmail存在就删了再做上链接,如果升级内核和升级系统后要重新做这一步。
mail# echo ‘postfix: root’ >> /etc/aliases
mail# /usr/local/bin/newaliases
mail# chown postfix:postfix /etc/opiekeys
mail# ee /usr/local/etc/postfix/main.cf
修改/usr/local/etc/postfix/main.cf,在文件最后加入以下内容
QUOTE:
# new add
myhostname = mail.test.com
smtp_helo_name = $myhostname
local_transport = maildrop
mailbox_transport = maildrop
smtpd_error_sleep_time = 0
smtpd_soft_error_limit = 10
smtpd_hard_error_limit = 20
default_process_limit = 100
#alias_maps = hash:/usr/local/etc/postfix/aliases
#Alias_database = hash:/usr/local/etc/postfix/aliases
mydestination = mysql:/usr/local/etc/postfix/mysql-mydest.cf
virtual_transport_maps = mysql:/usr/local/etc/postfix/mysql-transport.cf
virtual_alias_maps = mysql:/usr/local/etc/postfix/mysql-virtual.cf
#virtual_alias_maps = mysql:/usr/local/etc/postfix/mysql-alias.cf
recipient_bcc_maps = mysql:/usr/local/etc/postfix/mysql-autobbc-in.cf
sender_bcc_maps = mysql:/usr/local/etc/postfix/mysql-autobbc-out.cf
local_recipient_maps = $alias_maps $virtual_mailbox_maps $virtual_maps
virtual_mailbox_base = /mail
virtual_mailbox_maps = mysql:/usr/local/etc/postfix/mysql-virtual-maps.cf
virtual_create_maildirsize = yes
virtual_mailbox_extended = yes
virtual_mailbox_limit_maps = mysql:/usr/local/etc/postfix/mysql-virtual-quota.cf
virtual_mailbox_limit_override = yes
virtual_maildir_limit_message = Sorry, the user's maildir has overdrawn his diskspace quota, please try again later.
virtual_overquota_bounce = yes
virtual_uid_maps = mysql:/usr/local/etc/postfix/mysql-virtual-uid.cf
virtual_gid_maps = mysql:/usr/local/etc/postfix/mysql-virtual-gid.cf
broken_sasl_auth_clients = yes
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_client_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
check_client_access mysql:/usr/local/etc/postfix/mysql-access.cf,
permit_auth_destination,
reject
smtpd_sender_restrictions =
reject_unknown_sender_domain,
reject_non_fqdn_sender,
check_sender_access mysql:/usr/local/etc/postfix/mysql-access.cf,
permit
smtpd_recipient_restrictions =
permit_mynetworks,
check_client_access mysql:/usr/local/etc/postfix/mysql-access.cf,
permit_sasl_authenticated,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
reject_unknown_recipient_domain,
reject_unauth_pipelining,
reject_unauth_destination,
reject_rbl_client cblless.anti-spam.org.cn,
permit
default_destination_recipient_limit = 1
local_destination_concurrency_limit = 1
maildrop_destination_recipient_limit = 1
message_size_limit = 41943040
smtpd_recipient_limit = 10
bounce_queue_lifetime = 12h
maximal_queue_lifetime = 24h
smtpd_delay_reject = yes
smtpd_helo_required = yes
strict_rfc821_envelopes = yes
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-access.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
query = select access from tmail_access where source = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-alias.cf
host = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_virtual
query = select alias from tmail_users where email = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-autobbc-in.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
query = select autobbc from tmail_autobbc where email = '%s' AND come='1'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-autobbc-out.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
query = select autobbc from tmail_autobbc where email = '%s' AND 'out'='1'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-mydest.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_domaininfo
query = select domain from tmail_domaininfo where domain = '%s' AND yesno = '1'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-transport.cf
host = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_domaininfo
query = select transport from tmail_domaininfo where domain = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-virtual-gid.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_users
query = select gid from tmail_users where email = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-virtual-uid.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_users
query = select uid from tmail_users where email = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-virtual-maps.cf
host = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_users
query = select maildir from tmail_users where email = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-virtual-quota.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_users
query = select quota from tmail_users where email = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-virtual.cf
host = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_virtual
query = select destination from tmail_virtual where email = '%s'
5.3 配置Courier-imap
修改Courier相关设置,/usr/local/etc/courier-imap/imapd:
QUOTE:
IMAP_CAPABILITY="IMAP4rev1 CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA"
修改/usr/local/etc/courier-imap/pop3d
QUOTE:
POP3AUTH="LOGIN CRAM-MD5 CRAM-SHA1"
编辑修改/usr/local/etc/authlib/authmysqlrc
QUOTE:
MYSQL_SERVER localhost
MYSQL_USERNAME tmail
MYSQL_PASSWORD tmail
MYSQL_PORT 0
MYSQL_OPT 0
MYSQL_DATABASE tmail
MYSQL_USER_TABLE tmail_users
MYSQL_CRYPT_PWFIELD crypt
#MYSQL_CLEAR_PWFIELD clear
MYSQL_UID_FIELD uid
MYSQL_GID_FIELD gid
MYSQL_LOGIN_FIELD email
MYSQL_HOME_FIELD homedir
MYSQL_NAME_FIELD realname
MYSQL_MAILDIR_FIELD maildir
MYSQL_QUOTA_FIELD quota
MYSQL_AUXOPTIONS_FIELD CONCAT("disableimap=",disableimap,",disablepop3=",disablepop3,",disablewebmail=",disablewebmail,",sharedgroup=",sharedgroup)
MYSQL_WHERE_CLAUSE access='y'
注:红色部分为一行。这里得用tab键来跳格
编辑/usr/local/etc/authlib/authdaemonrc
QUOTE:
authmodulelist="authmysql"
authmodulelistorig="authmysql"
version="authdaemond.mysql"
daemons=5
authdaemonvar=/var/run/authdaemond
subsystem=mail
DEBUG_LOGIN=0
DEFAULTOPTIONS="wbnodsn=1"
5.4 安装Tmail管理工具
本节主要介绍如何安装Tmail管理工具。
更改httpd.conf中的Group www、User www修改为: Group vmail、User vmail
安装Tmail管理工具来设置第一个域名和用户
上传管理工具到网站目录
后修改config/config.inc.php中的
QUOTE:
define(MAILDIR,"/mail"); //邮件存放目录
define(MISC, ".misc");
define(MODE,0700);
$PageSize = 15; //用户列表和域名列表每一页显示多少行
$host = "localhost"; //数据库主机名
$user = "tmail"; //数据库用户名
$dbname = "tmail"; //数据库名称
$password = "tmail"; //数据库密码
设置好后,加一个域名为test.com,加一个邮件用户为: test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
5.5 登录测试
重新启动相关服务
QUOTE:
mail# /usr/local/etc/rc.d/apache22 restart
mail# /usr/local/etc/rc.d/mysql-server restart
mail# /usr/local/etc/rc.d/courier-authdaemond stop
mail# /usr/local/etc/rc.d/courier-authdaemond start
mail# /usr/local/etc/rc.d/courier-imap-imapd.sh restart
mail# /usr/local/etc/rc.d/courier-imap-pop3d.sh restart
mail# /usr/local/etc/rc.d/postfix reload
生成用户名的base64编码
QUOTE:
mail# perl -MMIME::Base64 -e 'print encode_base64(" test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 ");'
dGVzdEB0ZXN0LmNvbQ==
mail# perl -MMIME::Base64 -e 'print encode_base64("000000");'
MDAwMDAw
测试25发送
QUOTE:
mail# telnet localhost 25
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.test.com.
Escape character is '^]'.
220 mail.test.com ESMTP Postfix
ehlo mail
250-mail.test.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH LOGIN PLAIN
250-AUTH=LOGIN PLAIN
250 8BITMIME
auth login
334 VXNlcm5hbWU6
dGVzdEB0ZXN0LmNvbQ==
334 UGFzc3dvcmQ6
MDAwMDAw
235 Authentication successful
MAIL FROM:< test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 >
250 Ok
RCPT TO:< test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 >
250 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
SUBJECT:test
test
.
250 Ok: queued as 47C6CB83E
quit
221 Bye
Connection closed by foreign host.
测试110收邮件
QUOTE:
mail# telnet localhost 110
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.test.com.
Escape character is '^]'.
+OK Hello there.
user test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
+OK Password required.
pass 000000
+OK logged in.
list
+OK POP3 clients that break here, they violate STD53.
1 1563
2 401
.
retr 2
+OK 401 octets follow.
Return-Path: < test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 >
Delivered-To: test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
Received: from mail (localhost.test.com [127.0.0.1])
by mail.test.com (Postfix) with ESMTP id 47C6CB83E
for < test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 >; Tue, 11 Jul 2006 13:47:28 +0800 (CST)
SUBJECT:test
Message-Id: < 20060711054736.47C6CB83E@mail.test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 >
Date: Tue, 11 Jul 2006 13:47:28 +0800 (CST)
From: test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
To: undisclosed-recipients:;
test
.
dele 2
+OK Deleted.
quit
+OK Bye-bye.
Connection closed by foreign host.
5.6 垃圾邮件和病毒邮件过滤设置
设置MailScanner
编辑/usr/local/etc/postfix/main.cf
QUOTE:
mail# ee /usr/local/etc/postfix/main.cf
去掉513行的注释
QUOTE:
header_checks = regexp:/usr/local/etc/postfix/header_checks
编辑/usr/local/etc/postfix/header_checks
QUOTE:
mail# ee /usr/local/etc/postfix/header_checks
新加入
/^Received:/ HOLD
重新加载postfix配置文件
mail# postfix reload
编辑/usr/local/etc/MailScanner/MailScanner.conf,以下是我的配置文件,更改相应的参数为下面的格式。
QUOTE:
%org-name% = thismail.org
%org-long-name% = LCSoft
%web-site% = www.thismail.org
%etc-dir% = /usr/local/etc/MailScanner
%report-dir% = /usr/local/share/MailScanner/reports/en
%rules-dir% = /usr/local/etc/MailScanner/rules
%mcp-dir% = /usr/local/etc/MailScanner/mcp
Run As User = postfix
Run As Group = postfix
Incoming Queue Dir = /var/spool/postfix/hold
Outgoing Queue Dir = /var/spool/postfix/incoming
MTA = postfix
Sendmail = /usr/sbin/sendmail
Monitors for ClamAV Updates = /var/db/clamav/*.cvd
SpamAssassin User State Dir = /var/spool/MailScanner/spamassassin
Custom Functions Dir = /usr/local/lib/MailScanner/MailScanner/CustomFunctions
SpamAssassin Install Prefix = /usr/local/bin
SpamAssassin Site Rules Dir = /usr/local/etc/mail/spamassassin
SpamAssassin Local Rules Dir = /usr/local/share/spamassassin
编辑配置文件/usr/local/etc/MailScanner/mcp/mcp.spam.assassin.prefs.conf
QUOTE:
mail# ee /usr/local/etc/MailScanner/mcp/mcp.spam.assassin.prefs.conf
# MailScanner
# MailScanner users, please see the comments at the bottom of this file.
# MailScanner
#
# SpamAssassin user preferences file.
#
# Format:
#
# required_hits n
# (how many hits are required to tag a mail as spam.)
#
# score SYMBOLIC_TEST_NAME n
# (if this is omitted, 1 is used as a default score.
# Set the score to 0 to ignore the test.)
#
# # starts a comment, whitespace is not significant.
#
###########################################################################
# Whitelist and blacklist addresses are *not* patterns; they're just normal
# strings. one exception is that "*@isp.com" is allowed. They should be in
# lower-case. You can either add multiple addrs on one line,
# whitespace-separated, or you can use multiple lines.
#
# Monty Solomon: he posts from an ISP that has often been the source of spam
# (no fault of his own ;), and sometimes uses Bcc: when mailing.
#
#whitelist_from monty@roscom.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
# Add your blacklist entries in the same format...
#
# blacklist_from friend@public.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
# Mail using languages used in these country codes will not be marked
# as being possibly spam in a foreign language.
#
#ok_locales en
skip_rbl_checks 1
use_bayes 0
use_dcc 0
use_pyzor 0
use_razor1 0
use_razor2 0
decode_attachments 1
编辑配置文件/usr/local/etc/MailScanner/rules/max.message.size.rules
QUOTE:
mail# ee /usr/local/etc/MailScanner/rules/max.message.size.rules
# This is an example ruleset to show how rules can have resulting values
# other than yes and no. This ruleset demonstrates having a numerical result.
# The From: and To: rules show how simple domains can be used to select
# different values for the result of the ruleset.
# Note that the fields of each rule line can be separated by any whitespace,
# any combination of tabs and spaces.
#
# The 2 lines involving domain3.com show that for email to user@domain3.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
# has a limit of 5Mbytes per message, while email to any other user
# @domain3.com has a limit of 500Kbytes per message.
#
To: *@domain1.com 10M
To: *@domain2.com 20M
From: user@domain3.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 5M
From: *@domain3.com 500K
#
# The following line specifies the default result used when none of the
# other rules match. In this example,
# Maximum Message Size = 0
# means that there is no limit to the size of the message.
#
FromOrTo: default 0
编辑配置文件/usr/local/etc/MailScanner/rules/bounce.rules
QUOTE:
mail# ee /usr/local/etc/MailScanner/rules/bounce.rules
# You can use this ruleset to enable the "bounce" Spam Action.
# You must *only* enable this for mail from sites with which you have
# agreed to bounce possible spam. Use it on low-scoring spam only (<10)
# and only to your regular customers for use in the rare case that a
# message is mis-tagged as spam when it shouldn't have been.
# Beware that many sites will automatically delete the bounce messages
# created by using this option unless you have agreed this with them in
# advance.
# This next line gives an example of how you might enable this option for
# a frequent customer of yours.
#From: yourcustomer.com yes
# Under no circumstances should this be changed to "yes".
FromOrTo: default no
重新启动mailscanner.
QUOTE:
mail# /usr/local/etc/rc.d/mailscanner restart
加入中文垃圾邮件规则
QUOTE:
mail# cd /usr/local/share/spamassassin
mail# fetch http://www.ccert.edu.cn/spam/sa/Chinese_rules.cf
注意:由于加入了中文垃圾邮件规则,请把垃圾邮件的评分的分数设置为高一些。
我的设置为8,最高分设置为10,这样可以降低中文邮件的错误识别率。用户可以根据自己的需要来改变这两个分值。
QUOTE:
Required SpamAssassin Score = 8
High SpamAssassin Score = 10
为了避免邮件扫描后主题变成乱码,建议改变以下设置为no
QUOTE:
Virus Modify Subject = no
Filename Modify Subject = no
Content Modify Subject = no
Disarmed Modify Subject = no
Spam Modify Subject = yes
High Scoring Spam Modify Subject = no
扫描垃圾邮件所执行的运作
QUOTE:
Spam Actions = deliver
High Scoring Spam Actions = delete
相关运作参数说明:
QUOTE:
"deliver" -- 邮件正常的转送至原来的收信人。
"delete" -- 删除邮件。
"store" -- 将邮件存放至隔离区。
"bounce" -- 将邮件退给寄信人。
"forward" -- 提供一个 forward 的邮件位址给系统,系统会自动转寄一份。
"striphtml" -- 将内嵌 HTML 的邮件转成 Text,你必需要加入 "deliver",系统才会帮你寄邮件。
允许html邮件通行
QUOTE:
Allow IFrame Tags = yes
Allow Form Tags = yes
Allow Script Tags = yes
Allow WebBugs = disarm
Allow Object Codebase Tags = yes
Convert Dangerous HTML To Text = no
Convert HTML To Text = no
允许本机发送出去的邮件不被MailScanner视为垃圾邮件
QUOTE:
mail# ee /usr/local/etc/MailScanner/rules/spam.whitelist.rules
From: 127.0.0.1 yes
以上规则的说明:以上垃圾邮件的规则是加入了中文垃圾邮件主题和内容的评分规则,所以我设置了一个最低分为8分,最高分为10分的规则,8-10分间的邮件会被打上[spamd]的标识,高于10分的邮件会被自动的删除掉,以上规则允许html邮件通过。
第六章 安装webmail
到开邮件邮件技术论坛
http://www.thismail.org/bbs/thread.php?fid=17
取得最新的webmail
下载后传到服务器上的web目录,后配置php的全局变量为打开
QUOTE:
register_globals = On
并编辑webmail/config/config_inc.php中如下参数
QUOTE:
$CFG_BASEPATH = "/tmp/tmail/temp"; //临时目录,如果不存在,修改完配置文件后再手动创建,并附于相关的权限
// Mysql
define(MYSQL_HOST, 'localhost'); //数据库主机名
define(MYSQL_USER, 'postfix'); //数据库用户名
define(MYSQL_PASS, 'postfix'); //数据库密码
define(MYSQL_DATA, 'postfix'); //数据库名称
$CFG_NETDISK_PATH = "/mail/netdisk"; //文件管理(网络磁盘所在的系统路径)如果不存在,修改完配置文件后再手动创建,并附于相关的权限
$CFG_NETDISK_DEFAULT_QUOTA = 10; //文件管理(网络磁盘)默认大小为10MB,用户可根据自己的需要改变大小
。
QUOTE:
mail# mkdir -p /tmp/tmail/temp
mail# chown -R postfix:postfix /tmp/tmail
mail# mkdir -p /mail/netdisk
mail# chown -R postfix:postfix /mail/netdisk
为了webmail能配合maildrop做中文邮件的过滤。请写/usr/sbin/maildecode文件
QUOTE:
mail# ee /usr/sbin/maildecode
#!/usr/bin/perl
# Convert Base64 Or Quoted-printable TO Text
my $a = $ARGV[0] || '';
#Maybe arg is include Subject
if ($a=~/^Subject/) {
$a = $ARGV[1] || '';
};
if ($a=~/=?[w-]+?B?(.*)?=$/) {
use MIME::Base64;
$a = decode_base64($1);
};
if ($a=~/=?[w-]+?Q?(.*)?=$/) {
use MIME::QuotedPrint;
$a = decode_qp($1);
};
#open(OUTFILE, ">/tmp/list.log");
#print OUTFILE $a;
#close(OUTFILE);
print $a;
exit(0);
并改变相关的权限
QUOTE:
mail# chmod 755 /usr/sbin/maildecode
mail# chown -R vmail:vmail /usr/sbin/maildecode
安装成功后的webmail
附一、常见问题
正在收集中……
附二、MailScanner.conf中文参数说明
http://www.thismail.org/bbs/htm_data/7/0509/533.html
FreeBSD+Postfix+Cyrus-sasl+Courier-imap+Webmail+spamassassin+Clamav+mailscanner +maildrop来架构一个具有多域名,webmail、防病毒、防垃圾邮件、web管理界面的邮件系统。
Jacky, $Revision: 5.1 bate $Date: 2006-10-31
杨廷勇 QQ:187159779
Copyright © 2004、2005、2006
欢迎转载,保留版权和出处[http://www.thismail.org]
系统主要采用MailScanner+clamav+Spamd来对病毒过滤和垃圾邮件过滤。
----------------------------------
新增加对smtpd,imap,pop3,webmail的登录控制
新增加简单的邮件分组
新增邮件监控
加强对垃圾邮件的过滤
增加系统黑名单
-----------------------------------
本文在4.10、5.3、5.4、5.5、6.0、6.1上安装测试通过,采用MailSanner来对邮件过滤和垃圾邮件过滤,配置更容易,并且降低了 系统开消。让系统更加稳定,经过严格病毒邮件测试成功率达到了98%。垃圾邮件过滤基本上达到了95%的成功率。
本人水平有限,如有错误之处请发邮件到scyz2 at 163.com (at = @),本人不对本文档对你的系统造成的损失负任何连带责任。
――――――――――――――――――――――――――――――――――
第一章 FreeBSD 简介
1.1 什么是FreeBSD?
1.2 postfix简介
第二章 操作系统FreeBSD安装
第三章 更新ports
3.1 安装CVSUP
3.2 更新ports树
第四章 软件安装
4.1 安装perl
4.2 安装mysql
4.3 安装apache22
4.4 安装mod_php5
4.5 安装php5的扩展
4.6 安装openssl
4.7 安装phpMyAdmin
4.8 设置数据库
4.9 安装cyrus-sasl2
4.10 安装postfix2
4.11 安装Courier-imap
4.12 安装MailScanner
4.13 安装maildrop
第五章 软件系统配置
5.1 配置cyrus-sasl
5.2 配置postfix
5.3 配置Courier-imap
5.4 安装Tmail管理工具
5.5 登录测试
5.6 垃圾邮件和病毒邮件过滤设置
第六章 安装webmail
附一、常见问题
附二、MailScanner.conf中文参数说明
――――――――――――――――――――――――――――――――――
第一章 FreeBSD 简介
1.1 什么是FreeBSD?
我 想大家都知道 Microsoft Windows 是一套作业系统,FreeBSD 也是一套作业系统。FreeBSD 是一个可以在 Intel 相容个人计算机、DEC Alpha 或 PC98 架构的计算机上执行的 UNIX 作业系统。大家应该听过另一套UNIX的作业系统 Linux,FreeBSD 也是一套免费的作业系统。它可以让我们的个人计算机变成先进的工作站,更稳定的提供你所需的网络服务。
BSD UNIX 系统可以说是网络作业系统的始祖,FreeBSD 是众多 BSD UNIX 分支中的一个,它继承了 BSD 系统的高性能与可靠性。自从1993 年 FreeBSD 推出 1.0-RELEASE 以来,FreeBSD 开发团队便致力于系统的调校,使其发挥绝佳的效能。在 FreeBSD 团队的统筹努力下,使它比起其它免费的 UNIX 作业系统更有结构。在 FreeBSD 上有许多支援的免费软件,这些软件大都已移植收录于 FreeBSD ports 中,使得我们在安装软件时变得十分轻松。FreeBSD 支援 32 位元、64 位元的许多不同平台,具有高效能核心架构、动态函式库共享、绝佳的网络功能,比起其它商用 UNIX 系统毫不逊色。
FreeBSD 作业系统相当容易取得及安装,除了经由传统的光盘安装外,它也可以经由网络安装、MS-DOS 分割区安装等等。当然,我们也可以在计算机中同时安装多种不同的作业系统,例如 Windwos 98 和 FreeBSD 同时并存也是件十分容易的事。
在 FreeBSD 上的应用软件相当的多,也都可以免费取得,由于 FreeBSD 的稳定性高且功能强大,因此许多大型网站都以它为作业平台,其中最知名的就是 YAHOO!。YAHOO! 是一个流量相当大的入口网站,他们选择以 FreeBSD 为作业平台,由此可知 FreeBSD 的优异性。除此之外,在台湾,FreeBSD 普遍被应用于学术网络上,许多大专院校的服务器都是使用 FreeBSD 来提供网络服务。
但是您别以为 FreeBSD 只能用来做为网络服务器,FreeBSD 也可以是一个良好的个人作业系统。事实上,Mac OS X 就是使用 FreeBSD 做为系统核心。大家都知道 Mac OS 是个人计算机上有名的作业系统,它会使用 FreeBSD 做为系统核心,可见 FreeBSD 的质量亦深获 Apple 的肯定。
在宽带网络逐渐普及的台湾,每个人都可以自行架设一台网路服务器,以 FreeBSD 来提供网络服务(如网页、邮件、档案存取等)。值得一提的是 FreeBSD 并不像 MS-Windows 一样每每要求使用者升级计算机才能使用。FreeBSD 对于硬件的要求很低,你可以用一台 Intel 586-133MHz 的旧计算机来安装 FreeBSD,这也算是癈物利用吧。
1.2 为什么要选择FreeBSD?
现在的个人计算机作业系统市场中,是以 MS-Windows 独大,但在网络服务器市场中,UNIX 系统的使用率可不输 MS-Windows 喔。我个人认为 MS-Windows 之所以会有那么多的使用者主要是因为他的使用者介面对初学者而言较容易操作,再加上许多软件的配合及盗版的助长,安装软件只要一直按下一步就完成。使得使 用者即便它的稳定性不高也得乖乖的接受。
其实初学者没有试过其它的作业系统才会有这样的误解,因为一直用 MS-Windows 才会认为当机是无可避免的事,当机对于 MS-Windows 或许是无可避免,但在其它 UNIX 系统中可不常见。在 UNIX 系列的作业系统中,也可以有像 MS-Windows 的图形介面,几乎所有在 MS-Windows 上可以做的事,在 FreeBSD 上都可以做得到,唯一的不同点是你不必花钱去取得你想要的功能。包括排版、图形处理、MP3、多媒体、网络芳邻等等都可以在 FreeBSD 中做到。
重 点是,对于一台网路服务器而言,图形介面会占用系统资源,而且必须安装一堆有的没的软件。笔者偏好乾干脆净净的系统,而 FreeBSD 正好具有这个优点。不过这并不代表您不能使用图形介面,您还是可以安装类似 Windows 的图形介面,让您使用它来做为平日使用的个人计算机。此外,FreeBSD 把自己定位为最佳的网络服务器,它承袭了 BSD 优良的网络血统,在提供服务时能有绝佳的效能表现。而且,它是免费的。
然而,有这么多的免费 UNIX 作业系统中,为什么要选择 FreeBSD 而不使用其它作业系统(如 Linux )呢?在网络上在讨论这个问题时,每每会引发每个作业系统使用者的激辩。对于要使用何种作业系统,除了使用者偏好外,还有许多指标可以提供我们参考。
FreeBSD 是一套完整的作业系统
我 们平常所说的 Linux 指的是它的核心 (Kernel),Kernel 只是整个作业系统的一部份,除了 Kernel 外,我们还需要一些基本的指令、系统及目录架构、图形介面等。Linux 的 Distribution 就是各个不同的组织或公司自行收集一些系统必备的程序,制作出一个作业系统。Linux 系统有很多的 Distribution,如 Redhat、SUSE、Debian 等。因为每个 Distribution 都有自己的程序或架构,所以每个 Distribution 都长的不一样。如果您使用的是 Linux,在 Linux 三十多种 Distribution 中,每一种之间都有些许差异,在设定上用法都不同。
而 FreeBSD 指的不只是核心而已,它是一套完整的作业系统。从系统核心到使用者介面、各种常用指令都是由 FreeBSD 总部所统一推出。因此,FreeBSD 每一个版本之间有着共同的架构,不同软件之间的整合搭配性较为完整,不管是设定、学习都很容易。
由核心团队统筹开发
FreeBSD 整个系统都是由总部的核心团队所维护,所以整个系统都搭配得很好。而 Linux 只有 Kernel 由 Linus Torvlds 所维护。因为 FreeBSD 整个系统都是由总部所开发,不只系统整体较有规划,各个程序之间搭配起来也比较紧密。如果您使用 Linux,由于 Kernel 和其它的程序是由不同组织所维护,在升级时,比较麻烦。而 FreeBSD 就简单多了,每次一有新的版本,一定是整个系统一起升级。
我 觉得 FreeBSD 总部统筹发展 FreeBSD 是一件很棒的事,所有的问题回报都可以统筹管理并予以更新。FreeBSD 推陈出新的速度相当快,每一次安装都确保这个版本不会有上一版的缺失。所以在 Linux 或 MS-Windows 中「新版本不一定是最好」的定律并不适用于 FreeBSD。但这并不意味着你必须不断重新安装系统,FreeBSD 总部随时会发布最新更新的档案提供下载,而且如果使用 CVSUP 就可以和更新版的 FreeBSD 保持同步更新。
完整的使用手册
FreeBSD 的使用手册非常完整,而且会随着软件一起更新。每次一有新的版本推出,FreeBSD 的文件也会做相对的更新。在 FreeBSD 的官方网站上,我们可以轻松的取得这些高质量的手册及文件。
软件安装的便利性
FreeBSD 支援的软件相当多,它将常用的软件分类管理,当需要安装软件时,使用者不必自行到网络上找寻,只要到 FreeBSD ports 目录中下个指令就安装完成。
条理分明的系统架构
FreeBSD 目录结构非常有组织,结构严谨,对于系统管理者而言比较不会紊乱。FreeBSD 将系统预设的目录和管理者自行安装的程序目录划分得十分清楚,管理者在进行升级、维护时可以一目了然。
完美的程序开发平台
安 装完 FreeBSD 后,系统所有程序原始码非常有条理的存放在特定目录中。对于想要进行程序修改的管理者而言,不必四处寻找某支程序原始码。笔者在 FreeBSD 及 Linux 上开发程序多年,我觉得 FreeBSD 原始码及系统架构十分有组织,是程序开发最好的平台。
而且 FreeBSD 的版权较为宽松,对于想要以 FreeBSD 为基础开发产品的公司而言,它可以保护您的修改不会被其它对手抄袭。
1.3 为什么不选择FreeBSD?
许 多企业选择使用目前大多数人使用的 MS windows 做为一般作业用个人计算机的作业系统当然无可厚非。但以 MS windows 做服务器,除了资讯人员的偏好外(或许因为不会使用其它系统吧),还有部份原因是为了在企业内资讯人员技术不足时,能求助于系统供应商。而 FreeBSD 是免费的,企业也害怕有状况时无人可以支援,这对企业是很重要的一项因素。
然而,FreeBSD 的使用人数其实很多,而且使用者都十分热心,在台湾的 BBS 讨论区上,许多问题都可以获得解决。这当然还不够,现在有很多顾问公司提供 FreeBSD 的顾问服务,可以提供企业这方面的服务。虽然说 FreeBSD 的系统稳定,但在应用上如果没有资讯人员的支援,很难能在企业中存活。但正因市场上了解 FreeBSD 的人较少,若我们能主动学习,必能为自己创造更多价值。
FreeBSD 目前支援的平台不多,若您不是使用 X86 的系统,而是使用 ARM、MIPS 等平台,您就必须寻求其它的解决方案。
1.2 postfix简介
Postfix 是一个非常优秀的MTA,她素以高效、安全的特点而著称。Postfix是作者在UNIX上所见过的MTA中在反垃圾邮件(Anti-Spam或Anti -UCE)方面做得最好的一个,甚至有很多公司在Postfix代码的基础上进行二次开发而推出反垃圾邮件网关产品。MTA的反垃圾邮件功能,实际上就是 在MTA处理过程中对会话进行过滤。这个过滤不但过滤了发往自身的垃圾邮件,而且还防止了自身被恶意利用发送垃圾邮件。Postfix实现了目前所有主要 的MTA过滤技术。postfix是Wietse Venema在IBM的GPL协议之下开发的MTA(邮件传输代理)软件。和Sendmail相比Postfix更快、更容易管理、更灵活、更安全,同时 还与sendmail保持足够的兼容性。
第二章 操作系统FreeBSD安装
FreeBSD6.1系统安装手册
官方安装手册:
QUOTE:
http://www.freebsd.org/doc/zh_CN.GB2312/books/handbook/install.html
注意在系统安装过程中,分区方面一定要注意,最好是一个独立的mail分区来存放用户的邮件数据,因为默认的VAR分区读写频繁,数据存放在这样的分区中是很危险的;在安装系统的过程中,建议最小化安装加ports。
第三章 更新ports
3.1 安装CVSUP
QUOTE:
mail# cd /usr/ports/net/cvsup-without-gui
mail# make install clean
安装过程中由于依赖的关系,它会自动安装gettext,会出现下面的选项。
以下是我的选择。
QUOTE:
Options for gettext 0.14.5_2
[X] EXAMPLES install example files
[X] HTMLMAN install man pages in HTML format
3.2 更新ports树
(更新日期2006-07-09)
编辑ports-supfile
QUOTE:
mail# ee /usr/share/examples/cvsup/ports-supfile
QUOTE:
第51行
*default host=CHANGE_THIS.FreeBSD.org
改为:
*default host=cvsup2.freebsdchina.org
QUOTE:
mail# /usr/local/bin/cvsup -g -L 2 /usr/share/examples/cvsup/ports-supfile
Parsing supfile "ports"
Connecting to Cvsup2.FreeBSD.org.cn
Connected to Cvsup2.FreeBSD.org.cn
Server software version: SNAP_16_1h
Negotiating file attribute support
Exchanging collection information
Establishing multiplexed-mode data connection
Running
Updating collection ports-all/cvs
……
……
Shutting down connection to server
Finished successfully
出现上面的提示,表示更新成功。
第四章 软件安装
4.1 安装perl-5.8.8
QUOTE:
mail# cd /usr/ports/lang/perl5.8
mail# make install clean
4.2 安装mysql-5.0.27
4.2 安装mysql
QUOTE:
mail# cd /usr/ports/databases/mysql50-server
编辑Makefile
mail# ee Makefile
在CONFIGURE_ARGS这段话的
--enable-thread-safe-client前面加上三句参数
BUILD_OPTIMIZED=yes \
BUILD_STATIC=yes \
PTHREAD_LIBS=-lkse \
mail# make WITH_CHARSET=gb2312 WITH_XCHARSET=all BUILD_OPTIMIZED=yes BUILD_STATIC=yes install clean
在rc.conf中加入Mysql的启动参数
QUOTE:
mail# echo 'mysql_enable="YES"' >> /etc/rc.conf
启动mysql
QUOTE:
mail# /usr/local/etc/rc.d/mysql-server start
Starting mysql.
查看mysql是否启动成功
QUOTE:
mail# sockstat -4 | grep 3306
mysql mysqld 1510 3 tcp4 *:3306 *:*
出现上面的端口表示mysql启动成功
4.3 安装httpd-2.2.3
QUOTE:
mail# cd /usr/ports/www/apache22
mail# make install clean
在rc.conf中加入apache的启动参数
QUOTE:
mail# echo 'apache_enable="YES"' >> /etc/rc.conf
启动apache
QUOTE:
mail# /usr/local/etc/rc.d/apache22 start
Starting apache.
查看apache是否启动成功
QUOTE:
mail# sockstat -4 | grep 80
www httpd 10679 16 tcp4 *:80 *:*
www httpd 10678 16 tcp4 *:80 *:*
www httpd 10677 16 tcp4 *:80 *:*
www httpd 10676 16 tcp4 *:80 *:*
www httpd 10675 16 tcp4 *:80 *:*
root httpd 10674 16 tcp4 *:80 *:*
出现上面的80端口表示apache启动成功
4.4 安装php-5.1.6
QUOTE:
mail# cd /usr/ports/lang/php5
mail# make install clean
以下是我的选择
QUOTE:
Options for php5 5.1.6_3
[ ] CLI Build CLI version
[ ] CGI Build CGI version
[X] APACHE Build Apache module
[ ] DEBUG Enable debug
[ ] SUHOSIN Enable Suhosin protection system
[X] MULTIBYTE Enable zend multibyte support
[ ] IPV6 Enable ipv6 support
[ ] REDIRECT Enable force-cgi-redirect support (CGI only)
[ ] DISCARD Enable discard-path support (CGI only)
[ ] FASTCGI Enable fastcgi support (CGI only)
[ ] PATHINFO Enable path-info-check support (CGI only)
QUOTE:
mail# echo 'AddType application/x-httpd-php .php' >> /usr/local/etc/apache/httpd.conf
mail# echo 'AddType application/x-httpd-php-source .phps' >> /usr/local/etc/apache/httpd.conf
编辑httpd.conf加入index.php
QUOTE:
DirectoryIndex index.html index.html.var index.php
#注:在DirectoryIndex这里加入index.php,是为了让apache支持首页为index.php的首页文件
4.5 安装php5的扩展
QUOTE:
mail# cd /usr/ports/lang/php5-extensions
mail# make install clean
QUOTE:
Options for php5-extensions 1.0
[X] BCMATH bc style precision math functions
[X] BZ2 bzip2 library support
[X] CALENDAR calendar conversion support
[X] CTYPE ctype functions
[X] CURL CURL support
[ ] DBA dba support
[ ] DBASE dBase library support
[X] DOM DOM support
[ ] EXIF EXIF support
[ ] FILEINFO fileinfo support
[ ] FILEPRO filePro support
[ ] FRIBIDI FriBidi support
[X] FTP FTP support
[X] GD GD library support
[ ] GETTEXT gettext library support
[ ] GMP GNU MP support
[X] HASH HASH Message Digest Framework
[X] ICONV iconv support
[ ] IMAGICK ImageMagick support
[X] IMAP IMAP support
[ ] INTERBASE Interbase 6 database support (Firebird)
[ ] LDAP OpenLDAP support
[ ] MBSTRING multibyte string support
[X] MCRYPT Encryption support
[X] MHASH Crypto-hashing support
[ ] MING ming shockwave flash support
[ ] MSSQL MS-SQL database support
[X] MYSQL MySQL database support
[ ] MYSQLI MySQLi database support
[ ] NCURSES ncurses support (CLI only)
[ ] ODBC unixODBC support
[ ] OPENSSL OpenSSL support
[ ] PANDA panda support
[ ] PCNTL pcntl support (CLI only)
[X] PCRE Perl Compatible Regular Expression support
[ ] PDF PDFlib support (implies GD)
[X] PDO PHP Data Objects Interface (PDO)
[ ] PGSQL PostgreSQL database support
[X] POSIX POSIX-like functions
[ ] PSPELL pspell support
[ ] READLINE readline support (CLI only)
[ ] RECODE recode support
[X] SESSION session support
[ ] SHMOP shmop support
[X] SIMPLEXML simplexml support
[ ] SNMP SNMP support
[ ] SOAP SOAP support
[ ] SOCKETS sockets support
[X] SQLITE sqlite support
[ ] SYBASE_CT Sybase database support
[ ] SYSVMSG System V message support
[ ] SYSVSEM System V semaphore support
[ ] SYSVSHM System V shared memory support
[ ] TIDY TIDY support
[X] TOKENIZER tokenizer support
[ ] WDDX WDDX support (implies XML)
[X] XML XML support
[X] XMLREADER XMLReader support
[ ] XMLRPC XMLRPC-EPI support
[X] XMLWRITER XMLWriter support
[ ] XSL XSL support (Implies DOM)
[ ] YAZ YAZ support (ANSI/NISO Z39.50)
[X] ZIP ZIP support
[X] ZLIB ZLIB support
重启apache安装完成。
4.6 安装openssl-0.9.7l
QUOTE:
mail# cd /usr/ports/security/openssl
mail# make install clean
4.7 安装phpMyAdmin-2.9.0.2
QUOTE:
mail# cd /usr/ports/databases/phpmyadmin
mail# make fetch
注:(在这里建议直接下载后复制安装)
QUOTE:
mail# cd /usr/ports/distfiles
mail# tar –zxvf phpMyAdmin-2.9.0.2.tar.gz
mail# mv /usr/local/www/phpMyAdmin-2.9.0.2 /usr/local/www/apache22/data/dbadmin
修改/usr/local/www/apache22/data/dbadmin/config.inc.php
QUOTE:
$cfg['PmaAbsoluteUri'] = 'http://192.168.0.2/dbadmin/';
$cfg['Servers'][$i]['auth_type'] = 'http'; // Authentication method (config, http or cookie based)?
注:指定phpmyadmin的认证方式为http方式。
在浏览器输入http://192.168. 0.2/dbadmin/,首次进行登入的用户名为root密码为空,登入后可以修改你的密码。
4.8 设置数据库
建立postfix数据库(注意:数据库名称为postfix):
QUOTE:
mail# /usr/local/bin/mysql –u root –p
mysql# CREATE DATABASE `postfix` ;
mysql# use postfix;
把下面的sql语句导入到postfix数据库中去:
QUOTE:
-- phpMyAdmin SQL Dump
-- version 2.9.0.2
-- http://www.phpmyadmin.net
--
-- 主机: localhost:3306
-- 生成日期: 2006 年 10 月 30 日 22:17
-- 服务器版本: 4.0.26
-- PHP 版本: 5.1.6
--
-- 数据库: `tmail`
--
-- --------------------------------------------------------
--
-- 表的结构 `address`
--
CREATE TABLE `address` (
`id` int(11) unsigned NOT NULL auto_increment,
`pw_id` int(5) NOT NULL default '0',
`name` varchar(64) NOT NULL default '',
`email` varchar(128) NOT NULL default '',
UNIQUE KEY `id` (`id`),
KEY `pw_id` (`pw_id`)
) TYPE=MyISAM PACK_KEYS=1 AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `address`
--
-- --------------------------------------------------------
--
-- 表的结构 `admin`
--
CREATE TABLE `admin` (
`id` int(10) unsigned NOT NULL auto_increment,
`site_id` int(10) unsigned NOT NULL default '0',
`domain` varchar(128) NOT NULL default '',
`quota` smallint(5) unsigned NOT NULL default '0',
`total` smallint(5) unsigned NOT NULL default '0',
`createtime` timestamp(14) NOT NULL,
`login` char(1) NOT NULL default '',
`cur_total` smallint(5) NOT NULL default '0',
`cur_quota` smallint(5) NOT NULL default '0',
`gid` varchar(11) NOT NULL default '',
`expiration_time` timestamp(14) NOT NULL default '00000000000000',
`flag` int(10) unsigned NOT NULL default '0',
`maxmsg` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
UNIQUE KEY `domain` (`domain`)
) TYPE=MyISAM PACK_KEYS=1 AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `admin`
--
-- --------------------------------------------------------
--
-- 表的结构 `card`
--
CREATE TABLE `card` (
`id` int(5) unsigned NOT NULL auto_increment,
`pw_id` int(5) unsigned NOT NULL default '0',
`LinkMan` varchar(64) NOT NULL default '',
`CompanyName` varchar(100) NOT NULL default '',
`Address` varchar(255) NOT NULL default '',
`Position` varchar(32) NOT NULL default '',
`PhoneNumber` varchar(16) NOT NULL default '',
`Mobile` varchar(12) NOT NULL default '',
`Email` varchar(128) NOT NULL default '',
`Partaker` varchar(32) NOT NULL default '',
`Memo` varchar(255) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=27 ;
--
-- 导出表中的数据 `card`
--
-- --------------------------------------------------------
--
-- 表的结构 `lastauth`
--
CREATE TABLE `lastauth` (
`user` char(32) NOT NULL default '',
`domain` char(64) NOT NULL default '',
`remote_ip` char(18) NOT NULL default '',
`timestamp` bigint(20) NOT NULL default '0',
PRIMARY KEY (`user`,`domain`)
) TYPE=MyISAM;
--
-- 导出表中的数据 `lastauth`
--
-- --------------------------------------------------------
--
-- 表的结构 `logs`
--
CREATE TABLE `logs` (
`pw_id` int(5) default '0',
`ip` varchar(15) NOT NULL default '',
`action` varchar(15) NOT NULL default '',
`time` datetime default NULL,
`content` varchar(64) NOT NULL default '',
`email` varchar(128) NOT NULL default ''
) TYPE=MyISAM;
--
-- 导出表中的数据 `logs`
--
-- --------------------------------------------------------
--
-- 表的结构 `message`
--
CREATE TABLE `message` (
`id` int(5) unsigned NOT NULL auto_increment,
`title` varchar(255) NOT NULL default '',
`body` text NOT NULL,
`createtime` datetime NOT NULL default '0000-00-00 00:00:00',
`updatetime` datetime NOT NULL default '0000-00-00 00:00:00',
`pw_domain` varchar(64) NOT NULL default '',
UNIQUE KEY `id` (`id`)
) TYPE=MyISAM AUTO_INCREMENT=2 ;
--
-- 导出表中的数据 `message`
--
-- --------------------------------------------------------
--
-- 表的结构 `personal`
--
CREATE TABLE `personal` (
`id` int(11) unsigned NOT NULL auto_increment,
`pw_id` int(5) NOT NULL default '0',
`truename` varchar(10) NOT NULL default '',
`fax` varchar(20) NOT NULL default '',
`telephone` varchar(15) NOT NULL default '',
`sex` int(1) NOT NULL default '0',
`year` int(4) NOT NULL default '0',
`MONTH` int(2) NOT NULL default '0',
`DAY` int(2) NOT NULL default '0',
`education` varchar(4) NOT NULL default '',
`marital` int(1) NOT NULL default '0',
`occupation` varchar(15) NOT NULL default '',
`companyname` varchar(30) NOT NULL default '',
`province` varchar(6) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM PACK_KEYS=1 AUTO_INCREMENT=12 ;
--
-- 导出表中的数据 `personal`
--
-- --------------------------------------------------------
--
-- 表的结构 `scheduler`
--
CREATE TABLE `scheduler` (
`id` int(11) unsigned NOT NULL auto_increment,
`begin_time` int(11) unsigned default NULL,
`end_time` int(11) unsigned default NULL,
`title` varchar(255) NOT NULL default '',
`body` varchar(255) NOT NULL default '',
`pw_id` int(11) unsigned NOT NULL default '0',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `scheduler`
--
-- --------------------------------------------------------
--
-- 表的结构 `stow`
--
CREATE TABLE `stow` (
`id` int(5) unsigned NOT NULL auto_increment,
`pw_id` int(5) unsigned NOT NULL default '0',
`Name` varchar(128) NOT NULL default '',
`http` varchar(255) NOT NULL default 'http://',
`memo` varchar(255) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `stow`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_access`
--
CREATE TABLE `tmail_access` (
`id` int(11) unsigned NOT NULL auto_increment,
`ctime` int(11) unsigned default NULL,
`source` varchar(128) NOT NULL default '',
`access` varchar(16) NOT NULL default '',
`type` char(1) NOT NULL default 'S',
PRIMARY KEY (`id`),
KEY `source` (`source`,`type`,`access`,`ctime`)
) TYPE=MyISAM AUTO_INCREMENT=65 ;
--
-- 导出表中的数据 `tmail_access`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_alias`
--
CREATE TABLE `tmail_alias` (
`id` int(11) unsigned NOT NULL auto_increment,
`alias` varchar(128) NOT NULL default '',
`destination` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_alias`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_autobbc`
--
CREATE TABLE `tmail_autobbc` (
`id` int(9) NOT NULL auto_increment,
`realname` varchar(128) default NULL,
`email` varchar(128) NOT NULL default '',
`autobbc` varchar(128) NOT NULL default '',
`come` int(1) default NULL,
`out` int(1) default NULL,
`domain` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_autobbc`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_company`
--
CREATE TABLE `tmail_company` (
`id` int(6) NOT NULL auto_increment,
`domain` varchar(25) NOT NULL default '',
`name` varchar(50) NOT NULL default '',
`linkman` varchar(50) NOT NULL default '',
`tel` varchar(15) NOT NULL default '',
`Address` varchar(50) NOT NULL default '',
`zip` varchar(6) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_company`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_domaininfo`
--
CREATE TABLE `tmail_domaininfo` (
`id` tinyint(11) NOT NULL auto_increment,
`domain` varchar(25) NOT NULL default '',
`transport` varchar(128) NOT NULL default '',
`alias` varchar(50) default NULL,
`passwd` varchar(35) NOT NULL default '',
`clean` text NOT NULL,
`admin` tinytext NOT NULL,
`yesno` tinytext NOT NULL,
`usernum` text NOT NULL,
`quota` text NOT NULL,
`filequota` text NOT NULL,
`expire` date NOT NULL default '0000-00-00',
`create_time` datetime default NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `domain` (`domain`),
KEY `domain_id` (`id`)
) TYPE=MyISAM PACK_KEYS=0 COMMENT='domain information' AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_domaininfo`
--
INSERT INTO `tmail_domaininfo` (`id`, `domain`, `transport`, `alias`, `passwd`, `clean`, `admin`, `yesno`, `usernum`, `quota`, `filequota`, `expire`, `create_time`) VALUES
(1, 'admin', 'maildrop', NULL, '$1$t5h6XOby$UP3HJpdrozxEsRlcLF6tI0', 'hing4585', '0', '1', '0', '0', '0', '0000-00-00', '2006-10-06 16:44:13');
-- --------------------------------------------------------
--
-- 表的结构 `tmail_group_name`
--
CREATE TABLE `tmail_group_name` (
`id` int(9) NOT NULL auto_increment,
`group_name` varchar(128) NOT NULL default '',
`domain` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_group_name`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_monitor`
--
CREATE TABLE `tmail_monitor` (
`id` int(11) unsigned NOT NULL auto_increment,
`email` varchar(128) NOT NULL default '',
`cc` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`),
KEY `email` (`email`,`cc`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_monitor`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_pop`
--
CREATE TABLE `tmail_pop` (
`id` int(11) unsigned NOT NULL auto_increment,
`email` varchar(128) NOT NULL default '',
`pop` varchar(128) NOT NULL default '',
`name` varchar(128) NOT NULL default '',
`pass` varchar(32) NOT NULL default '32',
`timeout` int(10) unsigned NOT NULL default '60',
`port` tinyint(3) unsigned NOT NULL default '110',
`keep` char(1) NOT NULL default 'Y',
PRIMARY KEY (`id`),
KEY `email` (`email`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_pop`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_relay_domains`
--
CREATE TABLE `tmail_relay_domains` (
`id` int(11) unsigned NOT NULL auto_increment,
`domain` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`),
UNIQUE KEY `domain` (`domain`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_relay_domains`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_relocated`
--
CREATE TABLE `tmail_relocated` (
`id` int(11) unsigned NOT NULL auto_increment,
`email` varchar(128) NOT NULL default '',
`destination` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_relocated`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_transport`
--
CREATE TABLE `tmail_transport` (
`id` int(11) unsigned NOT NULL auto_increment,
`domain` varchar(128) NOT NULL default '',
`destination` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`),
UNIQUE KEY `domain` (`domain`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_transport`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_userpersonal`
--
CREATE TABLE `tmail_userpersonal` (
`id` int(11) NOT NULL auto_increment,
`address` varchar(50) default NULL,
`fax` varchar(20) NOT NULL default '',
`telephone` varchar(15) NOT NULL default '',
`sex` int(1) NOT NULL default '0',
`year` int(4) NOT NULL default '0',
`month` int(2) NOT NULL default '0',
`day` int(2) NOT NULL default '0',
`education` varchar(4) NOT NULL default '',
`marital` int(1) NOT NULL default '0',
`occupation` varchar(15) NOT NULL default '',
`companyname` varchar(30) NOT NULL default '',
`province` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) TYPE=InnoDB AUTO_INCREMENT=43 ;
--
-- 导出表中的数据 `tmail_userpersonal`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_users`
--
CREATE TABLE `tmail_users` (
`id` int(11) unsigned NOT NULL auto_increment,
`userid` tinytext NOT NULL,
`domain` varchar(128) NOT NULL default '',
`email` varchar(128) NOT NULL default '',
`clear` varchar(128) NOT NULL default '',
`crypt` varchar(128) NOT NULL default '',
`realname` tinytext NOT NULL,
`uid` int(11) unsigned NOT NULL default '1003',
`gid` int(11) unsigned NOT NULL default '1003',
`homedir` tinytext NOT NULL,
`maildir` tinytext NOT NULL,
`quota` tinytext NOT NULL,
`file_quota` tinytext NOT NULL,
`access` enum('Y','N') NOT NULL default 'Y',
`postfix` enum('Y','N') NOT NULL default 'Y',
`disablepop3` char(1) NOT NULL default '0',
`disableimap` char(1) NOT NULL default '0',
`disablewebmail` char(1) NOT NULL default '0',
`sharedgroup` varchar(128) NOT NULL default '0',
`smtpaccess` enum('Y','N') NOT NULL default 'Y',
`expire` date NOT NULL default '0000-00-00',
`create_time` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_users`
--
-- --------------------------------------------------------
--
-- 表的结构 `tmail_virtual`
--
CREATE TABLE `tmail_virtual` (
`id` int(11) unsigned NOT NULL auto_increment,
`email` varchar(128) NOT NULL default '',
`destination` varchar(128) NOT NULL default '',
`group_name` varchar(128) NOT NULL default '',
`domain` varchar(128) NOT NULL default '',
`group` int(1) default NULL,
`alias` int(1) default NULL,
`forward` int(1) default NULL,
`forward_bak` int(1) default NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `tmail_virtual`
--
-- --------------------------------------------------------
--
-- 表的结构 `vpopmail`
--
CREATE TABLE `vpopmail` (
`pw_id` int(5) unsigned NOT NULL auto_increment,
`pw_name` varchar(32) NOT NULL default '',
`pw_domain` varchar(64) NOT NULL default '',
`pw_passwd` varchar(40) NOT NULL default '',
`pw_uid` int(11) default NULL,
`pw_gid` int(11) default NULL,
`pw_gecos` varchar(48) default NULL,
`pw_dir` varchar(255) default NULL,
`pw_shell` varchar(20) default NULL,
`createtime` timestamp(14) NOT NULL,
PRIMARY KEY (`pw_id`),
KEY `pw_name` (`pw_name`,`pw_domain`)
) TYPE=MyISAM PACK_KEYS=1 AUTO_INCREMENT=1 ;
--
-- 导出表中的数据 `vpopmail`
注:对于初学者,建议以上操作都在phpmyadmin中操作更加的简便.
建立数据库用户并授以相应的权限
QUOTE:
mail# /usr/local/bin/mysql –u root –p
mysql# use mysql;
mysql# INSERT INTO user (host,user,password) VALUES('localhost','postfix','');
mysql# update user set password=password('postfix') where User='postfix';
重新启动mysql
mail# /usr/local/bin/mysql –u root –p
mysql# use mysql;
mysql# GRANT ALL ON postfix.* TO postfix@localhost IDENTIFIED BY "postfix";
注:这里加用户名和密码都为:postfix。并授权对postfix数据库进行操作
4.9 安装cyrus-sasl-2.1.22
QUOTE:
mail# cd /usr/ports/security/cyrus-sasl2
mail# make WITH_MYSQL=yes WITH_AUTHDAEMON=yes install clean
WITH_AUTHDAEMON=yes参数是为了让sasl支持authdamond的认证方式
WITH_MYSQL=yes参数是为了让sasl支持mysql认证方式
4.10 安装postfix-2.3.3
QUOTE:
mail# cd /usr/ports/mail/postfix
mail# make install clean
以下是我的选择
QUOTE:
Postfix configuration options
Please select desired options:
[ ] NOPCRE DISABLE Perl Compatible Regular Expressions
[ ] SASL Cyrus SASLv1 (Simple Authentication and Security Layer)
[X] SASL2 Cyrus SASLv2 (Simple Authentication and Security Layer)
[ ] SASLKRB If your SASL requires Kerberos select this option
[ ] SASLKRB5 If your SASL requires Kerberos5 select this option
[ ] SASLKRB5MIT If your SASL requires MIT Kerberos5 select this option
[ ] SPF SPF support
[ ] TLS SSL and TLS
[ ] BDB Berkeley DB (select version using WITH_BDB_VER variable)
[X] MySQL MySQL map lookups (choose version with WITH_MYSQL_VER)
[ ] PgSQL PostgreSQL map lookups (choose with DEFAULT_PGSQL_VER)
[ ] OpenLDAP OpenLDAP map lookups (choose ver. with WITH_OPENLDAP_VER)
[ ] CDB CDB map lookups
[ ] NIS NIS map lookups
[X] VDA VDA (Virtual Delivery Agent)
[ ] Test SMTP/LMTP test server and generator
安装过程中会让你回答两个问题,
QUOTE:
===> Installing for postfix-2.2.10_1,1
===> postfix-2.2.10_1,1 depends on shared library: sasl2.2 - found
===> postfix-2.2.10_1,1 depends on shared library: pcre.0 - found
===> postfix-2.2.10_1,1 depends on shared library: mysqlclient.14 - found
Added group "postfix".
Added group "maildrop".
Added user "postfix".
You need user "postfix" added to group "mail".
Would you like me to add it [y]? y
是否把postfix用户加入到mail用户组。回答Y
Installed HTML documentation in /usr/local/share/doc/postfix
Would you like to activate Postfix in /etc/mail/mailer.conf [n]? n
在rc.conf中加入postfix的启动选项
QUOTE:
mail# echo 'postfix_enable="YES"' >> /etc/rc.conf
mail# echo 'sendmail_enable="NO"' >> /etc/rc.conf
mail# echo 'sendmail_submit_enable="NO"' >> /etc/rc.conf
mail# echo 'sendmail_outbound_enable="NO"' >> /etc/rc.conf
mail# echo 'sendmail_msp_queue_enable="NO"' >> /etc/rc.conf
4.11 安装courier-imap-4.1.1
QUOTE:
mail# cd /usr/ports/mail/courier-imap
mail# make install clean
以下是我的选择
QUOTE:
Options for courier-imap 4.1.1,1
[ ] OPENSSL Build with OpenSSL support
[ ] FAM Build in fam support for IDLE command
[ ] TRASHQUOTA Include deleted mails in the quota
[ ] GDBM Use gdbm db instead of system bdb
[ ] IPV6 Build with IPv6 support
[ ] AUTH_LDAP LDAP support
[X] AUTH_MYSQL MySQL support
[ ] AUTH_PGSQL PostgreSQL support
[ ] AUTH_USERDB Userdb support
[ ] AUTH_VCHKPW Vpopmail/vchkpw support
在rc.conf中加入courier的启动选项
QUOTE:
mail# echo 'courier_authdaemond_enable="YES"' >> /etc/rc.conf
mail# echo 'courier_imap_imapd_enable="YES"' >> /etc/rc.conf
mail# echo 'courier_imap_pop3d_enable="YES"' >> /etc/rc.conf
QUOTE:
mail# /usr/local/etc/rc.d/courier-authdaemond start
Starting courier_authdaemond.
注:此时会在/var/run/authdaemond/下产生socket,如果没有下面这一步下面的认证无法通过。
对/var/run/authdaemond目录加执行权限
QUOTE:
mail# chmod +x /var/run/authdaemond
4.12 安装MailScanner-install-4.55.10-3
QUOTE:
mail# cd /usr/ports/mail/mailscanner
mail# make install clean
在安装MailScanner的过程中会出现以下几个选项。以下是我的选择
QUOTE:
Options for MailScanner 4.55.10
[X] SPAMASSASSIN Install SpamAssassin
[X] CLAMAV Install ClamAV
[X] CLAMAVMODULE Install ClamAV Module
[ ] BDC Install BitDefender
Options for sqlite 3.3.6
[ ] TCLWRAPPER TCL wrapper for SQLITE
[ ] THREADS Enable threads support
[ ] DEBUG Enable debugging & verbose explain
[ ] DOCS Building docs (depends on TCL)
Options for Mail-SpamAssassin-3.1.6
[X] AS_ROOT Run spamd as root (recommended)
[X] DOMAINKEYS DomainKeys support
[X] SSL Build with SSL support for spamd/spamc
[ ] MYSQL Add MySQL support
[ ] PGSQL Add PostreSQL support
[X] RAZOR Add Vipul's Razor support
[X] SPF_QUERY Add SPF query support
[X] RELAY_COUNTRY Relay country support
[X] TOOLS Install SpamAssassin tools
Options for clamav 0.88.5
[X] MILTER Compile the milter interface
[X] CURL Support URL downloading
[X] LIBUNRAR Support for external Unrar library
[X] STDERR Print logs to stderr instead of stdout
生成初始配置文件
QUOTE:
mail# make initial-config
根据提示创建MailScanner运行所需的目录
mail# mkdir -p /var/spool/MailScanner/incoming
mail# mkdir /var/spool/MailScanner/quarantine
mail# chown -R postfix:postfix /var/spool/MailScanner
分别在rc.conf中加入clamav、spamassassin和MailScanner的启动参数
QUOTE:
mail# echo 'clamav_clamd_enable="YES"' >> /etc/rc.conf
mail# echo 'clamav_freshclam_enable="YES"' >> /etc/rc.conf
mail# echo 'clamav_milter_enable="YES"' >> /etc/rc.conf
mail# echo 'mailscanner_enable="YES"' >> /etc/rc.conf
mail# echo 'spamd_enable="YES"' >> /etc/rc.conf
4.13 安装maildrop-2.0.2
QUOTE:
mail# cd /usr/ports/mail/maildrop
mail# make install WITH_AUTHLIB=yes
以下是我的选择
QUOTE:
Options for maildrop 2.0.2
[ ] AUTH_LDAP LDAP support
[X] AUTH_MYSQL MySQL support
[ ] AUTH_PGSQL PostgreSQL support
[ ] AUTH_USERDB Userdb support
[ ] AUTH_VCHKPW Vpopmail/vchkpw support
QUOTE:
mail# /usr/local/bin/maildrop -v
maildrop 2.0.2 Copyright 1998-2005 Double Precision, Inc.
Courier Authentication Library extension enabled.
Maildir quota extension enabled.
This program is distributed under the terms of the GNU General Public
License. See COPYING for additional information.
出现上面红色的部分表示maildrop支持Courier。
第五章 软件系统配置
为了支持maildrop,先加入vmail用户
QUOTE:
mail# pw adduser vmail -u 1003 -s /sbin/nologin -d /dev/null
5.1 配置cyrus-sasl
创建/usr/local/lib/sasl2/smtpd.conf
QUOTE:
mail# ee /usr/local/lib/sasl2/smtpd.conf
pwcheck_method: auxprop
auxprop_plugin: sql
mech_list: plain login
sql_engine: mysql
sql_hostnames: localhost
sql_user: tmail
sql_passwd: tmail
sql_database: tmail
sql_select: select clear from tmail_users where email='%u@%r' and smtpaccess='Y'
5.2 配置postfix
QUOTE:
mail# ln -s /usr/local/sbin/sendmail /usr/sbin/sendmail
注:如果/usr/sbin/sendmail存在就删了再做上链接,如果升级内核和升级系统后要重新做这一步。
mail# echo ‘postfix: root’ >> /etc/aliases
mail# /usr/local/bin/newaliases
mail# chown postfix:postfix /etc/opiekeys
mail# ee /usr/local/etc/postfix/main.cf
修改/usr/local/etc/postfix/main.cf,在文件最后加入以下内容
QUOTE:
# new add
myhostname = mail.test.com
smtp_helo_name = $myhostname
local_transport = maildrop
mailbox_transport = maildrop
smtpd_error_sleep_time = 0
smtpd_soft_error_limit = 10
smtpd_hard_error_limit = 20
default_process_limit = 100
#alias_maps = hash:/usr/local/etc/postfix/aliases
#Alias_database = hash:/usr/local/etc/postfix/aliases
mydestination = mysql:/usr/local/etc/postfix/mysql-mydest.cf
virtual_transport_maps = mysql:/usr/local/etc/postfix/mysql-transport.cf
virtual_alias_maps = mysql:/usr/local/etc/postfix/mysql-virtual.cf
#virtual_alias_maps = mysql:/usr/local/etc/postfix/mysql-alias.cf
recipient_bcc_maps = mysql:/usr/local/etc/postfix/mysql-autobbc-in.cf
sender_bcc_maps = mysql:/usr/local/etc/postfix/mysql-autobbc-out.cf
local_recipient_maps = $alias_maps $virtual_mailbox_maps $virtual_maps
virtual_mailbox_base = /mail
virtual_mailbox_maps = mysql:/usr/local/etc/postfix/mysql-virtual-maps.cf
virtual_create_maildirsize = yes
virtual_mailbox_extended = yes
virtual_mailbox_limit_maps = mysql:/usr/local/etc/postfix/mysql-virtual-quota.cf
virtual_mailbox_limit_override = yes
virtual_maildir_limit_message = Sorry, the user's maildir has overdrawn his diskspace quota, please try again later.
virtual_overquota_bounce = yes
virtual_uid_maps = mysql:/usr/local/etc/postfix/mysql-virtual-uid.cf
virtual_gid_maps = mysql:/usr/local/etc/postfix/mysql-virtual-gid.cf
broken_sasl_auth_clients = yes
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_client_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
check_client_access mysql:/usr/local/etc/postfix/mysql-access.cf,
permit_auth_destination,
reject
smtpd_sender_restrictions =
reject_unknown_sender_domain,
reject_non_fqdn_sender,
check_sender_access mysql:/usr/local/etc/postfix/mysql-access.cf,
permit
smtpd_recipient_restrictions =
permit_mynetworks,
check_client_access mysql:/usr/local/etc/postfix/mysql-access.cf,
permit_sasl_authenticated,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
reject_unknown_recipient_domain,
reject_unauth_pipelining,
reject_unauth_destination,
reject_rbl_client cblless.anti-spam.org.cn,
permit
default_destination_recipient_limit = 1
local_destination_concurrency_limit = 1
maildrop_destination_recipient_limit = 1
message_size_limit = 41943040
smtpd_recipient_limit = 10
bounce_queue_lifetime = 12h
maximal_queue_lifetime = 24h
smtpd_delay_reject = yes
smtpd_helo_required = yes
strict_rfc821_envelopes = yes
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-access.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
query = select access from tmail_access where source = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-alias.cf
host = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_virtual
query = select alias from tmail_users where email = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-autobbc-in.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
query = select autobbc from tmail_autobbc where email = '%s' AND come='1'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-autobbc-out.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
query = select autobbc from tmail_autobbc where email = '%s' AND 'out'='1'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-mydest.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_domaininfo
query = select domain from tmail_domaininfo where domain = '%s' AND yesno = '1'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-transport.cf
host = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_domaininfo
query = select transport from tmail_domaininfo where domain = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-virtual-gid.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_users
query = select gid from tmail_users where email = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-virtual-uid.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_users
query = select uid from tmail_users where email = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-virtual-maps.cf
host = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_users
query = select maildir from tmail_users where email = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-virtual-quota.cf
hosts = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_users
query = select quota from tmail_users where email = '%s'
QUOTE:
mail# ee /usr/local/etc/postfix/mysql-virtual.cf
host = localhost
user = tmail
password = tmail
dbname = tmail
table = tmail_virtual
query = select destination from tmail_virtual where email = '%s'
5.3 配置Courier-imap
修改Courier相关设置,/usr/local/etc/courier-imap/imapd:
QUOTE:
IMAP_CAPABILITY="IMAP4rev1 CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA"
修改/usr/local/etc/courier-imap/pop3d
QUOTE:
POP3AUTH="LOGIN CRAM-MD5 CRAM-SHA1"
编辑修改/usr/local/etc/authlib/authmysqlrc
QUOTE:
MYSQL_SERVER localhost
MYSQL_USERNAME tmail
MYSQL_PASSWORD tmail
MYSQL_PORT 0
MYSQL_OPT 0
MYSQL_DATABASE tmail
MYSQL_USER_TABLE tmail_users
MYSQL_CRYPT_PWFIELD crypt
#MYSQL_CLEAR_PWFIELD clear
MYSQL_UID_FIELD uid
MYSQL_GID_FIELD gid
MYSQL_LOGIN_FIELD email
MYSQL_HOME_FIELD homedir
MYSQL_NAME_FIELD realname
MYSQL_MAILDIR_FIELD maildir
MYSQL_QUOTA_FIELD quota
MYSQL_AUXOPTIONS_FIELD CONCAT("disableimap=",disableimap,",disablepop3=",disablepop3,",disablewebmail=",disablewebmail,",sharedgroup=",sharedgroup)
MYSQL_WHERE_CLAUSE access='y'
注:红色部分为一行。这里得用tab键来跳格
编辑/usr/local/etc/authlib/authdaemonrc
QUOTE:
authmodulelist="authmysql"
authmodulelistorig="authmysql"
version="authdaemond.mysql"
daemons=5
authdaemonvar=/var/run/authdaemond
subsystem=mail
DEBUG_LOGIN=0
DEFAULTOPTIONS="wbnodsn=1"
5.4 安装Tmail管理工具
本节主要介绍如何安装Tmail管理工具。
更改httpd.conf中的Group www、User www修改为: Group vmail、User vmail
安装Tmail管理工具来设置第一个域名和用户
上传管理工具到网站目录
后修改config/config.inc.php中的
QUOTE:
define(MAILDIR,"/mail"); //邮件存放目录
define(MISC, ".misc");
define(MODE,0700);
$PageSize = 15; //用户列表和域名列表每一页显示多少行
$host = "localhost"; //数据库主机名
$user = "tmail"; //数据库用户名
$dbname = "tmail"; //数据库名称
$password = "tmail"; //数据库密码
设置好后,加一个域名为test.com,加一个邮件用户为: test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
5.5 登录测试
重新启动相关服务
QUOTE:
mail# /usr/local/etc/rc.d/apache22 restart
mail# /usr/local/etc/rc.d/mysql-server restart
mail# /usr/local/etc/rc.d/courier-authdaemond stop
mail# /usr/local/etc/rc.d/courier-authdaemond start
mail# /usr/local/etc/rc.d/courier-imap-imapd.sh restart
mail# /usr/local/etc/rc.d/courier-imap-pop3d.sh restart
mail# /usr/local/etc/rc.d/postfix reload
生成用户名的base64编码
QUOTE:
mail# perl -MMIME::Base64 -e 'print encode_base64(" test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 ");'
dGVzdEB0ZXN0LmNvbQ==
mail# perl -MMIME::Base64 -e 'print encode_base64("000000");'
MDAwMDAw
测试25发送
QUOTE:
mail# telnet localhost 25
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.test.com.
Escape character is '^]'.
220 mail.test.com ESMTP Postfix
ehlo mail
250-mail.test.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH LOGIN PLAIN
250-AUTH=LOGIN PLAIN
250 8BITMIME
auth login
334 VXNlcm5hbWU6
dGVzdEB0ZXN0LmNvbQ==
334 UGFzc3dvcmQ6
MDAwMDAw
235 Authentication successful
MAIL FROM:< test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 >
250 Ok
RCPT TO:< test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 >
250 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
SUBJECT:test
test
.
250 Ok: queued as 47C6CB83E
quit
221 Bye
Connection closed by foreign host.
测试110收邮件
QUOTE:
mail# telnet localhost 110
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.test.com.
Escape character is '^]'.
+OK Hello there.
user test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
+OK Password required.
pass 000000
+OK logged in.
list
+OK POP3 clients that break here, they violate STD53.
1 1563
2 401
.
retr 2
+OK 401 octets follow.
Return-Path: < test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 >
Delivered-To: test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
Received: from mail (localhost.test.com [127.0.0.1])
by mail.test.com (Postfix) with ESMTP id 47C6CB83E
for < test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 >; Tue, 11 Jul 2006 13:47:28 +0800 (CST)
SUBJECT:test
Message-Id: < 20060711054736.47C6CB83E@mail.test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 >
Date: Tue, 11 Jul 2006 13:47:28 +0800 (CST)
From: test@test.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
To: undisclosed-recipients:;
test
.
dele 2
+OK Deleted.
quit
+OK Bye-bye.
Connection closed by foreign host.
5.6 垃圾邮件和病毒邮件过滤设置
设置MailScanner
编辑/usr/local/etc/postfix/main.cf
QUOTE:
mail# ee /usr/local/etc/postfix/main.cf
去掉513行的注释
QUOTE:
header_checks = regexp:/usr/local/etc/postfix/header_checks
编辑/usr/local/etc/postfix/header_checks
QUOTE:
mail# ee /usr/local/etc/postfix/header_checks
新加入
/^Received:/ HOLD
重新加载postfix配置文件
mail# postfix reload
编辑/usr/local/etc/MailScanner/MailScanner.conf,以下是我的配置文件,更改相应的参数为下面的格式。
QUOTE:
%org-name% = thismail.org
%org-long-name% = LCSoft
%web-site% = www.thismail.org
%etc-dir% = /usr/local/etc/MailScanner
%report-dir% = /usr/local/share/MailScanner/reports/en
%rules-dir% = /usr/local/etc/MailScanner/rules
%mcp-dir% = /usr/local/etc/MailScanner/mcp
Run As User = postfix
Run As Group = postfix
Incoming Queue Dir = /var/spool/postfix/hold
Outgoing Queue Dir = /var/spool/postfix/incoming
MTA = postfix
Sendmail = /usr/sbin/sendmail
Monitors for ClamAV Updates = /var/db/clamav/*.cvd
SpamAssassin User State Dir = /var/spool/MailScanner/spamassassin
Custom Functions Dir = /usr/local/lib/MailScanner/MailScanner/CustomFunctions
SpamAssassin Install Prefix = /usr/local/bin
SpamAssassin Site Rules Dir = /usr/local/etc/mail/spamassassin
SpamAssassin Local Rules Dir = /usr/local/share/spamassassin
编辑配置文件/usr/local/etc/MailScanner/mcp/mcp.spam.assassin.prefs.conf
QUOTE:
mail# ee /usr/local/etc/MailScanner/mcp/mcp.spam.assassin.prefs.conf
# MailScanner
# MailScanner users, please see the comments at the bottom of this file.
# MailScanner
#
# SpamAssassin user preferences file.
#
# Format:
#
# required_hits n
# (how many hits are required to tag a mail as spam.)
#
# score SYMBOLIC_TEST_NAME n
# (if this is omitted, 1 is used as a default score.
# Set the score to 0 to ignore the test.)
#
# # starts a comment, whitespace is not significant.
#
###########################################################################
# Whitelist and blacklist addresses are *not* patterns; they're just normal
# strings. one exception is that "*@isp.com" is allowed. They should be in
# lower-case. You can either add multiple addrs on one line,
# whitespace-separated, or you can use multiple lines.
#
# Monty Solomon: he posts from an ISP that has often been the source of spam
# (no fault of his own ;), and sometimes uses Bcc: when mailing.
#
#whitelist_from monty@roscom.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
# Add your blacklist entries in the same format...
#
# blacklist_from friend@public.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
# Mail using languages used in these country codes will not be marked
# as being possibly spam in a foreign language.
#
#ok_locales en
skip_rbl_checks 1
use_bayes 0
use_dcc 0
use_pyzor 0
use_razor1 0
use_razor2 0
decode_attachments 1
编辑配置文件/usr/local/etc/MailScanner/rules/max.message.size.rules
QUOTE:
mail# ee /usr/local/etc/MailScanner/rules/max.message.size.rules
# This is an example ruleset to show how rules can have resulting values
# other than yes and no. This ruleset demonstrates having a numerical result.
# The From: and To: rules show how simple domains can be used to select
# different values for the result of the ruleset.
# Note that the fields of each rule line can be separated by any whitespace,
# any combination of tabs and spaces.
#
# The 2 lines involving domain3.com show that for email to user@domain3.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。
# has a limit of 5Mbytes per message, while email to any other user
# @domain3.com has a limit of 500Kbytes per message.
#
To: *@domain1.com 10M
To: *@domain2.com 20M
From: user@domain3.com电子邮件地址已被防垃圾邮件功能所隐藏, 您需要把Javascript功能打开才能看到。 5M
From: *@domain3.com 500K
#
# The following line specifies the default result used when none of the
# other rules match. In this example,
# Maximum Message Size = 0
# means that there is no limit to the size of the message.
#
FromOrTo: default 0
编辑配置文件/usr/local/etc/MailScanner/rules/bounce.rules
QUOTE:
mail# ee /usr/local/etc/MailScanner/rules/bounce.rules
# You can use this ruleset to enable the "bounce" Spam Action.
# You must *only* enable this for mail from sites with which you have
# agreed to bounce possible spam. Use it on low-scoring spam only (<10)
# and only to your regular customers for use in the rare case that a
# message is mis-tagged as spam when it shouldn't have been.
# Beware that many sites will automatically delete the bounce messages
# created by using this option unless you have agreed this with them in
# advance.
# This next line gives an example of how you might enable this option for
# a frequent customer of yours.
#From: yourcustomer.com yes
# Under no circumstances should this be changed to "yes".
FromOrTo: default no
重新启动mailscanner.
QUOTE:
mail# /usr/local/etc/rc.d/mailscanner restart
加入中文垃圾邮件规则
QUOTE:
mail# cd /usr/local/share/spamassassin
mail# fetch http://www.ccert.edu.cn/spam/sa/Chinese_rules.cf
注意:由于加入了中文垃圾邮件规则,请把垃圾邮件的评分的分数设置为高一些。
我的设置为8,最高分设置为10,这样可以降低中文邮件的错误识别率。用户可以根据自己的需要来改变这两个分值。
QUOTE:
Required SpamAssassin Score = 8
High SpamAssassin Score = 10
为了避免邮件扫描后主题变成乱码,建议改变以下设置为no
QUOTE:
Virus Modify Subject = no
Filename Modify Subject = no
Content Modify Subject = no
Disarmed Modify Subject = no
Spam Modify Subject = yes
High Scoring Spam Modify Subject = no
扫描垃圾邮件所执行的运作
QUOTE:
Spam Actions = deliver
High Scoring Spam Actions = delete
相关运作参数说明:
QUOTE:
"deliver" -- 邮件正常的转送至原来的收信人。
"delete" -- 删除邮件。
"store" -- 将邮件存放至隔离区。
"bounce" -- 将邮件退给寄信人。
"forward" -- 提供一个 forward 的邮件位址给系统,系统会自动转寄一份。
"striphtml" -- 将内嵌 HTML 的邮件转成 Text,你必需要加入 "deliver",系统才会帮你寄邮件。
允许html邮件通行
QUOTE:
Allow IFrame Tags = yes
Allow Form Tags = yes
Allow Script Tags = yes
Allow WebBugs = disarm
Allow Object Codebase Tags = yes
Convert Dangerous HTML To Text = no
Convert HTML To Text = no
允许本机发送出去的邮件不被MailScanner视为垃圾邮件
QUOTE:
mail# ee /usr/local/etc/MailScanner/rules/spam.whitelist.rules
From: 127.0.0.1 yes
以上规则的说明:以上垃圾邮件的规则是加入了中文垃圾邮件主题和内容的评分规则,所以我设置了一个最低分为8分,最高分为10分的规则,8-10分间的邮件会被打上[spamd]的标识,高于10分的邮件会被自动的删除掉,以上规则允许html邮件通过。
第六章 安装webmail
到开邮件邮件技术论坛
http://www.thismail.org/bbs/thread.php?fid=17
取得最新的webmail
下载后传到服务器上的web目录,后配置php的全局变量为打开
QUOTE:
register_globals = On
并编辑webmail/config/config_inc.php中如下参数
QUOTE:
$CFG_BASEPATH = "/tmp/tmail/temp"; //临时目录,如果不存在,修改完配置文件后再手动创建,并附于相关的权限
// Mysql
define(MYSQL_HOST, 'localhost'); //数据库主机名
define(MYSQL_USER, 'postfix'); //数据库用户名
define(MYSQL_PASS, 'postfix'); //数据库密码
define(MYSQL_DATA, 'postfix'); //数据库名称
$CFG_NETDISK_PATH = "/mail/netdisk"; //文件管理(网络磁盘所在的系统路径)如果不存在,修改完配置文件后再手动创建,并附于相关的权限
$CFG_NETDISK_DEFAULT_QUOTA = 10; //文件管理(网络磁盘)默认大小为10MB,用户可根据自己的需要改变大小
。
QUOTE:
mail# mkdir -p /tmp/tmail/temp
mail# chown -R postfix:postfix /tmp/tmail
mail# mkdir -p /mail/netdisk
mail# chown -R postfix:postfix /mail/netdisk
为了webmail能配合maildrop做中文邮件的过滤。请写/usr/sbin/maildecode文件
QUOTE:
mail# ee /usr/sbin/maildecode
#!/usr/bin/perl
# Convert Base64 Or Quoted-printable TO Text
my $a = $ARGV[0] || '';
#Maybe arg is include Subject
if ($a=~/^Subject/) {
$a = $ARGV[1] || '';
};
if ($a=~/=?[w-]+?B?(.*)?=$/) {
use MIME::Base64;
$a = decode_base64($1);
};
if ($a=~/=?[w-]+?Q?(.*)?=$/) {
use MIME::QuotedPrint;
$a = decode_qp($1);
};
#open(OUTFILE, ">/tmp/list.log");
#print OUTFILE $a;
#close(OUTFILE);
print $a;
exit(0);
并改变相关的权限
QUOTE:
mail# chmod 755 /usr/sbin/maildecode
mail# chown -R vmail:vmail /usr/sbin/maildecode
安装成功后的webmail
附一、常见问题
正在收集中……
附二、MailScanner.conf中文参数说明
http://www.thismail.org/bbs/htm_data/7/0509/533.html
更深入一点理解 switch 语句 及 c/c++ 对 const 的处理
谢煜波
http://blog.csdn.net/xiaohan13916830/archive/2004/08/17/76724.aspx
前段时间在论坛上看见台湾李维在<>一书中对windows编程模式中,消息处理部分有如下的一些分析:
他说,在消息处理循环中,一般的形式是这样的
MSG msg ;
switch( msg ){
case WM_XXXXXXX :
....
case WM_XXXXXXX :
....
case WM_XXXXXXX :
....
} ;
李维说,这种模式是很低效的,因应经过汇编后,这种C代码会产生如下的汇编代码
cmp .... .....
jnz .... .....
cmp .... .....
jnz .... .....
cmp .... .....
jnz .... .....
如果你的 case 足够多,比如,你有一万条消息需要处理,而不幸的是你把一条最常用的消息
放在了最后一位,那么当这条消息要得到处理,会首先经过一万次的cmp与jnz, 李维认为,这
是非常非常低效的,实在是低效的忍无可忍,无需再忍~~:P
在起初,我也是这样认为的,但近来的阅读及实验却发现,这种看法非常片面,今天就来谈谈这个问题( 所有实验在 linux 平台下完成 )
首先看一到用 c 编写的程序
/* -------------------- filename : ta.c --------------- */
int switch_test_first( int x )
{
int res ;
switch( x ){
case 100 :
res = 1 ;
break ;
case 102 :
res = 2 ;
break ;
case 103 :
res = 3 ;
break ;
}
return res ;
}
然后,我们用 gcc 将它编译成汇编文件( 使用 -S 开关 )
gcc -S ta.c
将得到如下的汇编文件( ta.s )
.file "ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
.file "ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
movl %eax, -8(%ebp)
cmpl $102, -8(%ebp) // 1
je .L4 // 2
cmpl $102, -8(%ebp) // 3
jg .L8 // 4
cmpl $100, -8(%ebp) // 5
je .L3 // 6
jmp .L2 // 7
.L8:
cmpl $103, -8(%ebp)
je .L5
jmp .L2
.L3:
movl $1, -4(%ebp)
jmp .L2
.L4:
movl $2, -4(%ebp)
jmp .L2
.L5:
movl $3, -4(%ebp)
.L2:
movl -4(%ebp), %eax
leave
ret
.Lfe1:
.size switch_test_first,.Lfe1-switch_test_first
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
注意看文件中 // 1 ~ // 7 的部份,从这个部份,我们可以看出,gcc确实是把一些case语句转成了李维所说的那种方式进行处理,我们看见了代码中存在有众多的 cmpl 与 jmp 语句
这就相当于你使用if..else..一样,但是否总是这样呢?
我们下面改动一下 ta.c 这个文件,在里面再多加一些 case 语句
/* -------------- filename : new_ta.c ------------------- */
int switch_test_first( int x )
{
int res ;
switch( x ){
case 100 :
res = 1 ;
break ;
case 102 :
res = 2 ;
break ;
case 103 :
res = 3 ;
break ;
case 104 :
res = 4 ;
break ;
case 105 :
res = 5 ;
break ;
case 106 :
res = 6 ;
break ;
}
return res ;
}
这个 new_ta.c 与原来的 ta.c 在结构上完全相同,唯一不同的就是 case 语句的数量变多了,下面我们来编译一下这个文件
gcc -S new_ta.c
下面是我们产生的更新的汇编文件
.file "new_ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
subl $100, %eax
movl %eax, -8(%ebp)
cmpl $6, -8(%ebp)
ja .L2
movl -8(%ebp), %edx
movl .L9(,%edx,4), %eax
jmp *%eax
.section .rodata
.align 4
.align 4
.L9: // A
.long .L3
.long .L2
.long .L4
.long .L5
.long .L6
.long .L7
.long .L8
.text
.L3: // 1
movl $1, -4(%ebp)
jmp .L2
.L4: // 2
movl $2, -4(%ebp)
jmp .L2
.L5: // 3
movl $3, -4(%ebp)
jmp .L2 // 4
.L6:
movl $4, -4(%ebp)
jmp .L2 // 5
.L7:
movl $5, -4(%ebp) // 6
jmp .L2
.L8: // 7
movl $6, -4(%ebp)
.L2:
movl -4(%ebp), %eax
leave
ret
.Lfe1:
.size switch_test_first,.Lfe1-switch_test_first
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
仔细比较一下这个最新的 new_ta.s 与前面的 ta.s,精华全在里面了!
首先 new_ta.s 比前面的 ta.s 多了一个 .L9 部分,而且它的 // 1 ~ // 7 中没有了前面
ta.s 文件中所存在的众多的 cmpl 与 jmp 语句,那么,现在这样的代码又是怎么实现
switch 语句中的跳转的呢?我们来仔细分析一下它新多出来的 .L9 部份。
.section .rodata
.align 4
.align 4
.L9:
.long .L3
.long .L2
.long .L4
.long .L5
.long .L6
.long .L7
.long .L8
.text
显而易见,.L9 部份是一个我们最常见的数据结构——表,它的每一项都是一个标号,而这个标号,恰恰是每个 case 语句的入口标号!
这很容易让我们想到,它很可能是用了一张表来存放所有的 case 语句的入口,然后,在
执行 switch 语句的时候就从这个表中直接检出相应的 case 语句的入口地址,然后跳转
到相应的 case 语句去执行,就像hash_table似的。具体是不是这样呢?我们看看进入
switch 部份的代码:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
subl $100, %eax
movl %eax, -8(%ebp)
cmpl $6, -8(%ebp)
ja .L2
movl -8(%ebp), %edx
movl .L9(,%edx,4), %eax // 1
jmp *%eax // 2
果然如此!首先在 // 1 处根据%edp的值(其值相当于表的下标)在.L9的表中找到相应
case 语句的入口地址,并把这个地址存到%eax中,然后通过 // 2 (这是一个间接跳转
语句)转到%eax存放的地址中,也即相应的case语句处。
C编译器,果然聪明!
通过这个分析我们可以知道如下两点:
1. 当 case 语句少的时候,C编译器将其转成 if..else.. 类型进行处理,运用较多的
cmp 与 jmp 语句 ,而当 case 语句较多的时候,C编译器会出成一个跳转表,而直
接通过跳转表进行跳转,这让 switch 具有非常高的效律,而且效律几乎不会因为
case 语句的增长而减小,李维所担忧的问题是完全不会发生的
2. 可以问答下面几个问题:
1. 为什么 case 语句中需要的是整数类型而不能是其余的类型?
这是因为,case 语句中的这个值是用来做跳转表的下标的,因此,当然必须是整数
2. 为什么 case 语句在不加break的时候具有直通性?
这是因为跳转是在进入 switch 是计算出的,而不是在case语句中计算出的,整个
case 语句群就是一块完整而连续的代码,只是switch让其从不同的位置开始执行。
上面的内容,在《Computer Systems A Programmer's Perspective》中有很详细的论述,
感兴趣可以去找来仔细看看~~~
既然,case 语句需要的是整数的常量值,那么我们是否可用 const 类型呢?比如下面
一段代码:
const int c_1 = 100 ;
const int c_2 = 102 ;
void test( int x )
{
switch( x ){
case c_1 :
++x ;
case c_2 :
--x ;
}
}
这段代码,用 c 编译器编译,编译器会提示错误,但在 c++ 编译器中却不会,这主要是由于 c , 与 c++ 编译器对 const 这个东东的处理不同。我们来看看下面一段 c 程序
/*------------- filename : const_c.c -----------*/
const int a = 15 ;
void f( int x )
{
x = a ;
}
同样用 gcc 编译
gcc -S const_c.c
然后,来看看它的汇编文件
.file "const_c.c"
.globl a
.section .rodata
.align 4
.type a,@object
.size a,4
a: // 1
.long 15
.text
.globl f
.type f,@function
f:
pushl %ebp
movl %esp, %ebp
movl a, %eax // 2
movl %eax, 8(%ebp)
leave
ret
.Lfe1:
.size f,.Lfe1-f
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
注意 // 1 处,C 编译器为 a 分配了地址,并把它的值设为 15 ,而在 // 2 处,它是将
a 这个地址中的值赋给了 %eax,这同一般的普通变量而非const 变量赋值没什么两样
下面我们用 c++ 编译器来编译这段代码,它产生的汇编文件如下:
.file "const_cpp.cpp"
.text
.align 2
.globl _Z1fi
.type _Z1fi,@function
_Z1fi:
.LFB2:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
movl $15, 8(%ebp) // 1
leave
ret
.LFE2:
.Lfe1:
.size _Z1fi,.Lfe1-_Z1fi
.section .rodata
.align 4
.type a,@object
.size a,4
a:
.long 15
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
同样注意// 1 处,它以经把 a 的值用 15 来取代了,
也就是说,在c中const变量的行为更像一个非const变量,而在cpp中,const变量的行为就像是#define
由于 c++ 中,const 变量的值是在编译时就计算出来的,因此,它可以用在 case 语句中,而 c 中,const值在编译时只是一个变量的地址,因此,它无法用在 case 语句中.
-----------------------------------------------------------------------------
参考文献:<>
谢煜波
http://blog.csdn.net/xiaohan13916830/archive/2004/08/17/76724.aspx
前段时间在论坛上看见台湾李维在<
他说,在消息处理循环中,一般的形式是这样的
MSG msg ;
switch( msg ){
case WM_XXXXXXX :
....
case WM_XXXXXXX :
....
case WM_XXXXXXX :
....
} ;
李维说,这种模式是很低效的,因应经过汇编后,这种C代码会产生如下的汇编代码
cmp .... .....
jnz .... .....
cmp .... .....
jnz .... .....
cmp .... .....
jnz .... .....
如果你的 case 足够多,比如,你有一万条消息需要处理,而不幸的是你把一条最常用的消息
放在了最后一位,那么当这条消息要得到处理,会首先经过一万次的cmp与jnz, 李维认为,这
是非常非常低效的,实在是低效的忍无可忍,无需再忍~~:P
在起初,我也是这样认为的,但近来的阅读及实验却发现,这种看法非常片面,今天就来谈谈这个问题( 所有实验在 linux 平台下完成 )
首先看一到用 c 编写的程序
/* -------------------- filename : ta.c --------------- */
int switch_test_first( int x )
{
int res ;
switch( x ){
case 100 :
res = 1 ;
break ;
case 102 :
res = 2 ;
break ;
case 103 :
res = 3 ;
break ;
}
return res ;
}
然后,我们用 gcc 将它编译成汇编文件( 使用 -S 开关 )
gcc -S ta.c
将得到如下的汇编文件( ta.s )
.file "ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
.file "ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
movl %eax, -8(%ebp)
cmpl $102, -8(%ebp) // 1
je .L4 // 2
cmpl $102, -8(%ebp) // 3
jg .L8 // 4
cmpl $100, -8(%ebp) // 5
je .L3 // 6
jmp .L2 // 7
.L8:
cmpl $103, -8(%ebp)
je .L5
jmp .L2
.L3:
movl $1, -4(%ebp)
jmp .L2
.L4:
movl $2, -4(%ebp)
jmp .L2
.L5:
movl $3, -4(%ebp)
.L2:
movl -4(%ebp), %eax
leave
ret
.Lfe1:
.size switch_test_first,.Lfe1-switch_test_first
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
注意看文件中 // 1 ~ // 7 的部份,从这个部份,我们可以看出,gcc确实是把一些case语句转成了李维所说的那种方式进行处理,我们看见了代码中存在有众多的 cmpl 与 jmp 语句
这就相当于你使用if..else..一样,但是否总是这样呢?
我们下面改动一下 ta.c 这个文件,在里面再多加一些 case 语句
/* -------------- filename : new_ta.c ------------------- */
int switch_test_first( int x )
{
int res ;
switch( x ){
case 100 :
res = 1 ;
break ;
case 102 :
res = 2 ;
break ;
case 103 :
res = 3 ;
break ;
case 104 :
res = 4 ;
break ;
case 105 :
res = 5 ;
break ;
case 106 :
res = 6 ;
break ;
}
return res ;
}
这个 new_ta.c 与原来的 ta.c 在结构上完全相同,唯一不同的就是 case 语句的数量变多了,下面我们来编译一下这个文件
gcc -S new_ta.c
下面是我们产生的更新的汇编文件
.file "new_ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
subl $100, %eax
movl %eax, -8(%ebp)
cmpl $6, -8(%ebp)
ja .L2
movl -8(%ebp), %edx
movl .L9(,%edx,4), %eax
jmp *%eax
.section .rodata
.align 4
.align 4
.L9: // A
.long .L3
.long .L2
.long .L4
.long .L5
.long .L6
.long .L7
.long .L8
.text
.L3: // 1
movl $1, -4(%ebp)
jmp .L2
.L4: // 2
movl $2, -4(%ebp)
jmp .L2
.L5: // 3
movl $3, -4(%ebp)
jmp .L2 // 4
.L6:
movl $4, -4(%ebp)
jmp .L2 // 5
.L7:
movl $5, -4(%ebp) // 6
jmp .L2
.L8: // 7
movl $6, -4(%ebp)
.L2:
movl -4(%ebp), %eax
leave
ret
.Lfe1:
.size switch_test_first,.Lfe1-switch_test_first
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
仔细比较一下这个最新的 new_ta.s 与前面的 ta.s,精华全在里面了!
首先 new_ta.s 比前面的 ta.s 多了一个 .L9 部分,而且它的 // 1 ~ // 7 中没有了前面
ta.s 文件中所存在的众多的 cmpl 与 jmp 语句,那么,现在这样的代码又是怎么实现
switch 语句中的跳转的呢?我们来仔细分析一下它新多出来的 .L9 部份。
.section .rodata
.align 4
.align 4
.L9:
.long .L3
.long .L2
.long .L4
.long .L5
.long .L6
.long .L7
.long .L8
.text
显而易见,.L9 部份是一个我们最常见的数据结构——表,它的每一项都是一个标号,而这个标号,恰恰是每个 case 语句的入口标号!
这很容易让我们想到,它很可能是用了一张表来存放所有的 case 语句的入口,然后,在
执行 switch 语句的时候就从这个表中直接检出相应的 case 语句的入口地址,然后跳转
到相应的 case 语句去执行,就像hash_table似的。具体是不是这样呢?我们看看进入
switch 部份的代码:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
subl $100, %eax
movl %eax, -8(%ebp)
cmpl $6, -8(%ebp)
ja .L2
movl -8(%ebp), %edx
movl .L9(,%edx,4), %eax // 1
jmp *%eax // 2
果然如此!首先在 // 1 处根据%edp的值(其值相当于表的下标)在.L9的表中找到相应
case 语句的入口地址,并把这个地址存到%eax中,然后通过 // 2 (这是一个间接跳转
语句)转到%eax存放的地址中,也即相应的case语句处。
C编译器,果然聪明!
通过这个分析我们可以知道如下两点:
1. 当 case 语句少的时候,C编译器将其转成 if..else.. 类型进行处理,运用较多的
cmp 与 jmp 语句 ,而当 case 语句较多的时候,C编译器会出成一个跳转表,而直
接通过跳转表进行跳转,这让 switch 具有非常高的效律,而且效律几乎不会因为
case 语句的增长而减小,李维所担忧的问题是完全不会发生的
2. 可以问答下面几个问题:
1. 为什么 case 语句中需要的是整数类型而不能是其余的类型?
这是因为,case 语句中的这个值是用来做跳转表的下标的,因此,当然必须是整数
2. 为什么 case 语句在不加break的时候具有直通性?
这是因为跳转是在进入 switch 是计算出的,而不是在case语句中计算出的,整个
case 语句群就是一块完整而连续的代码,只是switch让其从不同的位置开始执行。
上面的内容,在《Computer Systems A Programmer's Perspective》中有很详细的论述,
感兴趣可以去找来仔细看看~~~
既然,case 语句需要的是整数的常量值,那么我们是否可用 const 类型呢?比如下面
一段代码:
const int c_1 = 100 ;
const int c_2 = 102 ;
void test( int x )
{
switch( x ){
case c_1 :
++x ;
case c_2 :
--x ;
}
}
这段代码,用 c 编译器编译,编译器会提示错误,但在 c++ 编译器中却不会,这主要是由于 c , 与 c++ 编译器对 const 这个东东的处理不同。我们来看看下面一段 c 程序
/*------------- filename : const_c.c -----------*/
const int a = 15 ;
void f( int x )
{
x = a ;
}
同样用 gcc 编译
gcc -S const_c.c
然后,来看看它的汇编文件
.file "const_c.c"
.globl a
.section .rodata
.align 4
.type a,@object
.size a,4
a: // 1
.long 15
.text
.globl f
.type f,@function
f:
pushl %ebp
movl %esp, %ebp
movl a, %eax // 2
movl %eax, 8(%ebp)
leave
ret
.Lfe1:
.size f,.Lfe1-f
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
注意 // 1 处,C 编译器为 a 分配了地址,并把它的值设为 15 ,而在 // 2 处,它是将
a 这个地址中的值赋给了 %eax,这同一般的普通变量而非const 变量赋值没什么两样
下面我们用 c++ 编译器来编译这段代码,它产生的汇编文件如下:
.file "const_cpp.cpp"
.text
.align 2
.globl _Z1fi
.type _Z1fi,@function
_Z1fi:
.LFB2:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
movl $15, 8(%ebp) // 1
leave
ret
.LFE2:
.Lfe1:
.size _Z1fi,.Lfe1-_Z1fi
.section .rodata
.align 4
.type a,@object
.size a,4
a:
.long 15
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
同样注意// 1 处,它以经把 a 的值用 15 来取代了,
也就是说,在c中const变量的行为更像一个非const变量,而在cpp中,const变量的行为就像是#define
由于 c++ 中,const 变量的值是在编译时就计算出来的,因此,它可以用在 case 语句中,而 c 中,const值在编译时只是一个变量的地址,因此,它无法用在 case 语句中.
-----------------------------------------------------------------------------
参考文献:<
曾看过一句话,给我非常深刻的印象:“在人生的每段记忆,总会有这么一个人带给记忆以生命。”是的,总有一些东西、一些人、一些细节在脑海徘徊。其实,记得太多东西也并非一件幸运的事,谁记得一切,谁就感到沉重!但当有一天,我突然强烈感到记忆正在慢慢被我忘却,而我一直觉得对一个人最大的伤害、最大的不公平,其实不是仇恨,而是漠视他或她的存在……于是,就有了这个新的分类栏目:转瞬红颜 ,用以怀念我那些逝去或即将逝去的时光,古人曾结绳纪事,我也学着用blog记述那些曾经的存在!
之所以喜欢这部连续剧,其实只是因为觉得女主角好看,才这么说的,一般一部不怎么好的连续剧,我都会努力找出它的优点(尽管有时候优点并不存在),譬如说这部连续剧《甘十九妹》中扮演甘十九妹的杨露mm就很PP,让人如此难忘……
后来,我在貂禅爱上了西施(QQYIRAN的空间)里,看到男女主角相遇时说的那段对白,这才觉得那还是蛮经典的,特别是在当年的连续剧中还是很大胆幽默的,《甘十九妹》这部连续剧改编自香港著名武侠小说家萧逸的同名作品,剧中人物的对白还是蛮简洁的,很有点古龙大师的味道,这可能也与剧中男主角张子健的形象蛮适合的……
那个时候,家里刚好买了台黑白电视,村里安装好了闭路电视,呵呵,山东台演的,感觉不错喔。。。追忆一下:
但愿有来生
-致我心目中最美的仙子甘十九妹
你确乎生于繁华人间,不然,你的喜怒哀乐,离合悲欢,又何以如此真切如斯,感慨缠绵;但,你更如来自瑶宇琼楼,不然,你的音容笑貌,举手投足,又何以如此典雅出尘,风致飘艳?
不明白为了什么,正是世间芸芸众生黯然消魂时,我写下这些文字,为你逝去的芳魂遥寄叹惋,为普天下钟情者送去一份遗恨后的慰藉,为我空虚惆怅的心灵寻一个自由倾诉空间,也许,也许,只是为了这颗赤诚的心.......
提起笔来,真不知以何言描摹你的绝代风华.
''沉鱼落雁,闭月羞花,千娇百媚,倾城倾国;风情万种,颠倒乾坤'',虽都一点也不觉过分,只怕这些给庸脂俗粉们的评语,玷辱了你的高洁.
''清水出芙蓉,天然去雕饰'',只可形容出你端妙仪容的十分之一.
''出淤泥而不染,濯清涟而不妖''.只可概括出你纯挚情操的百分之一.
你正如怒放在冰天雪地中的一枝寒梅,寒风呼啸,你依然含笑嫣然,香飘云天;只为伊人,无怨而有无悔,一颗痴心,亘古不渝.当明媚的春天来到,百卉争艳,因何只有你''零落成泥碾作尘''?当所有的恩怨情仇都勾销,又有谁能看到你秋湖般幽怨的明眸;当人们都陶醉在雪融冰消的欢乐里,又有谁能了解,波光滟滟的春水里,正流淌着你多情的泪花?
你正如清婉绝俗的玉兰,淡淡幽香,浸醉了几多痴儿女温馨的绮梦.你柔情似水,你吹气如兰;你秀发如云,你纤手凝脂.纵然是刀光剑影,绣幕重重,依然可仰你风流飘逸;纵然是血雨腥风,珠帘深深,依然可慕你高贵韵秀.你的芳心芷性,聪敏灵惠,终换得檀郎一世醉心温柔.然而悄然回眸,千帆尽处,花空烟水流.
''孤标傲世偕谁隐,一样花开为底迟'',落寞秋风.坎坷人生,聚散前缘,谁能料定?最难忘,你如泣如诉的琴声?你把爱恨都付与了这妙曼音符,可曾想,比翼双飞的心愿会成空?曾记否,桃源一游,空文萧赋;谁能料,英雄红颜,良辰虚度;诚可怨,神仙眷侣,都化了巫山残梦;最遗恨.知音苦觅,长相厮守只待来生.
一杯香茶,一盏孤灯,你堪比一竿青青翠竹生庭中.你用青春的火热与娇艳,只落得青山寂寂雾蒙蒙.你的良缘夙因,历历平生,只化做凄丽春梦;你的誓言,你的凄艳,只给人世留下长长久久的嗟呀与几缕浅浅淡淡的旧影,你的孜孜不懈地渴望与追求,你的追求的深深浅浅的足迹,重复了古往今来多少有情者曾跋涉过的荆棘路.
你生就寒梅的冰肌玉骨,你独具幽兰的端庄美淑,你占尽翠竹的娉婷娴静;你无愧素菊的高标脱俗......
纵集百卉之妍,也难将你描画出.
你有一颗温婉旖旎的女儿心,一腔得知己死亦无恨的女儿情
你挣扎于尘网又超脱世俗;你明知多情苦偏尝相思豆.
脉脉萧声,拨动了你少女情怀的一场痴恋.
熊熊烈火,记下了你今生最无悔的缱绻.
你爱地那般刻骨铭心,你恨得那般铭心刻骨.
你的笑容比云霞更灿烂,比彩虹更瑰丽;
你的心灵比美玉更无瑕,比明珠更璀璨;
你飘来飞去,绰约身姿,胜似碧霄飞雁;
你妙语连珠,空灵洒脱,真如落花微雨;
你是宿愿的冰湖秋月,你是天生的慧质兰心;
你是历史卷册中姗姗而出的古典仙子;
你是成人童话里令英雄倾倒的红粉佳人;
你是悠悠时空里最姝丽无伦的生灵;
你是茫茫红尘中真正爱过恨过的女人.
你匆匆来了-鲜花为你妍媚,百鸟为你歌唱;
你匆匆去了-落英为你缤纷,万物为你含悲;
桑田沧海,岁月如烟,人世几更改,世景多变迁.
何处寻,你巧笑倩兮的丽影;何处觅,你如诗如歌的琴声?
奈何桥头爱侣为伴,无论天上人间,你该不会寂寥;
三生石畔倾心相许,不管宿命轮回,前世今生,你应该很幸福.
也许,只有生生世世的相依相偎,正是你一生渴盼的天长地久.
也许,只有千载万代的朝朝暮暮.正是你苦苦追求的海烂石枯.
侧耳听,谁在吟,多情却似总无情?轻声唱,谁会听,生愿同欢死同冢,让鲜红的热血为这不该发生的一切恩怨划上一个结束号吧.爱,本没有错与对,更没有该与不该,只有完与未完,了与未了......
钟情的人们会感叹-自古美女英雄总痴情;
钟情的人们会吟唱-那首炽热绵长的恋曲;
钟情的人们会铭记-踏雪无痕里的侠骨柔情;
钟情的人们会传诵-那段轰轰烈烈的美丽故事.......
爱,又有谁能说得清?
又是莺飞草长,柳绿桃红的阳春时节,我默默祈祷-冥冥中自有牵线人,但愿有来世,但愿有来生.
之所以喜欢这部连续剧,其实只是因为觉得女主角好看,才这么说的,一般一部不怎么好的连续剧,我都会努力找出它的优点(尽管有时候优点并不存在),譬如说这部连续剧《甘十九妹》中扮演甘十九妹的杨露mm就很PP,让人如此难忘……
后来,我在貂禅爱上了西施(QQYIRAN的空间)里,看到男女主角相遇时说的那段对白,这才觉得那还是蛮经典的,特别是在当年的连续剧中还是很大胆幽默的,《甘十九妹》这部连续剧改编自香港著名武侠小说家萧逸的同名作品,剧中人物的对白还是蛮简洁的,很有点古龙大师的味道,这可能也与剧中男主角张子健的形象蛮适合的……
那个时候,家里刚好买了台黑白电视,村里安装好了闭路电视,呵呵,山东台演的,感觉不错喔。。。追忆一下:
但愿有来生
-致我心目中最美的仙子甘十九妹
你确乎生于繁华人间,不然,你的喜怒哀乐,离合悲欢,又何以如此真切如斯,感慨缠绵;但,你更如来自瑶宇琼楼,不然,你的音容笑貌,举手投足,又何以如此典雅出尘,风致飘艳?
不明白为了什么,正是世间芸芸众生黯然消魂时,我写下这些文字,为你逝去的芳魂遥寄叹惋,为普天下钟情者送去一份遗恨后的慰藉,为我空虚惆怅的心灵寻一个自由倾诉空间,也许,也许,只是为了这颗赤诚的心.......
提起笔来,真不知以何言描摹你的绝代风华.
''沉鱼落雁,闭月羞花,千娇百媚,倾城倾国;风情万种,颠倒乾坤'',虽都一点也不觉过分,只怕这些给庸脂俗粉们的评语,玷辱了你的高洁.
''清水出芙蓉,天然去雕饰'',只可形容出你端妙仪容的十分之一.
''出淤泥而不染,濯清涟而不妖''.只可概括出你纯挚情操的百分之一.
你正如怒放在冰天雪地中的一枝寒梅,寒风呼啸,你依然含笑嫣然,香飘云天;只为伊人,无怨而有无悔,一颗痴心,亘古不渝.当明媚的春天来到,百卉争艳,因何只有你''零落成泥碾作尘''?当所有的恩怨情仇都勾销,又有谁能看到你秋湖般幽怨的明眸;当人们都陶醉在雪融冰消的欢乐里,又有谁能了解,波光滟滟的春水里,正流淌着你多情的泪花?
你正如清婉绝俗的玉兰,淡淡幽香,浸醉了几多痴儿女温馨的绮梦.你柔情似水,你吹气如兰;你秀发如云,你纤手凝脂.纵然是刀光剑影,绣幕重重,依然可仰你风流飘逸;纵然是血雨腥风,珠帘深深,依然可慕你高贵韵秀.你的芳心芷性,聪敏灵惠,终换得檀郎一世醉心温柔.然而悄然回眸,千帆尽处,花空烟水流.
''孤标傲世偕谁隐,一样花开为底迟'',落寞秋风.坎坷人生,聚散前缘,谁能料定?最难忘,你如泣如诉的琴声?你把爱恨都付与了这妙曼音符,可曾想,比翼双飞的心愿会成空?曾记否,桃源一游,空文萧赋;谁能料,英雄红颜,良辰虚度;诚可怨,神仙眷侣,都化了巫山残梦;最遗恨.知音苦觅,长相厮守只待来生.
一杯香茶,一盏孤灯,你堪比一竿青青翠竹生庭中.你用青春的火热与娇艳,只落得青山寂寂雾蒙蒙.你的良缘夙因,历历平生,只化做凄丽春梦;你的誓言,你的凄艳,只给人世留下长长久久的嗟呀与几缕浅浅淡淡的旧影,你的孜孜不懈地渴望与追求,你的追求的深深浅浅的足迹,重复了古往今来多少有情者曾跋涉过的荆棘路.
你生就寒梅的冰肌玉骨,你独具幽兰的端庄美淑,你占尽翠竹的娉婷娴静;你无愧素菊的高标脱俗......
纵集百卉之妍,也难将你描画出.
你有一颗温婉旖旎的女儿心,一腔得知己死亦无恨的女儿情
你挣扎于尘网又超脱世俗;你明知多情苦偏尝相思豆.
脉脉萧声,拨动了你少女情怀的一场痴恋.
熊熊烈火,记下了你今生最无悔的缱绻.
你爱地那般刻骨铭心,你恨得那般铭心刻骨.
你的笑容比云霞更灿烂,比彩虹更瑰丽;
你的心灵比美玉更无瑕,比明珠更璀璨;
你飘来飞去,绰约身姿,胜似碧霄飞雁;
你妙语连珠,空灵洒脱,真如落花微雨;
你是宿愿的冰湖秋月,你是天生的慧质兰心;
你是历史卷册中姗姗而出的古典仙子;
你是成人童话里令英雄倾倒的红粉佳人;
你是悠悠时空里最姝丽无伦的生灵;
你是茫茫红尘中真正爱过恨过的女人.
你匆匆来了-鲜花为你妍媚,百鸟为你歌唱;
你匆匆去了-落英为你缤纷,万物为你含悲;
桑田沧海,岁月如烟,人世几更改,世景多变迁.
何处寻,你巧笑倩兮的丽影;何处觅,你如诗如歌的琴声?
奈何桥头爱侣为伴,无论天上人间,你该不会寂寥;
三生石畔倾心相许,不管宿命轮回,前世今生,你应该很幸福.
也许,只有生生世世的相依相偎,正是你一生渴盼的天长地久.
也许,只有千载万代的朝朝暮暮.正是你苦苦追求的海烂石枯.
侧耳听,谁在吟,多情却似总无情?轻声唱,谁会听,生愿同欢死同冢,让鲜红的热血为这不该发生的一切恩怨划上一个结束号吧.爱,本没有错与对,更没有该与不该,只有完与未完,了与未了......
钟情的人们会感叹-自古美女英雄总痴情;
钟情的人们会吟唱-那首炽热绵长的恋曲;
钟情的人们会铭记-踏雪无痕里的侠骨柔情;
钟情的人们会传诵-那段轰轰烈烈的美丽故事.......
爱,又有谁能说得清?
又是莺飞草长,柳绿桃红的阳春时节,我默默祈祷-冥冥中自有牵线人,但愿有来世,但愿有来生.
http://www.yuanma.org/data/2006/0803/article_1307.htm
架构基于FreeBSD和Postfix的IGENUS Webmail邮件系统
baidu
本文介绍使用FreeBSD+Postfix+Cyrus-sasl+Courier-imap+igenus+spamassassin+ Clamav+mailscanner+mailscanner-mrtg+mailman来架构一个具有多域名,有邮件列表、webmail、防病毒、防垃圾邮件、web管理界面的邮件系统。
Jacky, $Revision: 4.51 bate $Date: 2005-12-03
系统主要采用MailScanner+clamav+Spamd+APF来对病毒过滤和垃圾邮件过滤。
本文在4.10、5.3、5.4、6.0上安装测试通过,病毒过滤放弃采用amavisd。主要采用执行效率更高的MailSanner来对邮件过滤和垃圾邮件过滤,配置更容易,并且降低了系统开消。让系统更加稳定,经过严格病毒邮件测试成功率达到了100%。垃圾邮件过滤基本上达到了95%的成功率。
Table of Contents
Chapter 1. 系统安装
1.1 安装MySQL
1.2 安装Apache
1.3 安装PHP
1.4 安装zend
1.5 安装openssl
1.6 安装phpMyAdmin
1.7 通过phpMyadmin设置数据库
1.8安装Courier-imap
1.9安装 postfix 和 cyrus-sasl
1.10 安装expect
Chapter 2. 配置邮件服务器
2.1 配置rc.conf
2.2 配置postfix 和 cyrus-sasl
2.3 配置Courier-imap
Chapter 3. 手动设置第一个用户并测试
Chapter 4. 安装postfix管理工具
4.1安装本人开发的postfix管理工具
4.2 用户登录测试
Chapter 5. 防病毒与防垃圾邮件
5.1 安装Clamav
5.2 安装MailScanner
5.3 安装配置Spamassassin
5.4修改Postfix设定档main.cf
5.5修改mailscanner.conf
5.6新增MailScanner所要用到的资料夹
5.7把病毒提示信息改为中文
5.8MailScanner监管进出邮件
5.9 邮件流量监控(mailscanner-mrtg)安装与设置
5.10.安装APF防垃圾邮件
Chapter 6. 安装webmail
Chapter 7邮件列表(mailman)
Chapter 8. 查看系统状态
Chapter 1. 系统安装
安装之前:因用户数据都保存在/var目录下,因此安装FreeBSD时/var的空间应尽量大。FreeBSD的版本为5.3,按最小化安装,软件包只安装cvsup,安装结束后用cvsup更新ports树。在文档中假设服务器的ip地址为192.168.0.2,域名为 toping.net,主机名为mail.toping.net。
请兄弟们仔细一些,注意空格和TAB。
祝兄弟们好运。本人水平有限。如果发现文章中有什么错误和不当的地方请发邮件:scyz@toping.net。我会在第一时间给予答复。
1.1 安装MySQL
mail# cd /usr/ports/databases/mysql40-server
mail# make install clean
编辑/etc/rc.conf,加入
mysql_enable="YES"
1.2 安装Apache
mail# cd /usr/ports/www/apache2
mail# make install clean
编辑/etc/rc.conf,加入
Apache2_enable="YES"
1.3 安装PHP
mail# cd /usr/ports/www/mod_php4
mail# make install clean
我的选择:(注意别选DEBUG,否则会和ZEND有冲突)
[X] APACHE2 Use apache 2.x instead of apache 1.3.x
安装需要的PHP扩展模块
mail# cd /usr/ports/lang/php4-extensions
mail# make install clean
我选择了下面的模块:
[X] BCMATH bc style precision math functions
[X] BZ2 bzip2 library support
[X] CALENDAR calendar conversion support
[X] CRACK crack support
[X] CTYPE ctype functions
[X] CURL CURL support
[X] FTP FTP support
[X] GD GD library support
[X] GETTEXT gettext library support
[X] FILEINFO fileinfo support
[X] IMAP IMAP support
[X] MBSTRING multibyte string support
[X] MCAL Modular Calendar Access Library support
[X] MCRYPT Encryption support
[X] MCVE MCVE support
[X] MHASH Crypto-hashing support
[X] MYSQL MySQL database support
[X] PCRE Perl Compatible Regular Expression support
[X] POSIX POSIX-like functions
[X] SESSION session support
[X] TOKENIZER tokenizer support
[X] XML XML support
[X] ZLIB ZLIB support
最后在编辑/usr/local/etc/apache2/httpd.conf最后加入:
DirectoryIndex index.html index.html.var index.php
#注:在DirectoryIndex这里加入index.php,是为了让apache支持首页为index.php的首页文件
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
Group www、User www修改为: Group postfix、User postfix
注:以上这一步要在postfix安装后再操作
1.4.安装zend
mail# cd /usr/ports/devel/ZendOptimizer
mail# make install clean
因为版权的问题,他不会自动下载。这里你需要到他的官方网站去下载ZendOptimizer-2.5.10a-freebsd4.3-i386.tar.gz到/usr/ports/distfiles目录下面后再安装。
下载地址:
http://downloads.zend.com/optimizer/2.5.10/ZendOptimizer-2.5.10a-freebsd4.3-i386.tar.gz
完成后在/usr/local/etc/php.ini中加入:
[Zend]
zend_optimizer.optimization_level=15
zend_extension_manager.optimizer="/usr/local/lib/php/20020429/Optimizer"
zend_extension_manager.optimizer_ts="/usr/local/lib/php/20020429/Optimizer_TS"
zend_extension="/usr/local/lib/php/20020429/ZendExtensionManager.so"
zend_extension_ts="/usr/local/lib/php/20020429/ZendExtensionManager_TS.so"
重启apache安装完成。
1.5 安装openssl
mail# cd /usr/ports/security/openssl
mail# make install clean
1.6 安装phpMyAdmin
mail# cd /usr/ports/databases/phpmyadmin
mail# make fetch
注:(在这里建议直接下载后复制安装)
mail# cd /usr/ports/distfiles
mail# tar –zxvf PhpMyadmin-x.tar.gz
mail# mv /usr/local/www/phpMyAdmin-x /usr/local/www/data/dbadmin
修改/usr/local/www/data/dbadmin/config.inc.php
$cfg['PmaAbsoluteUri'] = 'http://192.168.0.2/dbadmin/';
$cfg['Servers'][$i]['auth_type'] = 'http'; // Authentication method (config, http or cookie based)?
注:指定phpmyadmin的认证方式为http方式。
在浏览器输入http://192.168. 0.2/dbadmin/,首次进行登入的用户名为root密码为空,登入后可以修改你的密码。
1.7 通过phpMyadmin设置数据库
建立postfix数据库(注意:数据库名称为postfix):
mail# mysql –u root –p
mysql# CREATE DATABASE `postfix` ;
mysql# use postfix;
下面为sql语句:
CREATE TABLE domaininfo (
domain_id int(5) NOT NULL auto_increment,
domain varchar(25) NOT NULL default '',
alias varchar(30) default NULL,
passwd varchar(35) NOT NULL default '',
usernum int(5) NOT NULL default '0',
quota int(11) NOT NULL default '0',
des varchar(30) default NULL,
expire date NOT NULL default '0000-00-00',
active tinyint(1) NOT NULL default '1',
create_time datetime default NULL,
PRIMARY KEY (domain_id),
UNIQUE KEY domain (domain),
KEY domain_id (domain_id)
) TYPE=MyISAM COMMENT='domain information';
INSERT INTO domaininfo VALUES (1,'admin',NULL,'$1$.j3.t12.$I7MGf7ZD2HrWwUWQF88Mg1',0,0,'Super Admin','0000-00-00',1,'0000-00-00 00:00:00');
CREATE TABLE userinfo (
id int(11) NOT NULL auto_increment,
userid varchar(20) NOT NULL default '',
domain_id int(5) NOT NULL default '0',
address varchar(50) NOT NULL default '',
alias varchar(60) default NULL,
passwd varchar(35) NOT NULL default '',
realname varchar(20) default NULL,
quota int(11) NOT NULL default '0',
active tinyint(1) NOT NULL default '0',
homedir varchar(60) NOT NULL default '',
maildir varchar(60) NOT NULL default '',
create_time datetime NOT NULL default '0000-00-00 00:00:00',
`fax` varchar(20) NOT NULL default '',
`telephone` varchar(15) NOT NULL default '',
`sex` int(1) NOT NULL default '0',
`year` int(4) NOT NULL default '0',
`MONTH` int(2) NOT NULL default '0',
`DAY` int(2) NOT NULL default '0',
`education` varchar(4) NOT NULL default '',
`marital` int(1) NOT NULL default '0',
`occupation` varchar(15) NOT NULL default '',
`companyname` varchar(30) NOT NULL default '',
`province` varchar(6) NOT NULL default '',
PRIMARY KEY (id),
UNIQUE KEY address (address)
) TYPE=InnoDB COMMENT='User Information';
注:对于初学者,建议以上操作都在phpmyadmin中操作更加的简便,如果后面要使用igenus请导入原来igenus的sql。
建立数据库用户并授以相应的权限
mail# mysql –u root –p
mysql# use mysql;
mysql# INSERT INTO user (host,user,password) VALUES('localhost','postfix','');
mysql# update user set password=password('postfix') where User='postfix';
mysql# GRANT ALL ON postfix.* TO postfix@localhost IDENTIFIED BY "postfix";
注:这里加用户名和密码都为:postfix。并授权对postfix数据库进行操作
1.8 安装Courier-imap
mail# cd /usr/ports/mail/courier-imap
mail# make install clean
我的选择:
[X] OPENSSL Build with OpenSSL support
[X] AUTH_MYSQL MySQL support
在/etc/rc.conf中加入:
courier_authdaemond_enable="YES"
courier_imap_pop3d_enable="YES"
courier_imap_imapd_enable="YES"
mail# cd /usr/local/etc/courier-imap
mail# cp imapd.cnf.dist imapd.cnf
mail# cp pop3d.cnf.dist pop3d.cnf
mail# /usr/local/etc/rc.d/courier-authdaemond.sh start
注:此时会在/var/run/authdaemond/下产生socket,如果没有下面这一步下面的认证无法通过。
mail# chmod +x /var/run/authdaemond
1.9 安装 postfix 和 cyrus-sasl
mail# cd /usr/ports/security/cyrus-sasl2
mail# make install WITH_AUTHDAEMON=yes
mail# make clean
创建/usr/local/lib/sasl2/smtpd.conf
pwcheck_method: authdaemond
log_level: 3
mech_list: PLAIN LOGIN
authdaemond_path:/var/run/authdaemond/socket
更详细的参数设置请看:
http://www.toping.net/bbs/htm_data/7/0508/330.html
至此,认证部分基本完成。
安装postfix
mail# cd /usr/ports/mail/postfix
mail# make install clean
我的选择:
[X] VDA VDA (Virtual Delivery Agent)
[X] MySQL MySQL map lookups (choose version with WITH_MYSQL_VER)
[X] TLS SSL and TLS
[X] SASL2 Cyrus SASLv2 (Simple Authentication and Security Layer)
回答下面的两问题:
You need user "postfix" added to group "mail".[是否将postfix用户加到mail用户组]
Would you like me to add it [y]? y
Would you like to activate Postfix in /etc/mail/mailer.conf [n]? n
在/etc/rc.conf中加入postfix启动所需的启动选项
在/etc/rc.conf中加入:
sendmail_enable="YES"
sendmail_flags="-bd"
sendmail_pidfile="/var/spool/postfix/pid/master.pid"
sendmail_procname="/usr/local/libexec/postfix/master"
sendmail_outbound_enable="NO"
sendmail_submit_enable="NO"
sendmail_msp_queue_enable="NO"
设置postfix启动所需
mail# ln -s /usr/local/sbin/sendmail /usr/sbin/sendmail
注:如果/usr/sbin/sendmail存在就删了再做上链接,如果升级内核和升级系统后要重新做这一步。
mail# echo ‘postfix: root’ >> /etc/aliases
mail# /usr/local/bin/newaliases
mail# chown postfix:postfix /etc/opiekeys
1.10 安装expect
用于Web客户端建立邮件用户
mail# cd /usr/ports/lang/expect
mail# make install clean
Chapter 2. 配置邮件服务器
本节主要讲述各种服务的参数配置。
2.1 配置rc.conf,编辑/etc/rc.conf
下面是前面所装软件都加入了启动选项的rc.conf配置:
mysql_enable="YES"
apache2_enable="YES"
courier_authdaemond_enable="YES"
courier_imap_pop3d_enable="YES"
courier_imap_imapd_enable="YES"
sendmail_enable="YES"
sendmail_flags="-bd"
sendmail_pidfile="/var/spool/postfix/pid/master.pid"
sendmail_procname="/usr/local/libexec/postfix/master"
sendmail_outbound_enable="NO"
sendmail_submit_enable="NO"
sendmail_msp_queue_enable="NO"
2.2 配置postfix 和 cyrus-sasl
(1)修改/usr/local/etc/postfix/main.cf,在文件最后加入以下内容
mail# ee /usr/local/etc/postfix/main.cf
smtpd_helo_required = yes
strict_rfc821_envelopes = yes
smtpd_etrn_restrictions = permit_mynetworks, reject
#=====================BASE=====================
myhostname = mail.toping.net
mydomain = toping.net
mydestination = $myhostname
local_recipient_maps =
command_directory = /usr/local/sbin
local_transport = virtual
#=====================MySQL=====================
virtual_alias_maps = mysql:/usr/local/etc/postfix/mysql_virtual_alias_maps.cf
virtual_gid_maps = static:125
virtual_mailbox_base = /
virtual_mailbox_domains = mysql:/usr/local/etc/postfix/mysql_virtual_domains_maps.cf
virtual_mailbox_limit = 51200000
virtual_mailbox_maps = mysql:/usr/local/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_minimum_uid = 125
virtual_transport = virtual
virtual_uid_maps = static:125
#=====================Quota=====================
virtual_create_maildirsize = yes
virtual_mailbox_extended = yes
virtual_mailbox_limit_maps = mysql:/usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
virtual_mailbox_limit_override = yes
virtual_maildir_limit_message = Sorry, the user's maildir has overdrawn his diskspace quota, please try again later.
virtual_overquota_bounce = yes
#====================SASL=====================
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
smtpd_delay_reject=yes
smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,permit_auth_destination,reject
smtpd_client_restrictions = permit_sasl_authenticated
更详细的参数设置请看论坛:
http://www.toping.net/bbs/htm_data/7/0601/871.html
(4)编辑/usr/local/etc/postfix/mysql_virtual_alias_maps.cf
mail# ee /usr/local/etc/postfix/mysql_virtual_alias_maps.cf
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT alias FROM userinfo WHERE address='%s' AND active = 1
(5)编辑/usr/local/etc/postfix/mysql_virtual_domains_maps.cf
mail# ee /usr/local/etc/postfix/mysql_virtual_domains_maps.cf
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT domain FROM domaininfo WHERE domain='%s'
(6)编辑/usr/local/etc/postfix/mysql_virtual_mailbox_maps.cf
mail# ee /usr/local/etc/postfix/mysql_virtual_mailbox_maps.cf
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT maildir FROM userinfo WHERE address='%s' AND active = 1
(7)编辑/usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
mail# ee /usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT quota FROM userinfo WHERE address='%s'
2.3 配置Courier-imap
(1)修改Courier相关设置,/usr/local/etc/courier-imap/imapd:
IMAP_CAPABILITY="IMAP4rev1 CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA"
(2)修改/usr/local/etc/courier-imap/pop3d
POP3AUTH="LOGIN CRAM-MD5 CRAM-SHA1"
(3)编辑修改/usr/local/etc/authlib/authmysqlrc
mail# mv /usr/local/etc/authlib/authmysqlrc /usr/local/etc/authlib/authmysqlrc_bak
mail# ee /usr/local/etc/authlib/authmysqlrc
MYSQL_SERVER localhost //数据库主机地址
MYSQL_USERNAME postfix //数据库用户名
MYSQL_PASSWORD postfix //数据库密码
MYSQL_PORT 0
MYSQL_OPT 0
MYSQL_DATABASE postfix //数据库名称
MYSQL_USER_TABLE userinfo
MYSQL_CRYPT_PWFIELD passwd
MYSQL_UID_FIELD '125'
MYSQL_GID_FIELD '125'
MYSQL_LOGIN_FIELD address
MYSQL_HOME_FIELD homedir
MYSQL_NAME_FIELD realname
MYSQL_MAILDIR_FIELD maildir
MYSQL_QUOTA_FIELD quota
注:这里得用tab键来跳格
(4)编辑/usr/local/etc/authlib/authdaemonrc
mail# mv /usr/local/etc/authlib/authdaemonrc /usr/local/etc/authlib/authdaemonrc_bak
mail# ee /usr/local/etc/authlib/authdaemonrc
authmodulelist="authmysql"
authmodulelistorig="authmysql"
version="authdaemond.mysql"
daemons=5
authdaemonvar=/var/run/authdaemond
subsystem=mail
DEBUG_LOGIN=0
DEFAULTOPTIONS="wbnodsn=1"
重启服务器
Chapter 3.手动设置第一个用户并测试
本章介绍如何开通用户,并且测试系统是否正常。
注:增加用户时请到这里生成加密后的密码后直接插入到数据库中就可以了。
http://www.toping.net/soft
mail# mysql
mysql> use postfix;
mysql> show tables;
+-------------------+
| Tables_in_postfix |
+-------------------+
| address |
| admin |
| card |
| domaininfo |
| lastauth |
| logs |
| message |
| personal |
| scheduler |
| stow |
| userinfo |
| vpopmail |
+-------------------+
12 rows in set (0.00 sec)
增加域名和管理员
mysql> desc domaininfo;
+-------------+-------------+------+-----+------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+------------+----------------+
| domain_id | int(5) | | PRI | NULL | auto_increment |
| domain | varchar(25) | | UNI | | |
| alias | varchar(30) | YES | | NULL | |
| passwd | varchar(35) | | | | |
| usernum | int(5) | | | 0 | |
| quota | int(11) | | | 0 | |
| des | varchar(30) | YES | | NULL | |
| expire | date | | | 0000-00-00 | |
| active | tinyint(1) | | | 1 | |
| create_time | datetime | YES | | NULL | |
+-------------+-------------+------+-----+------------+----------------+
10 rows in set (0.00 sec)
mysql> INSERT INTO `domaininfo` VALUES (7, 'toping.net', NULL, '$1$jNXThQXq$KPjm.WE2f2yX5rceY48vX. ', 50, 500, NULL, '0000-00-00', 1, '2005-04-19 23:19:11');
Query OK, 1 row affected (0.00 sec)
注:这里的toping.net的管理密码为:admin123
mysql> desc userinfo;
+-------------+-------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------------------+----------------+
| id | int(11) | | PRI | NULL | auto_increment |
| userid | varchar(20) | | | | |
| domain_id | int(5) | | | 0 | |
| address | varchar(50) | | UNI | | |
| alias | varchar(60) | YES | | NULL | |
| passwd | varchar(35) | | | | |
| realname | varchar(20) | YES | | NULL | |
| quota | int(11) | | | 0 | |
| active | tinyint(1) | | | 0 | |
| homedir | varchar(60) | | | | |
| maildir | varchar(60) | | | | |
| create_time | datetime | | | 0000-00-00 00:00:00 | |
| fax | varchar(20) | | | | |
| telephone | varchar(15) | | | | |
| sex | int(1) | | | 0 | |
| year | int(4) | | | 0 | |
| MONTH | int(2) | | | 0 | |
| DAY | int(2) | | | 0 | |
| education | varchar(4) | | | | |
| marital | int(1) | | | 0 | |
| occupation | varchar(15) | | | | |
| companyname | varchar(30) | | | | |
| province | varchar(6) | | | | |
+-------------+-------------+------+-----+---------------------+----------------+
23 rows in set (0.00 sec)
mysql> INSERT INTO `userinfo` VALUES (8, 'webmaster', 7, 'webmaster@toping.net', NULL,'$1$4DLQeNkz$QKCAQqg244XwvLl2SD11f0', 'webmaster', 209715200, 1, '/var/mail/toping.net/webmaster/', '/var/mail/toping.net/webmaster/Maildir/', '2005-04-20 23:45:17', '', '', 0, 0, 0, 0, '', 0, '', '', '');
注:这里的webmaster用户的邮箱密码为:000000
mysql>quit
设置用户的目录与权限:
Mail# mkdir -p /var/mail/toping.net/webmaster
Mail# /usr/local/bin/maildirmake /var/mail/toping.net/webmaster/Maildir
Mail# chmod -R 777 /var/mail/toping.net/
Mail# chown -R postfix:postfix /var/mail/toping.net
至此用户设置完毕,这里只使用一个域名,同理可以设置多个域名。
Chapter 4.安装postfix管理工具
本节主要介绍如何安装和使用本人开发的postfix管理工具。
4.1安装本人开发的postfix管理工具来设置第一个域名和用户
上传管理工具到网站目录
后修改include/config.inc.php中的
define(DOMAINSDIR,"/home/vmail"); 为 define(DOMAINSDIR,"/var/mail");
define(MYSQL_HOST, 'localhost'); 为 您的MySQL服务器的主机名
define(MYSQL_USER, 'root'); 为 您的MySQL的用户名
define(MYSQL_PASS, 'mypasswd'); 为 您的MySQL的密码
define(MYSQL_DATA, 'postfix'); 为 您的邮件服务器的数据库
修改完成后运行:http://mail.toping.net/webadmin/index.php
完装完成!!
4.2 用户登录测试
用户登录测试
安装p5-MIME-Base64
mail# cd /usr/ports/converters/p5-MIME-Base64/
mail# make install clean
通过p5-MIME-Base64来取得用户名和密码的base64编码
mail# perl -MMIME::Base64 -e 'print encode_base64("webmaster\@toping.net");'
d2VibWFzdGVyQHRvcGluZy5uZXQ=
mail# perl -MMIME::Base64 -e 'print encode_base64("000000");'
MDAwMDAw
测试发送邮件(端口:25):
mail# telnet 127.0.0.1 25
Trying 127.0.0.1...
Connected to 0.
Escape character is '^]'.
220 mail.toping.net ESMTP Postfix
ehlo mail
250-mail.toping.net
250-PIPELINING
250-SIZE 4194304
250-VRFY
250-ETRN
250-AUTH NTLM LOGIN PLAIN OTP
250-AUTH=NTLM LOGIN PLAIN OTP
250 8BITMIME
auth login
334 VXNlcm5hbWU6
d2VibWFzdGVyQHRvcGluZy5uZXQ= //此为用户名id:webmaster@toping.net
334 UGFzc3dvcmQ6
MDAwMDAw //此为用户密码password:000000
235 Authentication successful
MAIL FROM: //告诉服务器发件人的Email地址
250 Ok
RCPT TO: //告诉服务器收件人的地址
250 OK
DATA //告诉服务器开始写信
354 End data with .
SUBJECT:test //subject后面填写的是邮件的主题
test
. //换行后输入.后按回车,表示信件内容书写完毕
250 Ok: queued as 58DC71D5
quit //发送信件,结束对话,退出SMTP服务器
221 Bye
Connection closed by foreign host
测试收取邮件(端口:110):
mail# telnet 127.0.0.1 110
Trying 127.0.0.1...
Connected to 0
Escape character is '^]'
+OK Hello there
user webmaster@toping.net
+OK Password required
pass 000000
+OK logged in
list
+OK POP3 clients that break here, they violate STD53
1 2217
retr 1 //返回第一封信的全部内容
+OK 2217 octets follow.
Return-Path:
X-Original-To: webmaster@toping.net
Delivered-To: webmaster@toping.net
Received: from mail (localhost.toping.net [127.0.0.1])
by mail.toping.net (Postfix) with ESMTP id 58DC71D5
for ; Mon, 9 Aug 2004 21:11:20 +0800 (CST)
SUBJECT:test
Message-Id: <20040809131120.58DC71D5@mail.toping.net>
Date: Mon, 9 Aug 2004 21:11:20 +0800 (CST)
From: webmaster@toping.net
To: undisclosed-recipients:;
test
.
dele 1 //删除
+OK Deleted
quit
+OK Bye-bye
Connection closed by foreign host
也可以使用任何其它的邮件客户端程序来测试,如foxmail、Outlook Express等等。
Chapter 5. 防病毒与防垃圾邮件
本章介绍病毒与垃圾邮件的防范。
5.1 安装Clamav
mail# cd /usr/ports/security/clamav
mail# make install clean
我的选择:
[X] MILTER Compile the milter interface
[X] CURL Support URL downloading
[X] LIBUNRAR Support for external Unrar library
要想clamav能自动的启动请在/etc/rc.conf中加入:
clamav_clamd_enable="YES"
clamav_freshclam_enable="YES"
重启服务器
测试杀毒
mail# clamscan -r -i /usr/local/www/data
----------- SCAN SUMMARY -----------
Known viruses: 41293
Engine version: 0.87.1
Scanned directories: 53
Scanned files: 602
Infected files: 0
Data scanned: 41.51 MB
Time: 18.294 sec (0 m 18 s)
升级病毒库
mail# freshclam
ClamAV update process started at Sun Dec 4 01:10:02 2005
main.cvd is up to date (version: 34, sigs: 39625, f-level: 5, builder: tkojm)
daily.cvd is up to date (version: 1200, sigs: 1669, f-level: 6, builder: tomek)
5.2 安装MailScanner
mail# cd /usr/ports/mail/mailscanner
mail# make install
第一次执行安装因此需执行make initial-config以建立基本配置文件
mail# make initial-config
mail# make clean
5.3 安装SpamAssassin
mail# cd /usr/ports/mail/p5-Mail-SpamAssassin
mail# make install clean
我的选择:
[X] AS_ROOT Run spamd as root (recommended)
[X] DOMAINKEYS DomainKeys support
[X] SSL Build with SSL support for spamd/spamc
[X] MYSQL Add MySQL support
[X] RAZOR Add Vipul's Razor support
[X] SPF_QUERY Add SPF query support
[X] RELAY_COUNTRY Relay country support
[X] TOOLS Install SpamAssassin tools
5.4修改Postfix设定档main.cf
mail# ee /usr/local/etc/postfix/main.cf
#header_checks = regexp:/usr/local/etc/postfix/header_checks //默认值
header_checks = regexp:/usr/local/etc/postfix/header_checks //把注释去掉
编辑/usr/local/etc/postfix/header_checks
mail# ee /usr/local/etc/postfix/header_checks
/^Received:/ HOLD //新加入
5.5修改mailscanner.conf
mail# ee /usr/local/etc/MailScanner/MailScanner.conf
#Run As User = //默认值
Run As User = postfix //修改后
#Run As Group = //默认值
Run As Group = postfix //修改后
#Incoming Queue Dir = /var/spool/mqueue.in //默认值
Incoming Queue Dir = /var/spool/postfix/hold //修改后
#Outgoing Queue Dir = /var/spool/mqueue //默认值
Outgoing Queue Dir = /var/spool/postfix/incoming //修改后
#MTA = sendmail //默认值
MTA = postfix //修改后
#Virus Scanners = none //默认值
Virus Scanners = clamav //修改后
#Use SpamAssassin = no //默认值
Use SpamAssassin = yes //修改后
5.6新增MailScanner所要用到的资料夹
mail# mkdir /var/spool/MailScanner
mail# mkdir /var/spool/MailScanner/incoming
mail# mkdir /var/spool/MailScanner/quarantine
mail# chown postfix:postfix /var/spool/MailScanner/incoming
mail# chown postfix:postfix /var/spool/MailScanner/quarantine
mail# touch /usr/local/etc/MailScanner/rules/bounce.rules //新建一个空白文件,要不然会出错。
mail# chmod –R 777 /var/spool/postfix
mail#cp /usr/local/etc/MailScanner/mcp/10_example.cf.sample /usr/local/etc/MailScanner/mcp/10_example.cf
mail#cp /usr/local/etc/MailScanner/mcp/mcp.spam.assassin.prefs.conf.sample
/usr/local/etc/MailScanner/mcp/mcp.spam.assassin.prefs.conf
注:这里的倒数一、二行实际操作中为一行
重新启动服务器
测试病毒过滤:
mail# telnet localhost 25
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mail.toping.net ESMTP Postfix
mail from:webmaster@toping.net
250 Ok
rcpt to:webmaster@toping.net
250 Ok
data
354 End data with .
Subject:Virus test
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
.
250 Ok: queued as F0C221CC20 //出现F0C221CC20这行表示mailscanner运行成功了
quit
221 Bye
Connection closed by foreign host.
5.7把病毒提示信息改为中文
预设系统提示信息为英文,可以下载我修改的中文包。
http://mail.toping.net/mailscanner/cn.rar
注:把下载下来的文件解压后放到/usr/local/share/MailScanner/reports/cn下面去
mail# ee /usr/local/etc/MailScanner/MailScanner.conf
#%report-dir% = /usr/local/share/MailScanner/reports/en //默认值(加载英文)
%report-dir% = /usr/local/share/MailScanner/reports/cn //修改后为读取中文
5.8.用MailScanner来监管进出邮件
mail# ee MailScanner.conf
Archive Mail = %rules-dir%/archive.rules
mail# ee archive.rules
方法一:互相备份, 以上这样并不会造成 loop
FromOrTo: a@toping.net yes forward b@toping.net
FromOrTo: b@toping.net yes forward a@toping.net
方法二:可以 forward 到复数信箱
FromOrTo: a@toping.net yes forward b@toping.net c@toping.net d@toping.net
方法三:同时备份到一个或多个档案及一个或多个信箱
FromOrTo: a@toping.net yes forward /var/spool/MailScanner/archive/a_user_backup.mbx /var/spool/MailScanner/archive/a_user_backup.mbx b@toping.net scyz2@163.com
注:以上为一行,该档案要先建立且确定该档案拥有者与 MailScanner.conf 的 Run As User = XXXXXXX 相同
方法四:备份到数据夹及多个信箱或档案
FromOrTo: a@toping.net yes forward /var/spool/MailScanner/archive/ b@toping.net scyz2@163.com /var/spool/MailScanner/archive/a_user_backup.mbx
注:以上为一行,权限同SAMPLE3;注意事项, 他会依日期再分数据夹, 日期数据夹内的文件名称 mail queue ID, 格式为 postfix mail queue 格式
更详细的mailscanner.cf的参数请看论坛:
http://www.toping.net/bbs/htm_data/7/0509/533.html
5.9.安装MailScanner-mrtg
mail# cd /usr/ports/mail/ mailscanner-mrtg
mail# make install clean
mailscanner-mrtg相关设置
mail# cd /usr/local/etc/mailscanner-mrtg //切换至mailscanner-mrtg 目录
mail# cp mailscanner-mrtg.conf.sample mailscanner-mrtg.conf
mail# cp mailscanner-mrtg.cfg.sample mailscanner-mrtg.cfg
mail# chmod 644 mailscanner-mrtg.conf //更改权限为可修改
mail# chmod 644 mailscanner-mrtg.cfg //更改权限为可修改
mail# ee mailscanner-mrtg.conf //修改mailscanner-mrtg.conf内容如下
#MTA = sendmail //默认值
MTA = postfix //修改后
#Where the MTA puts mail before MailScanner gets it
#Incoming Queue Dir = /var/spool/postfix.in/deferred/ # Postfix
#Incoming Queue Dir = /var/spool/exim.in/input/ # Exim
Incoming Queue Dir = /var/spool/mqueue.in/ # Sendmail //默认值
Incoming Queue Dir = /var/spool/postfix/hold # Postfix //修改后
#Where MailScanner puts your mail after it is scanned
#Outgoing Queue Dir = /var/spool/postfix/incoming/ # Postfix
#Outgoing Queue Dir = /var/spool/exim/input # Exim (?)
Outgoing Queue Dir = /var/spool/mqueue/ # Sendmail //默认值
Outgoing Queue Dir = /var/spool/postfix/incoming/ # Postfix //修改后(把#拿掉)
#Which interfaces to monitor (comma separated list)
Interfaces to Monitor = fxp0 //默认值
Interfaces to Monitor = vr0 //修改后(改成您的网卡吧)
mail# ee /usr/local/etc/apache/httpd.conf //修改apache配置以便读取mailscanner-mrtg数据
内容如下:
#MailScanner Setting
Alias /mailscanner-mrtg/ "/usr/local/www/mailscanner-mrtg/"
Step5.产生MailScanner-Mrtg流量图
mail# /usr/local/bin/mrtg /usr/local/etc/mailscanner-mrtg/mailscanner-mrtg.cfg
mail# crontab -e
*/10 * * * * /usr/local/bin/mrtg /usr/local/etc/mailscanner-mrtg/mailscanner-mrtg.cfg
5.10.安装APF防垃圾邮件
http://apf.org.cn
下载地址:
http://mail.toping.net/apf/apf.rar
下载最新的APF包解压到/usr/local/etc/postfix目录。
这时/usr/local/etc/postfix目录里边有一个apf-posftix.pl的文件
修改/usr/local/etc/postfix/master.cf
mail# ee /usr/local/etc/postfix/master.cf
加入:
apf unix - n n - - spawn
user=nobody argv=/usr/bin/perl /usr/local/etc/postfix/apf-postfix.pl
增加黑白名单:
1、IP黑名单:
ip_black_list.txt
2、IP白名单
ip_white_list.txt
3、域名黑名单
dn_black_list.txt
4、域名白名单
dn_white_list.txt
Chapter 6. 安装webmail
如何使用本人修改的iGENUS for Postfix 2.01
下载地址:
http://mail.toping.net/igenus/igenus_for_postfix_2.01.rar
webmail使用igenus,版本是iGENUS for Postfix 2.01
建议下载本人修改的iGENUS for Postfix 2.01
安装方法参考论坛:
http://www.toping.net/bbs/htm_data/7/0507/174.html
mail# cd /usr/local/www/data
mail# chown -R postfix:postfix phpMyAdmin
mail# cd /usr/local/etc
mail# cp php.ini-dist php.ini
修改/usr/local/etc/php.ini
webmail上传附件设置:
register_globals = On
max_execution_time = 30 //改为60 (增加处理脚本的时间限制)
memory_limit = 8M //改为40M (这样才能发10M的附件)
post_max_size = 8M //改为10M
upload_max_filesize = 2M //改为10M
重启apache
mail# /usr/local/etc/rc.d/apache2.sh restart
Chapter 7. 安装邮件列表(MAILMAN)安装
本节主要讲述邮件列表mailman的安装和配置
设置/etc/make.conf
mail# ee /etc/make.conf
# mail/mailman
MAIL_GID="mailman"
安装mailman
mail# portinstall -m BATCH=yes mail/mailman
配置apache(新加入)
mail# ee /usr/local/etc/apache2/httpd.conf
ScriptAlias /mailman "/usr/local/mailman/cgi-bin"
AllowOverride None
Options none
Order allow,deny
Allow from all
Alias /pipermail "/usr/local/mailman/archives/public"
AllowOverride None
Options +FollowSymlinks
Order allow,deny
Allow from all
用check_perms
mail# /usr/local/mailman/bin/check_perms -f
注:加上-f参数可以修复。
修改/usr/local/mailman/Mailman/mm_cfg.py,加上:
DEFAULT_EMAIL_HOST = 'lists.toping.net'
MTA = 'Postfix'
POSTFIX_STYLE_VIRTUAL_DOMAINS = ['lists.toping.net', 'toping.net']
添加一个邮件列表:
mail# /usr/local/mailman/bin/newlist mailman
把用户添加到邮件列表里测试,建一个文本文件,比如maillists.txt,一行一个邮件地址,然后执行如下命令:
mail# /usr/local/mailman/bin/add_members -n maillists.txt mailman
mail# ee postfix/main.cf
owner_request_special = no
recipient_delimiter = +
virtual_alias_maps = hash:/usr/local/mailman/data/virtual-mailman,
mysql:/usr/local/etc/postfix/mysql/mysql_virtual_alias_maps.cf
alias_maps = hash:/usr/local/mailman/data/aliases,hash:/usr/local/etc/postfix/aliases
mail# ee Default.py
DEFAULT_EMAIL_HOST = 'lists.toping.net'
DEFAULT_URL_HOST = 'lists.toping.net'
DEFAULT_URL_PATTERN = 'http://%s/mailman/'
DEFAULT_SERVER_LANGUAGE = 'zh_CN'
设置virtual-mailman
mail# ee /usr/local/mailman/data/virtual-mailman
lists.meilai.com anything
邮件列表的配置
Default.py和mm_cfg.py的配置是针对全局的,对全局配置文件的修改不会影响到已经存在的邮件列表。mailman提供了config_list这个命令来对单个邮件列表进行配置,先导出该邮件列表的配置:
mail# /usr/local/mailman/bin/config_list -o /tmp/config mailman
然后修改/tmp/config文件,里面有很多选项,可以根据自己的要求修改,比如加上回复到邮件列表的邮件头、去掉mailman自动加的边脚等等,最后把这个配置文件导回给邮件列表就可以了:
mail# /usr/local/mailman/bin/config_list -i /tmp/config mailman
下面你可以通过web去管理你的邮件列表:
http://lists.toping.net/mailman/admin/mailman
查看邮件列表信息:
http://lists.toping.net/mailman/listinfo/mailman/
功能太强大了,这里不一一的讲解,自己去发现吧
Chapter 7. 查看系统状态
本节主要讲述phpSysInfo工具的安装和配置
安装phpSysInfo(2.2)
mail# cd /usr/ports/www/phpSysInfo
mail# make install clean
mail# cd /usr/local/www/data-dist/phpSysInfo
mail# cp config.php.new config.php
架构基于FreeBSD和Postfix的IGENUS Webmail邮件系统
baidu
本文介绍使用FreeBSD+Postfix+Cyrus-sasl+Courier-imap+igenus+spamassassin+ Clamav+mailscanner+mailscanner-mrtg+mailman来架构一个具有多域名,有邮件列表、webmail、防病毒、防垃圾邮件、web管理界面的邮件系统。
Jacky, $Revision: 4.51 bate $Date: 2005-12-03
系统主要采用MailScanner+clamav+Spamd+APF来对病毒过滤和垃圾邮件过滤。
本文在4.10、5.3、5.4、6.0上安装测试通过,病毒过滤放弃采用amavisd。主要采用执行效率更高的MailSanner来对邮件过滤和垃圾邮件过滤,配置更容易,并且降低了系统开消。让系统更加稳定,经过严格病毒邮件测试成功率达到了100%。垃圾邮件过滤基本上达到了95%的成功率。
Table of Contents
Chapter 1. 系统安装
1.1 安装MySQL
1.2 安装Apache
1.3 安装PHP
1.4 安装zend
1.5 安装openssl
1.6 安装phpMyAdmin
1.7 通过phpMyadmin设置数据库
1.8安装Courier-imap
1.9安装 postfix 和 cyrus-sasl
1.10 安装expect
Chapter 2. 配置邮件服务器
2.1 配置rc.conf
2.2 配置postfix 和 cyrus-sasl
2.3 配置Courier-imap
Chapter 3. 手动设置第一个用户并测试
Chapter 4. 安装postfix管理工具
4.1安装本人开发的postfix管理工具
4.2 用户登录测试
Chapter 5. 防病毒与防垃圾邮件
5.1 安装Clamav
5.2 安装MailScanner
5.3 安装配置Spamassassin
5.4修改Postfix设定档main.cf
5.5修改mailscanner.conf
5.6新增MailScanner所要用到的资料夹
5.7把病毒提示信息改为中文
5.8MailScanner监管进出邮件
5.9 邮件流量监控(mailscanner-mrtg)安装与设置
5.10.安装APF防垃圾邮件
Chapter 6. 安装webmail
Chapter 7邮件列表(mailman)
Chapter 8. 查看系统状态
Chapter 1. 系统安装
安装之前:因用户数据都保存在/var目录下,因此安装FreeBSD时/var的空间应尽量大。FreeBSD的版本为5.3,按最小化安装,软件包只安装cvsup,安装结束后用cvsup更新ports树。在文档中假设服务器的ip地址为192.168.0.2,域名为 toping.net,主机名为mail.toping.net。
请兄弟们仔细一些,注意空格和TAB。
祝兄弟们好运。本人水平有限。如果发现文章中有什么错误和不当的地方请发邮件:scyz@toping.net。我会在第一时间给予答复。
1.1 安装MySQL
mail# cd /usr/ports/databases/mysql40-server
mail# make install clean
编辑/etc/rc.conf,加入
mysql_enable="YES"
1.2 安装Apache
mail# cd /usr/ports/www/apache2
mail# make install clean
编辑/etc/rc.conf,加入
Apache2_enable="YES"
1.3 安装PHP
mail# cd /usr/ports/www/mod_php4
mail# make install clean
我的选择:(注意别选DEBUG,否则会和ZEND有冲突)
[X] APACHE2 Use apache 2.x instead of apache 1.3.x
安装需要的PHP扩展模块
mail# cd /usr/ports/lang/php4-extensions
mail# make install clean
我选择了下面的模块:
[X] BCMATH bc style precision math functions
[X] BZ2 bzip2 library support
[X] CALENDAR calendar conversion support
[X] CRACK crack support
[X] CTYPE ctype functions
[X] CURL CURL support
[X] FTP FTP support
[X] GD GD library support
[X] GETTEXT gettext library support
[X] FILEINFO fileinfo support
[X] IMAP IMAP support
[X] MBSTRING multibyte string support
[X] MCAL Modular Calendar Access Library support
[X] MCRYPT Encryption support
[X] MCVE MCVE support
[X] MHASH Crypto-hashing support
[X] MYSQL MySQL database support
[X] PCRE Perl Compatible Regular Expression support
[X] POSIX POSIX-like functions
[X] SESSION session support
[X] TOKENIZER tokenizer support
[X] XML XML support
[X] ZLIB ZLIB support
最后在编辑/usr/local/etc/apache2/httpd.conf最后加入:
DirectoryIndex index.html index.html.var index.php
#注:在DirectoryIndex这里加入index.php,是为了让apache支持首页为index.php的首页文件
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
Group www、User www修改为: Group postfix、User postfix
注:以上这一步要在postfix安装后再操作
1.4.安装zend
mail# cd /usr/ports/devel/ZendOptimizer
mail# make install clean
因为版权的问题,他不会自动下载。这里你需要到他的官方网站去下载ZendOptimizer-2.5.10a-freebsd4.3-i386.tar.gz到/usr/ports/distfiles目录下面后再安装。
下载地址:
http://downloads.zend.com/optimizer/2.5.10/ZendOptimizer-2.5.10a-freebsd4.3-i386.tar.gz
完成后在/usr/local/etc/php.ini中加入:
[Zend]
zend_optimizer.optimization_level=15
zend_extension_manager.optimizer="/usr/local/lib/php/20020429/Optimizer"
zend_extension_manager.optimizer_ts="/usr/local/lib/php/20020429/Optimizer_TS"
zend_extension="/usr/local/lib/php/20020429/ZendExtensionManager.so"
zend_extension_ts="/usr/local/lib/php/20020429/ZendExtensionManager_TS.so"
重启apache安装完成。
1.5 安装openssl
mail# cd /usr/ports/security/openssl
mail# make install clean
1.6 安装phpMyAdmin
mail# cd /usr/ports/databases/phpmyadmin
mail# make fetch
注:(在这里建议直接下载后复制安装)
mail# cd /usr/ports/distfiles
mail# tar –zxvf PhpMyadmin-x.tar.gz
mail# mv /usr/local/www/phpMyAdmin-x /usr/local/www/data/dbadmin
修改/usr/local/www/data/dbadmin/config.inc.php
$cfg['PmaAbsoluteUri'] = 'http://192.168.0.2/dbadmin/';
$cfg['Servers'][$i]['auth_type'] = 'http'; // Authentication method (config, http or cookie based)?
注:指定phpmyadmin的认证方式为http方式。
在浏览器输入http://192.168. 0.2/dbadmin/,首次进行登入的用户名为root密码为空,登入后可以修改你的密码。
1.7 通过phpMyadmin设置数据库
建立postfix数据库(注意:数据库名称为postfix):
mail# mysql –u root –p
mysql# CREATE DATABASE `postfix` ;
mysql# use postfix;
下面为sql语句:
CREATE TABLE domaininfo (
domain_id int(5) NOT NULL auto_increment,
domain varchar(25) NOT NULL default '',
alias varchar(30) default NULL,
passwd varchar(35) NOT NULL default '',
usernum int(5) NOT NULL default '0',
quota int(11) NOT NULL default '0',
des varchar(30) default NULL,
expire date NOT NULL default '0000-00-00',
active tinyint(1) NOT NULL default '1',
create_time datetime default NULL,
PRIMARY KEY (domain_id),
UNIQUE KEY domain (domain),
KEY domain_id (domain_id)
) TYPE=MyISAM COMMENT='domain information';
INSERT INTO domaininfo VALUES (1,'admin',NULL,'$1$.j3.t12.$I7MGf7ZD2HrWwUWQF88Mg1',0,0,'Super Admin','0000-00-00',1,'0000-00-00 00:00:00');
CREATE TABLE userinfo (
id int(11) NOT NULL auto_increment,
userid varchar(20) NOT NULL default '',
domain_id int(5) NOT NULL default '0',
address varchar(50) NOT NULL default '',
alias varchar(60) default NULL,
passwd varchar(35) NOT NULL default '',
realname varchar(20) default NULL,
quota int(11) NOT NULL default '0',
active tinyint(1) NOT NULL default '0',
homedir varchar(60) NOT NULL default '',
maildir varchar(60) NOT NULL default '',
create_time datetime NOT NULL default '0000-00-00 00:00:00',
`fax` varchar(20) NOT NULL default '',
`telephone` varchar(15) NOT NULL default '',
`sex` int(1) NOT NULL default '0',
`year` int(4) NOT NULL default '0',
`MONTH` int(2) NOT NULL default '0',
`DAY` int(2) NOT NULL default '0',
`education` varchar(4) NOT NULL default '',
`marital` int(1) NOT NULL default '0',
`occupation` varchar(15) NOT NULL default '',
`companyname` varchar(30) NOT NULL default '',
`province` varchar(6) NOT NULL default '',
PRIMARY KEY (id),
UNIQUE KEY address (address)
) TYPE=InnoDB COMMENT='User Information';
注:对于初学者,建议以上操作都在phpmyadmin中操作更加的简便,如果后面要使用igenus请导入原来igenus的sql。
建立数据库用户并授以相应的权限
mail# mysql –u root –p
mysql# use mysql;
mysql# INSERT INTO user (host,user,password) VALUES('localhost','postfix','');
mysql# update user set password=password('postfix') where User='postfix';
mysql# GRANT ALL ON postfix.* TO postfix@localhost IDENTIFIED BY "postfix";
注:这里加用户名和密码都为:postfix。并授权对postfix数据库进行操作
1.8 安装Courier-imap
mail# cd /usr/ports/mail/courier-imap
mail# make install clean
我的选择:
[X] OPENSSL Build with OpenSSL support
[X] AUTH_MYSQL MySQL support
在/etc/rc.conf中加入:
courier_authdaemond_enable="YES"
courier_imap_pop3d_enable="YES"
courier_imap_imapd_enable="YES"
mail# cd /usr/local/etc/courier-imap
mail# cp imapd.cnf.dist imapd.cnf
mail# cp pop3d.cnf.dist pop3d.cnf
mail# /usr/local/etc/rc.d/courier-authdaemond.sh start
注:此时会在/var/run/authdaemond/下产生socket,如果没有下面这一步下面的认证无法通过。
mail# chmod +x /var/run/authdaemond
1.9 安装 postfix 和 cyrus-sasl
mail# cd /usr/ports/security/cyrus-sasl2
mail# make install WITH_AUTHDAEMON=yes
mail# make clean
创建/usr/local/lib/sasl2/smtpd.conf
pwcheck_method: authdaemond
log_level: 3
mech_list: PLAIN LOGIN
authdaemond_path:/var/run/authdaemond/socket
更详细的参数设置请看:
http://www.toping.net/bbs/htm_data/7/0508/330.html
至此,认证部分基本完成。
安装postfix
mail# cd /usr/ports/mail/postfix
mail# make install clean
我的选择:
[X] VDA VDA (Virtual Delivery Agent)
[X] MySQL MySQL map lookups (choose version with WITH_MYSQL_VER)
[X] TLS SSL and TLS
[X] SASL2 Cyrus SASLv2 (Simple Authentication and Security Layer)
回答下面的两问题:
You need user "postfix" added to group "mail".[是否将postfix用户加到mail用户组]
Would you like me to add it [y]? y
Would you like to activate Postfix in /etc/mail/mailer.conf [n]? n
在/etc/rc.conf中加入postfix启动所需的启动选项
在/etc/rc.conf中加入:
sendmail_enable="YES"
sendmail_flags="-bd"
sendmail_pidfile="/var/spool/postfix/pid/master.pid"
sendmail_procname="/usr/local/libexec/postfix/master"
sendmail_outbound_enable="NO"
sendmail_submit_enable="NO"
sendmail_msp_queue_enable="NO"
设置postfix启动所需
mail# ln -s /usr/local/sbin/sendmail /usr/sbin/sendmail
注:如果/usr/sbin/sendmail存在就删了再做上链接,如果升级内核和升级系统后要重新做这一步。
mail# echo ‘postfix: root’ >> /etc/aliases
mail# /usr/local/bin/newaliases
mail# chown postfix:postfix /etc/opiekeys
1.10 安装expect
用于Web客户端建立邮件用户
mail# cd /usr/ports/lang/expect
mail# make install clean
Chapter 2. 配置邮件服务器
本节主要讲述各种服务的参数配置。
2.1 配置rc.conf,编辑/etc/rc.conf
下面是前面所装软件都加入了启动选项的rc.conf配置:
mysql_enable="YES"
apache2_enable="YES"
courier_authdaemond_enable="YES"
courier_imap_pop3d_enable="YES"
courier_imap_imapd_enable="YES"
sendmail_enable="YES"
sendmail_flags="-bd"
sendmail_pidfile="/var/spool/postfix/pid/master.pid"
sendmail_procname="/usr/local/libexec/postfix/master"
sendmail_outbound_enable="NO"
sendmail_submit_enable="NO"
sendmail_msp_queue_enable="NO"
2.2 配置postfix 和 cyrus-sasl
(1)修改/usr/local/etc/postfix/main.cf,在文件最后加入以下内容
mail# ee /usr/local/etc/postfix/main.cf
smtpd_helo_required = yes
strict_rfc821_envelopes = yes
smtpd_etrn_restrictions = permit_mynetworks, reject
#=====================BASE=====================
myhostname = mail.toping.net
mydomain = toping.net
mydestination = $myhostname
local_recipient_maps =
command_directory = /usr/local/sbin
local_transport = virtual
#=====================MySQL=====================
virtual_alias_maps = mysql:/usr/local/etc/postfix/mysql_virtual_alias_maps.cf
virtual_gid_maps = static:125
virtual_mailbox_base = /
virtual_mailbox_domains = mysql:/usr/local/etc/postfix/mysql_virtual_domains_maps.cf
virtual_mailbox_limit = 51200000
virtual_mailbox_maps = mysql:/usr/local/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_minimum_uid = 125
virtual_transport = virtual
virtual_uid_maps = static:125
#=====================Quota=====================
virtual_create_maildirsize = yes
virtual_mailbox_extended = yes
virtual_mailbox_limit_maps = mysql:/usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
virtual_mailbox_limit_override = yes
virtual_maildir_limit_message = Sorry, the user's maildir has overdrawn his diskspace quota, please try again later.
virtual_overquota_bounce = yes
#====================SASL=====================
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
smtpd_delay_reject=yes
smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,permit_auth_destination,reject
smtpd_client_restrictions = permit_sasl_authenticated
更详细的参数设置请看论坛:
http://www.toping.net/bbs/htm_data/7/0601/871.html
(4)编辑/usr/local/etc/postfix/mysql_virtual_alias_maps.cf
mail# ee /usr/local/etc/postfix/mysql_virtual_alias_maps.cf
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT alias FROM userinfo WHERE address='%s' AND active = 1
(5)编辑/usr/local/etc/postfix/mysql_virtual_domains_maps.cf
mail# ee /usr/local/etc/postfix/mysql_virtual_domains_maps.cf
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT domain FROM domaininfo WHERE domain='%s'
(6)编辑/usr/local/etc/postfix/mysql_virtual_mailbox_maps.cf
mail# ee /usr/local/etc/postfix/mysql_virtual_mailbox_maps.cf
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT maildir FROM userinfo WHERE address='%s' AND active = 1
(7)编辑/usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
mail# ee /usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
user = postfix
password = postfix
hosts = localhost
dbname = postfix
query = SELECT quota FROM userinfo WHERE address='%s'
2.3 配置Courier-imap
(1)修改Courier相关设置,/usr/local/etc/courier-imap/imapd:
IMAP_CAPABILITY="IMAP4rev1 CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA"
(2)修改/usr/local/etc/courier-imap/pop3d
POP3AUTH="LOGIN CRAM-MD5 CRAM-SHA1"
(3)编辑修改/usr/local/etc/authlib/authmysqlrc
mail# mv /usr/local/etc/authlib/authmysqlrc /usr/local/etc/authlib/authmysqlrc_bak
mail# ee /usr/local/etc/authlib/authmysqlrc
MYSQL_SERVER localhost //数据库主机地址
MYSQL_USERNAME postfix //数据库用户名
MYSQL_PASSWORD postfix //数据库密码
MYSQL_PORT 0
MYSQL_OPT 0
MYSQL_DATABASE postfix //数据库名称
MYSQL_USER_TABLE userinfo
MYSQL_CRYPT_PWFIELD passwd
MYSQL_UID_FIELD '125'
MYSQL_GID_FIELD '125'
MYSQL_LOGIN_FIELD address
MYSQL_HOME_FIELD homedir
MYSQL_NAME_FIELD realname
MYSQL_MAILDIR_FIELD maildir
MYSQL_QUOTA_FIELD quota
注:这里得用tab键来跳格
(4)编辑/usr/local/etc/authlib/authdaemonrc
mail# mv /usr/local/etc/authlib/authdaemonrc /usr/local/etc/authlib/authdaemonrc_bak
mail# ee /usr/local/etc/authlib/authdaemonrc
authmodulelist="authmysql"
authmodulelistorig="authmysql"
version="authdaemond.mysql"
daemons=5
authdaemonvar=/var/run/authdaemond
subsystem=mail
DEBUG_LOGIN=0
DEFAULTOPTIONS="wbnodsn=1"
重启服务器
Chapter 3.手动设置第一个用户并测试
本章介绍如何开通用户,并且测试系统是否正常。
注:增加用户时请到这里生成加密后的密码后直接插入到数据库中就可以了。
http://www.toping.net/soft
mail# mysql
mysql> use postfix;
mysql> show tables;
+-------------------+
| Tables_in_postfix |
+-------------------+
| address |
| admin |
| card |
| domaininfo |
| lastauth |
| logs |
| message |
| personal |
| scheduler |
| stow |
| userinfo |
| vpopmail |
+-------------------+
12 rows in set (0.00 sec)
增加域名和管理员
mysql> desc domaininfo;
+-------------+-------------+------+-----+------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+------------+----------------+
| domain_id | int(5) | | PRI | NULL | auto_increment |
| domain | varchar(25) | | UNI | | |
| alias | varchar(30) | YES | | NULL | |
| passwd | varchar(35) | | | | |
| usernum | int(5) | | | 0 | |
| quota | int(11) | | | 0 | |
| des | varchar(30) | YES | | NULL | |
| expire | date | | | 0000-00-00 | |
| active | tinyint(1) | | | 1 | |
| create_time | datetime | YES | | NULL | |
+-------------+-------------+------+-----+------------+----------------+
10 rows in set (0.00 sec)
mysql> INSERT INTO `domaininfo` VALUES (7, 'toping.net', NULL, '$1$jNXThQXq$KPjm.WE2f2yX5rceY48vX. ', 50, 500, NULL, '0000-00-00', 1, '2005-04-19 23:19:11');
Query OK, 1 row affected (0.00 sec)
注:这里的toping.net的管理密码为:admin123
mysql> desc userinfo;
+-------------+-------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------------------+----------------+
| id | int(11) | | PRI | NULL | auto_increment |
| userid | varchar(20) | | | | |
| domain_id | int(5) | | | 0 | |
| address | varchar(50) | | UNI | | |
| alias | varchar(60) | YES | | NULL | |
| passwd | varchar(35) | | | | |
| realname | varchar(20) | YES | | NULL | |
| quota | int(11) | | | 0 | |
| active | tinyint(1) | | | 0 | |
| homedir | varchar(60) | | | | |
| maildir | varchar(60) | | | | |
| create_time | datetime | | | 0000-00-00 00:00:00 | |
| fax | varchar(20) | | | | |
| telephone | varchar(15) | | | | |
| sex | int(1) | | | 0 | |
| year | int(4) | | | 0 | |
| MONTH | int(2) | | | 0 | |
| DAY | int(2) | | | 0 | |
| education | varchar(4) | | | | |
| marital | int(1) | | | 0 | |
| occupation | varchar(15) | | | | |
| companyname | varchar(30) | | | | |
| province | varchar(6) | | | | |
+-------------+-------------+------+-----+---------------------+----------------+
23 rows in set (0.00 sec)
mysql> INSERT INTO `userinfo` VALUES (8, 'webmaster', 7, 'webmaster@toping.net', NULL,'$1$4DLQeNkz$QKCAQqg244XwvLl2SD11f0', 'webmaster', 209715200, 1, '/var/mail/toping.net/webmaster/', '/var/mail/toping.net/webmaster/Maildir/', '2005-04-20 23:45:17', '', '', 0, 0, 0, 0, '', 0, '', '', '');
注:这里的webmaster用户的邮箱密码为:000000
mysql>quit
设置用户的目录与权限:
Mail# mkdir -p /var/mail/toping.net/webmaster
Mail# /usr/local/bin/maildirmake /var/mail/toping.net/webmaster/Maildir
Mail# chmod -R 777 /var/mail/toping.net/
Mail# chown -R postfix:postfix /var/mail/toping.net
至此用户设置完毕,这里只使用一个域名,同理可以设置多个域名。
Chapter 4.安装postfix管理工具
本节主要介绍如何安装和使用本人开发的postfix管理工具。
4.1安装本人开发的postfix管理工具来设置第一个域名和用户
上传管理工具到网站目录
后修改include/config.inc.php中的
define(DOMAINSDIR,"/home/vmail"); 为 define(DOMAINSDIR,"/var/mail");
define(MYSQL_HOST, 'localhost'); 为 您的MySQL服务器的主机名
define(MYSQL_USER, 'root'); 为 您的MySQL的用户名
define(MYSQL_PASS, 'mypasswd'); 为 您的MySQL的密码
define(MYSQL_DATA, 'postfix'); 为 您的邮件服务器的数据库
修改完成后运行:http://mail.toping.net/webadmin/index.php
完装完成!!
4.2 用户登录测试
用户登录测试
安装p5-MIME-Base64
mail# cd /usr/ports/converters/p5-MIME-Base64/
mail# make install clean
通过p5-MIME-Base64来取得用户名和密码的base64编码
mail# perl -MMIME::Base64 -e 'print encode_base64("webmaster\@toping.net");'
d2VibWFzdGVyQHRvcGluZy5uZXQ=
mail# perl -MMIME::Base64 -e 'print encode_base64("000000");'
MDAwMDAw
测试发送邮件(端口:25):
mail# telnet 127.0.0.1 25
Trying 127.0.0.1...
Connected to 0.
Escape character is '^]'.
220 mail.toping.net ESMTP Postfix
ehlo mail
250-mail.toping.net
250-PIPELINING
250-SIZE 4194304
250-VRFY
250-ETRN
250-AUTH NTLM LOGIN PLAIN OTP
250-AUTH=NTLM LOGIN PLAIN OTP
250 8BITMIME
auth login
334 VXNlcm5hbWU6
d2VibWFzdGVyQHRvcGluZy5uZXQ= //此为用户名id:webmaster@toping.net
334 UGFzc3dvcmQ6
MDAwMDAw //此为用户密码password:000000
235 Authentication successful
MAIL FROM: //告诉服务器发件人的Email地址
250 Ok
RCPT TO: //告诉服务器收件人的地址
250 OK
DATA //告诉服务器开始写信
354 End data with .
SUBJECT:test //subject后面填写的是邮件的主题
test
. //换行后输入.后按回车,表示信件内容书写完毕
250 Ok: queued as 58DC71D5
quit //发送信件,结束对话,退出SMTP服务器
221 Bye
Connection closed by foreign host
测试收取邮件(端口:110):
mail# telnet 127.0.0.1 110
Trying 127.0.0.1...
Connected to 0
Escape character is '^]'
+OK Hello there
user webmaster@toping.net
+OK Password required
pass 000000
+OK logged in
list
+OK POP3 clients that break here, they violate STD53
1 2217
retr 1 //返回第一封信的全部内容
+OK 2217 octets follow.
Return-Path:
X-Original-To: webmaster@toping.net
Delivered-To: webmaster@toping.net
Received: from mail (localhost.toping.net [127.0.0.1])
by mail.toping.net (Postfix) with ESMTP id 58DC71D5
for ; Mon, 9 Aug 2004 21:11:20 +0800 (CST)
SUBJECT:test
Message-Id: <20040809131120.58DC71D5@mail.toping.net>
Date: Mon, 9 Aug 2004 21:11:20 +0800 (CST)
From: webmaster@toping.net
To: undisclosed-recipients:;
test
.
dele 1 //删除
+OK Deleted
quit
+OK Bye-bye
Connection closed by foreign host
也可以使用任何其它的邮件客户端程序来测试,如foxmail、Outlook Express等等。
Chapter 5. 防病毒与防垃圾邮件
本章介绍病毒与垃圾邮件的防范。
5.1 安装Clamav
mail# cd /usr/ports/security/clamav
mail# make install clean
我的选择:
[X] MILTER Compile the milter interface
[X] CURL Support URL downloading
[X] LIBUNRAR Support for external Unrar library
要想clamav能自动的启动请在/etc/rc.conf中加入:
clamav_clamd_enable="YES"
clamav_freshclam_enable="YES"
重启服务器
测试杀毒
mail# clamscan -r -i /usr/local/www/data
----------- SCAN SUMMARY -----------
Known viruses: 41293
Engine version: 0.87.1
Scanned directories: 53
Scanned files: 602
Infected files: 0
Data scanned: 41.51 MB
Time: 18.294 sec (0 m 18 s)
升级病毒库
mail# freshclam
ClamAV update process started at Sun Dec 4 01:10:02 2005
main.cvd is up to date (version: 34, sigs: 39625, f-level: 5, builder: tkojm)
daily.cvd is up to date (version: 1200, sigs: 1669, f-level: 6, builder: tomek)
5.2 安装MailScanner
mail# cd /usr/ports/mail/mailscanner
mail# make install
第一次执行安装因此需执行make initial-config以建立基本配置文件
mail# make initial-config
mail# make clean
5.3 安装SpamAssassin
mail# cd /usr/ports/mail/p5-Mail-SpamAssassin
mail# make install clean
我的选择:
[X] AS_ROOT Run spamd as root (recommended)
[X] DOMAINKEYS DomainKeys support
[X] SSL Build with SSL support for spamd/spamc
[X] MYSQL Add MySQL support
[X] RAZOR Add Vipul's Razor support
[X] SPF_QUERY Add SPF query support
[X] RELAY_COUNTRY Relay country support
[X] TOOLS Install SpamAssassin tools
5.4修改Postfix设定档main.cf
mail# ee /usr/local/etc/postfix/main.cf
#header_checks = regexp:/usr/local/etc/postfix/header_checks //默认值
header_checks = regexp:/usr/local/etc/postfix/header_checks //把注释去掉
编辑/usr/local/etc/postfix/header_checks
mail# ee /usr/local/etc/postfix/header_checks
/^Received:/ HOLD //新加入
5.5修改mailscanner.conf
mail# ee /usr/local/etc/MailScanner/MailScanner.conf
#Run As User = //默认值
Run As User = postfix //修改后
#Run As Group = //默认值
Run As Group = postfix //修改后
#Incoming Queue Dir = /var/spool/mqueue.in //默认值
Incoming Queue Dir = /var/spool/postfix/hold //修改后
#Outgoing Queue Dir = /var/spool/mqueue //默认值
Outgoing Queue Dir = /var/spool/postfix/incoming //修改后
#MTA = sendmail //默认值
MTA = postfix //修改后
#Virus Scanners = none //默认值
Virus Scanners = clamav //修改后
#Use SpamAssassin = no //默认值
Use SpamAssassin = yes //修改后
5.6新增MailScanner所要用到的资料夹
mail# mkdir /var/spool/MailScanner
mail# mkdir /var/spool/MailScanner/incoming
mail# mkdir /var/spool/MailScanner/quarantine
mail# chown postfix:postfix /var/spool/MailScanner/incoming
mail# chown postfix:postfix /var/spool/MailScanner/quarantine
mail# touch /usr/local/etc/MailScanner/rules/bounce.rules //新建一个空白文件,要不然会出错。
mail# chmod –R 777 /var/spool/postfix
mail#cp /usr/local/etc/MailScanner/mcp/10_example.cf.sample /usr/local/etc/MailScanner/mcp/10_example.cf
mail#cp /usr/local/etc/MailScanner/mcp/mcp.spam.assassin.prefs.conf.sample
/usr/local/etc/MailScanner/mcp/mcp.spam.assassin.prefs.conf
注:这里的倒数一、二行实际操作中为一行
重新启动服务器
测试病毒过滤:
mail# telnet localhost 25
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mail.toping.net ESMTP Postfix
mail from:webmaster@toping.net
250 Ok
rcpt to:webmaster@toping.net
250 Ok
data
354 End data with .
Subject:Virus test
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
.
250 Ok: queued as F0C221CC20 //出现F0C221CC20这行表示mailscanner运行成功了
quit
221 Bye
Connection closed by foreign host.
5.7把病毒提示信息改为中文
预设系统提示信息为英文,可以下载我修改的中文包。
http://mail.toping.net/mailscanner/cn.rar
注:把下载下来的文件解压后放到/usr/local/share/MailScanner/reports/cn下面去
mail# ee /usr/local/etc/MailScanner/MailScanner.conf
#%report-dir% = /usr/local/share/MailScanner/reports/en //默认值(加载英文)
%report-dir% = /usr/local/share/MailScanner/reports/cn //修改后为读取中文
5.8.用MailScanner来监管进出邮件
mail# ee MailScanner.conf
Archive Mail = %rules-dir%/archive.rules
mail# ee archive.rules
方法一:互相备份, 以上这样并不会造成 loop
FromOrTo: a@toping.net yes forward b@toping.net
FromOrTo: b@toping.net yes forward a@toping.net
方法二:可以 forward 到复数信箱
FromOrTo: a@toping.net yes forward b@toping.net c@toping.net d@toping.net
方法三:同时备份到一个或多个档案及一个或多个信箱
FromOrTo: a@toping.net yes forward /var/spool/MailScanner/archive/a_user_backup.mbx /var/spool/MailScanner/archive/a_user_backup.mbx b@toping.net scyz2@163.com
注:以上为一行,该档案要先建立且确定该档案拥有者与 MailScanner.conf 的 Run As User = XXXXXXX 相同
方法四:备份到数据夹及多个信箱或档案
FromOrTo: a@toping.net yes forward /var/spool/MailScanner/archive/ b@toping.net scyz2@163.com /var/spool/MailScanner/archive/a_user_backup.mbx
注:以上为一行,权限同SAMPLE3;注意事项, 他会依日期再分数据夹, 日期数据夹内的文件名称 mail queue ID, 格式为 postfix mail queue 格式
更详细的mailscanner.cf的参数请看论坛:
http://www.toping.net/bbs/htm_data/7/0509/533.html
5.9.安装MailScanner-mrtg
mail# cd /usr/ports/mail/ mailscanner-mrtg
mail# make install clean
mailscanner-mrtg相关设置
mail# cd /usr/local/etc/mailscanner-mrtg //切换至mailscanner-mrtg 目录
mail# cp mailscanner-mrtg.conf.sample mailscanner-mrtg.conf
mail# cp mailscanner-mrtg.cfg.sample mailscanner-mrtg.cfg
mail# chmod 644 mailscanner-mrtg.conf //更改权限为可修改
mail# chmod 644 mailscanner-mrtg.cfg //更改权限为可修改
mail# ee mailscanner-mrtg.conf //修改mailscanner-mrtg.conf内容如下
#MTA = sendmail //默认值
MTA = postfix //修改后
#Where the MTA puts mail before MailScanner gets it
#Incoming Queue Dir = /var/spool/postfix.in/deferred/ # Postfix
#Incoming Queue Dir = /var/spool/exim.in/input/ # Exim
Incoming Queue Dir = /var/spool/mqueue.in/ # Sendmail //默认值
Incoming Queue Dir = /var/spool/postfix/hold # Postfix //修改后
#Where MailScanner puts your mail after it is scanned
#Outgoing Queue Dir = /var/spool/postfix/incoming/ # Postfix
#Outgoing Queue Dir = /var/spool/exim/input # Exim (?)
Outgoing Queue Dir = /var/spool/mqueue/ # Sendmail //默认值
Outgoing Queue Dir = /var/spool/postfix/incoming/ # Postfix //修改后(把#拿掉)
#Which interfaces to monitor (comma separated list)
Interfaces to Monitor = fxp0 //默认值
Interfaces to Monitor = vr0 //修改后(改成您的网卡吧)
mail# ee /usr/local/etc/apache/httpd.conf //修改apache配置以便读取mailscanner-mrtg数据
内容如下:
#MailScanner Setting
Alias /mailscanner-mrtg/ "/usr/local/www/mailscanner-mrtg/"
Step5.产生MailScanner-Mrtg流量图
mail# /usr/local/bin/mrtg /usr/local/etc/mailscanner-mrtg/mailscanner-mrtg.cfg
mail# crontab -e
*/10 * * * * /usr/local/bin/mrtg /usr/local/etc/mailscanner-mrtg/mailscanner-mrtg.cfg
5.10.安装APF防垃圾邮件
http://apf.org.cn
下载地址:
http://mail.toping.net/apf/apf.rar
下载最新的APF包解压到/usr/local/etc/postfix目录。
这时/usr/local/etc/postfix目录里边有一个apf-posftix.pl的文件
修改/usr/local/etc/postfix/master.cf
mail# ee /usr/local/etc/postfix/master.cf
加入:
apf unix - n n - - spawn
user=nobody argv=/usr/bin/perl /usr/local/etc/postfix/apf-postfix.pl
增加黑白名单:
1、IP黑名单:
ip_black_list.txt
2、IP白名单
ip_white_list.txt
3、域名黑名单
dn_black_list.txt
4、域名白名单
dn_white_list.txt
Chapter 6. 安装webmail
如何使用本人修改的iGENUS for Postfix 2.01
下载地址:
http://mail.toping.net/igenus/igenus_for_postfix_2.01.rar
webmail使用igenus,版本是iGENUS for Postfix 2.01
建议下载本人修改的iGENUS for Postfix 2.01
安装方法参考论坛:
http://www.toping.net/bbs/htm_data/7/0507/174.html
mail# cd /usr/local/www/data
mail# chown -R postfix:postfix phpMyAdmin
mail# cd /usr/local/etc
mail# cp php.ini-dist php.ini
修改/usr/local/etc/php.ini
webmail上传附件设置:
register_globals = On
max_execution_time = 30 //改为60 (增加处理脚本的时间限制)
memory_limit = 8M //改为40M (这样才能发10M的附件)
post_max_size = 8M //改为10M
upload_max_filesize = 2M //改为10M
重启apache
mail# /usr/local/etc/rc.d/apache2.sh restart
Chapter 7. 安装邮件列表(MAILMAN)安装
本节主要讲述邮件列表mailman的安装和配置
设置/etc/make.conf
mail# ee /etc/make.conf
# mail/mailman
MAIL_GID="mailman"
安装mailman
mail# portinstall -m BATCH=yes mail/mailman
配置apache(新加入)
mail# ee /usr/local/etc/apache2/httpd.conf
ScriptAlias /mailman "/usr/local/mailman/cgi-bin"
AllowOverride None
Options none
Order allow,deny
Allow from all
Alias /pipermail "/usr/local/mailman/archives/public"
AllowOverride None
Options +FollowSymlinks
Order allow,deny
Allow from all
用check_perms
mail# /usr/local/mailman/bin/check_perms -f
注:加上-f参数可以修复。
修改/usr/local/mailman/Mailman/mm_cfg.py,加上:
DEFAULT_EMAIL_HOST = 'lists.toping.net'
MTA = 'Postfix'
POSTFIX_STYLE_VIRTUAL_DOMAINS = ['lists.toping.net', 'toping.net']
添加一个邮件列表:
mail# /usr/local/mailman/bin/newlist mailman
把用户添加到邮件列表里测试,建一个文本文件,比如maillists.txt,一行一个邮件地址,然后执行如下命令:
mail# /usr/local/mailman/bin/add_members -n maillists.txt mailman
mail# ee postfix/main.cf
owner_request_special = no
recipient_delimiter = +
virtual_alias_maps = hash:/usr/local/mailman/data/virtual-mailman,
mysql:/usr/local/etc/postfix/mysql/mysql_virtual_alias_maps.cf
alias_maps = hash:/usr/local/mailman/data/aliases,hash:/usr/local/etc/postfix/aliases
mail# ee Default.py
DEFAULT_EMAIL_HOST = 'lists.toping.net'
DEFAULT_URL_HOST = 'lists.toping.net'
DEFAULT_URL_PATTERN = 'http://%s/mailman/'
DEFAULT_SERVER_LANGUAGE = 'zh_CN'
设置virtual-mailman
mail# ee /usr/local/mailman/data/virtual-mailman
lists.meilai.com anything
邮件列表的配置
Default.py和mm_cfg.py的配置是针对全局的,对全局配置文件的修改不会影响到已经存在的邮件列表。mailman提供了config_list这个命令来对单个邮件列表进行配置,先导出该邮件列表的配置:
mail# /usr/local/mailman/bin/config_list -o /tmp/config mailman
然后修改/tmp/config文件,里面有很多选项,可以根据自己的要求修改,比如加上回复到邮件列表的邮件头、去掉mailman自动加的边脚等等,最后把这个配置文件导回给邮件列表就可以了:
mail# /usr/local/mailman/bin/config_list -i /tmp/config mailman
下面你可以通过web去管理你的邮件列表:
http://lists.toping.net/mailman/admin/mailman
查看邮件列表信息:
http://lists.toping.net/mailman/listinfo/mailman/
功能太强大了,这里不一一的讲解,自己去发现吧
Chapter 7. 查看系统状态
本节主要讲述phpSysInfo工具的安装和配置
安装phpSysInfo(2.2)
mail# cd /usr/ports/www/phpSysInfo
mail# make install clean
mail# cd /usr/local/www/data-dist/phpSysInfo
mail# cp config.php.new config.php
$ su - root
Password:
# sysinstall
选择Configure>Distributions,选择man项,点击OK。
Password:
# sysinstall
选择Configure>Distributions,选择man项,点击OK。