基于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()。