(→XMPP实现) |
(→C结构的存取) |
||
(未显示同一用户的5个中间版本) | |||
第1行: | 第1行: | ||
基于Python的BBS实现,提供[[BBS数据接口]]。 | 基于Python的BBS实现,提供[[BBS数据接口]]。 | ||
+ | == 基础结构 == | ||
+ | === C结构的存取 === | ||
+ | ==== 初版实现 ==== | ||
+ | 需要在对应的类定义域:_fields与_parser。之后利用Util.Pack()和Util.Unpack()将对象打包成一个和结构一样长的字符串,或者从一个字符串解包成整个对象。 | ||
+ | |||
+ | _fields: | ||
+ | * _fields为一个list,其中每项对应一个C结构里的域。 | ||
+ | * 每项可以是一个字符串(域名),或者是一个list。 | ||
+ | * 如果是一个list,那第一个必须是个字符串(域名),第二个是类型,之后是类型参数。 | ||
+ | ** 类型1: C字符串。从内存中读取之后,会自动截断到第一个\0 | ||
+ | ** 类型2: 需要再次parse的域。目前主要用来实现数组。类型参数是再次parse的格式字符串。 | ||
+ | *** 例如,对于一个int数组,['friends_uid', 2, '=%di' % Config.MAXFRIENDS ],会再次用后面那个格式进行parse,得到一个list。 | ||
+ | |||
+ | _parser: | ||
+ | * 定义为struct.Struct(格式字符串) | ||
+ | * 格式字符串里,每项对应了_fields里的一个域。 | ||
+ | * 对于普通类型,例如int,直接在对应位置写'i'就可以。 | ||
+ | * 对于需要再次parse的类型,这里只要取出一个字符串就完了,一般是'9s'这样的形式。 | ||
+ | |||
+ | 样例: | ||
+ | |||
+ | 来自UserInfo类 | ||
+ | <source lang="python"> | ||
+ | _fields = ['active', 'uid', 'pid', 'invisible', 'sockactive', 'sockaddr', 'destuid', 'mode', 'pager', 'in_chat', ['chatid', 1], ['from', 1], 'logintime', 'fill', 'freshtime', 'utmpkey', 'mailbox_prop', ['userid', 1], ['realname', 1], ['username', 1], 'friendsnum', ['friends_uid', 2, '=%di' % Config.MAXFRIENDS ], 'currentboard', 'mailcheck'] | ||
+ | _parser = struct.Struct('=iiiiiiiiii16s%dsi36siiI20s20s40si%dsiI' % (Config.IPLEN + 4, Config.MAXFRIENDS * 4)) | ||
+ | </source> | ||
+ | 对应c结构: | ||
+ | <source lang="c"> | ||
+ | struct user_info { /* Structure used in UTMP file */ | ||
+ | int active; /* When allocated this field is true */ | ||
+ | int uid; /* Used to find user name in passwd file */ | ||
+ | int pid; /* kill() to notify user of talk request */ | ||
+ | int invisible; /* Used by cloaking function in Xyz menu */ | ||
+ | int sockactive; /* Used to coordinate talk requests */ | ||
+ | int sockaddr; /* ... */ | ||
+ | int destuid; /* talk uses this to identify who called */ | ||
+ | int mode; /* UL/DL, Talk Mode, Chat Mode, ... */ | ||
+ | int pager; /* pager toggle, true, or false */ | ||
+ | int in_chat; /* for in_chat commands */ | ||
+ | char chatid[16]; /* chat id, if in chat mode */ | ||
+ | char from[IPLEN + 4]; /* machine name the user called in from */ | ||
+ | time_t logintime; | ||
+ | #ifdef HAVE_WFORUM | ||
+ | char fill[35]; | ||
+ | unsigned char yank; | ||
+ | #else | ||
+ | char fill[36]; | ||
+ | #endif | ||
+ | time_t freshtime; | ||
+ | int utmpkey; | ||
+ | unsigned int mailbox_prop; /* properties of currentuser's mailbox */ | ||
+ | char userid[20]; | ||
+ | char realname[20]; | ||
+ | char username[40]; | ||
+ | int friendsnum; | ||
+ | int friends_uid[MAXFRIENDS]; | ||
+ | #ifdef FRIEND_MULTI_GROUP | ||
+ | unsigned int friends_p[MAXFRIENDS]; | ||
+ | #endif | ||
+ | int currentboard; | ||
+ | unsigned int mailcheck; /* if have new mail or new msg, stiger */ | ||
+ | }; | ||
+ | </source> | ||
+ | |||
+ | ==== 新版实现 ==== | ||
+ | 利用python的property系统,将每个域添加为该类的一个property。 | ||
+ | |||
+ | 需要在类中定义_fields成员,之后在类上加上@init_fields这个decorator来自动根据_fields添加property。 | ||
+ | _fields为一个list,其中每项也是一个list,每项的第一个元素为域名,第二个为该域的类型。之所以不用hash,是因为域是有次序的。 | ||
+ | |||
+ | 需要实现read()和write()接口,在读写域时会调用这俩函数。 | ||
+ | |||
+ | 样例: | ||
+ | <source lang="python"> | ||
+ | @init_fields | ||
+ | class UserRecord(object): | ||
+ | _fields = [ | ||
+ | ['userid' , Str(Config.IDLEN + 2)], | ||
+ | ['flags' , I8()], | ||
+ | ['title' , U8()], | ||
+ | ['firstlogin' , U32()], | ||
+ | ['lasthost' , Str(16)], | ||
+ | ['numlogins' , U32()], | ||
+ | ['numposts' , U32()], | ||
+ | ['passwd' , FixStr(Config.OLDPASSLEN)], | ||
+ | ['padding' , FixStr(2)], | ||
+ | ['username' , Str(Config.NAMELEN)], | ||
+ | ['club_read_rights' , Array(I32, Config.MAXCLUB / 32)], | ||
+ | ['club_write_rights' , Array(I32, Config.MAXCLUB / 32)], | ||
+ | ['md5passwd' , FixStr(Config.MD5PASSLEN)], | ||
+ | ['userlevel' , U32()], | ||
+ | ['lastlogin' , U32()], | ||
+ | ['stay' , U32()], | ||
+ | ['signature' , I32()], | ||
+ | ['userdef' , Array(I32, 2)], | ||
+ | ['notedate' , U32()], | ||
+ | ['noteline' , I32()], | ||
+ | ['notemode' , I32()], | ||
+ | ['exittime' , U32()], | ||
+ | ['usedspace' , U32()] | ||
+ | ] | ||
+ | </source> | ||
+ | 对应c结构: | ||
+ | <source lang="c"> | ||
+ | struct userec { /* Structure used to hold information in */ | ||
+ | char userid[IDLEN + 2]; /* PASSFILE */ | ||
+ | char flags; /*一些标志,戒网,版面排序之类的*/ | ||
+ | unsigned char title; /*用户级别*/ | ||
+ | time_t firstlogin; | ||
+ | char lasthost[16]; | ||
+ | unsigned int numlogins; | ||
+ | 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]; | ||
+ | unsigned userlevel; | ||
+ | time_t lastlogin; | ||
+ | time_t stay; | ||
+ | int signature; | ||
+ | unsigned int userdefine[2]; | ||
+ | time_t notedate; | ||
+ | int noteline; | ||
+ | int notemode; | ||
+ | time_t exittime; | ||
+ | /* 生日数据转移到 userdata 结构中 */ | ||
+ | unsigned int usedspace; /* used space of user's mailbox, in bytes */ | ||
+ | #ifdef HAVE_USERMONEY | ||
+ | int money; | ||
+ | int score; | ||
+ | char unused[20]; | ||
+ | #endif | ||
+ | }; | ||
+ | </source> | ||
+ | |||
== 数据接口实现 == | == 数据接口实现 == | ||
* 登录 | * 登录 | ||
第24行: | 第163行: | ||
: 在XMPP接口中实现 | : 在XMPP接口中实现 | ||
* 邮箱 | * 邮箱 | ||
− | |||
== XMPP实现 == | == XMPP实现 == | ||
第42行: | 第180行: | ||
** 跟踪好友状态并更新好友列表 | ** 跟踪好友状态并更新好友列表 | ||
:: '''已实现'''。在rosters.Rosters类中,包括update_sessions()等 | :: '''已实现'''。在rosters.Rosters类中,包括update_sessions()等 | ||
+ | ** 隐身功能 | ||
+ | :: '''已实现''',看不见的人,就是看不见…… | ||
+ | :: 但是在消息方面有个问题。原来是允许给隐身的人回复消息的,但现在不好判断是否回复,干脆统统不许。 | ||
** 增加好友 | ** 增加好友 | ||
** 删除好友 | ** 删除好友 | ||
第54行: | 第195行: | ||
:: '''已实现'''。保存内部已读索引,每次检查消息,如果有新消息,则发送给用户。 | :: '''已实现'''。保存内部已读索引,每次检查消息,如果有新消息,则发送给用户。 | ||
* 修改状态 | * 修改状态 | ||
+ | * 扩展 | ||
+ | 很多成本不大…… 有空搞搞就好了 | ||
+ | ** jabber:iq:last | ||
+ | :: 上次活动时间(在线)/上次上线时间(下线)/uptime(服务器) | ||
+ | ** jabber:iq:version | ||
+ | :: 软件版本/系统信息 | ||
+ | ** vcard-temp | ||
+ | :: 用户信息,例如名字、全名、…… | ||
+ | :: '''初步实现''',返回用户名…… | ||
+ | ** urn:xmpp:time | ||
+ | :: 用户本地时间…… | ||
+ | ** disco#info | ||
+ | :: 查询支持的扩展列表。'''已实现''' | ||
* 注销 | * 注销 | ||
** 更新用户信息 | ** 更新用户信息 | ||
** 移除session | ** 移除session | ||
:: '''已实现'''。用Session.Unregister()。 | :: '''已实现'''。用Session.Unregister()。 |
2012年7月2日 (一) 16:21的最新版本
基于Python的BBS实现,提供BBS数据接口。
基础结构
C结构的存取
初版实现
需要在对应的类定义域:_fields与_parser。之后利用Util.Pack()和Util.Unpack()将对象打包成一个和结构一样长的字符串,或者从一个字符串解包成整个对象。
_fields:
- _fields为一个list,其中每项对应一个C结构里的域。
- 每项可以是一个字符串(域名),或者是一个list。
- 如果是一个list,那第一个必须是个字符串(域名),第二个是类型,之后是类型参数。
- 类型1: C字符串。从内存中读取之后,会自动截断到第一个\0
- 类型2: 需要再次parse的域。目前主要用来实现数组。类型参数是再次parse的格式字符串。
- 例如,对于一个int数组,['friends_uid', 2, '=%di' % Config.MAXFRIENDS ],会再次用后面那个格式进行parse,得到一个list。
_parser:
- 定义为struct.Struct(格式字符串)
- 格式字符串里,每项对应了_fields里的一个域。
- 对于普通类型,例如int,直接在对应位置写'i'就可以。
- 对于需要再次parse的类型,这里只要取出一个字符串就完了,一般是'9s'这样的形式。
样例:
来自UserInfo类
_fields = ['active', 'uid', 'pid', 'invisible', 'sockactive', 'sockaddr', 'destuid', 'mode', 'pager', 'in_chat', ['chatid', 1], ['from', 1], 'logintime', 'fill', 'freshtime', 'utmpkey', 'mailbox_prop', ['userid', 1], ['realname', 1], ['username', 1], 'friendsnum', ['friends_uid', 2, '=%di' % Config.MAXFRIENDS ], 'currentboard', 'mailcheck']
_parser = struct.Struct('=iiiiiiiiii16s%dsi36siiI20s20s40si%dsiI' % (Config.IPLEN + 4, Config.MAXFRIENDS * 4))
对应c结构:
struct user_info { /* Structure used in UTMP file */
int active; /* When allocated this field is true */
int uid; /* Used to find user name in passwd file */
int pid; /* kill() to notify user of talk request */
int invisible; /* Used by cloaking function in Xyz menu */
int sockactive; /* Used to coordinate talk requests */
int sockaddr; /* ... */
int destuid; /* talk uses this to identify who called */
int mode; /* UL/DL, Talk Mode, Chat Mode, ... */
int pager; /* pager toggle, true, or false */
int in_chat; /* for in_chat commands */
char chatid[16]; /* chat id, if in chat mode */
char from[IPLEN + 4]; /* machine name the user called in from */
time_t logintime;
#ifdef HAVE_WFORUM
char fill[35];
unsigned char yank;
#else
char fill[36];
#endif
time_t freshtime;
int utmpkey;
unsigned int mailbox_prop; /* properties of currentuser's mailbox */
char userid[20];
char realname[20];
char username[40];
int friendsnum;
int friends_uid[MAXFRIENDS];
#ifdef FRIEND_MULTI_GROUP
unsigned int friends_p[MAXFRIENDS];
#endif
int currentboard;
unsigned int mailcheck; /* if have new mail or new msg, stiger */
};
新版实现
利用python的property系统,将每个域添加为该类的一个property。
需要在类中定义_fields成员,之后在类上加上@init_fields这个decorator来自动根据_fields添加property。 _fields为一个list,其中每项也是一个list,每项的第一个元素为域名,第二个为该域的类型。之所以不用hash,是因为域是有次序的。
需要实现read()和write()接口,在读写域时会调用这俩函数。
样例:
@init_fields
class UserRecord(object):
_fields = [
['userid' , Str(Config.IDLEN + 2)],
['flags' , I8()],
['title' , U8()],
['firstlogin' , U32()],
['lasthost' , Str(16)],
['numlogins' , U32()],
['numposts' , U32()],
['passwd' , FixStr(Config.OLDPASSLEN)],
['padding' , FixStr(2)],
['username' , Str(Config.NAMELEN)],
['club_read_rights' , Array(I32, Config.MAXCLUB / 32)],
['club_write_rights' , Array(I32, Config.MAXCLUB / 32)],
['md5passwd' , FixStr(Config.MD5PASSLEN)],
['userlevel' , U32()],
['lastlogin' , U32()],
['stay' , U32()],
['signature' , I32()],
['userdef' , Array(I32, 2)],
['notedate' , U32()],
['noteline' , I32()],
['notemode' , I32()],
['exittime' , U32()],
['usedspace' , U32()]
]
对应c结构:
struct userec { /* Structure used to hold information in */
char userid[IDLEN + 2]; /* PASSFILE */
char flags; /*一些标志,戒网,版面排序之类的*/
unsigned char title; /*用户级别*/
time_t firstlogin;
char lasthost[16];
unsigned int numlogins;
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];
unsigned userlevel;
time_t lastlogin;
time_t stay;
int signature;
unsigned int userdefine[2];
time_t notedate;
int noteline;
int notemode;
time_t exittime;
/* 生日数据转移到 userdata 结构中 */
unsigned int usedspace; /* used space of user's mailbox, in bytes */
#ifdef HAVE_USERMONEY
int money;
int score;
char unused[20];
#endif
};
数据接口实现
- 登录
- 用户名/密码登录
- 已实现,考虑安全原因禁用
- OAuth登录
- 部分实现,只是能用而已……
- 帖子
- 获取帖子
- 已实现
- 发帖
- 同主题上一帖/下一贴
- 已实现
- 版面
- 获取所有版面列表
- 已实现
- 精华区
- 收藏夹
- 获取收藏夹列表
- 已实现
- 消息
- 在XMPP接口中实现
- 好友列表
- 在XMPP接口中实现
- 邮箱
XMPP实现
XMPP阶段:
- 认证
- 基于外部OAuth
- 基于明文
- TLS层已经加密了,所以无所谓。
- 已实现,见xmppauth.XMPPAuth类
- 登录
- 更新用户信息
- 增加session
- 已实现。在xmppserver.XMPPServer类中__init__部分。调用Session.Register()进行注册。
- 好友列表
- 获取好友列表(roaster)
- 已实现。在roster.Roster类中。
- 跟踪好友状态并更新好友列表
- 已实现。在rosters.Rosters类中,包括update_sessions()等
- 隐身功能
- 已实现,看不见的人,就是看不见……
- 但是在消息方面有个问题。原来是允许给隐身的人回复消息的,但现在不好判断是否回复,干脆统统不许。
- 增加好友
- 删除好友
- 修改好友昵称
- 消息
- 发送消息
- 已实现。参见rosters.Rosters.send_msg()
- 消息通知
- 已实现。
- 目前设计为,当收到SIGUSR2,让所有登录用户检查是否有新消息。
- 接收消息
- 已实现。保存内部已读索引,每次检查消息,如果有新消息,则发送给用户。
- 修改状态
- 扩展
很多成本不大…… 有空搞搞就好了
- jabber:iq:last
- 上次活动时间(在线)/上次上线时间(下线)/uptime(服务器)
- jabber:iq:version
- 软件版本/系统信息
- vcard-temp
- 用户信息,例如名字、全名、……
- 初步实现,返回用户名……
- 用户本地时间……
- disco#info
- 查询支持的扩展列表。已实现
- 注销
- 更新用户信息
- 移除session
- 已实现。用Session.Unregister()。