/** * This module keeps lists of channels that are JAIL or TRAP channels. * The channels are configured at runtime. The module monitors the configured * channels for users to join. If a user joins, it will take one of three * actions: GLOBOPS, KILL, or AKILL. This option is configured on a per * channel basis at run time. ALL GLOBAL IRC OPERATORS ARE EXEMPT FROM * BEING KILLED. If your IRCd propogates local operators, they are also * exempt (but most IRCd's don't). There is also an option, on a per-chan * basis, to exempt registered nicknames. Configuration isn't necessary, and * is for customization only. * * BUG REPORTS, COMMENTS, PRAISE ARE APPRECIATED! :-p * * By: DaPrivateer * DaPrivateer@off-hours.com * DaPrivateer on irc.criten.net and irc.irchighway.net */ #include "../services.h" //I shouldn't need these, but compiler complains #include "../pseudo.h" //if they are not there :-( #include "module.h" #define MYNAME "cs_jail" #define AUTHOR "DaPrivateer" #define VERSION "1.0" //Actions that can be taken #define ACT_GLOBOPS 1 #define ACT_KILL 2 #define ACT_AKILL 3 /**************************************** * Begin Configuration Block * ****************************************/ /* This is the default action for for a JAIL channel. Number is from the list above. If a user doesn't specify an action when adding a channel, this will be used. [Required] */ #define DEFAULT_ACT 1 /* This is the default kill reason for a JAIL channel. If the user doesn't specify a message when they add a channel, this one will be used instead. [Required] */ #define DEFAULT_REASON "The channel you entered is a known bot channel. Bots are not permitted here." /* This is the time used when JAIL adds someone to the AKILL list. The time is in seconds. [Required] */ #define EXPIRY "604800" /* This is the name of the database file on the server. [Required] */ #define KC_DATABASE "jail.db" /* This is the version of the database file on the server. End users SHOULD NOT modify this. [Required] */ #define KC_DB_VER 1 /* These are the messages used by JAIL when communicating with users. If you edit them, do NOT modify the number of anything starting with a %! You should leave these alone unless you really need to modify them. */ #define KC_USAGE "Syntax: \002JAIL { ADD | SET | DEL | VIEW | LIST }\002" #define KC_ADD_USAGE "Syntax: \002JAIL ADD \037channel\037 [\037action\037 [\037reason\037]]\002" #define KC_SET_USAGE "Syntax: \002JAIL SET \037channel\037 { ACTION | REASON | REGEXEMPT } \037value\037\002" #define KC_SET_ACTION_USAGE "Syntax: \002JAIL SET \037channel\037 ACTION { GLOBOPS | KILL | AKILL }\002" #define KC_SET_REASON_USAGE "Syntax: \002JAIL SET \037channel\037 REASON \037reason\037\002" #define KC_SET_REGEXEMPT_USAGE "Syntax: \002JAIL SET \037channel\037 REGEXEMPT {ON | OFF }\002" #define KC_DEL_USAGE "Syntax: \002JAIL DEL \037channel\037\002" #define KC_VIEW_USAGE "Syntax: \002JAIL VIEW \037channel\037\002" #define KC_LIST_USAGE "Syntax: \002JAIL LIST\002" /**************************************** * End Configuration Block * ****************************************/ //******* DO NOT MODIFY ANYTHING BELOW THIS LINE ********* //******* UNLESS YOU KNOW WHAT YOU ARE DOING! ********* //Data definitions typedef struct KillChanInfo_ KillChanInfo; typedef struct KillChanHash_ KillChanHash; //typedef struct dbFILE_ dbFILE; struct KillChanInfo_ { char *name; int action; int regs; char *reason; }; struct KillChanHash_ { KillChanInfo *chan; KillChanHash *next; }; //A list of channels to monitor KillChanHash *myChans[MAX_CMD_HASH]; //Interactive functions int m_jail(User *u); int m_stats(User *u); int m_update(User *u); //Help functions void AddHelp(void); int KC_Help(User *u); int KC_Help_ADD(User *u); int KC_Help_SET(User *u); int KC_Help_SET_ACTION(User *u); int KC_Help_SET_REASON(User *u); int KC_Help_SET_REGEXEMPT(User *u); int KC_Help_DEL(User *u); int KC_Help_VIEW(User *u); int KC_Help_LIST(User *u); void KC_CS_Help(User *u); //Event handlers int handle_sjoin(char *source, int ac, char **av); //Hash functions KillChanInfo *createChannel(char *chan, int action, int regs, char *reason); KillChanInfo *findChannel(char *chan); int addChanHash(KillChanInfo *chan); int delChanHash(KillChanInfo *chan); //Database functions void load_kc_db(void); void load_kc_db_v1(dbFILE *f); void save_kc_db(void); //********************************************************************************** /**************************************** * Module Init/Term Functions * ****************************************/ //Initialization function void AnopeInit(void) { int i, status=0; Message *msg=NULL; Command *c=NULL; msg = createMessage ("SJOIN", handle_sjoin); alog("%s: Adding handler for JOIN; Result: %d", MYNAME, moduleAddMessage(msg,MOD_TAIL)); c = createCommand("JAIL", m_jail, is_services_admin, -1, -1, -1, -1, -1); status += moduleAddCommand(CHANSERV, c, MOD_HEAD); moduleAddHelp(c, KC_Help); moduleSetChanHelp(KC_CS_Help); c = createCommand("STATS", m_stats, NULL, -1, -1, -1, -1, -1); status += moduleAddCommand(OPERSERV, c, MOD_TAIL); c = createCommand("UPDATE", m_update, NULL, -1, -1, -1, -1, -1); status += moduleAddCommand(OPERSERV, c, MOD_TAIL); c = createCommand("RESTART", m_update, NULL, -1, -1, -1, -1, -1); status += moduleAddCommand(OPERSERV, c, MOD_TAIL); c = createCommand("SHUTDOWN", m_update, NULL, -1, -1, -1, -1, -1); status += moduleAddCommand(OPERSERV, c, MOD_TAIL); if (status == 0) { alog("[%s.so] Loaded successfully", MYNAME); } else { alog("[%s.so] FAILED to load - result %d", MYNAME, status); } AddHelp(); for (i=0; inick, KC_ADD_USAGE); return MOD_CONT; } reason = strtok(NULL, " "); if (reason) { if (!stricmp(reason,"GLOBOPS")) type = 1; if (!stricmp(reason,"KILL")) type = 2; if (!stricmp(reason,"AKILL")) type = 3; if (type==-1) { notice(s_ChanServ, u->nick, KC_ADD_USAGE); return MOD_CONT; } reason = strtok(NULL,""); if (!reason) reason = sstrdup(DEFAULT_REASON); } else { type = DEFAULT_ACT; reason = sstrdup(DEFAULT_REASON); } kc = findChannel(chan); if (kc) { notice(s_ChanServ,u->nick,"%s is already a Jail. Use \002SET\002 to modify it.", chan); return MOD_CONT; } kc = createChannel(chan,type,0,reason); addChanHash(kc); alog("Jail: %s added %s to Jail (Type %d - %s)", u->nick, chan, type, reason); notice(s_ChanServ, u->nick, "%s added to Jail", chan); wallops(s_ChanServ, "Jail: %s added %s to Jail (Type %d - %s)", u->nick, chan, type, reason); return MOD_CONT; } if (!stricmp(cmd,"SET")) { chan = strtok(NULL, " "); cmd = strtok(NULL, " "); if ((!chan) || (!cmd)) { notice(s_ChanServ, u->nick, KC_SET_USAGE); return MOD_CONT; } kc = findChannel(chan); if (!kc) { notice(s_ChanServ, u->nick, "%s is not on the Jail list", chan); return MOD_CONT; } if (!stricmp(cmd,"ACTION")) { reason = strtok(NULL," "); if (!reason) { notice(s_ChanServ, u->nick, KC_SET_ACTION_USAGE); return MOD_CONT; } type = -1; if (!stricmp(reason,"GLOBOPS")) type = 1; if (!stricmp(reason,"KILL")) type = 2; if (!stricmp(reason,"AKILL")) type = 3; if (type > 0) { kc->action = type; alog("Jail: %s changed ACTION for %s to %d", u->nick, chan, type); notice(s_ChanServ, u->nick, "ACTION for %s is now %s", chan, (type==1) ? "GLOBOPS" : ((type==2) ? "KILL" : "AKILL")); return MOD_CONT; } notice(s_ChanServ, u->nick, KC_SET_ACTION_USAGE); return MOD_CONT; } if (!stricmp(cmd,"REASON")) { reason = strtok(NULL, ""); if (!reason) { notice(s_ChanServ, u->nick, KC_SET_REASON_USAGE); return MOD_CONT; } kc->reason = sstrdup(reason); alog("Jail: %s changed reason for %s (%s)", u->nick, chan, reason); notice(s_ChanServ, u->nick, "REASON for %s has been updated", chan); return MOD_CONT; } if (!stricmp(cmd,"REGEXEMPT")) { reason = strtok(NULL, ""); if (!reason) { notice(s_ChanServ, u->nick, KC_SET_REGEXEMPT_USAGE); return MOD_CONT; } if (!stricmp(reason,"ON")) { kc->regs=1; alog("Jail: %s changed REGEXEMPT for %s (%s)", u->nick, chan, "ON"); notice(s_ChanServ, u->nick, "Registered nicknames are now \002EXEMPT\002 on %s", chan); return MOD_CONT; } if (!stricmp(reason,"OFF")) { kc->regs=0; alog("Jail: %s changed REGEXEMPT for %s (%s)", u->nick, chan, "OFF"); notice(s_ChanServ, u->nick, "Registered nicknames are \002NOT EXEMPT\002 on %s", chan); return MOD_CONT; } notice(s_ChanServ, u->nick, KC_SET_REGEXEMPT_USAGE); return MOD_CONT; } notice(s_ChanServ, u->nick, KC_SET_USAGE); return MOD_CONT; } if (!stricmp(cmd,"DEL")) { chan = strtok(NULL," "); if (!chan) { notice(s_ChanServ, u->nick, KC_DEL_USAGE); return MOD_CONT; } kc = findChannel(chan); if (!kc) { notice(s_ChanServ, u->nick, "%s is not in the Jail list", chan); return MOD_CONT; } delChanHash(kc); alog("Jail: %s deleted %s from Jail", u->nick, chan); notice(s_ChanServ, u->nick, "%s deleted from Jail", chan); return MOD_CONT; } if (!stricmp(cmd,"VIEW")) { chan = strtok(NULL," "); if (!chan) { notice(s_ChanServ, u->nick, KC_VIEW_USAGE); return MOD_CONT; } kc = findChannel(chan); if (!kc) { notice(s_ChanServ, u->nick, "%s is not in the Jail list", chan); return MOD_CONT; } notice(s_ChanServ, u->nick, "Jail Info For %s", kc->name); notice(s_ChanServ, u->nick, "Action: %s", (kc->action==1) ? "GLOBOPS" : ((kc->action==2) ? "KILL" : "AKILL")); notice(s_ChanServ, u->nick, "Reason: %s", kc->reason); if (kc->regs) notice(s_ChanServ, u->nick, "Registered nicks are exempt for this channel."); else notice(s_ChanServ, u->nick, "Registered nicks are NOT exempt for this channel."); notice(s_ChanServ, u->nick, "End of info"); return MOD_CONT; } if (!stricmp(cmd,"LIST")) { notice(s_ChanServ, u->nick, "Jail Channel Listing"); notice(s_ChanServ, u->nick, "Chan Action Reason"); for (type=0; typenext) { notice(s_ChanServ, u->nick, "%s %s %s", kch->chan->name, (kch->chan->action==1) ? "GLOBOPS" : ((kch->chan->action==2) ? "KILL" : "AKILL"), kch->chan->reason); } } notice(s_ChanServ, u->nick, "End of list"); return MOD_CONT; } notice(s_ChanServ, u->nick, KC_USAGE); notice(s_ChanServ, u->nick, "/%s HELP JAIL for more information", s_ChanServ); return MOD_CONT; } //Handles OperServ STATS requests int m_stats(User *u) { int count=0, i=0, mem=0; KillChanHash *current=NULL; if (!is_services_admin(u)) return MOD_CONT; for (i=0; ichan->name) mem += strlen(current->chan->name) + 1; if (current->chan->reason) mem += strlen(current->chan->reason) + 1; current=current->next; } } mem += count * sizeof(KillChanInfo); mem += 512; mem /= 1024; notice(s_OperServ, u->nick, "Jail : %d records, %d kB", count, mem); return MOD_CONT; } //Handles saving the database on OperServ events int m_update(User *u) { if (!readonly) save_kc_db(); return MOD_CONT; } /******************************************* * Help Functions * *******************************************/ //Create all the help stuff void AddHelp(void) { Command *c = NULL; c = createCommand("JAIL ADD", NULL, NULL, -1, -1, -1, -1, -1); moduleAddCommand(CHANSERV,c,MOD_TAIL); moduleAddHelp(c, KC_Help_ADD); c = createCommand("JAIL SET", NULL, NULL, -1, -1, -1, -1, -1); moduleAddCommand(CHANSERV,c,MOD_TAIL); moduleAddHelp(c, KC_Help_SET); c = createCommand("JAIL SET ACTION", NULL, NULL, -1, -1, -1, -1, -1); moduleAddCommand(CHANSERV,c,MOD_TAIL); moduleAddHelp(c, KC_Help_SET_ACTION); c = createCommand("JAIL SET REASON", NULL, NULL, -1, -1, -1, -1, -1); moduleAddCommand(CHANSERV,c,MOD_TAIL); moduleAddHelp(c, KC_Help_SET_REASON); c = createCommand("JAIL SET REGEXEMPT", NULL, NULL, -1, -1, -1, -1, -1); moduleAddCommand(CHANSERV,c,MOD_TAIL); moduleAddHelp(c, KC_Help_SET_REGEXEMPT); c = createCommand("JAIL DEL", NULL, NULL, -1, -1, -1, -1, -1); moduleAddCommand(CHANSERV,c,MOD_TAIL); moduleAddHelp(c, KC_Help_DEL); c = createCommand("JAIL LIST", NULL, NULL, -1, -1, -1, -1, -1); moduleAddCommand(CHANSERV,c,MOD_TAIL); moduleAddHelp(c, KC_Help_LIST); c = createCommand("JAIL VIEW", NULL, NULL, -1, -1, -1, -1, -1); moduleAddCommand(CHANSERV,c,MOD_TAIL); moduleAddHelp(c, KC_Help_VIEW); return; } //Handles /chanserv help jail requests int KC_Help(User *u) { notice(s_ChanServ, u->nick, KC_USAGE); notice(s_ChanServ, u->nick, " "); notice(s_ChanServ, u->nick, "JAIL allows \002Services Administrators\002 to set certain"); notice(s_ChanServ, u->nick, "channels so that ChanServ takes administrative action "); notice(s_ChanServ, u->nick, "whenever a user joins those channels. The options are "); notice(s_ChanServ, u->nick, "1) GLOBOPS whenever a user joins the channel 2) KILL a"); notice(s_ChanServ, u->nick, "user whenever they join the channel 3) AKILL a user if"); notice(s_ChanServ, u->nick, "they join the channel. The channel does not have to be"); notice(s_ChanServ, u->nick, "registered."); notice(s_ChanServ, u->nick, "The commands are: "); notice(s_ChanServ, u->nick, "ADD - Adds a channel to the list"); notice(s_ChanServ, u->nick, "SET - Set options for a channel on the list"); notice(s_ChanServ, u->nick, "DEL - Delete a channel from the list"); notice(s_ChanServ, u->nick, "VIEW - View information about a channel"); notice(s_ChanServ, u->nick, "LIST - List all channels in the list"); notice(s_ChanServ, u->nick, "For more help \002/%s HELP JAIL command\002", s_ChanServ); return MOD_CONT; } int KC_Help_ADD(User *u) { notice(s_ChanServ, u->nick, KC_ADD_USAGE); notice(s_ChanServ, u->nick, " "); notice(s_ChanServ, u->nick, "Adds a channel to the JAIL list. Channels on the "); notice(s_ChanServ, u->nick, "list are monitored by services, and action is taken "); notice(s_ChanServ, u->nick, "whenever a user joins a monitored channel."); notice(s_ChanServ, u->nick, " "); notice(s_ChanServ, u->nick, "action is one of: GLOBOPS - KILL - AKILL"); notice(s_ChanServ, u->nick, "action and reason do not have to be specified"); return MOD_CONT; } int KC_Help_SET(User *u) { notice(s_ChanServ, u->nick, KC_SET_USAGE); notice(s_ChanServ, u->nick, " "); notice(s_ChanServ, u->nick, "SET allows you to set various options for channels that are"); notice(s_ChanServ, u->nick, "in the JAIL database."); notice(s_ChanServ, u->nick, "Options are:"); notice(s_ChanServ, u->nick, "ACTION - The action taken by ChanServ"); notice(s_ChanServ, u->nick, "REASON - The reason given by ChanServ"); notice(s_ChanServ, u->nick, "REGEXEMPT - Sets if registered nicknames are exempt"); notice(s_ChanServ, u->nick, "For more info \002/%s HELP JAIL SET option\002", s_ChanServ); return MOD_CONT; } int KC_Help_SET_ACTION(User *u) { notice(s_ChanServ, u->nick, KC_SET_ACTION_USAGE); notice(s_ChanServ, u->nick, " "); notice(s_ChanServ, u->nick, "Sets the action for ChanServ to take when a user joins a "); notice(s_ChanServ, u->nick, "monitored channel. Options are: GLOBOPS - KILL - AKILL"); return MOD_CONT; } int KC_Help_SET_REASON(User *u) { notice(s_ChanServ, u->nick, KC_SET_REASON_USAGE); notice(s_ChanServ, u->nick, " "); notice(s_ChanServ, u->nick, "Sets the reason ChanServ gives when KILLing or AKILLing a "); notice(s_ChanServ, u->nick, "user for joining a monitored channel."); return MOD_CONT; } int KC_Help_SET_REGEXEMPT(User *u) { notice(s_ChanServ, u->nick, KC_SET_REGEXEMPT_USAGE); notice(s_ChanServ, u->nick, " "); notice(s_ChanServ, u->nick, "Determines if ChanServ will take action against register nicks"); notice(s_ChanServ, u->nick, "when they join a monitored channel. If set to \002YES\002 registered "); notice(s_ChanServ, u->nick, "nicknames are exempt from action by Jail. If \002NO\002, they will "); notice(s_ChanServ, u->nick, "not be exempt."); return MOD_CONT; } int KC_Help_DEL(User *u) { notice(s_ChanServ, u->nick, KC_DEL_USAGE); notice(s_ChanServ, u->nick, " "); notice(s_ChanServ, u->nick, "Deletes a channel from the JAIL list and removes "); notice(s_ChanServ, u->nick, "services monitoring of that channel"); return MOD_CONT; } int KC_Help_VIEW(User *u) { notice(s_ChanServ, u->nick, KC_VIEW_USAGE); notice(s_ChanServ, u->nick, " "); notice(s_ChanServ, u->nick, "Allows you to view the information for a channel on the"); notice(s_ChanServ, u->nick, "JAIL list."); return MOD_CONT; } int KC_Help_LIST(User *u) { notice(s_ChanServ, u->nick, KC_LIST_USAGE); notice(s_ChanServ, u->nick, " "); notice(s_ChanServ, u->nick, "Lists all the channels in the JAIL list."); return MOD_CONT; } void KC_CS_Help(User *u) { if (is_services_admin(u)) { notice(s_ChanServ, u->nick, " JAIL Manipulate the jail list"); } return; } /**************************************** * Event Handlers * ****************************************/ //Handles incoming joins int handle_sjoin(char *source, int ac, char **av) { KillChanInfo *kc = NULL; char *userH, *user; char mask[100]; SList victims; User *u = NULL; int i=0; if (!source || !av[1]) return MOD_CONT; if (!(kc=findChannel(av[1]))) return MOD_CONT; //Not in our list, ignore it! slist_init(&victims); slist_setcapacity(&victims,0); if (!av[3]) { if ((u=finduser(source))) slist_add(&victims,u); } else { user = myStrGetToken(av[3],' ',0); userH = user; while (user) { while (user && ((*user==':') || (*user=='@'))) user++; if (!user) { free(userH); user = myStrGetToken(av[3],' ',++i); userH = user; continue; } if ((u=finduser(user))) slist_add(&victims,u); free(userH); user = myStrGetToken(av[3],' ',++i); userH = user; } } if (kc->action == ACT_GLOBOPS) { for (i=0; iregs && (((User *)victims.list[i])->mode & UMODE_r)) continue; if (is_oper((User *)victims.list[i])) continue; #ifdef UMODE_O if (((User *)victims.list[i])->mode & UMODE_O) continue; #endif wallops(s_ChanServ, "JAIL: %s!%s@%s join a Jail (%s)", u->nick, u->username, u->host, kc->name); } slist_clear(&victims, 0); return MOD_CONT; } if (kc->action == ACT_KILL) { for (i=0; iregs && (((User *)victims.list[i])->mode & UMODE_r)) continue; if (is_oper((User *)victims.list[i])) continue; #ifdef UMODE_O if (((User *)victims.list[i])->mode & UMODE_O) continue; #endif alog("Jail: Killing %s!%s@%s (user joined %s)", u->nick, u->username, u->host, kc->name); kill_user(s_ChanServ, u->nick, kc->reason); } slist_clear(&victims, 0); return MOD_CONT; } if (kc->action == ACT_AKILL) { for (i=0; iregs && (((User *)victims.list[i])->mode & UMODE_r)) continue; if (is_oper((User *)victims.list[i])) continue; #ifdef UMODE_O if (((User *)victims.list[i])->mode & UMODE_O) continue; #endif sprintf(mask,"*@%s",u->host); alog("Jail: AKilling %s!%s@%s (user joined %s)", u->nick, u->username, u->host, kc->name); wallops(s_OperServ, "%s added an akill for %s (Jail: %s)", s_ChanServ, mask, kc->name); add_akill(NULL, mask, s_ChanServ, dotime(EXPIRY), kc->reason); } slist_clear(&victims, 0); return MOD_CONT; } slist_clear(&victims, 0); return MOD_CONT; } /******************************************* * Hash Utilities - DO NOT MODIFY! * *******************************************/ KillChanInfo *createChannel(char *chan, int action, int regs, char *reason) { KillChanInfo *kc; if (!chan) return NULL; if ((kc = malloc(sizeof(KillChanInfo))) == NULL) fatal("Out Of Memory!"); kc->name = sstrdup(chan); kc->action = action; kc->regs = regs; kc->reason = sstrdup(reason); return kc; } KillChanInfo *findChannel(char *name) { int index; KillChanHash *current = NULL; if (!name) return NULL; index = CMD_HASH((name)); for (current = myChans[index]; current; current = current->next) { if (!stricmp(name,current->chan->name)) { return current->chan; } } return NULL; } int addChanHash(KillChanInfo *chan) { int index; KillChanHash *current = NULL; KillChanHash *previous = NULL; KillChanHash *nexthash = NULL; index = CMD_HASH((chan->name)); for (current = myChans[index]; current; current = current->next) { if (!stricmp(chan->name, current->chan->name)) return -1; previous = current; } if ((nexthash = malloc(sizeof(KillChanHash))) == NULL) { fatal("Out Of Memory!"); } nexthash->next = NULL; nexthash->chan = chan; if (previous == NULL) myChans[index] = nexthash; else previous->next = nexthash; return 0; } int delChanHash(KillChanInfo *chan) { int index; KillChanHash *current = NULL; KillChanHash *previous = NULL; if (!chan) { return -1; } index = CMD_HASH((chan->name)); for (current = myChans[index]; current; current = current->next) { if (!stricmp(chan->name, current->chan->name)) { if (!previous) { myChans[index] = current->next; } else { previous->next = current->next; } free(current->chan->name); free(current->chan->reason); free(current->chan); free(current); return 0; } previous = current; } return -1; } /******************************************* * Database Utilities - DO NOT MODIFY! * *******************************************/ #define SAFE(x) do { \ if ((x) < 0) { \ if (!forceload) \ fatal("Read error on %s", KC_DATABASE); \ failed = 1; \ break; \ } \ } while (0) void load_kc_db(void) { dbFILE *f; int ver; if (!(f = open_db(s_ChanServ, KC_DATABASE, "r", KC_DB_VER))) { return; } ver = get_file_version(f); if (ver == 1) { load_kc_db_v1(f); } close_db(f); } void load_kc_db_v1(dbFILE *f) { int c; int failed; char *name; int action, regs; char *reason; KillChanInfo *kc; alog("Jail: Loading database"); failed = 0; while(!failed && ((c = getc_db(f)) == 1)) { if (c==1) { SAFE(read_string(&name,f)); SAFE(read_int8(&action,f)); SAFE(read_int8(®s,f)); SAFE(read_string(&reason,f)); kc = createChannel(name,action, regs, reason); addChanHash(kc); free(name); free(reason); } else { fatal("Invalid format in %s %d", KC_DATABASE, c); } } } #undef SAFE #define SAFE(x) do { \ if ((x) < 0) { \ restore_db(f); \ log_perror("Write error on %s", KC_DATABASE); \ if (time(NULL) - lastwarn > WarningTimeout) { \ wallops(NULL, "Write error on %s: %s", KC_DATABASE, strerror(errno)); \ lastwarn = time(NULL); \ } \ return; \ } \ } while (0) void save_kc_db(void) { dbFILE *f; static time_t lastwarn=0; int i; KillChanHash *current; if (!(f = open_db(s_ChanServ, KC_DATABASE, "w", KC_DB_VER))) { return; } for (i=0; ichan->name,f)); SAFE(write_int8(current->chan->action,f)); SAFE(write_int8(current->chan->regs,f)); SAFE(write_string(current->chan->reason,f)); current=current->next; } } SAFE(write_int8(0,f)); close_db(f); } #undef SAFE