/* * ================================================================== * Filename: m_staff.c * Description: Command /staff * Written by: AngryWolf * Documentation: m_staff.txt (comes with the package) * ================================================================== */ #include "config.h" #include "struct.h" #include "common.h" #include "sys.h" #include "numeric.h" #include "msg.h" #include "channel.h" #include #include #include #include #include #ifdef _WIN32 #include #endif #include #include "h.h" #ifdef STRIPBADWORDS #include "badwords.h" #endif #ifdef _WIN32 #include "version.h" #endif extern void sendto_one(aClient *to, char *pattern, ...); extern void sendto_realops(char *pattern, ...); extern ConfigEntry *config_find_entry(ConfigEntry *, char *); #ifdef USE_LIBCURL extern char *download_file(char *url, char **error); extern void download_file_async(char *url, time_t cachetime, vFP callback); extern char *url_getfilename(char *url); extern int url_is_valid(char *string); #endif #define MSG_STAFF "STAFF" #define TOK_STAFF "ST" #define DEF_STAFF_FILE "network.staff" #define CONF_STAFF_FILE (staff_file ? staff_file : DEF_STAFF_FILE) #ifdef USE_LIBCURL #define STAFF_FILE (Download.path ? Download.path : CONF_STAFF_FILE) #else #define STAFF_FILE CONF_STAFF_FILE #endif #define RPL_STAFF ":%s 700 %s :- %s" #define RPL_STAFFSTART ":%s 701 %s :- %s IRC Network Staff Information -" #define RPL_ENDOFSTAFF ":%s 702 %s :End of /STAFF command." #define RPL_NOSTAFF ":%s 703 %s :Network Staff File is missing" #define DelHook(x) if (x) HookDel(x); x = NULL #define DelCommand(x) if (x) CommandDel(x); x = NULL #define ircstrdup(x,y) if (x) MyFree(x); if (!y) x = NULL; else x = strdup(y) #define ircfree(x) if (x) MyFree(x); x = NULL static Command *AddCommand(Module *module, char *msg, char *token, iFP func); static void unload_motd_file(aMotd **list); static int m_staff(aClient *cptr, aClient *sptr, int parc, char *parv[]); #ifdef HOOKTYPE_PRE_LOCAL_PART static int cb_rehashflag(aClient *cptr, aClient *sptr, char *flag); #else static int cb_rehashflag(char *flag); #endif static int cb_test(ConfigFile *, ConfigEntry *, int, int *); static int cb_conf(ConfigFile *, ConfigEntry *, int); static int cb_rehash(); static int cb_stats(aClient *sptr, char *flag); #ifdef USE_LIBCURL static int download_staff_file(ConfigEntry *ce); static void download_staff_file_complete(char *url, char *file, char *errorbuf, int cached); #endif static void InitConf(); static void FreeConf(); Command *CmdStaff; Hook *HookConfTest, *HookConfRun, *HookConfRehash, *HookStats; Hook *HookRehashFlag; static aMotd *staff; static char *staff_file; #ifdef USE_LIBCURL struct { unsigned is_url : 1; unsigned once_completed : 1; unsigned in_progress : 1; char *file; // File name char *path; // File path char *url; // Full URL address } Download; #endif ModuleHeader MOD_HEADER(m_staff) = { "staff", "$Id: m_staff.c,v 2.5 2004/03/27 18:04:51 angrywolf Exp $", "command /staff", "3.2-b8-1", NULL }; DLLFUNC int MOD_TEST(m_staff)(ModuleInfo *modinfo) { HookConfTest = HookAddEx(modinfo->handle, HOOKTYPE_CONFIGTEST, cb_test); return MOD_SUCCESS; } DLLFUNC int MOD_INIT(m_staff)(ModuleInfo *modinfo) { #ifdef USE_LIBCURL memset(&Download, 0, sizeof Download); #ifndef STATIC_LINKING ModuleSetOptions(modinfo->handle, MOD_OPT_PERM); #endif #endif InitConf(); CmdStaff = AddCommand(modinfo->handle, MSG_STAFF, TOK_STAFF, m_staff); HookConfRun = HookAddEx(modinfo->handle, HOOKTYPE_CONFIGRUN, cb_conf); HookConfRehash = HookAddEx(modinfo->handle, HOOKTYPE_REHASH, cb_rehash); HookRehashFlag = HookAddEx(modinfo->handle, HOOKTYPE_REHASHFLAG, cb_rehashflag); HookStats = HookAddEx(modinfo->handle, HOOKTYPE_STATS, cb_stats); if (!CmdStaff) return MOD_FAILED; return MOD_SUCCESS; } DLLFUNC int MOD_LOAD(m_staff)(int module_load) { return MOD_SUCCESS; } DLLFUNC int MOD_UNLOAD(m_staff)(int module_unload) { FreeConf(); unload_motd_file(&staff); DelCommand(CmdStaff); DelHook(HookStats); DelHook(HookRehashFlag); DelHook(HookConfRehash); DelHook(HookConfRun); DelHook(HookConfTest); #ifdef USE_LIBCURL ircfree(Download.path); ircfree(Download.file); ircfree(Download.url); #endif return MOD_SUCCESS; } static int cb_rehash() { FreeConf(); InitConf(); return 1; } static void InitConf() { staff_file = NULL; } static void FreeConf() { if (staff_file) MyFree(staff_file); } static Command *AddCommand(Module *module, char *msg, char *token, iFP func) { Command *cmd; if (CommandExists(msg)) { config_error("Command %s already exists", msg); return NULL; } if (CommandExists(token)) { config_error("Token %s already exists", token); return NULL; } cmd = CommandAdd(module, msg, token, func, MAXPARA, 0); #ifndef STATIC_LINKING if (ModuleGetError(module) != MODERR_NOERROR || !cmd) #else if (!cmd) #endif { #ifndef STATIC_LINKING config_error("Error adding command %s: %s", msg, ModuleGetErrorStr(module)); #else config_error("Error adding command %s", msg); #endif return NULL; } return cmd; } #ifdef USE_LIBCURL static void remove_staff_file() { if (Download.path) { if (remove(Download.path) == -1) { if (config_verbose > 0) config_status("Cannot remove file %s: %s", Download.path, strerror(errno)); } MyFree(Download.path); Download.path = NULL; } } static int download_staff_file(ConfigEntry *ce) { int ret = 0; struct stat sb; char *file, *filename; if (Download.in_progress) return 0; Download.is_url = 1; ircstrdup(Download.url, ce->ce_vardata); file = url_getfilename(ce->ce_vardata); filename = unreal_getfilename(file); ircstrdup(Download.file, filename); MyFree(file); if (!loop.ircd_rehashing && !Download.once_completed) { char *error; if (config_verbose > 0) config_status("Downloading %s", Download.url); if (!(file = download_file(ce->ce_vardata, &error))) { config_error("%s:%i: test: error downloading '%s': %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata, error); return -1; } Download.once_completed = 1; ircstrdup(Download.path, file); staff = (aMotd *) read_file(Download.path, &staff); MyFree(file); return 0; } file = Download.path ? Download.path : Download.file; if ((ret = stat(file, &sb)) && errno != ENOENT) { /* I know, stat shouldn't fail... */ config_error("%s:%i: could not get the creation time of %s: stat() returned %d: %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, Download.file, ret, strerror(errno)); return -1; } if (config_verbose > 0) config_status("Downloading %s", Download.url); Download.in_progress = 1; download_file_async(Download.url, sb.st_ctime, download_staff_file_complete); return 0; } static void download_staff_file_complete(char *url, char *file, char *errorbuf, int cached) { Download.in_progress = 0; Download.once_completed = 1; if (!cached) { if (!file) { config_error("Error downloading %s: %s", url, errorbuf); return; } remove_staff_file(); Download.path = strdup(file); staff = (aMotd *) read_file(Download.path, &staff); } else { char *urlfile = url_getfilename(url); char *file = unreal_getfilename(urlfile); char *tmp = unreal_mktemp("tmp", file); unreal_copyfile(Download.path, tmp); remove_staff_file(); Download.path = strdup(tmp); MyFree(urlfile); } } #endif static void unload_motd_file(aMotd **list) { aMotd *old; if (!list) return; while (*list) { old = (*list)->next; MyFree((*list)->line); MyFree(*list); *list = old; } } #define CHECK_EMPTY(ce, parent) \ if (!(ce)->ce_varname) \ { \ config_error("%s:%i: blank %s item", \ (ce)->ce_fileptr->cf_filename, \ (ce)->ce_varlinenum, (parent)->ce_varname); \ errors++; \ continue; \ } \ if (!(ce)->ce_vardata) \ { \ config_error("%s:%i: %s::%s without value", \ (ce)->ce_fileptr->cf_filename, \ (ce)->ce_varlinenum, \ (parent)->ce_varname, (ce)->ce_varname); \ errors++; \ continue; \ } static int cb_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) { int errors = 0; #ifdef USE_LIBCURL char *file = NULL, *filename = NULL; #endif if (type == CONFIG_SET) { if (!strcmp(ce->ce_varname, "staff-file")) { #ifdef USE_LIBCURL if (url_is_valid(ce->ce_vardata)) { if (!(file = url_getfilename(ce->ce_vardata)) || !(filename = unreal_getfilename(file))) { config_error("%s:%i: invalid filename in URL", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); errors++; } ircfree(file); } #endif *errs = errors; return errors ? -1 : 1; } } return 0; } static int cb_conf(ConfigFile *cf, ConfigEntry *ce, int type) { if (type == CONFIG_SET) { if (!strcmp(ce->ce_varname, "staff-file")) { #ifdef USE_LIBCURL if (!Download.in_progress) { ircstrdup(staff_file, ce->ce_vardata); if (url_is_valid(ce->ce_vardata)) download_staff_file(ce); else #endif staff = (aMotd *) read_file(CONF_STAFF_FILE, &staff); #ifdef USE_LIBCURL } #endif return 1; } } return 0; } static int cb_stats(aClient *sptr, char *flag) { if (*flag == 'S') sendto_one(sptr, ":%s %i %s :staff-file: %s", me.name, RPL_TEXT, sptr->name, STAFF_FILE); return 0; } #ifdef HOOKTYPE_PRE_LOCAL_PART static int cb_rehashflag(aClient *cptr, aClient *sptr, char *flag) #else static int cb_rehashflag(char *flag) #endif { int myflag = 0; /* "-all" only keeps compatibility with beta19 */ if (!_match("-all", flag) || (myflag = !_match("-staff", flag))) { if (myflag) #ifdef HOOKTYPE_PRE_LOCAL_PART sendto_ops("%sRehashing network staff file on the request of %s", cptr != sptr ? "Remotely " : "", sptr->name); #else sendto_ops("Rehashing network staff file"); #endif #ifdef USE_LIBCURL if (Download.is_url) staff = (aMotd *) read_file(Download.path, &staff); else #endif staff = (aMotd *) read_file(CONF_STAFF_FILE, &staff); } return 0; } int m_staff(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aMotd *temp; if (!IsPerson(sptr)) return -1; if (hunt_server_token(cptr, sptr, MSG_STAFF, TOK_STAFF, ":%s", 1, parc, parv) != HUNTED_ISME) return 0; if (staff == (aMotd *) NULL) { sendto_one(sptr, RPL_NOSTAFF, me.name, sptr->name); return 0; } sendto_one(sptr, RPL_STAFFSTART, me.name, sptr->name, ircnetwork); for (temp = staff; temp; temp = temp->next) sendto_one(sptr, RPL_STAFF, me.name, sptr->name, temp->line); sendto_one(sptr, RPL_ENDOFSTAFF, me.name, sptr->name); return 0; }