summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpiaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2013-08-16 13:16:11 +0800
committerpiaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2013-08-16 13:16:11 +0800
commit1ed420a0f4400c8bd9791fad9aec8f6add8aab00 (patch)
tree3d9322bf679548a4cf9f4a24828e578b990d4dd8
parent0c7e57a96f6e95e783934f4823cfe60b8e0aa829 (diff)
downloadpttbbs-1ed420a0f4400c8bd9791fad9aec8f6add8aab00.tar
pttbbs-1ed420a0f4400c8bd9791fad9aec8f6add8aab00.tar.gz
pttbbs-1ed420a0f4400c8bd9791fad9aec8f6add8aab00.tar.bz2
pttbbs-1ed420a0f4400c8bd9791fad9aec8f6add8aab00.tar.lz
pttbbs-1ed420a0f4400c8bd9791fad9aec8f6add8aab00.tar.xz
pttbbs-1ed420a0f4400c8bd9791fad9aec8f6add8aab00.tar.zst
pttbbs-1ed420a0f4400c8bd9791fad9aec8f6add8aab00.zip
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
-rw-r--r--pttbbs/util/pyutil/pttstruct.py257
1 files 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')