diff --git a/doc/conf/modules.default.conf b/doc/conf/modules.default.conf index 36f9f0223..feadfb7cd 100644 --- a/doc/conf/modules.default.conf +++ b/doc/conf/modules.default.conf @@ -118,6 +118,7 @@ loadmodule "m_sjoin"; loadmodule "m_sqline"; loadmodule "m_swhois"; loadmodule "m_umode2"; +loadmodule "m_sinfo"; // Services commands // You could disable these if you don't use Services diff --git a/include/h.h b/include/h.h index f416839c6..cc5c0cfbb 100644 --- a/include/h.h +++ b/include/h.h @@ -43,6 +43,8 @@ extern MODVAR struct stats *ircstp; extern MODVAR int bootopt; extern MODVAR time_t TSoffset; extern MODVAR time_t timeofday; +extern MODVAR char cmodestring[512]; +extern MODVAR char umodestring[UMODETABLESZ+1]; /* newconf */ #define get_sendq(x) ((x)->local->class ? (x)->local->class->sendq : MAXSENDQLENGTH) /* get_recvq is only called in send.c for local connections */ @@ -491,6 +493,7 @@ extern void flag_add(char ch); extern void flag_del(char ch); extern void init_dynconf(void); extern char *pretty_time_val(long); +extern char *pretty_date(TS t); extern int init_conf(char *filename, int rehash); extern void validate_configuration(void); extern void run_configuration(void); @@ -669,6 +672,7 @@ extern MODVAR void (*send_join_to_local_users)(aClient *sptr, aChannel *chptr); extern MODVAR int (*do_nick_name)(char *nick); extern MODVAR int (*do_remote_nick_name)(char *nick); extern MODVAR char *(*charsys_get_current_languages)(void); +extern MODVAR void *(*broadcast_sinfo)(aClient *acptr, aClient *to, aClient *except); /* /Efuncs */ extern MODVAR aMotdFile opermotd, svsmotd, motd, botmotd, smotd, rules; @@ -741,6 +745,7 @@ extern MODVAR BOOL IsService; #endif extern int match_ip46(char *a, char *b); extern void extcmodes_check_for_changes(void); +extern void umodes_check_for_changes(void); extern int config_parse_flood(char *orig, int *times, int *period); extern int swhois_add(aClient *acptr, char *tag, int priority, char *swhois, aClient *from, aClient *skip); extern int swhois_delete(aClient *acptr, char *tag, char *swhois, aClient *from, aClient *skip); @@ -809,3 +814,6 @@ extern void setmaxtargets(char *cmd, int limit); extern void freemaxtargets(void); extern int max_targets_for_command(char *cmd); extern void set_targmax_defaults(void); +extern void parse_chanmodes_protoctl(aClient *sptr, char *str); +extern void concat_params(char *buf, int len, int parc, char *parv[]); +extern void charsys_check_for_changes(void); diff --git a/include/modules.h b/include/modules.h index 6e3f412bf..8e6bbb407 100644 --- a/include/modules.h +++ b/include/modules.h @@ -1101,6 +1101,7 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum #define EFUNC_DO_NICK_NAME 57 #define EFUNC_DO_REMOTE_NICK_NAME 58 #define EFUNC_CHARSYS_GET_CURRENT_LANGUAGES 59 +#define EFUNC_BROADCAST_SINFO 60 /* Module flags */ #define MODFLAG_NONE 0x0000 diff --git a/include/struct.h b/include/struct.h index 527835f29..fb6ef096d 100644 --- a/include/struct.h +++ b/include/struct.h @@ -696,8 +696,9 @@ struct Server { char *up; /* uplink for this server */ char by[NICKLEN + 1]; ConfigItem_link *conf; - TS timestamp; /* Remotely determined connect try time */ - long users; + TS timestamp; /* Remotely determined connect try time */ + long users; + TS boottime; /* Startup time of server */ #ifdef LIST_DEBUG aClient *bcptr; #endif @@ -706,9 +707,11 @@ struct Server { unsigned server_sent:1; /* SERVER message sent to this link? (for outgoing links) */ } flags; struct { + char *usermodes; char *chanmodes[4]; int protocol; char *software; + char *nickchars; } features; }; diff --git a/makefile.win32 b/makefile.win32 index 27a483c9d..036028986 100644 --- a/makefile.win32 +++ b/makefile.win32 @@ -232,6 +232,7 @@ DLL_FILES=SRC/MODULES/M_CHGHOST.DLL SRC/MODULES/M_SDESC.DLL SRC/MODULES/M_SETIDE SRC/MODULES/CHARSYS.DLL \ SRC/MODULES/ANTIMIXEDUTF8.DLL \ SRC/MODULES/AUTHPROMPT.DLL \ + SRC/MODULES/M_SINFO.DLL \ SRC/MODULES/CHANMODES/CENSOR.DLL \ SRC/MODULES/CHANMODES/DELAYJOIN.DLL \ SRC/MODULES/CHANMODES/FLOODPROT.DLL \ @@ -867,6 +868,9 @@ src/modules/antimixedutf8.dll: src/modules/antimixedutf8.c $(INCLUDES) src/modules/authprompt.dll: src/modules/authprompt.c $(INCLUDES) $(CC) $(MODCFLAGS) src/modules/authprompt.c $(MODLFLAGS) +src/modules/m_sinfo.dll: src/modules/m_sinfo.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/m_sinfo.c $(MODLFLAGS) + src/modules/chanmodes/censor.dll: src/modules/chanmodes/censor.c $(INCLUDES) $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/censor.c $(MODLFLAGS) diff --git a/src/extcmodes.c b/src/extcmodes.c index 73ea63e82..edce9efab 100644 --- a/src/extcmodes.c +++ b/src/extcmodes.c @@ -39,8 +39,6 @@ #include #include "h.h" -extern char cmodestring[512]; - /* Channel parameter to slot# mapping */ MODVAR unsigned char param_to_slot_mapping[256]; @@ -93,12 +91,25 @@ void extcmodes_check_for_changes(void) { char chanmodes[256]; Isupport *isup; - + make_cmodestr(); make_extcmodestr(); - ircsnprintf(chanmodes, sizeof(chanmodes), CHPAR1 "%s," CHPAR2 "%s," CHPAR3 "%s," CHPAR4 "%s", - EXPAR1, EXPAR2, EXPAR3, EXPAR4); - + + snprintf(chanmodes, sizeof(chanmodes), "%s%s", CHPAR1, EXPAR1); + safestrdup(me.serv->features.chanmodes[0], chanmodes); + snprintf(chanmodes, sizeof(chanmodes), "%s%s", CHPAR2, EXPAR2); + safestrdup(me.serv->features.chanmodes[1], chanmodes); + snprintf(chanmodes, sizeof(chanmodes), "%s%s", CHPAR3, EXPAR3); + safestrdup(me.serv->features.chanmodes[2], chanmodes); + snprintf(chanmodes, sizeof(chanmodes), "%s%s", CHPAR4, EXPAR4); + safestrdup(me.serv->features.chanmodes[3], chanmodes); + + ircsnprintf(chanmodes, sizeof(chanmodes), "%s,%s,%s,%s", + me.serv->features.chanmodes[0], + me.serv->features.chanmodes[1], + me.serv->features.chanmodes[2], + me.serv->features.chanmodes[3]); + isup = IsupportFind("CHANMODES"); if (!isup) { diff --git a/src/ircd.c b/src/ircd.c index beddd47f6..44504bbd8 100644 --- a/src/ircd.c +++ b/src/ircd.c @@ -1347,7 +1347,13 @@ int InitUnrealIRCd(int argc, char *argv[]) booted = TRUE; load_tunefile(); make_umodestr(); + me.flags = FLAGS_LISTEN; + me.fd = -1; + SetMe(&me); + make_server(&me); extcmodes_check_for_changes(); + umodes_check_for_changes(); + charsys_check_for_changes(); clicap_init(); if (!find_Command_simple("AWAY") /*|| !find_Command_simple("KILL") || !find_Command_simple("OPER") || !find_Command_simple("PING")*/) @@ -1379,10 +1385,6 @@ int InitUnrealIRCd(int argc, char *argv[]) portnum = PORTNUM; me.local->port = portnum; (void)init_sys(); - me.flags = FLAGS_LISTEN; - me.fd = -1; - SetMe(&me); - make_server(&me); applymeblock(); #ifdef HAVE_SYSLOG openlog("ircd", LOG_PID | LOG_NDELAY, LOG_DAEMON); @@ -1409,7 +1411,9 @@ int InitUnrealIRCd(int argc, char *argv[]) me_hash = find_or_add(me.name); me.serv->up = me_hash; timeofday = time(NULL); - me.local->lasttime = me.local->since = me.local->firsttime = TStime(); + me.local->lasttime = me.local->since = me.local->firsttime = me.serv->boottime = TStime(); + me.serv->features.protocol = UnrealProtocol; + me.serv->features.software = strdup(version); (void)add_to_client_hash_table(me.name, &me); (void)add_to_id_hash_table(me.id, &me); list_add(&me.client_node, &global_server_list); diff --git a/src/modules.c b/src/modules.c index 8530f6938..6abd0d666 100644 --- a/src/modules.c +++ b/src/modules.c @@ -137,6 +137,7 @@ void (*send_join_to_local_users)(aClient *sptr, aChannel *chptr); int (*do_nick_name)(char *nick); int (*do_remote_nick_name)(char *nick); char *(*charsys_get_current_languages)(void); +void *(*broadcast_sinfo)(aClient *acptr, aClient *to, aClient *except); static const EfunctionsList efunction_table[MAXEFUNCTIONS] = { /* 00 */ {NULL, NULL}, @@ -199,7 +200,8 @@ static const EfunctionsList efunction_table[MAXEFUNCTIONS] = { /* 57 */ {"do_nick_name", (void *)&do_nick_name}, /* 58 */ {"do_remote_nick_name", (void *)&do_remote_nick_name}, /* 59 */ {"charsys_get_current_languages", (void *)&charsys_get_current_languages}, -/* 60 */ {NULL, NULL} +/* 60 */ {"broadcast_sinfo", (void *)&broadcast_sinfo}, +/* 61 */ {NULL, NULL} }; #ifdef UNDERSCORE diff --git a/src/modules/Makefile.in b/src/modules/Makefile.in index 1af45f85b..6f460b384 100644 --- a/src/modules/Makefile.in +++ b/src/modules/Makefile.in @@ -62,7 +62,7 @@ R_MODULES= \ blacklist.so jointhrottle.so \ antirandom.so hideserver.so jumpserver.so \ m_ircops.so m_staff.so nocodes.so \ - charsys.so antimixedutf8.so authprompt.so + charsys.so antimixedutf8.so authprompt.so m_sinfo.so MODULES=cloak.so $(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ @@ -526,6 +526,10 @@ authprompt.so: authprompt.c $(INCLUDES) $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ -o authprompt.so authprompt.c +m_sinfo.so: m_sinfo.c $(INCLUDES) + $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ + -o m_sinfo.so m_sinfo.c + ############################################################################# # capabilities ############################################################################# diff --git a/src/modules/charsys.c b/src/modules/charsys.c index 076427b83..07534b15d 100644 --- a/src/modules/charsys.c +++ b/src/modules/charsys.c @@ -420,6 +420,7 @@ ILangList *e, *e_next; if (strlen(langsinuse) > 490) abort(); #endif + charsys_check_for_changes(); } /** Add a character range to the multibyte list. diff --git a/src/modules/m_nick.c b/src/modules/m_nick.c index bc6187778..e93d03683 100644 --- a/src/modules/m_nick.c +++ b/src/modules/m_nick.c @@ -1173,8 +1173,6 @@ CMD_FUNC(m_nick) ** this is not fair. It should actually request another ** nick from local user or kill him/her... */ -extern MODVAR char cmodestring[512]; -extern MODVAR char umodestring[UMODETABLESZ+1]; extern int short_motd(aClient *sptr); diff --git a/src/modules/m_protoctl.c b/src/modules/m_protoctl.c index ade1e168d..1694d2df0 100644 --- a/src/modules/m_protoctl.c +++ b/src/modules/m_protoctl.c @@ -276,6 +276,12 @@ CMD_FUNC(m_protoctl) get_client_name(cptr, FALSE), charsys_get_current_languages(), s+10); /* return exit_client(cptr, cptr, &me, "Nick charset mismatch"); */ } + if (cptr->serv) + safestrdup(cptr->serv->features.nickchars, s+10); + + /* If this is a runtime change (so post-handshake): */ + if (IsServer(sptr)) + broadcast_sinfo(sptr, NULL, cptr); } else if (strncmp(s, "SID=", 4) == 0) { @@ -455,32 +461,21 @@ CMD_FUNC(m_protoctl) } else if ((strncmp(s, "CHANMODES=", 10) == 0) && sptr->serv) { - char *ch = s + 4; - char *modes, *p; - char copy[256]; - - strlcpy(copy, s+10, sizeof(copy)); - - modes = strtoken(&p, copy, ","); - if (modes) - { - safestrdup(sptr->serv->features.chanmodes[0], modes); - modes = strtoken(&p, NULL, ","); - if (modes) - { - safestrdup(sptr->serv->features.chanmodes[1], modes); - modes = strtoken(&p, NULL, ","); - if (modes) - { - safestrdup(sptr->serv->features.chanmodes[2], modes); - modes = strtoken(&p, NULL, ","); - if (modes) - { - safestrdup(sptr->serv->features.chanmodes[3], modes); - } - } - } - } + parse_chanmodes_protoctl(sptr, s+10); + /* If this is a runtime change (so post-handshake): */ + if (IsServer(sptr)) + broadcast_sinfo(sptr, NULL, cptr); + } + else if ((strncmp(s, "USERMODES=", 10) == 0) && sptr->serv) + { + safestrdup(sptr->serv->features.usermodes, s+10); + /* If this is a runtime change (so post-handshake): */ + if (IsServer(sptr)) + broadcast_sinfo(sptr, NULL, cptr); + } + else if ((strncmp(s, "BOOTED=", 7) == 0) && sptr->serv) + { + sptr->serv->boottime = atol(s+7); } else if (!strcmp(s, "EXTSWHOIS")) { diff --git a/src/modules/m_server.c b/src/modules/m_server.c index b9028a09d..66d84561c 100644 --- a/src/modules/m_server.c +++ b/src/modules/m_server.c @@ -22,6 +22,7 @@ #include "unrealircd.h" +/* Forward declarations */ void send_channel_modes(aClient *cptr, aChannel *chptr); void send_channel_modes_sjoin(aClient *cptr, aChannel *chptr); void send_channel_modes_sjoin3(aClient *cptr, aChannel *chptr); @@ -32,10 +33,11 @@ void _send_protoctl_servers(aClient *sptr, int response); void _send_server_message(aClient *sptr); void _introduce_user(aClient *to, aClient *acptr); int _check_deny_version(aClient *cptr, char *software, int protocol, char *flags); +void _broadcast_sinfo(aClient *acptr, aClient *to, aClient *except); +/* Global variables */ static char buf[BUFSIZE]; - #define MSG_SERVER "SERVER" ModuleHeader MOD_HEADER(m_server) @@ -55,6 +57,7 @@ MOD_TEST(m_server) EfunctionAdd(modinfo->handle, EFUNC_VERIFY_LINK, _verify_link); EfunctionAddVoid(modinfo->handle, EFUNC_INTRODUCE_USER, _introduce_user); EfunctionAdd(modinfo->handle, EFUNC_CHECK_DENY_VERSION, _check_deny_version); + EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_SINFO, _broadcast_sinfo); return MOD_SUCCESS; } @@ -815,6 +818,49 @@ void tls_link_notification_verify(aClient *acptr, ConfigItem_link *aconf) } } +#define SafeStr(x) ((x && *(x)) ? (x) : "*") + +/** Broadcast SINFO. + * @param cptr The server to send the information about. + * @param to The server to send the information TO (NULL for broadcast). + * @param except The direction NOT to send to. + * This function takes into account that the server may not + * provide all of the detailed info. If any information is + * absent we will send 0 for numbers and * for NULL strings. + */ +void _broadcast_sinfo(aClient *acptr, aClient *to, aClient *except) +{ + char chanmodes[128], buf[512]; + + if (acptr->serv->features.chanmodes[0]) + { + snprintf(chanmodes, sizeof(chanmodes), "%s,%s,%s,%s", + acptr->serv->features.chanmodes[0], + acptr->serv->features.chanmodes[1], + acptr->serv->features.chanmodes[2], + acptr->serv->features.chanmodes[3]); + } else { + strlcpy(chanmodes, "*", sizeof(chanmodes)); + } + + snprintf(buf, sizeof(buf), "%ld %d %s %s %s :%s", + acptr->serv->boottime, + acptr->serv->features.protocol, + SafeStr(acptr->serv->features.usermodes), + chanmodes, + SafeStr(acptr->serv->features.nickchars), + SafeStr(acptr->serv->features.software)); + + if (to) + { + /* Targetted to one server */ + sendto_one(to, ":%s SINFO %s", acptr->name, buf); + } else { + /* Broadcast (except one side...) */ + sendto_server(except, 0, 0, ":%s SINFO %s", acptr->name, buf); + } +} + int m_server_synch(aClient *cptr, ConfigItem_link *aconf) { char *inpath = get_client_name(cptr, TRUE); @@ -912,6 +958,7 @@ int m_server_synch(aClient *cptr, ConfigItem_link *aconf) cptr->local->class = cptr->serv->conf->class; RunHook(HOOKTYPE_SERVER_CONNECT, cptr); + /* Broadcast new server to the rest of the network */ if (*cptr->id) { sendto_server(cptr, PROTO_SID, 0, ":%s SID %s 2 %s :%s", @@ -922,7 +969,12 @@ int m_server_synch(aClient *cptr, ConfigItem_link *aconf) cptr->serv->up, cptr->name, cptr->info); - send_moddata_client(cptr, &me); /* send moddata of &me (if any, likely minimal) */ + /* Broadcast the just-linked-in featureset to other servers on our side */ + broadcast_sinfo(cptr, NULL, cptr); + + /* Send moddata of &me (if any, likely minimal) */ + send_moddata_client(cptr, &me); + list_for_each_entry_reverse(acptr, &global_server_list, client_node) { /* acptr->from == acptr for acptr == cptr */ @@ -961,6 +1013,8 @@ int m_server_synch(aClient *cptr, ConfigItem_link *aconf) cptr->name, acptr->name); #endif } + /* Send SINFO of our servers to their side */ + broadcast_sinfo(acptr, cptr, NULL); send_moddata_client(cptr, acptr); /* send moddata of server 'acptr' (if any, likely minimal) */ } } diff --git a/src/modules/m_sinfo.c b/src/modules/m_sinfo.c new file mode 100644 index 000000000..e4339f48b --- /dev/null +++ b/src/modules/m_sinfo.c @@ -0,0 +1,159 @@ +/* + * m_sinfo - Server information + * (C) Copyright 2019 Bram Matthys (Syzop) and the UnrealIRCd team. + * License: GPLv2 + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER(sinfo) + = { + "sinfo", + "4.2", + "Server information", + "3.2-b8-1", + NULL + }; + +/* Forward declarations */ +CMD_FUNC(m_sinfo); + +MOD_INIT(sinfo) +{ + CommandAdd(modinfo->handle, "SINFO", m_sinfo, MAXPARA, M_USER|M_SERVER); + + return MOD_SUCCESS; +} + +MOD_LOAD(sinfo) +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD(sinfo) +{ + return MOD_SUCCESS; +} + +int sinfo_server(aClient *cptr, aClient *sptr, int parc, char *parv[]) +{ + char buf[512]; + + if (cptr == sptr) + { + /* It is a protocol violation to send an SINFO for yourself, + * eg if you are server 001, then you cannot send :001 SINFO .... + * Exiting the client may seem harsh, but this way we force users + * to use the correct protocol. If we would not do this then some + * services coders may think they should use only SINFO while in + * fact for directly connected servers they should use things like + * PROTOCTL CHANMODES=... USERMODES=... NICKCHARS=.... etc, and + * failure to do so will lead to potential desyncs or other major + * issues. + */ + return exit_client(cptr, sptr, &me, "Protocol error: you cannot send SINFO about yourself"); + } + + /* :SID SINFO up_since protocol umodes chanmodes nickchars :software name + * 1 2 3 4 5 6 (last one) + * If we extend it then 'software name' will still be the last one, so + * it may become 7, 8 or 9. New elements are inserted right before it. + */ + + if ((parc < 6) || BadPtr(parv[6])) + { + sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, sptr->name, "SINFO"); + return 0; + } + + sptr->serv->boottime = atol(parv[1]); + sptr->serv->features.protocol = atoi(parv[2]); + + if (!strcmp(parv[3], "*")) + safefree(parv[3]); + else + safestrdup(sptr->serv->features.usermodes, parv[3]); + + if (!strcmp(parv[4], "*")) + { + safefree(sptr->serv->features.chanmodes[0]); + safefree(sptr->serv->features.chanmodes[1]); + safefree(sptr->serv->features.chanmodes[2]); + safefree(sptr->serv->features.chanmodes[3]); + } else { + parse_chanmodes_protoctl(sptr, parv[4]); + } + + if (!strcmp(parv[5], "*")) + safefree(sptr->serv->features.nickchars); + else + safestrdup(sptr->serv->features.nickchars, parv[5]); + + /* Software is always the last parameter. It is currently parv[6] + * but may change later. So always use parv[parc-1]. + */ + if (!strcmp(parv[parc-1], "*")) + safefree(sptr->serv->features.software); + else + safestrdup(sptr->serv->features.software, parv[parc-1]); + + /* Broadcast to 'the other side' of the net */ + concat_params(buf, sizeof(buf), parc, parv); + sendto_server(cptr, 0, 0, ":%s SINFO %s", sptr->name, buf); +} + +#define SafeDisplayStr(x) ((x && *(x)) ? (x) : "-") +int sinfo_user(aClient *cptr, aClient *sptr, int parc, char *parv[]) +{ + aClient *acptr; + + if (!IsOper(sptr)) + { + sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, sptr->name); + return 0; + } + + list_for_each_entry(acptr, &global_server_list, client_node) + { + sendtxtnumeric(sptr, "*** Server %s:", acptr->name); + sendtxtnumeric(sptr, "Protocol: %d", + acptr->serv->features.protocol); + sendtxtnumeric(sptr, "Software: %s", + SafeDisplayStr(acptr->serv->features.software)); + if (!acptr->serv->boottime) + { + sendtxtnumeric(sptr, "Up since: -"); + sendtxtnumeric(sptr, "Uptime: -"); + } else { + sendtxtnumeric(sptr, "Up since: %s", + pretty_date(acptr->serv->boottime)); + sendtxtnumeric(sptr, "Uptime: %s", + pretty_time_val(TStime() - acptr->serv->boottime)); + } + sendtxtnumeric(sptr, "User modes: %s", + SafeDisplayStr(acptr->serv->features.usermodes)); + if (!acptr->serv->features.chanmodes[0]) + { + sendtxtnumeric(sptr, "Channel modes: -"); + } else { + sendtxtnumeric(sptr, "Channel modes: %s,%s,%s,%s", + SafeDisplayStr(acptr->serv->features.chanmodes[0]), + SafeDisplayStr(acptr->serv->features.chanmodes[1]), + SafeDisplayStr(acptr->serv->features.chanmodes[2]), + SafeDisplayStr(acptr->serv->features.chanmodes[3])); + } + sendtxtnumeric(sptr, "Allowed nick characters: %s", + SafeDisplayStr(acptr->serv->features.nickchars)); + } + + return 0; +} + +CMD_FUNC(m_sinfo) +{ + if (IsServer(sptr)) + return sinfo_server(cptr, sptr, parc, parv); + else if (MyClient(sptr)) + return sinfo_user(cptr, sptr, parc, parv); + return 0; +} diff --git a/src/s_conf.c b/src/s_conf.c index 56e28284a..22f038da5 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -3020,11 +3020,14 @@ char *pretty_time_val(long timeval) if (timeval/86400) snprintf(buf, sizeof(buf), "%ld day%s ", timeval/86400, timeval/86400 != 1 ? "s" : ""); if ((timeval/3600) % 24) - snprintf(buf, sizeof(buf), "%s%ld hour%s ", buf, (timeval/3600)%24, (timeval/3600)%24 != 1 ? "s" : ""); + snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%ld hour%s ", (timeval/3600)%24, (timeval/3600)%24 != 1 ? "s" : ""); if ((timeval/60)%60) - snprintf(buf, sizeof(buf), "%s%ld minute%s ", buf, (timeval/60)%60, (timeval/60)%60 != 1 ? "s" : ""); + snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%ld minute%s ", (timeval/60)%60, (timeval/60)%60 != 1 ? "s" : ""); if ((timeval%60)) - snprintf(buf, sizeof(buf), "%s%ld second%s", buf, timeval%60, timeval%60 != 1 ? "s" : ""); + snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%ld second%s", timeval%60, timeval%60 != 1 ? "s" : ""); + /* Strip space at the end (if any) */ + if (*buf && (buf[strlen(buf)-1] == ' ')) + buf[strlen(buf)-1] = '\0'; return buf; } @@ -10598,6 +10601,8 @@ int rehash_internal(aClient *cptr, aClient *sptr, int sig) unload_all_unused_extcmodes(); // unload_all_unused_moddata(); -- this will crash extcmodes_check_for_changes(); + umodes_check_for_changes(); + charsys_check_for_changes(); loop.ircd_rehashing = 0; remote_rehash_client = NULL; return 1; diff --git a/src/s_misc.c b/src/s_misc.c index 2964ea1c1..22fcb6ace 100644 --- a/src/s_misc.c +++ b/src/s_misc.c @@ -1323,3 +1323,56 @@ size_t add_sjsby(char *buf, char *setby, TS seton) return p - buf; } + +/** Concatenate the entire parameter string. + * The function will take care of spaces in the final parameter (if any). + * @param buf The buffer to output in. + * @param len Length of the buffer. + * @param parc Parameter count, ircd style. + * @param parv Parameters, ircd style, so we will start at parv[1]. + * @example + * char buf[512]; + * concat_params(buf, sizeof(buf), parc, parv); + * sendto_server(cptr, 0, 0, ":%s SOMECOMMAND %s", sptr->name, buf); + */ +void concat_params(char *buf, int len, int parc, char *parv[]) +{ + int i; + + *buf = '\0'; + for (i = 1; i < parc; i++) + { + char *param = parv[i]; + + if (param && !*buf) + strlcat(buf, " ", sizeof(buf)); + + if (strchr(param, ' ')) + { + /* Last parameter, with : */ + strlcat(buf, ":", sizeof(buf)); + strlcat(buf, parv[i], sizeof(buf)); + break; + } + strlcat(buf, parv[i], sizeof(buf)); + } +} + +char *pretty_date(TS t) +{ + static char buf[128]; + struct tm *tm; + + if (!t) + time(&t); + tm = gmtime(&t); + snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d GMT", + 1900 + tm->tm_year, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + + return buf; +} diff --git a/src/s_serv.c b/src/s_serv.c index 8ddc722b9..3d4e09bbf 100644 --- a/src/s_serv.c +++ b/src/s_serv.c @@ -216,7 +216,6 @@ char *num = NULL; */ void send_proto(aClient *cptr, ConfigItem_link *aconf) { - char buf[1024]; Isupport *prefix = IsupportFind("PREFIX"); /* CAUTION: If adding a token to an existing PROTOCTL line below, @@ -228,10 +227,8 @@ void send_proto(aClient *cptr, ConfigItem_link *aconf) iConf.ban_setter_sync ? "SJSBY" : ""); /* Second line */ - snprintf(buf, sizeof(buf), "CHANMODES=%s%s,%s%s,%s%s,%s%s PREFIX=%s NICKCHARS=%s SID=%s MLOCK TS=%ld EXTSWHOIS", - CHPAR1, EXPAR1, CHPAR2, EXPAR2, CHPAR3, EXPAR3, CHPAR4, EXPAR4, prefix->value, charsys_get_current_languages(), me.id, (long)TStime()); - - sendto_one(cptr, "PROTOCTL %s", buf); + sendto_one(cptr, "PROTOCTL CHANMODES=%s%s,%s%s,%s%s,%s%s USERMODES=%s BOOTED=%ld PREFIX=%s NICKCHARS=%s SID=%s MLOCK TS=%ld EXTSWHOIS", + CHPAR1, EXPAR1, CHPAR2, EXPAR2, CHPAR3, EXPAR3, CHPAR4, EXPAR4, umodestring, me.local->since, prefix->value, charsys_get_current_languages(), me.id, (long)TStime()); } #ifndef IRCDTOTALVERSION @@ -1386,3 +1383,61 @@ aClient *find_non_pending_net_duplicates(aClient *cptr) return NULL; } +void parse_chanmodes_protoctl(aClient *sptr, char *str) +{ + char *modes, *p; + char copy[256]; + + strlcpy(copy, str, sizeof(copy)); + + modes = strtoken(&p, copy, ","); + if (modes) + { + safestrdup(sptr->serv->features.chanmodes[0], modes); + modes = strtoken(&p, NULL, ","); + if (modes) + { + safestrdup(sptr->serv->features.chanmodes[1], modes); + modes = strtoken(&p, NULL, ","); + if (modes) + { + safestrdup(sptr->serv->features.chanmodes[2], modes); + modes = strtoken(&p, NULL, ","); + if (modes) + { + safestrdup(sptr->serv->features.chanmodes[3], modes); + } + } + } + } +} + +static char previous_langsinuse[512]; +static int previous_langsinuse_ready = 0; + +void charsys_check_for_changes(void) +{ + char *langsinuse = charsys_get_current_languages(); + /* already called by charsys_finish() */ + safestrdup(me.serv->features.nickchars, langsinuse); + + if (!previous_langsinuse_ready) + { + previous_langsinuse_ready = 1; + strlcpy(previous_langsinuse, langsinuse, sizeof(previous_langsinuse)); + return; /* not booted yet. then we are done here. */ + } + + if (strcmp(langsinuse, previous_langsinuse)) + { + ircd_log(LOG_ERROR, "Permitted nick characters changed at runtime: %s -> %s", + previous_langsinuse, langsinuse); + sendto_realops("Permitted nick characters changed at runtime: %s -> %s", + previous_langsinuse, langsinuse); + /* Broadcast change to all (locally connected) servers */ + sendto_server(&me, 0, 0, "PROTOCTL NICKCHARS=%s", langsinuse); + } + + strlcpy(previous_langsinuse, langsinuse, sizeof(previous_langsinuse)); +} + diff --git a/src/umodes.c b/src/umodes.c index 1cdfc1484..eaee1b240 100644 --- a/src/umodes.c +++ b/src/umodes.c @@ -141,6 +141,32 @@ void make_umodestr(void) *m = '\0'; } +static char previous_umodestring[256]; + +void umodes_check_for_changes(void) +{ + make_umodestr(); + safestrdup(me.serv->features.usermodes, umodestring); + + if (!*previous_umodestring) + { + strlcpy(previous_umodestring, umodestring, sizeof(previous_umodestring)); + return; /* not booted yet. then we are done here. */ + } + + if (*previous_umodestring && strcmp(umodestring, previous_umodestring)) + { + ircd_log(LOG_ERROR, "User modes changed at runtime: %s -> %s", + previous_umodestring, umodestring); + sendto_realops("User modes changed at runtime: %s -> %s", + previous_umodestring, umodestring); + /* Broadcast change to all (locally connected) servers */ + sendto_server(&me, 0, 0, "PROTOCTL USERMODES=%s", umodestring); + } + + strlcpy(previous_umodestring, umodestring, sizeof(previous_umodestring)); +} + /* UmodeAdd: * Add a usermode with character 'ch', if global is set to 1 the usermode is global * (sent to other servers) otherwise it's a local usermode