最近拉了个壮丁帮我写点 smthbbs 的东西,希望写出来以后能让更多新手入门
另外下面这个破东西看起来没什么必要更新了,就发这里吧...
有错误请指正...
$Id: xxx2smth.txt,v 1.8 2004/12/06 20:09:59 atppp Exp $
CVS View:
http://bbs.stanford.edu/cgi-bin/viewcvs.cgi/smthdoc/标题:[xxx2smth]一般论坛数据转换到 smthbbs 系统
作者:atppp @ bbs.stanford.edu
发布于:
http://atppp.mysmth.net/0. 序
0.0 声明
尽管 smthbbs 系统基础数据结构一般很少变化,但是本文档只针对 smthbbs CVS 主分支
目前的代码,不保证对其它时间或其它分支代码的正确性。另外,除修正错误外本文可能
不会再有更新。
虽然作者尽力保证文档的正确性,但是作者对读者在本文帮助下写出的程序所带来的任何
损失不负责任。换而言之,作者请读者相信作者的人品,但是作者不向读者担保作者的人
品,哈哈~~
本文可以在网络上自由转载,但请保留全文的完整性。
0.1 为什么要转换成 smthbbs 系统?
你有兴趣看我这个文章就说明你觉得 smthbbs 系统还是有可取之处的。所以我就不具体
说了...
0.2 转换之前都需要做些什么工作?
你必须相当的熟悉你要转换的那个老系统的结构,特别是版面文章数据和用户数据(如果
你打算转换用户帐号的话)的结构。
然后你要装一个 smthbbs 系统稳定运行起来。代码方面,你需要熟悉 src/site.h
src/site.c src/default.h src/default.c 这 4 个文件是怎么配合工作的,一些重要的
参数比如最多允许的版面数量你必须知道如何修改。站务方面,你必须相当熟悉一般的管
理操作,可以参考 doc/README.SYSOP 文档。
0.3 共享内存
共享内存在 smthbbs 系统中主要用于进程间通信。比方,你在 web 注册了之后,马上就
能在 telnet 下登录了,这是因为 web 注册的那个程序修改了相应的共享内存数据,当
你在 telnet 试图登录的时候,telnet 程序就能在共享内存中发现你的信息。当然,这
些工作完全可以用文件系统来做,但是用共享内存来做进程间通信效率就会高很多。BBS
的很多重要数据都在共享内存里面,比如转换数据会碰到两个重要的系统文件:
$BBSHOME/.PASSWDS 这个是用户的帐号信息,包括密码。
$BBSHOME/.BOARDS 所有版面的信息。
当系统正常启动之后,这两个文件的信息在共享内存里面,系统会定时写磁盘同步数据。
当系统正常运行的时候,直接打开这两个文件修改是不对的!转换数据或者修改数据之前
,一般必须要停掉 BBS 服务,清除掉共享内存数据。如果不会请看 ipcs 和 ipcrm 的
man page。
0.4 静态转换和动态转换
静态转换的意思是,把所有的重要文件都写好了,然后一次性启动系统。动态转换的意思
是,在系统正常运行的情况下,尽可能利用 BBS 已有的函数一点一点的转换数据。动态
转换需要你对 BBS 共享内存数据结构和 BBS 函数有相当程度的了解,下面基本不使用动
态转换的方法。
0.5 本文一些补充说明
下面有一些对数据结构的说明。如果某个字段没有特殊的说明,转换的时候可以全部写入
0。另外,大多数 char[] 的字段都是以 '\0' 结束的字符串,转换程序一定不要忘记最
后那个 '\0'。
对本文或相关问题的疑问,欢迎到 smth.org BBSMan_Dev 版讨论。
1. 转换版面及文章
1.1 转换文章
每个版面都是一个目录。比如 SYSOP 版的目录是 $BBSHOME/boards/SYSOP/。在这个目录
下面有一个 .DIR 文件,这是所有帖子的索引。然后有一大堆类似 M.1085385291.w0 这
样文件名的文件,每一个这样的文件就是一个帖子。这个文件名的规则是:前两个字符是
M.,紧跟着一个 10 位数的 timestamp,是帖子发表的时间,然后是 .??,这里问号可
以随便填什么,推荐就用 .A0 好了,如果同一个时间点有两个帖子,可以把最后两个字
符换成 B0 等依次推类。转换程序转换版面文章的核心,应该就是从老的系统里面读取这
个版面每个帖子的作者、标题、内容、发表时间,然后按照 BBS 帖子的样板写成相应的
文件,具体格式可以在你已经稳定运行起来的系统里面试验发帖来得到。
注1:smthbbs 也可以采用版面目录下 52 个子目录分散储存帖子的方法。一般站点不会
使用这种方法,我就不具体针对这种情况说明了。
注2:在 $BBSHOME/vote/ 下面每个版面也会有一个目录,主要用于储存投票等数据。如
果你是手动转换所有版面,不要忘记在 $BBSHOME/vote/ 下面建立相应的目录。
1.2 最简单的转换办法
最简单的办法可能是,先正常开启相应的讨论区,接着用一个自制转换程序转换所有的帖
子。然后,使用 local_utl/fixdir 程序产生相应的帖子索引 .DIR;最后用
local_utl/gen_title 程序同步 .DIR 里面的帖子主题。这个方法最简单,不过会有各种
各样的问题,可以作为转换程序入门练习用。另外注意这个转换是动态转换,整个过程要
保持系统正常运行。
1.3 产生版面文章索引
具体的帖子转换已经在上面的 1.1 节说过。现在说一下最关键的 .DIR 文件。这个文件
是 n 个 fileheader 结构,n = 版面帖子数量。这个 fileheader 定义在
src/default.h 或者 src/site.h 里面,具体解释如下:
typedef struct fileheader {
char filename[FILENAME_LEN];
帖子的文件名,比方 1.1 节那个例子,这个字段就是 M.1085385291.w0,注意
第
3 个字节到第 12 个字节是帖子的发表时间戳,这个非常重要,一定要搞对。
unsigned int id, groupid, reid;
这三个字段非常重要,下面一节具体说明。
int o_bid;
unsigned int o_id;
unsigned int o_groupid;
unsigned int o_reid;
以上四个字段用于需审核的文章和推荐文章,表示原文的位置。转换程序可以
全部写入 0。
char innflag[2];
帖子是否转信,两个字节都设置成 'L' 就可以了。
char owner[OWNER_LEN];
发文作者。
unsigned int eff_size;
帖子的有效字节数,转换的时候设置成 0 就好了,转换完了以后可以用
local_utl/calc_effsize 程序同步。
time_t posttime;
帖子发表时间的 timestamp,最好设置一下。
long attachment;
附件位置偏移量,设置成 0 就可以了。
如果你要转换附件,这个字段的设置参考下面的 1.8 节。
char title[ARTICLE_TITLE_LEN];
帖子的标题。注意超长标题需要截短。
unsigned char accessed[4];
一些帖子的属性,bitwise-ORs,设置成 0 就可以了。两个可能可以转换的
flag:
accessed[0]: FILE_MARKED (0x08) 被 m 的帖子(精华帖)
FILE_DIGEST (0x10) 文摘区贴子(参考下面的 1.9 节)
} fileheader;
转换程序可以在转换文章的同时产生 .DIR 文件而不使用 fixdir 程序。
1.4 id, groupid, reid 三个字段
这三个字段是帖子的索引 ID 和同主题信息。.DIR 里面 fileheader 结构的 id 字段依
次递增。注意一定是递增,否则 web 下浏览会不正常!另外两个字段的作用举例如下:
有人新发表了帖子 A,这个帖子系统自动给了 id = 10
然后有人回复帖子 A,我们叫它帖子 B;
再有人回复了帖子 B,我们叫它帖子 C;
最后有人回复帖子 A,称为帖子 D。这四个帖子的三个字段会是这样:
帖子 id groupid reid
================================
A 10 10 10
B 11 10 10
C 12 10 11
D 13 10 10
具体不用多解释了吧。groupid 就是用来判断帖子同主题的,注意,帖子同主题和帖子标
题无关。reid 主要用来产生回复树结构,目前只在 wForum 里面用到。如果老的系统支
持这些结构,可以想办法转换过来,否则可以使用 gen_title 程序,它不但产生依次递
增的 id 字段,同时也根据帖子标题来产生 groupid,用来同步主题信息。
1.5 产生 .BOARDS 文件
$BBSHOME/.BOARDS 文件是所有版面的信息,实际上是 MAXBOARD 个 boardheader 结构。
这个结构定义在 src/struct.h 里面,具体解释如下,转换不需要关心的字段就不解释了
:
struct boardheader {
char filename[STRLEN];
版面的英文名称,STRLEN 是 80
char BM[BM_LEN];
版主列表,BM_LEN 是 60。多个版主用空格隔开
char title[STRLEN];
版面的说明,就是开启讨论区的时候输入的那些东西,比方:
0[站务] 测试用版
unsigned level;
版面存取权限。设置成 0 就好了,转换完了再修改特殊讨论区的存取权限
也不迟。
unsigned int idseq;
和 bcache 里面的 nowid 同步,这个 nowid 的问题后面会详细说。
unsigned int clubnum;
unsigned int flag;
版面的一些属性比方是否参与转信。转换的时候设置成 0 就好了。
union {
unsigned int adv_club;
unsigned int group_total;
} board_data;
time_t createtime;
版面创建时间,“新开启的讨论区”列表会用到。设置成 0 也没事。
int unused;
char ann_path[128];
精华区路径。
int group;
所属目录。
char title_level;
设定用户需要什么 title 可见这个版面。
char des[195];
版面描述,用于 www 的版面说明和 search,可以以后再加。
#ifdef FLOWBANNER
下面这两个用于版面底部流动信息。
int bannercount;
char banners[MAXBANNER][BANNERSIZE];
#endif
};
转换程序需要做什么呢?就是从老的系统里面取出所有版面的信息,参考上面的说明转换
出一个 .BOARDS 文件来。版面数量不到 MAXBOARD 的话,剩下的部分全部填 0 就好了。
不过我推荐直接在 BBS 系统里面开版,这样 .BOARDS 由系统自动产生,精华区路径也会
自动建立好。转换程序只需要转换版面文章就可以了。如果老系统版面实在太多,考虑用
程序自动产生 .BOARDS 才比较有意义。
注:smthbbs 系统内部每个版面都有一个 bid(getbnum(版名字符串) 的返回值),这个
bid 就是该版面在 .BOARDS 里面的位置(1-based)。.BOARDS 内第一个版面的 bid 是
1,依次推类...
1.6 boardheader 里面的 idseq
这个字段就是当前最后一篇帖子的 id 值,和 boardstatus 共享内存里面相应的 nowid
同步。发表帖子的时候系统会自动根据 nowid 分配下一个 id 值。如果用静态转换,可
以先转换所有的版面文章,获得各个版面最后一片帖子的 id 值,然后再产生或者修改
.BOARDS,写入相应的 idseq 值。如果使用动态转换,可以参考 gen_title 程序的办法
,启动 BBS 系统然后用一个外部程序遍历所有的 boardstatus 写入 nowid 值。动态转
换的情况下,boardheader 的 idseq 不用你操心,系统正常关闭时会自动根据
boardstatus 的 nowid 值写回 .BOARDS 中的 idseq 字段。
结合 1.4 节的说明我们可以知道,如果转换程序没有正确设置这个字段,就会造成新系
统发文在 web 下浏览不正常。
1.7 同主题模式的文章列表
同主题模式依赖于版面目录下面的一个 .ORIGIN 文件。这个文件就是 .DIR 文件里面
id==groupid 的 fileheaders。转换程序可以加简单的代码自动产生 .ORIGIN 文件,否
则下一次版主作区段删除的时候也会自动生成这个文件。
1.8 帖子附件的转换
附件的内容就添加在它所属帖子的那个文件的末尾。有附件的帖子文件其组成是:帖子正
文(最后是一个回车符),附件一,附件二,...其中每个附件段是由四个部分组成的:
*) 第一部分:八个 '\0' 字节。
*) 第二部分:附件的原始文件名字符串,可以含有中文字符,长度不应该超过 40 个字
符。文件名的后缀将决定附件类型。本部分长度不定,所以千万不要忘记最后的 '\0'
字符串结束符。
*) 第三部分:四个字节,unsigned int 二进制格式整数,表示本附件的长度;
*) 第四部分:二进制格式储存的这个附件,本部分长度由第三部分决定。
另外,这个帖子在 .DIR 里面相应的那个 fileheader 结构的 attachment 字段应该设置
为第一个附件段的起始偏移量(ftell)。参考:
bbs2www/lib/bbslib.c post_article() 函数
bbs2www/html/bbscon.php get_mimetype() 函数
1.9 置顶/文摘区转换
每个版面的置顶有一个单独的索引文件 .DINGDIR,格式和 .DIR 完全相同。系统默认每
个版面置顶个数不能超过 10 个。置顶的文件是和这个置顶的原文文件连在一起的(ln
硬连接),置顶文件的文件名就是原文文件名的第一个字母改成 Z。
文摘区的原理和置顶类似。文摘区索引文件是 .DIGEST,文摘区帖子数目受 MAX_DIGEST
(在 bbs.h 中定义为 3000)限制。文摘文件和相应原文文件连在一起,文件名是原文文
件名的第一个字母改为 G。要注意的一点是,原文在 .DIR 中相应的 fileheader 的
access[0] 字段应该有 FILE_DIGEST 这个 bit,参考上面的 1.3 节。
2. 转换用户
转换版面和文章可能是整个转换程序中最简单的部分,而且一般转换只保留文章就大功告
成了,用户反正可以让忠实用户再注册一遍。不过还是讨论一下自动转换用户的可能性。
2.1 什么样的 ID 在 smthbbs 系统中是合法的
smthbbs 系统合法 ID 的规则是:至少 2 个字符,至多 12 个字符。第一个字符必须是
字母,后面的字符必须是字母或者数字。如果你老的系统里面含有在 smthbbs 系统中不
合法的 ID,建议在转换之前杀掉这些帐号或者转换的时候判断出来不予转换。如果你非
要保留这些 ID,你必须有足够的功力修改 smthbbs 程序,特别是 userid hash 函数,
否则建议不要轻易尝试。
2.2 用户帐号信息的数据结构
$BBSHOME/.PASSWDS 是用户帐号基本信息,实质上是 MAXUSERS 个 userec 结构,结构说
明后面详细写。另外,每个用户还有一个 home 目录和一个信件目录,比方 atppp 同学
这两个目录分别是 $BBSHOME/home/A/atppp/ 和 $BBSHOME/mail/A/atppp/。用户 home
目录下有一些杂七杂八的文件,其中有一个文件叫做 .userdata,这个是用户帐号的补充
信息,实际上就是一个 userdata 结构,结构说明后面详细写,转换程序最好能够自动产
生这个文件。信件目录到后面一大章节再说,转换程序即使不转换用户信件,也一定要把
这个信件目录建立好。
2.3 userec 结构
这个结构定义在 src/struct.h 里面。
struct userec {
char userid[IDLEN + 2];
用户名。IDLEN 是 12,不要轻易修改。
char flags;
一些标志,戒网,版面排序之类的。转换用户建议设置成 0x81,也就是
PAGER_FLAG | CURSOR_FLAG
参考源代码 contrib/fb2k2smth/README 相关说明。
unsigned char title;
用户 title,和 boardheader 里面的 title_level 配合。
time_t firstlogin;
注册时间或者第一次登录的时间戳。smthbbs 系统判断普通用户是否有权利修改
自己 ID 发表的文章会检查该文章发文时间是不是在这个用户的 firstlogin 之
后。所以这个字段一定要转换正确。如果老系统没有这个信息,可以自行处理,
但是如果有必要应该保证用户能合法修改自己以前发表的帖子。
char lasthost[16];
最后登录的 IP。
unsigned int numlogins;
登录次数。这个数字在默认配置下和用户生命力有关,见 2.8 节。
unsigned int numposts;
发帖数。
#ifdef CONV_PASS
char passwd[OLDPASSLEN];
上面这个字段和密码转换相关,后面再详细说。
char unused_padding[2];
#endif
char username[NAMELEN];
用户昵称。
unsigned int club_read_rights[MAXCLUB>>5];
unsigned int club_write_rights[MAXCLUB>>5];
unsigned char md5passwd[MD5PASSLEN];
md5 消化过的密码。密码问题后面会一并详细说明。
本字段定长 16,不以 '\0' 结束!
unsigned userlevel;
用户权限。推荐转换程序将所有的用户权限都设置成
PERM_BASIC | PERM_CHAT | PERM_PAGE | PERM_POST | PERM_LOGINOK
也就是八进制 037。版主应该再 OR 上 PERM_BOARDS 权限(八进制 02000)。
其他像站务之类的权限可以转换完了启动系统以后再通过管理操作给。
time_t lastlogin;
上次登录的时间戳。这个时间戳在默认配置下和用户生命力有关,见 2.8 节。
time_t stay;
总上线时间。单位是秒。
int signature;
当前使用的签名档号码。一般设置成 0 就可以了。
unsigned int userdefine[2];
用户自定义参数,两个字段可以全部设置成 0xFFFFFFFF。这里注意,如果系统
开启 wForum 编译参数,这样设置会导致用户在 wForum 里面默认公开所有的信
息。而 wForum 的标准是默认关闭公布详细信息,userdefine[0] 应该设置成
0xFFFFFFFF & (~DEF_SHOWREALUSERDATA)
也就是 0xBFFFFFFF。
time_t notedate;
int noteline;
int notemode;
上面三个都和查看留言板相关。都设置成 0 就好了。
time_t exittime;
上次退出登录的时间戳。
unsigned int usedspace;
用户信件使用的磁盘空间。详见转换用户信件的说明。如果不转换用户信件,这
个字段自然就设置成 0 了。
#ifdef HAVE_USERMONEY
int money;
int score;
char unused[20];
#endif
};
2.4 userdata 结构
userdata 是用户 home 目录下 .userdata 文件的结构。转换程序最好能够产生这个文件
。另外用户 home 目录下还有一个文件是 usermemo(用来 mmap 的),这个文件的内容
要和 .userdata 严格一致,但是如果 usermemo 文件不存在系统会自动建立,所以如果
是转换老的 BBS 系统,一定注意删除掉所有用户 home 目录下面的 usermemo 文件。
userdata 结构定义在 src/struct.h 里面。
struct userdata
{
char userid[IDLEN + 2];
用户名。
char __reserved[2];
char realemail[STRLEN - 16];
真实 email。
char realname[NAMELEN];
真实姓名。
char address[STRLEN];
通讯地址。
char email[STRLEN];
email。
这个结构里面一共有三个 email 字段。其他两个可以都设置成空字符串。
#ifdef HAVE_BIRTHDAY
char gender;
性别,写 'M' 或者 'F'。如果不是这两个字符可能出错。
unsigned char birthyear;
出生年的后两位。
unsigned char birthmonth;
出生月。
unsigned char birthday;
出生日。上面三个字段注意类型是 unsigned char。
#endif
char reg_email[STRLEN];
注册使用的 email。
/*#ifdef SMS_SUPPORT*/
bool mobileregistered;
char mobilenumber[MOBILE_NUMBER_LEN];
/*#endif*/
/* add by roy 2003.07.23 for wForum */
char OICQ[STRLEN];
char ICQ[STRLEN];
char MSN[STRLEN];
char homepage[STRLEN];
int userface_img;
设置成 0。如果有自定义头像,设置成 -1。
char userface_url[STRLEN];
这个是自定义头像的完整 URL 地址。
unsigned char userface_width;
unsigned char userface_height;
上面两个字段是自定义头像的长和宽。必须是 0~120 之间的整数。
unsigned int group;
char country[STRLEN];
char province[STRLEN];
char city[STRLEN];
unsigned char shengxiao;
unsigned char bloodtype;
unsigned char religion;
unsigned char profession;
unsigned char married;
unsigned char education;
char graduateschool[STRLEN];
unsigned char character;
char photo_url[STRLEN];
个人相片的完整 URL 地址。
char telephone[STRLEN];
char smsprefix[41];
char smsend[41];
unsigned int smsdef;
上面这堆 "add by roy" 的东西目前只在 wForum 里面用到。
int signum;
签名档个数。如果不打算转换签名档,设置成 0。否则参考下面的签名档
转换说明。
};
2.5 密码转换
smthbbs 的用户密码使用 md5 加密储存于 userec 结构的 md5passwd 字段内,但是
smthbbs 系统并不是对用户密码直接 md5 加密处理,md5passwd 字段是下面这四个字符
串顺序连接起来的字符串的 md5:
passmagic 密码 passmagic 用户名
其中 passmagic 是(不包括前后两个引号):
"wwj&kcn4SMTHBBS MD5 p9w2d gen2rat8, //grin~~, 2001/5/7"
作为例子,如果 SYSOP 的密码设置为 kickKCN,那么 SYSOP 的 md5passwd 字段就是:
d7fda13460d88166d1abb6b466b84b35
相关代码请看 libBBS/pass.c igenpass() 函数。注意 md5passwd 字段的类型是
unsigned char md5passwd[16],也就是所谓的 raw-binary format,而不是有些 md5 程
序返回的 32 个字符的字符串。如果你老的系统用户密码是用明码存储的,那转换起来就
很方便了,随便找个 md5 的代码甚至现成函数就可以转换密码了。如果你老的系统用户
密码采用了不可逆的加密方式存储,那么转换起来就有点费劲了,下面简单说明一下自动
转换 fb2k 系统的不可逆加密的密码的原理,别的系统可以参考这个方法来做。
2.5.1 定义 CONV_PASS 转换老的 firebird 2000 BBS(fb2k)系统的用户密码
当 site.h 定义了 CONV_PASS 之后,系统会自动转换 fb2k 系统的用户密码,而这个用
户密码在 fb2k 系统里面使用的是不可逆的加密方式存储。具体的转换过程是这样的:转
换 .PASSWDS 文件之后,老的加密密码存储在这个用户 userec 结构的 passwd 字段内,
当用户登录、系统验证密码的时候,系统会判断 passwd 字段是否为空字符串,如果不是
,就使用从 fb2k 系统原封不动搬过来的那个验证密码的函数,如果验证通过,这个时候
可以利用内存里面存在的明码密码进行上面所述的 md5 加密写入 md5passwd 字段,同时
改动 passwd 字段写入空字符串。以后当系统发现 passwd 字段为空字符串,就会自动利
用新系统的密码验证函数。如果碰到一个有洁癖的管理员,他可能会在半年之后检查所有
用户看是否 passwd 字段都是空字符串了,如果还有死不要脸的荣誉帐号半年未上线还没
有被杀掉,他可能就手起刀落...然后转换 .PASSWDS 文件去掉 passwd 字段,在
site.h 里面去掉 CONV_PASS 的定义重新编译系统。这样整个转换过程就干干净净的完成
了。
2.6 用户 home 目录下面其它一些杂七杂八的文件说明
2.6.1 .boardrc.gz 已读记录
转换已读记录可能是整个转换程序最复杂的部分,而且也没有太多的意义,建议不要轻易
尝试。如果老的系统支持已读记录,而你又想做一个真正有专业精神的转换程序,那么就
慢慢听我说下去。用户 home 目录下面的 .boardrc.gz 存储用户的已读记录,它是一个
使用 gzip 压缩的文件,解压后的长度是
BRC_FILESIZE = MAXBOARD * BRC_MAXNUM * sizeof(unsigned int)。
BRC_MAXNUM 默认是 50,不要轻易修改。这个文件分为 MAXBOARD 段,第 i 段就是 bid
= i 的那个版面的已读记录;每个版面的已读记录就是 BRC_MAXNUM 个非负整数:
n1 n2 n3 ... np 0 ... 0
其中 n1 > n2 > n3 > ... > np > 0。这组已读记录的意义是,该版面 id > n1 的文章
都是未读的,id < np 的文章都是已读的;而 np <= id <= n1 的文章中,只有
id = n1,n2,n3,...,np
的文章才是已读的,其余全部未读。已读记录用这个方法来存储是有利有弊的,最大的好
处就是比较有效的记录了用户最需要的那部分已读记录。
已读记录处理代码可以参考 libBBS/boards.c 里面 brc_initial() brc_add_read()
brc_unread() 等函数,不过要读懂这几个函数还需要进一步了解系统 cache 已读记录的
原理。
2.6.2 favboard 收藏版面
用户自定义了收藏版面之后会在用户 home 目录下创建文件 favboard。该文件的格式可
以参考 libBBS/boards.c load_favboard() save_favboard() 函数,转换程序可以参考
contrib/fb2k2smth/chfavbrd.c 程序。favboard 文件有多种允许的格式,而且
smthbbs 支持复杂的多目录层次收藏夹结构。下面说明只有根目录下有若干版面的一种收
藏夹结构,请注意这并不是 favboard 完整功能的数据结构说明。favboard 可以是这样
一个数据结构:
struct {
int magic_version_number;
写 0x8081
int favbrd_list_t;
收藏目录个数,写 1
struct favbrd_struct fav_boards;
具体的收藏版面
};
favbrd_struct 在 src/struct.h 内定义,具体的数据结构如下:
struct favbrd_struct {
int bnum;
本目录中收藏版面的个数,决定下一个数组字段中多少个元素是有效的
int bid[MAXBOARDPERDIR];
/* bid >= 0: 版面
bid < 0: 目录, 表示子目录是 favbrd_list[-bid]
*/
在不涉及多层目录结构的情况下,bid[i] 表示本目录下第 i 个收藏版面,
这里千万注意,bid[i] 是相应版面的 bid - 1,而不是 bid!也就是说,
这里 bid[i] 是有可能为 0 的。
char title[61];
char ename[20];
int father;
根目录这个字段写 -1。
int level;
其余未解释的字段可以全部写 0。
};
另外,$BBSHOME/etc/initial_favboard 是新注册用户默认的收藏版面,格式是每行一个
版名。如果该文件不存在,默认的收藏版面是 .BOARDS 文件里面的第一个版面。
2.6.3 friends 好友列表
这是 n 个 friends 结构的文件,每个结构都是一个好友,具体数据结构定义在
src/struct.h 里面:
struct friends {
char id[13];
好友 id
char exp[LEN_FRIEND_EXP];
好友说明,可以留空。
};
2.6.4 plans 个人说明档
这个没什么好说的,就是个人说明档。查询用户的时候会显示出来。
2.6.5 signatures 签名档
这个文件是用户签名档,每六行是一个单位,支持 ansi 控制符,wForum 额外支持少量
ubb。如果老系统支持签名档,可以考虑通过适当的方式转换。userdata 结构的 signum
字段应该存储用户签名档个数,转换签名档之后应该运行一下 local_utl/recalc_signum
程序来同步所有用户的 signum 字段。
2.7 SYSOP 和 guest 两个用户
SYSOP(注意 5 个字母全部大写)在 smthbbs 系统中自动拥有全部权限,guest 用户是
smthbbs 系统的 web 部分必须的。建议从一般论坛系统转换的时候禁止转换这两个 ID,
待转换完成后再手动注册这两个用户。
2.8 用户生命力和自动杀档
smthbbs 系统为了节省资源,在默认配置下,一般用户长期不上线会自动被杀掉帐号。杀
账号的代码每天会自动运行一次,具体代码可以参考 daemon/miscd.c dokilluser()
killauser() 两个函数。另外,决定账号是否被杀是由账号的生命力决定的,生命力 <
0 就会被杀,guest 用户永远不会被杀。计算用户生命力的函数是 default.c 里面的
compute_user_value(),该函数也可以在 site.c 里面定制(需要在 site.h 里面去掉
USE_DEFAULT_USER_LIFE 的 define)。
如果你不愿意使用自动杀账号的功能,可以在 site.h 里面定义 SAVELIVE。但是这种情
况下查询用户可能会显示生命力为负值,所以还是推荐在 site.c 里面定制
compute_user_value() 函数的方法。
3. 转换用户站内信件
用户信件,包括信件索引和具体信件内容,全部位于用户信件目录下(参考 2.2 节)。
3.1 信件目录及数据结构
用户信件的总体构架基本类似于讨论区文章。每封信件都是一个文件,文件名的规则和讨
论区普通文章的文件名相同。索引文件除了 .DIR 还有两个,如下:
.DIR 收件箱
.SENT 发件箱
.DELETED 垃圾箱
这三个索引文件的结构和讨论区文章索引 .DIR 的结构很类似,也是 n 个 fileheader
结构,少数几个字段的意义略有不同,具体解释如下:
typedef struct fileheader {
char filename[FILENAME_LEN];
帖子的文件名,注意第 3 个字节到第 12 个字节是帖子的发表时间戳。
unsigned int id, groupid, reid;
int o_bid;
unsigned int o_id;
unsigned int o_groupid;
unsigned int o_reid;
char innflag[2];
以上八个字段没用。
char owner[OWNER_LEN];
对方 ID。.DIR 中表示发件人 ID,.SENT 中表示收件人 ID,.DELETED 里面
既有可能是发件人也可能是收件人 :(
unsigned int eff_size;
信件大小。目前只在 wForum 里面显示出来。转换程序可以暂时不管这个字段。
如果安装 wForum 可以使用 sync_mailsize 程序来同步这个字段。
time_t posttime;
信件发送时间的 timestamp,最好设置一下。
long attachment;
附件偏移量,设置成 0 就可以了。
char title[ARTICLE_TITLE_LEN];
帖子的标题。注意超长标题需要截短。
unsigned char accessed[4];
一些属性,bitwise-ORs,下面几个是可能可以转换的 flag:
accessed[0]: FILE_READ (0x01) 已读
FILE_REPLIED (0x20) 已回复
FILE_MARKED (0x08) 被 m 的信件
FILE_FORWARDED (0x40) 已转发
} fileheader;
信件在索引文件中的排序没有特定的规则,但是转换程序最好将信件按发送时间排序。
3.2 信件使用空间和信件大小
smthbbs 系统对用户信件使用的空间和信件数量都有限定。检查是否超容的代码可以参考
src/bbs_sendmail.c 的 chkusermail() 函数。具体限定在 default.c 里面的
get_mail_limit() 函数,当然也可以在 site.c 里面定制:
get_mail_limit(struct userec *user,int *sumlimit,int *numlimit)
sumlimit: 返回用户信箱容量的上限,单位是 KB。
numlimit: 返回用户信件数量的上限。
这里特别注意,如果 *sumlimit 返回 9999,则表示对信件总大小和总数量都没有限制。
另外,用户 userec 结构的 usedspace 字段应该设置为用户信件已用空间大小。转换程
序可以写入 0xffff。参考 local_utl/resetMailTotal 程序。
3.3 自定义信箱
smthbbs 系统除了上面提到的三个预定义信箱外,还支持用户自定义信箱。载入自定义信
箱的代码可以参考 libBBS/record.c load_mail_list() 函数。具体来说,在用户 home
目录下有一个 maildir 文件是自定义信箱的记录,文件结构是:
struct {
int mail_list_t;
自定义信箱的个数
char mail_list[MAILBOARDNUM][40];
每个自定义信箱的具体配置,每个配置是一个 40 个字节的字符串。
0~29 字节是信箱名称。30~39 字节是该信箱索引文件的后半段名称。
比如,这个字符串前半段是“KCN 情书”,30~39 字节是“MAILBOX1”,
那么这个自定义信箱的显示名称就是“KCN 情书”,而索引文件的名称
就是 .MAILBOX1,注意文件名第一个字符是附加上去的点。
};
3.4 信箱属性
在用户 home 目录下有一个文件 .mailbox.prop 是用户信箱的选项配置。该文件就是一
个 int 变量,bitwise-ORs 以下属性:
#define MBP_SAVESENTMAIL 0x00000001 //发信时保存信件到发件箱
#define MBP_FORCEDELETEMAIL 0x00000002 //删除信件时不保存到垃圾箱
#define MBP_MAILBOXSHORTCUT 0x00000004
//版面按 'v' 时进入: 收件箱(OFF) / 信箱主界面(ON)
如果用户 home 目录下 .mailbox.prop 文件不存在,系统自动使用 MBP_DEFAULT 作为信
箱选项配置。如果转换程序不写入用户 .mailbox.prop 文件,推荐在 site.h 里面将
MBP_DEFAULT 设置成 0x00000001,这个选项的含义是发信时自动将信件保存到发件箱,
并且删除信件会保存到垃圾箱。
--
FROM 128.12.181.30