/* $Id$ */
#ifndef INCLUDE_STRUCT_H
#define INCLUDE_STRUCT_H
#include <netinet/in.h>
#include "cmsys.h" // for time4_t
#include "config.h" // various sizes in SHM
#include "statistic.h" // for MAX_STATS
// warning: because some other places is using #IDLEN to convert as string,
// so don't quote it - otherwise those code will fail.
#define IDLEN 12 /* Length of bid/uid */
#define IPV4LEN (15) /* a.b.c.d form */
// GCC pragma to prevent paddings
#define PACKSTRUCT __attribute__ ((packed))
/* 小雞的資料 */
typedef struct chicken_t { /* 128 bytes */
char name[20];
uint8_t type; /* 物種 */
uint8_t tech[16]; /* 技能 (unused) */
uint8_t pad0[3];
time4_t birthday; /* 生日 */
time4_t lastvisit; /* 上次照顧時間 */
int32_t oo; /* 補品 */
int32_t food; /* 食物 */
int32_t medicine; /* 藥品 */
int32_t weight; /* 體重 */
int32_t clean; /* 乾淨 */
int32_t run; /* 敏捷度 */
int32_t attack; /* 攻擊力 */
int32_t book; /* 知識 */
int32_t happy; /* 快樂 */
int32_t satis; /* 滿意度 */
int32_t temperament; /* 氣質 */
int32_t tiredstrong; /* 疲勞度 */
int32_t sick; /* 病氣指數 */
int32_t hp; /* 血量 */
int32_t hp_max; /* 滿血量 */
int32_t mm; /* 法力 */
int32_t mm_max; /* 滿法力 */
time4_t cbirth; /* 實際計算用的生日 */
int32_t pad[2]; /* 留著以後用 */
} PACKSTRUCT chicken_t;
#define PASSLEN 14 /* Length of encrypted passwd field */
#define REGLEN 38 /* Length of registration data */
#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 uflag2; /* 習慣2 , see uflags.h */
uint32_t userlevel; /* 權限 */
uint32_t numlogins; /* 上站次數 */
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 sex; /* 性別 */
uint8_t unused_2;
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 numlogindays; /* 登入天次 */
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;
#ifdef CONST_CUSER
// const userec_t cuser;
# define cuser_ref ((const userec_t*)&pwcuser)
# define cuser (*cuser_ref)
#else
# define cuser_ref (&cuser)
# define cuser pwcuser
#endif
/* flags in userec_t.withme */
#define WITHME_ALLFLAG 0x55555555
#define WITHME_TALK 0x00000001
#define WITHME_NOTALK 0x00000002
#define WITHME_FIVE 0x00000004
#define WITHME_NOFIVE 0x00000008
#define WITHME_PAT 0x00000010
#define WITHME_NOPAT 0x00000020
#define WITHME_CHESS 0x00000040
#define WITHME_NOCHESS 0x00000080
#define WITHME_DARK 0x00000100
#define WITHME_NODARK 0x00000200
#define WITHME_GO 0x00000400
#define WITHME_NOGO 0x00000800
#define BTLEN 48 /* Length of board title */
/* TODO 動態更新的欄位不應該跟要寫入檔案的混在一起,
* 至少用個 struct 包起來之類 */
typedef struct boardheader_t { /* 256 bytes */
char brdname[IDLEN + 1]; /* bid */
char title[BTLEN + 1];
char BM[IDLEN * 3 + 3]; /* BMs' userid, token '/' */
char pad1[3];
uint32_t brdattr; /* board的屬性 */
char chesscountry; /* 棋國 */
uint8_t vote_limit_posts; /* 連署 : 文章篇數下限 */
uint8_t expired_vote_limit_logins; /* (已停用) 連署 : 登入次數下限 */
uint8_t vote_limit_regtime; /* 連署 : 註冊時間限制 */
time4_t bupdate; /* note update time */
uint8_t post_limit_posts; /* 發表文章 : 文章篇數下限 */
uint8_t expired_post_limit_logins; /* (已停用) 發表文章 : 登入次數下限 */
uint8_t post_limit_regtime; /* 發表文章 : 註冊時間限制 */
uint8_t bvote; /* 正舉辦 Vote 數 */
time4_t vtime; /* Vote close time */
uint32_t level; /* 可以看此板的權限 */
time4_t perm_reload; /* 最後設定看板的時間 */
int32_t gid; /* 看板所屬的類別 ID */
int32_t next[2]; /* 在同一個gid下一個看板 動態產生*/
int32_t firstchild[2]; /* 屬於這個看板的第一個子看板 */
int32_t parent; /* 這個看板的 parent 看板 bid */
int32_t childcount; /* 有多少個child */
int32_t nuser; /* 多少人在這板 */
int32_t postexpire; /* postexpire */
time4_t endgamble;
char posttype[33];
char posttype_f;
uint8_t fastrecommend_pause; /* 快速連推間隔 */
uint8_t vote_limit_badpost; /* 連署 : 劣文上限 */
uint8_t post_limit_badpost; /* 發表文章 : 劣文上限 */
char pad3[3];
time4_t SRexpire; /* SR Records expire time */
char pad4[40];
} PACKSTRUCT boardheader_t;
// TODO BRD 快爆了,怎麼辦? 準備從 pad3 偷一個來當 attr2 吧...
#define BRD_NOZAP 0x00000001 /* 不可zap */
#define BRD_NOCOUNT 0x00000002 /* 不列入統計 */
#define BRD_NOTRAN 0x00000004 /* 不轉信 */
#define BRD_GROUPBOARD 0x00000008 /* 群組板 */
#define BRD_HIDE 0x00000010 /* 隱藏板 (看板好友才可看) */
#define BRD_POSTMASK 0x00000020 /* 限制發表或閱讀 */
#define BRD_ANONYMOUS 0x00000040 /* 匿名板 */
#define BRD_DEFAULTANONYMOUS 0x00000080 /* 預設匿名板 */
#define BRD_BAD 0x00000100 /* 違法改進中看板 */
#define BRD_VOTEBOARD 0x00000200 /* 連署機看板 */
#define BRD_WARNEL 0x00000400 /* 連署機看板 */
#define BRD_TOP 0x00000800 /* 熱門看板群組 */
#define BRD_NORECOMMEND 0x00001000 /* 不可推薦 */
#define BRD_BLOG 0x00002000 /* BLOG */
#define BRD_BMCOUNT 0x00004000 /* 板主設定列入記錄 */
#define BRD_SYMBOLIC 0x00008000 /* symbolic link to board */
#define BRD_NOBOO 0x00010000 /* 不可噓 */
#define BRD_LOCALSAVE 0x00020000 /* 預設 Local Save */
#define BRD_RESTRICTEDPOST 0x00040000 /* 板友才能發文 */
#define BRD_GUESTPOST 0x00080000 /* guest能 post */
#define BRD_COOLDOWN 0x00100000 /* 冷靜 */
#define BRD_CPLOG 0x00200000 /* 自動留轉錄記錄 */
#define BRD_NOFASTRECMD 0x00400000 /* 禁止快速推文 */
#define BRD_IPLOGRECMD 0x00800000 /* 推文記錄 IP */
#define BRD_OVER18 0x01000000 /* 十八禁 */
#define BRD_NOREPLY 0x02000000 /* 不可回文 */
#define BRD_ALIGNEDCMT 0x04000000 /* 對齊式的推文 */
#define BRD_LINK_TARGET(x) ((x)->postexpire)
#define GROUPOP() (currmode & MODE_GROUPOP)
#ifdef CHESSCOUNTRY
#define CHESSCODE_NONE 0
#define CHESSCODE_FIVE 1
#define CHESSCODE_CCHESS 2
#define CHESSCODE_GO 3
#define CHESSCODE_REVERSI 4
#define CHESSCODE_MAX 4
#endif /* defined(CHESSCOUNTRY) */
#define TTLEN 64 /* Length of title */
#define FNLEN 28 /* Length of filename */
typedef struct fileheader_t { /* 128 bytes */
char filename[FNLEN]; /* M.1120582370.A.1EA [19+1], create time */
time4_t modified; /* last modified time */
char pad; /* padding, not used */
char recommend; /* important level */
char owner[IDLEN + 2]; /* uid[.] */
char date[6]; /* [02/02] or space(5) */
char title[TTLEN + 1];
/* TODO this multi is a mess now. */
char pad2;
union {
/* TODO: MOVE money to outside multi!!!!!! */
int money;
int anon_uid;
/* different order to match alignment */
struct {
unsigned char posts;
unsigned char logins;
unsigned char regtime;
unsigned char badpost;
} vote_limits;
struct {
/* is this ordering correct? */
unsigned int ref:31;
unsigned int flag:1;
} refer;
} multi; /* rocker: if bit32 on ==> reference */
/* XXX dirty, split into flag and money if money of each file is less than 16bit? */
unsigned char filemode; /* must be last field @ boards.c */
char pad3[3];
} PACKSTRUCT fileheader_t;
#define FILE_LOCAL 0x01 /* local saved, non-mail */
#define FILE_READ 0x01 /* already read, mail only */
#define FILE_MARKED 0x02 /* non-mail + mail */
#define FILE_DIGEST 0x04 /* digest, non-mail */
#define FILE_REPLIED 0x04 /* replied, mail only */
#define FILE_BOTTOM 0x08 /* push_bottom, non-mail */
#define FILE_MULTI 0x08 /* multi send, mail only */
#define FILE_SOLVED 0x10 /* problem solved, sysop/BM non-mail only */
#define FILE_HIDE 0x20 /* hide, in announce */
#define FILE_BID 0x20 /* bid, in non-announce */
#define FILE_BM 0x40 /* BM only, in announce */
#define FILE_VOTE 0x40 /* for vote, in non-announce */
#define FILE_ANONYMOUS 0x80 /* anonymous file */
#define STRLEN 80 /* Length of most string data */
#define FAVMAX 1024 /* Max boards of Myfavorite */
#define FAVGMAX 32 /* Max groups of Myfavorite */
#define FAVGSLEN 8 /* Max Length of Description String */
/* values of msgque_t::msgmode */
#define MSGMODE_TALK 0
#define MSGMODE_WRITE 1
#define MSGMODE_FROMANGEL 2
#define MSGMODE_TOANGEL 3
typedef struct msgque_t {
pid_t pid;
char userid[IDLEN + 1];
char last_call_in[76];
int msgmode;
} msgque_t;
#define ALERT_NEW_MAIL (0x01)
#define ISNEWMAIL(utmp) (utmp->alerts & ALERT_NEW_MAIL)
#define ALERT_PWD_PERM (0x02)
#define ISNEWPERM(utmp) (utmp->alerts & ALERT_PWD_PERM)
// userinfo_t.angelpause values
#define ANGELPAUSE_NONE (0) // reject none (accept all)
#define ANGELPAUSE_REJNEW (1) // reject only new requests
#define ANGELPAUSE_REJALL (2) // reject all requests
#define ANGELPAUSE_MODES (3) // max value, used as (angelpause % ANGELPAUSE_MODES)
/* user data in shm */
/* use GAP to detect and avoid data overflow and overriding */
typedef struct userinfo_t {
int uid; /* Used to find user name in passwd file */
pid_t pid; /* kill() to notify user of talk request */
int sockaddr; /* ... */
/* user data */
unsigned int userlevel;
char userid[IDLEN + 1];
char nickname[24];
char from[27]; /* machine name the user called in from */
in_addr_t from_ip; // was: int from_alias;
char sex;
char nonuse[4];
/*
unsigned char goodpost;
unsigned char badpost;
unsigned char goodsale;
unsigned char badsale;
*/
unsigned char angelpause;
/* friends */
int friendtotal; /* 好友比較的cache 大小 */
short nFriends; /* 下面 friend[] 只用到前幾個,
用來 bsearch */
int myfriend[MAX_FRIEND];
char gap_1[4];
unsigned int friend_online[MAX_FRIEND];/* point到線上好友 utmpshm的位置 */
/* 好友比較的cache 前兩個bit是狀態 */
char gap_2[4];
int reject[MAX_REJECT];
char gap_3[4];
/* messages */
char msgcount;
msgque_t msgs[MAX_MSGS];
char gap_4[sizeof(msgque_t)]; /* avoid msgs racing and overflow */
/* user status */
char birth; /* 是否是生日 Ptt*/
unsigned char active; /* When allocated this field is true */
unsigned char invisible; /* Used by cloaking function in Xyz menu */
unsigned char mode; /* UL/DL, Talk Mode, Chat Mode, ... */
unsigned char pager; /* pager toggle, YEA, or NA */
time4_t lastact; /* 上次使用者動的時間 */
char alerts; /* mail alert, passwd update... */
char mind[4];
/* chatroom/talk/games calling */
unsigned char sig; /* signal type */
int destuid; /* talk uses this to identify who called */
int destuip; /* dest index in utmpshm->uinfo[] */
unsigned char sockactive; /* Used to coordinate talk requests */
/* chat */
unsigned char in_chat; /* for in_chat commands */
char chatid[11]; /* chat id, if in chat mode */
/* games */
unsigned char lockmode; /* 不准 multi_login 玩的東西 */
char turn; /* 遊戲的先後 */
char mateid[IDLEN + 1]; /* 遊戲對手的 id */
char color; /* 暗棋 顏色 */
/* game record */
unsigned short int five_win;
unsigned short int five_lose;
unsigned short int five_tie;
unsigned short int chc_win;
unsigned short int chc_lose;
unsigned short int chc_tie;
unsigned short int chess_elo_rating;
unsigned short int go_win;
unsigned short int go_lose;
unsigned short int go_tie;
/* misc */
unsigned int withme;
unsigned int brc_id;
#ifdef NOKILLWATERBALL
time4_t wbtime;
#endif
} userinfo_t;
typedef struct water_t {
pid_t pid;
char userid[IDLEN + 1];
int top, count;
msgque_t msg[MAX_REVIEW];
userinfo_t *uin; // Ptt:這可以取代alive
} water_t;
typedef struct {
int row, col;
int y, x;
void *raw_memory;
} screen_backup_t;
// menu_t 其實是 gmenu_t (deprecated), 精華區專用 menu
typedef struct menu_t {
int num, page, now, level, bid;
int header_size;
fileheader_t *header;
const char *path;
struct menu_t *next;
char mtitle[STRLEN];
} menu_t;
/* Used to pass commands to the readmenu.
* direct mapping, indexed by ascii code. */
#define onekey_size ((int) 'z')
/* keymap, 若 needitem = 0 表示不需要 item, func 的 type 應為 int (*)(void).
* 否則應為 int (*)(int ent, const fileheader_t *fhdr, const char *direct) */
typedef struct {
char needitem;
int (*func)();
} onekey_t;
#define ANSILINELEN (511) /* Maximum Screen width in chars */
/* anti_crosspost */
typedef struct crosspost_t {
int checksum[4]; /* 0 -> 'X' cross post 1-3 -> 檢查文章行 */
short times; /* 第幾次 */
short last_bid; /* crossport to which board */
} crosspost_t;
#define SORT_BY_ID 0
#define SORT_BY_CLASS 1
#define SORT_BY_STAT 1
#define SORT_BY_IDLE 2
#define SORT_BY_FROM 3
#define SORT_BY_FIVE 4
#define SORT_BY_SEX 5
typedef struct keeploc_t {
unsigned int hashkey;
int top_ln;
int crs_ln;
} keeploc_t;
#define VALID_USHM_ENTRY(X) ((X) >= 0 && (X) < USHM_SIZE)
#define USHM_SIZE ((MAX_ACTIVE)*41/40)
/* USHM_SIZE 比 MAX_ACTIVE 大是為了防止檢查人數上限時, 又同時衝進來
* 會造成找 shm 空位的無窮迴圈.
* 又, 因 USHM 中用 hash, 空間稍大時效率較好. */
/* MAX_BMs is dirty hardcode 4 in mbbsd/cache.c:is_BM_cache() */
#define MAX_BMs 4 /* for BMcache, 一個看板最多幾板主 */
// TODO
// 哪天請好心人整理 shm:
// (1) 增加 shmsize
// (2) userinfo_t 可以移掉一些已不用的
#define SHM_VERSION 3276
typedef struct {
int version;
// int shmsize; // TODO add this: sizeof(SHM_t), for verification
/* uhash */
/* uhash is a userid->uid hash table -- jochang */
char userid[MAX_USERS][IDLEN + 1];
char gap_1[IDLEN+1];
int next_in_hash[MAX_USERS];
char gap_2[sizeof(int)];
int money[MAX_USERS];
char gap_3[sizeof(int)];
#ifdef USE_COOLDOWN
time4_t cooldowntime[MAX_USERS];
#endif
char gap_4[sizeof(int)];
int hash_head[1 << HASH_BITS];
char gap_5[sizeof(int)];
int number; /* # of users total */
int loaded; /* .PASSWD has been loaded? */
/* utmpshm */
userinfo_t uinfo[USHM_SIZE];
char gap_6[sizeof(userinfo_t)];
int sorted[2][9][USHM_SIZE];
/* 第一維double buffer 由currsorted指向目前使用的
第二維sort type */
char gap_7[sizeof(int)];
int currsorted;
time4_t UTMPuptime;
int UTMPnumber;
char UTMPneedsort;
char UTMPbusystate;
/* brdshm */
char gap_8[sizeof(int)];
int BMcache[MAX_BOARD][MAX_BMs];
char gap_9[sizeof(int)];
boardheader_t bcache[MAX_BOARD];
char gap_10[sizeof(int)];
int bsorted[2][MAX_BOARD]; /* 0: by name 1: by class */ /* 裡頭存的是 bid-1 */
char gap_11[sizeof(int)];
#if HOTBOARDCACHE
unsigned char nHOTs;
int HBcache[HOTBOARDCACHE];
#endif
char gap_12[sizeof(int)];
time4_t busystate_b[MAX_BOARD];
char gap_13[sizeof(int)];
int total[MAX_BOARD];
char gap_14[sizeof(int)];
unsigned char n_bottom[MAX_BOARD]; /* number of bottom */
char gap_15[sizeof(int)];
int hbfl[MAX_BOARD][MAX_FRIEND + 1]; /* hidden board friend list, 0: load time, 1-MAX_FRIEND: uid */
char gap_16[sizeof(int)];
time4_t lastposttime[MAX_BOARD];
char gap_17[sizeof(int)];
time4_t Buptime;
time4_t Btouchtime;
int Bnumber;
int Bbusystate;
time4_t close_vote_time;
/* pttcache */
char notes[MAX_MOVIE][256*11];
char gap_18[sizeof(int)];
char today_is[20];
// FIXME remove it
int __never_used__n_notes[MAX_MOVIE_SECTION]; /* 一節中有幾個 看板 */
char gap_19[sizeof(int)];
// FIXME remove it
int __never_used__next_refresh[MAX_MOVIE_SECTION]; /* 下一次要refresh的 看板 */
char gap_20[sizeof(int)];
msgque_t loginmsg; /* 進站水球 */
int last_film;
// FIXME remove it
int __never_used__max_history;
time4_t Puptime;
time4_t Ptouchtime;
int Pbusystate;
/* SHM 中的全域變數, 可用 shmctl 設定或顯示. 供動態調整或測試使用 */
union {
int v[512];
struct {
int dymaxactive; /* 動態設定最大人數上限 */
int toomanyusers; /* 超過人數上限不給進的個數 */
int noonlineuser; /* 站上使用者不高亮度顯示 */
#ifdef OUTTA_TIMER
time4_t now;
#endif
int nWelcomes;
int shutdown; /* shutdown flag */
/* 注意, 應保持 align sizeof(int) */
} e;
} GV2;
/* statistic */
int statistic[STAT_MAX];
// TODO XXX 有 fromd 後可以拔掉了。
/* 故鄉 fromcache */
unsigned int home_ip[MAX_FROM];
unsigned int home_mask[MAX_FROM];
char home_desc[MAX_FROM][32];
int home_num;
int max_user;
time4_t max_time;
time4_t Fuptime;
time4_t Ftouchtime;
int Fbusystate;
#ifdef I18N
/* i18n(internationlization) */
char *i18nstrptr[MAX_LANG][MAX_STRING];
char i18nstrbody[22 * MAX_LANG * MAX_STRING];
/* Based on the statistis, we found the lengh of one string
is 22 bytes approximately.
*/
#endif
} SHM_t;
#ifdef SHMALIGNEDSIZE
# define SHMSIZE (sizeof(SHM_t)/(SHMALIGNEDSIZE)+1)*SHMALIGNEDSIZE
#else
# define SHMSIZE (sizeof(SHM_t))
#endif
typedef struct {
unsigned short oldlen; /* previous line length */
unsigned short len; /* current length of line */
unsigned short smod; /* start of modified data */
unsigned short emod; /* end of modified data */
unsigned short sso; /* start stand out */
unsigned short eso; /* end stand out */
unsigned char mode; /* status of line, as far as update */
/* data 必需是最後一個欄位, see screen_backup() */
unsigned char data[ANSILINELEN + 1];
} screenline_t;
typedef struct {
int r, c;
} rc_t;
typedef struct MailQueue {
char filepath[FNLEN];
char subject[STRLEN];
time4_t mailtime;
char sender[IDLEN + 1];
char username[24];
char rcpt[50];
int method;
char *niamod;
} MailQueue;
enum {MQ_TEXT, MQ_UUENCODE, MQ_JUSTIFY};
/*
* signature information
*/
typedef struct
{
int total; /* total sig files found */
int max; /* max number of available sig */
int show_start; /* by which page to start display */
int show_max; /* max covered range in last display */
} SigInfo;
/* type in gomo.c, structure passing through socket */
typedef struct {
char x;
char y;
} Horder_t;
#endif