/* * ================================================================= * Filename: langhelper.c * Description: Language helper module * Written by: AngryWolf * Requested by: Sergios * Documentation: langhelper.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 #ifndef EXTCMODE #error "This module requires extended channel modes to be enabled." #error "See the EXTCMODE macro in include/config.h for more information." #error "Compilation aborted." #endif /* Backward compatibility */ #ifndef EX_DENY #define EX_DENY 0 #define EX_ALLOW 1 #define EX_ALWAYS_DENY 0 #endif typedef struct _langlist LangList; typedef struct _convlist ConvList; typedef struct _langsetting LangSetting; typedef struct { EXTCM_PAR_HEADER char *lang; } aModeLHEntry; struct _langlist { LangList *prev, *next; char *name; ConvList *conv; }; struct _convlist { ConvList *prev, *next; char *from, *to; }; struct _langsetting { LangSetting *prev, *next; aClient *cptr; char *lang; }; extern void sendto_one(aClient *to, char *pattern, ...); #define FLAG_LANGHELPER 'H' #define MSG_LANGUAGE "LANGUAGE" #define TOK_LANGUAGE NULL #define HasLangHelper(x) ((x)->mode.extmode & MODE_LANGHELPER) #define DelHook(x) if (x) HookDel(x); x = NULL #define DelCmode(x) if (x) CmodeDel(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 #define IsParam(x) (parc > (x) && !BadPtr(parv[(x)])) #define IsNotParam(x) (parc <= (x) || BadPtr(parv[(x)])) static int m_language(aClient *cptr, aClient *sptr, int parc, char *parv[]); static int cb_config_test(ConfigFile *, ConfigEntry *, int, int *); static int cb_config_run(ConfigFile *, ConfigEntry *, int); static int cb_config_rehash(); static int cb_rehash_complete(); static char *cb_privmsg(aClient *, aClient *, aClient *, char *, int); static char *cb_chanmsg(aClient *, aClient *, aChannel *, char *, int); static int cb_quit(aClient *, char *); static char *convert_msg(LangList *lang, char *text); static void InitConf(); static void FreeConf(); static void del_LangSettings(); static LangList *find_Language(char *name); static Command *AddCommand(Module *module, char *msg, char *token, iFP func); static Cmode *AddCmode(Module *module, CmodeInfo *req, Cmode_t *mode); static int chmode_is_ok(aClient *, aChannel *, char *, int, int); static CmodeParam *chmode_put_param(CmodeParam *, char *); static char *chmode_get_param(CmodeParam *); static char *chmode_conv_param(char *); static void chmode_free_param(CmodeParam *); static CmodeParam *chmode_dup_struct(CmodeParam *); static int chmode_sjoin_check(aChannel *, CmodeParam *, CmodeParam *); static Hook *HookConfTest, *HookConfRun, *HookConfRehash; static Hook *HookPrivMsg, *HookChanMsg, *HookQuit; static Hook *HookRehashDone; static LangList *Languages, *GlobalLang; static LangSetting *LangSettings; static Cmode_t MODE_LANGHELPER = 0L; static Cmode *CmodeLanghelper = NULL; static Command *CmdLanguage; static char *global_language = NULL; ModuleHeader MOD_HEADER(langhelper) = { "langhelper", "$Id: langhelper.c,v 1.4 2004/06/29 19:49:17 angrywolf Exp $", "Language helper", "3.2-b8-1", NULL }; DLLFUNC int MOD_TEST(langhelper)(ModuleInfo *modinfo) { HookConfTest = HookAddEx(modinfo->handle, HOOKTYPE_CONFIGTEST, cb_config_test); return MOD_SUCCESS; } DLLFUNC int MOD_INIT(langhelper)(ModuleInfo *modinfo) { CmodeInfo CmodeLH; #ifndef STATIC_LINKING ModuleSetOptions(modinfo->handle, MOD_OPT_PERM); #endif InitConf(); memset(&CmodeLH, 0, sizeof CmodeLH); CmodeLH.flag = FLAG_LANGHELPER; CmodeLH.paracount = 1; CmodeLH.is_ok = chmode_is_ok; CmodeLH.put_param = chmode_put_param; CmodeLH.get_param = chmode_get_param; CmodeLH.conv_param = chmode_conv_param; CmodeLH.free_param = chmode_free_param; CmodeLH.sjoin_check = chmode_sjoin_check; CmodeLH.dup_struct = chmode_dup_struct; CmodeLanghelper = AddCmode(modinfo->handle, &CmodeLH, &MODE_LANGHELPER); HookConfRun = HookAddEx(modinfo->handle, HOOKTYPE_CONFIGRUN, cb_config_run); HookConfRehash = HookAddEx(modinfo->handle, HOOKTYPE_REHASH, cb_config_rehash); HookRehashDone = HookAddEx(modinfo->handle, HOOKTYPE_REHASH_COMPLETE, cb_rehash_complete); HookPrivMsg = HookAddPCharEx(modinfo->handle, HOOKTYPE_USERMSG, cb_privmsg); HookChanMsg = HookAddPCharEx(modinfo->handle, HOOKTYPE_CHANMSG, cb_chanmsg); HookQuit = HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_QUIT, cb_quit); CmdLanguage = AddCommand(modinfo->handle, MSG_LANGUAGE, TOK_LANGUAGE, m_language); if (!CmdLanguage) return MOD_FAILED; return MOD_SUCCESS; } DLLFUNC int MOD_LOAD(langhelper)(int module_load) { cb_rehash_complete(); return MOD_SUCCESS; } DLLFUNC int MOD_UNLOAD(langhelper)(int module_unload) { FreeConf(); del_LangSettings(); DelCommand(CmdLanguage); DelCmode(CmodeLanghelper); DelHook(HookChanMsg); DelHook(HookPrivMsg); DelHook(HookRehashDone); DelHook(HookConfRehash); DelHook(HookConfRun); DelHook(HookConfTest); DelHook(HookQuit); return MOD_SUCCESS; } static void InitConf() { Languages = NULL; GlobalLang = NULL; global_language = NULL; } static void FreeConf() { ListStruct *next, *next2; LangList *l; ConvList *c; if (global_language) MyFree(global_language); for (l = Languages; l; l = (LangList *) next) { next = (ListStruct *) l->next; DelListItem(l, Languages); MyFree(l->name); for (c = l->conv; c; c = (ConvList *) next2) { next2 = (ListStruct *) c->next; DelListItem(c, l->conv); MyFree(c->from); MyFree(c->to); MyFree(c); } MyFree(l); } } static int cb_config_rehash() { FreeConf(); InitConf(); return 1; } static int cb_rehash_complete() { if (global_language) { if (!(GlobalLang = find_Language(global_language))) config_status("set::global-language: " "unknown language name %s", global_language); } return 0; } static Cmode *AddCmode(Module *module, CmodeInfo *req, Cmode_t *mode) { Cmode *cmode; *mode = 0; cmode = CmodeAdd(module, *req, mode); #ifndef STATIC_LINKING if (ModuleGetError(module) != MODERR_NOERROR || !cmode) #else if (!cmode) #endif { #ifndef STATIC_LINKING config_error("[langhelper] Error adding channel mode +%c when loading module %s: %s", req->flag, MOD_HEADER(langhelper).name, ModuleGetErrorStr(module)); #else config_error("[langhelper] Error adding channel mode +%c when loading module %s", req->flag, MOD_HEADER(langhelper).name); #endif return NULL; } return cmode; } static Command *AddCommand(Module *module, char *msg, char *token, iFP func) { Command *cmd; if (CommandExists(msg)) { config_error("[langhelper] Command %s already exists", msg); return NULL; } if (token && CommandExists(token)) { config_error("[langhelper] 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("[langhelper] Error adding command %s: %s", msg, ModuleGetErrorStr(module)); #else config_error("[langhelper] Error adding command %s", msg); #endif return NULL; } return cmd; } LangList *find_Language(char *name) { LangList *l; for (l = Languages; l; l = l->next) if (!strcmp(name, l->name)) break; return l; } ConvList *find_Conversion(ConvList *conv, char *from) { ConvList *c; for (c = conv; c; c = c->next) if (!strcmp(from, c->from)) break; return c; } static LangSetting *find_LangSetting(aClient *cptr) { LangSetting *s; for (s = LangSettings; s; s = s->next) if (s->cptr == cptr) break; return s; } static LangSetting *do_LangSetting(aClient *cptr, char *lang) { LangSetting *s = find_LangSetting(cptr); if (!lang) { if (s) { DelListItem(s, LangSettings); MyFree(s->lang); MyFree(s); } return NULL; } if (!s) { s = (LangSetting *) MyMallocEx(sizeof(LangSetting)); s->cptr = cptr; AddListItem(s, LangSettings); } else MyFree(s->lang); s->lang = strdup(lang); return s; } static void del_LangSettings() { ListStruct *next; LangSetting *s; for (s = LangSettings; s; s = (LangSetting *) next) { next = (ListStruct *) s->next; DelListItem(s, LangSettings); MyFree(s->lang); MyFree(s); } } #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_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) { ConfigEntry *cep; int errors = 0; if (type == CONFIG_MAIN) { if (!strcmp(ce->ce_varname, "language")) { if (!ce->ce_vardata) { config_error("%s:%i: %s block without parameter", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_varname); errors++; } else { for (cep = ce->ce_entries; cep; cep = cep->ce_next) { CHECK_EMPTY(cep, ce) } } *errs = errors; return errors ? -1 : 1; } } else if (type == CONFIG_SET) { if (!strcmp(ce->ce_varname, "global-language")) { if (!ce->ce_vardata) { config_error("%s:%i: set::%s without parameter", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_varname); errors++; } *errs = errors; return errors ? -1 : 1; } } return 0; } static int cb_config_run(ConfigFile *cf, ConfigEntry *ce, int type) { ConfigEntry *cep; LangList *l; ConvList *c; if (type == CONFIG_MAIN) { if (!strcmp(ce->ce_varname, "language")) { if (find_Language(ce->ce_vardata)) { config_status("%s:%i: language %s already exists", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); return 1; } l = (LangList *) MyMallocEx(sizeof(LangList)); l->name = strdup(ce->ce_vardata); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (find_Conversion(l->conv, cep->ce_varname)) { config_status("%s:%i: multiple definition for %s::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, ce->ce_varname, cep->ce_varname); continue; } c = (ConvList *) MyMallocEx(sizeof(ConvList)); c->from = strdup(cep->ce_varname); c->to = strdup(cep->ce_vardata); AddListItem(c, l->conv); } AddListItem(l, Languages); return 1; } } else if (type == CONFIG_SET) { if (!strcmp(ce->ce_varname, "global-language")) { ircstrdup(global_language, ce->ce_vardata); } return 1; } return 0; } static int cb_quit(aClient *sptr, char *comment) { do_LangSetting(sptr, NULL); return 0; } static int chmode_is_ok(aClient *sptr, aChannel *chptr, char *param, int type, int what) { if (type == EXCHK_ACCESS || type == EXCHK_ACCESS_ERR) { if (!IsPerson(sptr) || IsULine(sptr)) return EX_ALLOW; if (!is_chan_op(sptr, chptr)) { if (type == EXCHK_ACCESS_ERR) sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, sptr->name, chptr->chname); return EX_DENY; } return EX_ALLOW; } else if (type == EXCHK_PARAM) { if (!find_Language(param)) { sendnotice(sptr, "%s: No such language", param); return 0; } return 1; } return 0; } static CmodeParam *chmode_put_param(CmodeParam *param, char *text) { aModeLHEntry *m = (aModeLHEntry *) param; if (!m) { m = (aModeLHEntry *) MyMallocEx(sizeof(aModeLHEntry)); m->flag = FLAG_LANGHELPER; } if (m->lang) MyFree(m->lang); m->lang = strdup(text); return (CmodeParam *) m; } static char *chmode_get_param(CmodeParam *param) { aModeLHEntry *m = (aModeLHEntry *) param; if (!m) return NULL; return m->lang; } static char *chmode_conv_param(char *param) { return param; } static void chmode_free_param(CmodeParam *param) { aModeLHEntry *m = (aModeLHEntry *) param; MyFree(m->lang); MyFree(m); } static CmodeParam *chmode_dup_struct(CmodeParam *param) { aModeLHEntry *in, *out; out = (aModeLHEntry *) MyMallocEx(sizeof(aModeLHEntry)); in = (aModeLHEntry *) param; out->flag = in->flag; out->lang = strdup(in->lang); return (CmodeParam *) out; } static int chmode_sjoin_check(aChannel *chptr, CmodeParam *ours, CmodeParam *theirs) { int ret; aModeLHEntry *o, *t; o = (aModeLHEntry *) ours; t = (aModeLHEntry *) theirs; ret = strcmp(o->lang, t->lang); if (!ret) return EXSJ_SAME; else if (ret > 0) return EXSJ_THEYWON; else return EXSJ_WEWON; } static char *cb_privmsg(aClient *cptr, aClient *from, aClient *to, char *text, int notice) { LangSetting *s; LangList *lang; if (!IsPerson(from) || IsULine(from)) return text; if ((s = find_LangSetting(to)) && (lang = find_Language(s->lang))) return convert_msg(lang, text); else if (GlobalLang) return convert_msg(GlobalLang, text); return text; } static char *cb_chanmsg(aClient *cptr, aClient *from, aChannel *chptr, char *text, int notice) { LangList *lang; if (!IsPerson(from) || IsULine(from)) return text; if (HasLangHelper(chptr)) { aModeLHEntry *mode = (aModeLHEntry *) extcmode_get_struct( chptr->mode.extmodeparam, FLAG_LANGHELPER); if ((lang = find_Language(mode->lang))) return convert_msg(lang, text); } else if (GlobalLang) return convert_msg(GlobalLang, text); return text; } static char *convert_msg(LangList *lang, char *text) { static char buf[1024], buf2[1024]; ConvList *conv; char *start, *stop; for (conv = lang->conv, strcpy(buf, text); conv; conv = conv->next, strcpy(buf, buf2)) { buf2[0] = 0; for (start = buf, stop = strstr(buf, conv->from); stop; start = stop + strlen(conv->from), stop = strstr(start, conv->from)) { strncat(buf2, start, stop - start); strcat(buf2, conv->to); } strcat(buf2, start); } return buf; } /* * m_language * * parv[0]: sender prefix * parv[1]: language (optional) */ static int m_language(aClient *cptr, aClient *sptr, int parc, char *parv[]) { LangList *lang = NULL; if (!MyClient(sptr) || !IsPerson(sptr)) return 0; if (IsParam(1) && !(lang = find_Language(parv[1]))) { sendnotice(sptr, "*** %s: No such language", parv[1]); return 0; } do_LangSetting(sptr, lang ? lang->name : NULL); if (lang) sendnotice(sptr, "*** Language has been set to %s", parv[1]); else sendnotice(sptr, "*** Your language setting has been cleared"); return 0; }