From 1ed420a0f4400c8bd9791fad9aec8f6add8aab00 Mon Sep 17 00:00:00 2001 From: piaip Date: Fri, 16 Aug 2013 05:16:11 +0000 Subject: The structure decoder in python is still helpful for exporting data. git-svn-id: http://opensvn.csie.org/pttbbs/trunk@5856 63ad8ddf-47c3-0310-b6dd-a9e9d9715204 --- pttbbs/util/pyutil/pttstruct.py | 257 ++++++++++++++++++++-------------------- 1 file changed, 129 insertions(+), 128 deletions(-) diff --git a/pttbbs/util/pyutil/pttstruct.py b/pttbbs/util/pyutil/pttstruct.py index c93db975..0a0ddeb1 100644 --- a/pttbbs/util/pyutil/pttstruct.py +++ b/pttbbs/util/pyutil/pttstruct.py @@ -11,133 +11,113 @@ REGLEN = 38 PASSWD_VERSION = 4194 -#define PASSWD_VERSION 4194 - -""" -typedef struct userec_t { - uint32_t version; /* version number of this sturcture, we - * use revision number of project to denote.*/ - - char userid[IDLEN+1];/* 使用者ID */ - char realname[20]; /* 真實姓名 */ - char nickname[24]; /* 暱稱 */ - char passwd[PASSLEN];/* 密碼 */ - - char pad_1; - - uint32_t uflag; /* 習慣1 , see uflags.h */ - uint32_t deprecated_uflag2; /* deprecated: 習慣2 , see uflags.h */ - uint32_t userlevel; /* 權限 */ - uint32_t numlogindays; /* 上線資歷 (每日最多+1的登入次數) */ - uint32_t numposts; /* 文章篇數 */ - time4_t firstlogin; /* 註冊時間 */ - time4_t lastlogin; /* 最近上站時間(包含隱身) */ - char lasthost[IPV4LEN+1];/* 上次上站來源 */ - int32_t money; /* Ptt幣 */ - - char unused_1[4]; - - char email[50]; /* Email */ - char address[50]; /* 住址 */ - char justify[REGLEN + 1];/* 審核資料 */ - uint8_t month; /* 生日 月 */ - uint8_t day; /* 生日 日 */ - uint8_t year; /* 生日 年 */ - uint8_t nonuse_sex; /* 性別 (已停用), 未清空過 */ - - uint8_t pager_ui_type; /* 呼叫器界面類別 (was: WATER_*) */ - uint8_t pager; /* 呼叫器狀態 */ - uint8_t invisible; /* 隱形狀態 */ - - char unused_3[2]; - - uint32_t exmailbox; /* 購買信箱數 TODO short 就夠了 */ - - // r3968 移出 sizeof(chicken_t)=128 bytes - char chkpad0[4]; - - char career[40]; /* 學歷職業 */ - char phone[20]; /* 電話 */ - - uint32_t old_numlogins; /* 轉換前的 numlogins, 備份檢視用 */ - char chkpad1[48]; - time4_t lastseen; /* 最近上站時間(隱身不計) */ - time4_t chkpad2[2]; /* in case 有人忘了把 time4_t 調好... */ - // 以上應為 sizeof(chicken_t) 同等大小 - - time4_t lastsong; /* 上次點歌時間 */ - uint32_t loginview; /* 進站畫面 */ - - uint8_t unused_4; // was: channel - uint8_t pad_2; - - uint16_t vl_count; /* 違法記錄 ViolateLaw counter */ - uint16_t five_win; /* 五子棋戰績 勝 */ - uint16_t five_lose; /* 五子棋戰績 敗 */ - uint16_t five_tie; /* 五子棋戰績 和 */ - uint16_t chc_win; /* 象棋戰績 勝 */ - uint16_t chc_lose; /* 象棋戰績 敗 */ - uint16_t chc_tie; /* 象棋戰績 和 */ - int32_t mobile; /* 手機號碼 */ - char mind[4]; /* 心情 XXX not a null-terminate string */ - uint16_t go_win; /* 圍棋戰績 勝 */ - uint16_t go_lose; /* 圍棋戰績 敗 */ - uint16_t go_tie; /* 圍棋戰績 和 */ - - char unused_5[5]; /* 從前放 ident 身份證字號,使用前請先清0 */ - - uint8_t signature; /* 慣用簽名檔 */ - uint8_t goodpost; /* 評價為好文章數 */ - uint8_t badpost; /* 評價為壞文章數 */ - uint8_t unused_6; /* 從前放競標好評(goodsale), 使用前請先清0 */ - uint8_t unused_7; /* 從前放競標壞評(badsale), 使用前請先清0 */ - char myangel[IDLEN+1];/* 我的小天使 */ - - char pad_3; - - uint16_t chess_elo_rating;/* 象棋等級分 */ - uint32_t withme; /* 我想找人下棋,聊天.... */ - time4_t timeremovebadpost;/* 上次刪除劣文時間 */ - time4_t timeviolatelaw; /* 被開罰單時間 */ - - char pad_tail[28]; -} PACKSTRUCT userec_t; -""" +USEREC_SIZE = 512 +USEREC_FMT = ( + ("version", "I"), + ("userid", "%ds" % (IDLEN+1)), + ("realname", "20s"), + ("nickname", "24s"), + ("passwd", "%ds" % PASSLEN), + ("pad_1", "B"), + ("uflag", "I"), + ("deprecated_uflag2", "I"), + ("userlevel", "I"), + ("numlogindays", "I"), + ("numposts", "I"), + ("firstlogin", "I"), + ("lastlogin", "I"), + ("lasthost", "%ds" % (IPV4LEN+1)), + ("money", "I"), + ("_unused", "4s"), + ("email", "50s"), + ("address", "50s"), + ("justify", "%ds" % (REGLEN + 1)), + ("month", "B"), + ("day", "B"), + ("year", "B"), + ("_unused3", "B"), + ("pager_ui_type", "B"), + ("pager", "B"), + ("invisible", "B"), + ("_unused4", "2s"), + ("exmailbox", "I"), + ("_unused5", "4s"), + ("career", "40s"), + ("phone", "20s"), + ("_unused6", "I"), + ("chkpad1", "44s"), + ("role", "I"), + ("lastseen", "I"), + ("timesetangel", "I"), + ("timeplayangel", "I"), + ("lastsong", "I"), + ("loginview", "I"), + ("_unused8", "B"), + ("pad_2", "B"), + ("vl_count", "H"), + ("five_win", "H"), + ("five_lose", "H"), + ("five_tie", "H"), + ("chc_win", "H"), + ("chc_lose", "H"), + ("chc_tie", "H"), + ("mobile", "I"), + ("mind", "4s"), + ("go_win", "H"), + ("go_lose", "H"), + ("go_tie", "H"), + ("dark_win", "H"), + ("dark_lose", "H"), + ("_unused9", "B"), + ("signature", "B"), + ("_unused19", "B"), + ("badpost", "B"), + ("dark_tie", "H"), + ("myangel", "%ds" % (IDLEN + 1)), + ("pad_3", "B"), + ("chess_elo_rating", "H"), + ("withme", "I"), + ("timeremovebadpost", "I"), + ("timeviolatelaw", "I"), + ("pad_trail", "28s"), + ) BTLEN = 48 BOARDHEADER_SIZE = 256 BOARDHEADER_FMT = ( - ("brdname": "%ds" % (IDLEN + 1)), - ("title": "%ds" % (BTLEN + 1)), - ("BM": "%ds" % (IDLEN * 3 + 3)), - ("pad1": "3s"), - ("brdattr": "I"), # uint32_t - ("chesscountry": "B"), - ("vote_limit_posts": "B"), - ("vote_limit_logins": "B"), - ("pad2_1": "1B"), - ("bupdate": "I"), # time4_t - ("post_limit_posts": "B"), - ("post_limit_logins": "B"), - ("pad2_2": "1B"), - ("bvote": "1B"), - ("vtime": "I"), # time4_t - ("level": "I"), # uint32_t - ("perm_reload": "I"), # time4_t - ("gid": "I"), # uint32_t - ("next": "2I"), # uint32_t - ("firstchild": "2I"), # uint32_t - ("parent": "I"), # uint32_t - ("childcount": "I"), # uint32_t - ("nuser": "I"), # uint32_t - ("postexpire": "I"), # uint32_t - ("endgamble": "I"), # time4_t + ("brdname", "%ds" % (IDLEN + 1)), + ("title", "%ds" % (BTLEN + 1)), + ("BM", "%ds" % (IDLEN * 3 + 3)), + ("pad1", "3s"), + ("brdattr", "I"), # uint32_t + ("chesscountry", "B"), + ("vote_limit_posts", "B"), + ("vote_limit_logins", "B"), + ("pad2_1", "B"), + ("bupdate", "I"), # time4_t + ("_post_limit_posts", "B"), + ("post_limit_logins", "B"), + ("pad2_2", "B"), + ("bvote", "B"), + ("vtime", "I"), # time4_t + ("level", "I"), # uint32_t + ("perm_reload", "I"), # time4_t + ("gid", "I"), # uint32_t + ("next0", "I"), # uint32_t + ("next1", "I"), # uint32_t + ("firstchild0", "I"), # uint32_t + ("firstchild1", "I"), # uint32_t + ("parent", "I"), # uint32_t + ("childcount", "I"), # uint32_t + ("nuser", "I"), # uint32_t + ("postexpire", "I"), # uint32_t + ("endgamble", "I"), # time4_t ("posttype", "33s"), ("posttype_f", "B"), ("fastrecommend_pause", "B"), - ("vote_limit_badpost": "B"), - ("post_limit_badpost": "B"), + ("vote_limit_badpost", "B"), + ("post_limit_badpost", "B"), ("pad3", "3s"), ("SRexpire", "I"), # time4_t ("pad4", "40s"), @@ -191,14 +171,18 @@ FILEHEADER_FMT = ( FILEHEADER_SIZE = 128 -def get_fileheader_format(): - return '<' + ''.join(value for _, value in FILEHEADER_FMT) +def get_format(format_pattern): + return '<' + ''.join(value for _, value in format_pattern) -def unpack_fileheader(blob): - fmt = get_fileheader_format() - assert struct.calcsize(fmt) == FILEHEADER_SIZE - return dict(zip((name for name, _ in FILEHEADER_FMT), - struct.unpack_from(fmt, blob))) +def unpack_data(blob, format_pattern): + fmt = get_format(format_pattern) + data = dict(zip((name for name, _ in format_pattern), + struct.unpack_from(fmt, blob))) + # Convert C-style strings. + for name, pat in format_pattern: + if 's' in pat: + data[name] = data[name].partition(chr(0))[0] + return data FILE_LOCAL = 0x01 # local saved, non-mail FILE_READ = 0x01 # already read, mail only @@ -218,8 +202,25 @@ STRLEN = 80 # Length of most string data if __name__ == '__main__': + assert struct.calcsize(get_format(BOARDHEADER_FMT)) == BOARDHEADER_SIZE + assert struct.calcsize(get_format(FILEHEADER_FMT)) == FILEHEADER_SIZE + assert struct.calcsize(get_format(USEREC_FMT)) == USEREC_SIZE + with open('/home/bbs/boards/A/ALLPOST/.DIR', 'rb') as f: entry = f.read(FILEHEADER_SIZE) - header = unpack_fileheader(entry) + header = unpack_data(entry, FILEHEADER_FMT) print header - print header['title'].decode('big5') + + with open('/home/bbs/.PASSWDS', 'rb') as f: + entry = f.read(USEREC_SIZE) + user = unpack_data(entry, USEREC_FMT) + print user + + with open('/home/bbs/.BRD', 'rb') as f: + entry = f.read(BOARDHEADER_SIZE) + board = unpack_data(entry, BOARDHEADER_FMT) + print board + + print 'file title: ', header['title'].decode('big5').encode('big5') + print 'user name: ', user['realname'].decode('big5').encode('big5') + print 'board title: ', board['title'].decode('big5').encode('big5') -- cgit v1.2.3