(未显示同一用户的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()。