/* * Copyright (c) 2007 Jilles Tjoelker * Copyright (c) 2008 Robin Burchell * Rights to this code are as documented in doc/LICENSE. * * Services-side autojoin using SVSJOIN * */ #include "atheme-compat.h" #include "uplink.h" DECLARE_MODULE_V1 ( "contrib/ns_ajoin", false, _modinit, _moddeinit, PACKAGE_STRING, "Atheme Development Group " ); static void ajoin_on_identify(user_t *u); static void ns_cmd_ajoin(sourceinfo_t *si, int parc, char *parv[]) { char buf[512]; char *chan; metadata_t *md; if (!parv[0]) { command_fail(si, fault_badparams, STR_INSUFFICIENT_PARAMS, "AJOIN"); command_fail(si, fault_badparams, "Syntax: AJOIN [#channel]"); return; } if (!strcasecmp(parv[0], "LIST")) { command_success_nodata(si, "\2AJOIN LIST\2:"); if ((md = metadata_find(si->smu, "private:autojoin"))) { mowgli_strlcpy(buf, md->value, sizeof buf); chan = strtok(buf, ","); while (chan != NULL) { command_success_nodata(si, "%s", chan); chan = strtok(NULL, ","); } } command_success_nodata(si, "End of \2AJOIN LIST\2"); } else if (!strcasecmp(parv[0], "ADD")) { if (!parv[1]) { command_fail(si, fault_badparams, STR_INSUFFICIENT_PARAMS, "AJOIN"); command_fail(si, fault_badparams, "Syntax: AJOIN [#channel]"); return; } if ((md = metadata_find(si->smu, "private:autojoin"))) { mowgli_strlcpy(buf, md->value, sizeof buf); chan = strtok(buf, ","); while (chan != NULL) { if (!strcasecmp(chan, parv[1])) { command_fail(si, fault_badparams, "%s is already on your AJOIN list.", parv[1]); return; } chan = strtok(NULL, ","); } // Little arbitrary, but stop both overflow and RAM consumption going out of control if (strlen(md->value) + strlen(parv[1]) > 400) { command_fail(si, fault_badparams, "Sorry, you have too many AJOIN entries set."); return; } mowgli_strlcpy(buf, md->value, sizeof buf); mowgli_strlcat(buf, ",", sizeof buf); mowgli_strlcat(buf, parv[1], sizeof buf); metadata_delete(si->smu, "private:autojoin"); metadata_add(si->smu, "private:autojoin", buf); } else { metadata_add(si->smu, "private:autojoin", parv[1]); } command_success_nodata(si, "%s added to AJOIN successfully.", parv[1]); } else if (!strcasecmp(parv[0], "CLEAR")) { metadata_delete(si->smu, "private:autojoin"); command_success_nodata(si, "AJOIN list cleared successfully."); } else if (!strcasecmp(parv[0], "DEL")) { if (!parv[1]) { command_fail(si, fault_badparams, STR_INSUFFICIENT_PARAMS, "AJOIN"); command_fail(si, fault_badparams, "Syntax: AJOIN [#channel]"); return; } if (!(md = metadata_find(si->smu, "private:autojoin"))) { command_fail(si, fault_badparams, "%s is not on your AJOIN list.", parv[1]); return; } // Thanks to John Brooks for his help with this. char *list = md->value; char *remove1 = parv[1]; int listlen = 0; int rmlen = 0; int itempos = 0; int i = 0, j = 0; // This loop will find the item (if present), find the length of the item, and find the length of the entire string. for (; list[i]; i++) { if (!rmlen) { // We have not found the string yet if (tolower(list[i]) == tolower(remove1[j])) { if (j == 0) { // First character of a potential match; remember it's location itempos = i; } j++; if (!remove1[j]) { // Found the entire string rmlen = j; } } else j = 0; } } if (remove1[j]) { command_fail(si, fault_badparams, "%s is not on your AJOIN list.", parv[1]); return; } listlen = i; // listlen is the length of the list, rmlen is the length of the item to remove, itempos is the beginning of that item. if (!list[itempos + rmlen]) { // This item is the last item in the list, so we can simply truncate if (itempos > 0) { itempos--; list[itempos] = '\0'; } else metadata_delete(si->smu, "private:autojoin"); } else { // There are items after this one, so we must copy memory // Account for the comma following this item (if there is a space, account for that too, depends on how you format your list) rmlen += 1; memmove(list + itempos, list + itempos + rmlen, listlen - rmlen - itempos); list[listlen - rmlen] = '\0'; } command_success_nodata(si, "%s removed from AJOIN successfully.", parv[1]); } } command_t ns_ajoin = { "AJOIN", "Manages automatic-join on identify.", AC_AUTHENTICATED, 2, ns_cmd_ajoin, { .path = "contrib/ajoin" } }; void _modinit(module_t *m) { hook_add_event("user_identify"); hook_add_user_identify(ajoin_on_identify); service_named_bind_command("nickserv", &ns_ajoin); } void _moddeinit(module_unload_intent_t intent) { hook_del_user_identify(ajoin_on_identify); service_named_unbind_command("nickserv", &ns_ajoin); } static void ajoin_on_identify(user_t *u) { myuser_t *mu = u->myuser; metadata_t *md; char buf[512]; char *chan; if (!(md = metadata_find(mu, "private:autojoin"))) return; mowgli_strlcpy(buf, md->value, sizeof buf); chan = strtok(buf, " ,"); while (chan != NULL) { if(ircd->type == PROTOCOL_SHADOWIRCD) { sts(":%s ENCAP * SVSJOIN %s %s", ME, CLIENT_NAME(u), chan); } else { sts(":%s SVSJOIN %s %s", CLIENT_NAME(nicksvs.me->me), CLIENT_NAME(u), chan); } chan = strtok(NULL, ","); } }