/* * Unreal Internet Relay Chat Daemon, src/serv.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * See file AUTHORS in IRC package for additional names of * the programmers. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* s_serv.c 2.55 2/7/94 (C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen */ #include "unrealircd.h" #ifndef _WIN32 /* for uname(), is POSIX so should be OK... */ #include #endif extern VOIDSIG s_die(); static char buf[BUFSIZE]; MODVAR int max_connection_count = 1, max_client_count = 1; extern ircstats IRCstats; extern int do_garbage_collect; /* We need all these for cached MOTDs -- codemastr */ extern char *buildid; aMotdFile opermotd; aMotdFile rules; aMotdFile motd; aMotdFile svsmotd; aMotdFile botmotd; aMotdFile smotd; void read_motd(const char *filename, aMotdFile *motd); void do_read_motd(const char *filename, aMotdFile *themotd); #ifdef USE_LIBCURL void read_motd_asynch_downloaded(const char *url, const char *filename, const char *errorbuf, int cached, aMotdDownload *motd_download); #endif extern aMotdLine *Find_file(char *, short); void reread_motdsandrules(); /* ** m_functions execute protocol messages on this server: ** CMD_FUNC(functionname) causes it to use the header ** int functionname (aClient *cptr, ** aClient *sptr, int parc, char *parv[]) ** ** ** cptr is always NON-NULL, pointing to a *LOCAL* client ** structure (with an open socket connected!). This ** identifies the physical socket where the message ** originated (or which caused the m_function to be ** executed--some m_functions may call others...). ** ** sptr is the source of the message, defined by the ** prefix part of the message if present. If not ** or prefix not found, then sptr==cptr. ** ** (!IsServer(cptr)) => (cptr == sptr), because ** prefixes are taken *only* from servers... ** ** (IsServer(cptr)) ** (sptr == cptr) => the message didn't ** have the prefix. ** ** (sptr != cptr && IsServer(sptr) means ** the prefix specified servername. (?) ** ** (sptr != cptr && !IsServer(sptr) means ** that message originated from a remote ** user (not local). ** ** combining ** ** (!IsServer(sptr)) means that, sptr can safely ** taken as defining the target structure of the ** message in this server. ** ** *Always* true (if 'parse' and others are working correct): ** ** 1) sptr->from == cptr (note: cptr->from == cptr) ** ** 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr ** *cannot* be a local connection, unless it's ** actually cptr!). [MyConnect(x) should probably ** be defined as (x == x->from) --msa ] ** ** parc number of variable parameter strings (if zero, ** parv is allowed to be NULL) ** ** parv a NULL terminated list of parameter pointers, ** ** parv[1]...parv[parc-1] ** pointers to additional parameters ** parv[parc] == NULL, *always* ** ** note: it is guaranteed that parv[1]..parv[parc-1] are all ** non-NULL pointers. */ #ifndef _WIN32 char *getosname(void) { static char buf[1024]; struct utsname osinf; char *p; memset(&osinf, 0, sizeof(osinf)); if (uname(&osinf) != 0) return ""; snprintf(buf, sizeof(buf), "%s %s %s %s %s", osinf.sysname, osinf.nodename, osinf.release, osinf.version, osinf.machine); /* get rid of cr/lf */ for (p=buf; *p; p++) if ((*p == '\n') || (*p == '\r')) { *p = '\0'; break; } return buf; } #endif void send_version(aClient* sptr, int reply) { int i; for (i = 0; IsupportStrings[i]; i++) { sendnumeric(sptr, reply, IsupportStrings[i]); } } /* ** m_version ** parv[1] = remote server */ CMD_FUNC(m_version) { /* Only allow remote VERSIONs if registered -- Syzop */ if (!IsPerson(sptr) && !IsServer(cptr)) { send_version(sptr,RPL_ISUPPORT); return 0; } if (hunt_server(cptr, sptr, recv_mtags, ":%s VERSION :%s", 1, parc, parv) == HUNTED_ISME) { sendnumeric(sptr, RPL_VERSION, version, debugmode, me.name, (ValidatePermissionsForPath("server:info",sptr,NULL,NULL,NULL) ? serveropts : "0"), extraflags ? extraflags : "", tainted ? "3" : "", (ValidatePermissionsForPath("server:info",sptr,NULL,NULL,NULL) ? MYOSNAME : "*"), UnrealProtocol); if (ValidatePermissionsForPath("server:info",sptr,NULL,NULL,NULL)) { sendnotice(sptr, "%s", SSLeay_version(SSLEAY_VERSION)); sendnotice(sptr, "%s", pcre2_version()); #ifdef USE_LIBCURL sendnotice(sptr, "%s", curl_version()); #endif } if (MyClient(sptr)) send_version(sptr,RPL_ISUPPORT); else send_version(sptr,RPL_REMOTEISUPPORT); } return 0; } char *num = NULL; /* * send_proto: * Sends PROTOCTL message to server * Now split up into multiple PROTOCTL messages (again), since we have * too many for a single line. If this breaks your services because * you fail to maintain PROTOCTL state, then fix them! */ void send_proto(aClient *cptr, ConfigItem_link *aconf) { Isupport *prefix = IsupportFind("PREFIX"); /* CAUTION: If adding a token to an existing PROTOCTL line below, * then ensure that MAXPARA is not reached! */ /* First line */ sendto_one(cptr, NULL, "PROTOCTL NOQUIT NICKv2 SJOIN SJOIN2 UMODE2 VL SJ3 TKLEXT TKLEXT2 NICKIP ESVID %s %s", iConf.ban_setter_sync ? "SJSBY" : "", ClientCapabilityFindReal("message-tags") ? "MTAGS" : ""); /* Second line */ sendto_one(cptr, NULL, "PROTOCTL CHANMODES=%s%s,%s%s,%s%s,%s%s USERMODES=%s BOOTED=%lld PREFIX=%s NICKCHARS=%s SID=%s MLOCK TS=%lld EXTSWHOIS", CHPAR1, EXPAR1, CHPAR2, EXPAR2, CHPAR3, EXPAR3, CHPAR4, EXPAR4, umodestring, (long long)me.local->since, prefix->value, charsys_get_current_languages(), me.id, (long long)TStime()); } #ifndef IRCDTOTALVERSION #define IRCDTOTALVERSION BASE_VERSION "-" PATCH1 PATCH2 PATCH3 PATCH4 PATCH5 PATCH6 PATCH7 PATCH8 PATCH9 #endif int remotecmdfilter(aClient *sptr, int parc, char *parv[]) { /* no remote requests permitted from non-ircops */ if (MyClient(sptr) && !ValidatePermissionsForPath("server:remote",sptr,NULL,NULL,NULL) && !BadPtr(parv[1])) { parv[1] = NULL; parc = 1; } /* same as above, but in case an old server forwards a request to us: we ignore it */ if (!MyClient(sptr) && !ValidatePermissionsForPath("server:remote",sptr,NULL,NULL,NULL)) return 1; /* STOP (return) */ return 0; /* Continue */ } /* * sends m_info into to sptr */ char *unrealinfo[] = { "This release was brought to you by the following people:", "", "Head coder:", "* Bram Matthys (Syzop) ", "", "Coders:", "* Gottem ", "* i ", "", "Past UnrealIRCd 4.x coders/contributors:", "* Heero, binki, nenolod, ..", "", "Past UnrealIRCd 3.2.x coders/contributors:", "* Stskeeps (ret. head coder / project leader)", "* codemastr (ret. u3.2 head coder)", "* aquanight, WolfSage, ..", "* McSkaf, Zogg, NiQuiL, chasm, llthangel, nighthawk, ..", NULL }; void m_info_send(aClient *sptr) { char **text = unrealinfo; sendnumericfmt(sptr, RPL_INFO, "========== %s ==========", IRCDTOTALVERSION); while (*text) sendnumericfmt(sptr, RPL_INFO, "| %s", *text++); sendnumericfmt(sptr, RPL_INFO, "|"); sendnumericfmt(sptr, RPL_INFO, "|"); sendnumericfmt(sptr, RPL_INFO, "| Credits - Type /Credits"); sendnumericfmt(sptr, RPL_INFO, "| DALnet Credits - Type /DalInfo"); sendnumericfmt(sptr, RPL_INFO, "|"); sendnumericfmt(sptr, RPL_INFO, "| This is an UnrealIRCd-style server"); sendnumericfmt(sptr, RPL_INFO, "| If you find any bugs, please report them at:"); sendnumericfmt(sptr, RPL_INFO, "| https://bugs.unrealircd.org/"); sendnumericfmt(sptr, RPL_INFO, "| UnrealIRCd Homepage: https://www.unrealircd.org"); sendnumericfmt(sptr, RPL_INFO, "============================================"); sendnumericfmt(sptr, RPL_INFO, "Birth Date: %s, compile # %s", creation, generation); sendnumericfmt(sptr, RPL_INFO, "On-line since %s", myctime(me.local->firsttime)); sendnumericfmt(sptr, RPL_INFO, "ReleaseID (%s)", buildid); sendnumeric(sptr, RPL_ENDOFINFO); } /* ** m_info ** parv[1] = servername ** Modified for hardcode by Stskeeps */ CMD_FUNC(m_info) { if (remotecmdfilter(sptr, parc, parv)) return 0; if (hunt_server(cptr, sptr, recv_mtags, ":%s INFO :%s", 1, parc, parv) == HUNTED_ISME) { m_info_send(sptr); } return 0; } /* ** m_dalinfo ** parv[1] = servername */ CMD_FUNC(m_dalinfo) { char **text = dalinfotext; if (remotecmdfilter(sptr, parc, parv)) return 0; if (hunt_server(cptr, sptr, recv_mtags, ":%s DALINFO :%s", 1, parc, parv) == HUNTED_ISME) { while (*text) sendnumeric(sptr, RPL_INFO, *text++); sendnumeric(sptr, RPL_INFO, ""); sendnumericfmt(sptr, RPL_INFO, "Birth Date: %s, compile # %s", creation, generation); sendnumericfmt(sptr, RPL_INFO, "On-line since %s", myctime(me.local->firsttime)); sendnumeric(sptr, RPL_ENDOFINFO); } return 0; } /* ** m_license ** parv[1] = servername */ CMD_FUNC(m_license) { char **text = gnulicense; if (remotecmdfilter(sptr, parc, parv)) return 0; if (hunt_server(cptr, sptr, recv_mtags, ":%s LICENSE :%s", 1, parc, parv) == HUNTED_ISME) { while (*text) sendnumeric(sptr, RPL_INFO, *text++); sendnumeric(sptr, RPL_INFO, ""); sendnumeric(sptr, RPL_ENDOFINFO); } return 0; } /* ** m_credits ** parv[1] = servername */ CMD_FUNC(m_credits) { char **text = unrealcredits; if (remotecmdfilter(sptr, parc, parv)) return 0; if (hunt_server(cptr, sptr, recv_mtags, ":%s CREDITS :%s", 1, parc, parv) == HUNTED_ISME) { while (*text) sendnumeric(sptr, RPL_INFO, *text++); sendnumeric(sptr, RPL_INFO, ""); sendnumericfmt(sptr, RPL_INFO, "Birth Date: %s, compile # %s", creation, generation); sendnumericfmt(sptr, RPL_INFO, "On-line since %s", myctime(me.local->firsttime)); sendnumeric(sptr, RPL_ENDOFINFO); } return 0; } char *get_cptr_status(aClient *acptr) { static char buf[10]; char *p = buf; *p = '\0'; *p++ = '['; if (acptr->flags & FLAGS_LISTEN) { if (acptr->umodes & LISTENER_NORMAL) *p++ = '*'; if (acptr->umodes & LISTENER_SERVERSONLY) *p++ = 'S'; if (acptr->umodes & LISTENER_CLIENTSONLY) *p++ = 'C'; if (acptr->umodes & LISTENER_TLS) *p++ = 's'; } else { if (acptr->flags & FLAGS_TLS) *p++ = 's'; } *p++ = ']'; *p++ = '\0'; return (buf); } /* Used to blank out ports -- Barubary */ char *get_client_name2(aClient *acptr, int showports) { char *pointer = get_client_name(acptr, TRUE); if (!pointer) return NULL; if (showports) return pointer; if (!strrchr(pointer, '.')) return NULL; /* * This may seem like wack but remind this is only used * in rows of get_client_name2's, so it's perfectly fair * */ strcpy(strrchr(pointer, '.'), ".0]"); return pointer; } /* ** m_summon */ CMD_FUNC(m_summon) { /* /summon is old and out dated, we just return an error as * required by RFC1459 -- codemastr */ sendnumeric(sptr, ERR_SUMMONDISABLED); return 0; } /* ** m_users ** parv[1] = servername */ CMD_FUNC(m_users) { /* /users is out of date, just return an error as required by * RFC1459 -- codemastr */ sendnumeric(sptr, ERR_USERSDISABLED); return 0; } /* ** Note: At least at protocol level ERROR has only one parameter, ** although this is called internally from other functions ** --msa ** ** parv[*] = parameters */ CMD_FUNC(m_error) { char *para; para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>"; Debug((DEBUG_ERROR, "Received ERROR message from %s: %s", sptr->name, para)); /* Errors from untrusted sources only go to the junk snomask * (which is only for debugging issues and such). * This to prevent flooding and confusing IRCOps by * malicious users. */ if (!IsServer(cptr) && !cptr->serv) { sendto_snomask(SNO_JUNK, "ERROR from %s -- %s", get_client_name(cptr, FALSE), para); return 0; } sendto_umode_global(UMODE_OPER, "ERROR from %s -- %s", get_client_name(cptr, FALSE), para); return 0; } EVENT(save_tunefile) { FILE *tunefile; tunefile = fopen(conf_files->tune_file, "w"); if (!tunefile) { #if !defined(_WIN32) && !defined(_AMIGA) sendto_ops("Unable to write tunefile.. %s", strerror(errno)); #else sendto_ops("Unable to write tunefile.."); #endif return; } fprintf(tunefile, "0\n"); fprintf(tunefile, "%d\n", IRCstats.me_max); fclose(tunefile); } void load_tunefile(void) { FILE *tunefile; char buf[1024]; tunefile = fopen(conf_files->tune_file, "r"); if (!tunefile) return; fprintf(stderr, "Loading tunefile..\n"); if (!fgets(buf, sizeof(buf), tunefile)) fprintf(stderr, "Warning: error while reading the timestamp offset from the tunefile%s%s\n", errno? ": ": "", errno? strerror(errno): ""); if (!fgets(buf, sizeof(buf), tunefile)) fprintf(stderr, "Warning: error while reading the peak user count from the tunefile%s%s\n", errno? ": ": "", errno? strerror(errno): ""); IRCstats.me_max = atol(buf); fclose(tunefile); } /** Rehash motd and rule files (motd_file/rules_file and all tld entries). */ void rehash_motdrules() { ConfigItem_tld *tlds; reread_motdsandrules(); for (tlds = conf_tld; tlds; tlds = tlds->next) { /* read_motd() accepts NULL in first arg and acts sanely */ read_motd(tlds->motd_file, &tlds->motd); read_motd(tlds->rules_file, &tlds->rules); read_motd(tlds->smotd_file, &tlds->smotd); read_motd(tlds->opermotd_file, &tlds->opermotd); read_motd(tlds->botmotd_file, &tlds->botmotd); } } void reread_motdsandrules() { read_motd(conf_files->motd_file, &motd); read_motd(conf_files->rules_file, &rules); read_motd(conf_files->smotd_file, &smotd); read_motd(conf_files->botmotd_file, &botmotd); read_motd(conf_files->opermotd_file, &opermotd); read_motd(conf_files->svsmotd_file, &svsmotd); } extern void reinit_resolver(aClient *sptr); /* ** m_rehash ** remote rehash by binary ** now allows the -flags in remote rehash ** ugly code but it seems to work :) -- codemastr ** added -all and fixed up a few lines -- niquil (niquil@programmer.net) ** fixed remote rehashing, but it's getting a bit weird code again -- Syzop ** removed '-all' code, this is now considered as '/rehash', this is ok ** since we rehash everything with simple '/rehash' now. Syzop/20040205 */ CMD_FUNC(m_rehash) { int x = 0; if (!ValidatePermissionsForPath("server:rehash",sptr,NULL,NULL,NULL)) { sendnumeric(sptr, ERR_NOPRIVILEGES); return 0; } if ((parc < 3) || BadPtr(parv[2])) { /* If the argument starts with a '-' (like -motd, -opermotd, etc) then it's * assumed not to be a server. -- Syzop */ if (parv[1] && (parv[1][0] == '-')) x = HUNTED_ISME; else x = hunt_server(cptr, sptr, recv_mtags, ":%s REHASH :%s", 1, parc, parv); } else { if (match_simple("-glob*", parv[1])) /* This is really ugly... hack to make /rehash -global -something work */ { x = HUNTED_ISME; } else { x = hunt_server(cptr, sptr, NULL, ":%s REHASH %s :%s", 1, parc, parv); // XXX: FIXME: labeled-response can't handle this, multiple servers. } } if (x != HUNTED_ISME) return 0; /* Now forwarded or server didnt exist */ if (MyClient(sptr) && IsWebsocket(sptr)) { sendnotice(sptr, "Sorry, for technical reasons it is not possible to REHASH " "the local server from a WebSocket connection."); /* Issue details: * websocket_handle_packet -> process_packet -> parse_client_queued -> * dopacket -> parse -> m_rehash... and then 'websocket' is unloaded so * we "cannot get back" as that websocket_handle_packet function is gone. * * Solution would be either to delay the rehash or to make websocket perm. * The latter removes all our ability to upgrade the module on the fly * and the former is rather ugly.. not going to do that hassle now anyway. */ return 0; } if (cptr != sptr) { #ifndef REMOTE_REHASH sendnumeric(sptr, ERR_NOPRIVILEGES); return 0; #endif if (parv[2] == NULL) { if (loop.ircd_rehashing) { sendnotice(sptr, "A rehash is already in progress"); return 0; } sendto_umode_global(UMODE_OPER, ":%s GLOBOPS :%s is remotely rehashing server config file", me.name, sptr->name); remote_rehash_client = sptr; reread_motdsandrules(); return rehash(cptr, sptr, (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0); } parv[1] = parv[2]; } else { /* Ok this is in an 'else' because it should be only executed for sptr == cptr, * but it's totally unrelated to the above ;). */ if (parv[1] && match_simple("-glob*", parv[1])) { /* /REHASH -global [options] */ aClient *acptr; /* Shift parv's to the left */ parv[1] = parv[2]; parv[2] = NULL; parc--; /* Only netadmins may use /REHASH -global, which is because: * a) it makes sense * b) remote servers don't support remote rehashes by non-netadmins */ if (!ValidatePermissionsForPath("server:rehash",sptr,NULL,NULL,NULL)) { sendnumeric(sptr, ERR_NOPRIVILEGES); sendnotice(sptr, "'/REHASH -global' requires you to have server::rehash permissions"); return 0; } if (parv[1] && *parv[1] != '-') { sendnotice(sptr, "You cannot specify a server name after /REHASH -global, for obvious reasons"); return 0; } /* Broadcast it in an inefficient, but backwards compatible way. */ list_for_each_entry(acptr, &global_server_list, client_node) { if (acptr == &me) continue; sendto_one(acptr, NULL, ":%s REHASH %s %s", sptr->name, acptr->name, parv[1] ? parv[1] : "-all"); } /* Don't return, continue, because we need to REHASH ourselves as well. */ } } if (!BadPtr(parv[1]) && stricmp(parv[1], "-all")) { if (!ValidatePermissionsForPath("server:rehash",sptr,NULL,NULL,NULL)) { sendnumeric(sptr, ERR_NOPRIVILEGES); return 0; } if (*parv[1] == '-') { if (!strnicmp("-gar", parv[1], 4)) { loop.do_garbage_collect = 1; RunHook3(HOOKTYPE_REHASHFLAG, cptr, sptr, parv[1]); return 0; } if (!strnicmp("-dns", parv[1], 4)) { reinit_resolver(sptr); return 0; } if (match_simple("-ssl*", parv[1]) || match_simple("-tls*", parv[1])) { reinit_ssl(sptr); return 0; } if (match_simple("-o*motd", parv[1])) { if (cptr != sptr) sendto_umode_global(UMODE_OPER, "Remotely rehashing OPERMOTD on request of %s", sptr->name); else sendto_ops("Rehashing OPERMOTD on request of %s", sptr->name); read_motd(conf_files->opermotd_file, &opermotd); RunHook3(HOOKTYPE_REHASHFLAG, cptr, sptr, parv[1]); return 0; } if (match_simple("-b*motd", parv[1])) { if (cptr != sptr) sendto_umode_global(UMODE_OPER, "Remotely rehashing BOTMOTD on request of %s", sptr->name); else sendto_ops("Rehashing BOTMOTD on request of %s", sptr->name); read_motd(conf_files->botmotd_file, &botmotd); RunHook3(HOOKTYPE_REHASHFLAG, cptr, sptr, parv[1]); return 0; } if (!strnicmp("-motd", parv[1], 5) || !strnicmp("-rules", parv[1], 6)) { if (cptr != sptr) sendto_umode_global(UMODE_OPER, "Remotely rehasing all MOTDs and RULES on request of %s", sptr->name); else sendto_ops("Rehashing all MOTDs and RULES on request of %s", sptr->name); rehash_motdrules(); RunHook3(HOOKTYPE_REHASHFLAG, cptr, sptr, parv[1]); return 0; } RunHook3(HOOKTYPE_REHASHFLAG, cptr, sptr, parv[1]); return 0; } } else { if (loop.ircd_rehashing) { sendnotice(sptr, "A rehash is already in progress"); return 0; } sendto_ops("%s is rehashing server config file", sptr->name); } /* Normal rehash, rehash motds&rules too, just like the on in the tld block will :p */ if (cptr == sptr) sendnumeric(sptr, RPL_REHASHING, configfile); x = rehash(cptr, sptr, (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0); reread_motdsandrules(); return x; } /* ** m_restart ** ** parv[1] - password *OR* reason if no drpass { } block exists ** parv[2] - reason for restart (optional & only if drpass block exists) */ CMD_FUNC(m_restart) { char *reason = parv[1]; aClient *acptr; int i; /* Check permissions */ if (!ValidatePermissionsForPath("server:restart",sptr,NULL,NULL,NULL)) { sendnumeric(sptr, ERR_NOPRIVILEGES); return 0; } /* Syntax: /restart */ if (parc == 1) { if (conf_drpass) { sendnumeric(sptr, ERR_NEEDMOREPARAMS, "RESTART"); return 0; } } else if (parc >= 2) { /* Syntax: /restart [reason] */ if (conf_drpass) { int ret; ret = Auth_Check(cptr, conf_drpass->restartauth, parv[1]); if (ret == -1) { sendnumeric(sptr, ERR_PASSWDMISMATCH); return 0; } if (ret < 1) return 0; reason = parv[2]; } } sendto_ops("Server is Restarting by request of %s", sptr->name); list_for_each_entry(acptr, &lclient_list, lclient_node) { if (IsClient(acptr)) sendnotice(acptr, "Server Restarted by %s", sptr->name); else if (IsServer(acptr)) sendto_one(acptr, NULL, ":%s ERROR :Restarted by %s: %s", me.name, get_client_name(sptr, TRUE), reason ? reason : "No reason"); } server_reboot(reason ? reason : "No reason"); return 0; } /* * Heavily modified from the ircu m_motd by codemastr * Also svsmotd support added */ int short_motd(aClient *sptr) { ConfigItem_tld *tld; aMotdFile *themotd; aMotdLine *motdline; struct tm *tm; char is_short; tm = NULL; is_short = 1; tld = Find_tld(sptr); /* * Try different sources of short MOTDs, falling back to the * long MOTD. */ themotd = &smotd; if (tld && tld->smotd.lines) themotd = &tld->smotd; /* try long MOTDs */ if (!themotd->lines) { is_short = 0; if (tld && tld->motd.lines) themotd = &tld->motd; else themotd = &motd; } if (!themotd->lines) { sendnumeric(sptr, ERR_NOMOTD); return 0; } if (themotd->last_modified.tm_year) { tm = &themotd->last_modified; /* for readability */ sendnumeric(sptr, RPL_MOTDSTART, me.name); sendnumericfmt(sptr, RPL_MOTD, "- %d/%d/%d %d:%02d", tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year, tm->tm_hour, tm->tm_min); } if (is_short) { sendnumeric(sptr, RPL_MOTD, "This is the short MOTD. To view the complete MOTD type /motd"); sendnumeric(sptr, RPL_MOTD, ""); } motdline = NULL; if (themotd) motdline = themotd->lines; while (motdline) { sendnumeric(sptr, RPL_MOTD, motdline->line); motdline = motdline->next; } sendnumeric(sptr, RPL_ENDOFMOTD); return 0; } /* * A merge from ircu and bahamut, and some extra stuff added by codemastr * we can now use 1 function for multiple files -- codemastr * Merged read_motd/read_rules stuff into this -- Syzop */ /** Read motd-like file, used for rules/motd/botmotd/opermotd/etc. * Multiplexes to either directly reading the MOTD or downloading it asynchronously. * @param filename Filename of file to read or URL. NULL is accepted and causes the *motd to be free()d. * @param motd Reference to motd pointer (used for freeing if needed and for asynchronous remote MOTD support) */ void read_motd(const char *filename, aMotdFile *themotd) { #ifdef USE_LIBCURL time_t modtime; aMotdDownload *motd_download; #endif /* TODO: if themotd points to a tld's motd, could a rehash disrupt this pointer?*/ #ifdef USE_LIBCURL if(themotd->motd_download) { themotd->motd_download->themotd = NULL; /* * It is not our job to free() motd_download, the * read_motd_asynch_downloaded() function will do that * when it sees that ->themod == NULL. */ themotd->motd_download = NULL; } /* if filename is NULL, do_read_motd will catch it */ if(filename && url_is_valid(filename)) { /* prepare our payload for read_motd_asynch_downloaded() */ motd_download = MyMallocEx(sizeof(aMotdDownload)); if(!motd_download) outofmemory(); motd_download->themotd = themotd; themotd->motd_download = motd_download; modtime = unreal_getfilemodtime(unreal_mkcache(filename)); download_file_async(filename, modtime, (vFP)read_motd_asynch_downloaded, motd_download); return; } #endif /* USE_LIBCURL */ do_read_motd(filename, themotd); return; } #ifdef USE_LIBCURL /** Callback for download_file_async() called from read_motd() below. @param url the URL curl groked or NULL if the MOTD is stored locally. @param filename the path to the local copy of the MOTD or NULL if either cached=1 or there's an error. @param errorbuf NULL or an errorstring if there was an error while downloading the MOTD. @param cached 0 if the URL was downloaded freshly or 1 if the last download was canceled and the local copy should be used. */ void read_motd_asynch_downloaded(const char *url, const char *filename, const char *errorbuf, int cached, aMotdDownload *motd_download) { aMotdFile *themotd; themotd = motd_download->themotd; /* check if the download was soft-canceled. See struct.h's docs on struct MotdDownload for details. */ if(!themotd) { MyFree(motd_download); return; } /* errors -- check for specialcached version if applicable */ if(!cached && !filename) { if(has_cached_version(url)) { config_warn("Error downloading MOTD file from \"%s\": %s -- using cached version instead.", displayurl(url), errorbuf); filename = unreal_mkcache(url); } else { config_error("Error downloading MOTD file from \"%s\": %s", displayurl(url), errorbuf); /* remove reference to this chunk of memory about to be freed. */ motd_download->themotd->motd_download = NULL; MyFree(motd_download); return; } } /* * We need to move our newly downloaded file to its cache file * if it isn't there already. */ if(!cached) { /* create specialcached version for later */ unreal_copyfileex(filename, unreal_mkcache(url), 1); } else { /* * The file is cached. Thus we must look for it at the * cache location where we placed it earlier. */ filename = unreal_mkcache(url); } do_read_motd(filename, themotd); MyFree(motd_download); } #endif /* USE_LIBCURL */ /** Does the actual reading of the MOTD. To be called only by read_motd() or read_motd_asynch_downloaded(). */ void do_read_motd(const char *filename, aMotdFile *themotd) { FILE *fd; struct tm *tm_tmp; time_t modtime; char line[512]; char *tmp; aMotdLine *last, *temp; free_motd(themotd); if(!filename) return; fd = fopen(filename, "r"); if (!fd) return; /* record file modification time */ modtime = unreal_getfilemodtime(filename); tm_tmp = localtime(&modtime); memcpy(&themotd->last_modified, tm_tmp, sizeof(struct tm)); last = NULL; while (fgets(line, sizeof(line), fd)) { if ((tmp = strchr(line, '\n'))) *tmp = '\0'; if ((tmp = strchr(line, '\r'))) *tmp = '\0'; if (strlen(line) > 510) line[510] = '\0'; temp = MyMallocEx(sizeof(aMotdLine)); temp->line = strdup(line); if(last) last->next = temp; else /* handle the special case of the first line */ themotd->lines = temp; last = temp; } /* the file could be zero bytes long? */ if(last) last->next = NULL; fclose(fd); return; } /** Frees the contents of a aMotdFile structure. The aMotdFile structure itself should be statically allocated and deallocated. If the caller wants, it must manually free the aMotdFile structure itself. */ void free_motd(aMotdFile *themotd) { aMotdLine *next, *motdline; if(!themotd) return; for (motdline = themotd->lines; motdline; motdline = next) { next = motdline->next; MyFree(motdline->line); MyFree(motdline); } themotd->lines = NULL; memset(&themotd->last_modified, '\0', sizeof(struct tm)); #ifdef USE_LIBCURL /* see struct.h for more information about motd_download */ themotd->motd_download = NULL; #endif } /* m_die, this terminates the server, and it intentionally does not * have a reason. If you use it you should first do a GLOBOPS and * then a server notice to let everyone know what is going down... */ CMD_FUNC(m_die) { aClient *acptr; int i; if (!ValidatePermissionsForPath("server:die",sptr,NULL,NULL,NULL)) { sendnumeric(sptr, ERR_NOPRIVILEGES); return 0; } if (conf_drpass) /* See if we have and DIE/RESTART password */ { if (parc < 2) /* And if so, require a password :) */ { sendnumeric(sptr, ERR_NEEDMOREPARAMS, "DIE"); return 0; } i = Auth_Check(cptr, conf_drpass->dieauth, parv[1]); if (i == -1) { sendnumeric(sptr, ERR_PASSWDMISMATCH); return 0; } if (i < 1) { return 0; } } /* Let the +s know what is going on */ sendto_ops("Server Terminating by request of %s", sptr->name); list_for_each_entry(acptr, &lclient_list, lclient_node) { if (IsClient(acptr)) sendnotice(acptr, "Server Terminated by %s", sptr->name); else if (IsServer(acptr)) sendto_one(acptr, NULL, ":%s ERROR :Terminated by %s", me.name, get_client_name(sptr, TRUE)); } (void)s_die(); return 0; } #ifdef _WIN32 /* * Added to let the local console shutdown the server without just * calling exit(-1), in Windows mode. -Cabal95 */ int localdie(void) { aClient *acptr; list_for_each_entry(acptr, &lclient_list, lclient_node) { if (IsClient(acptr)) sendnotice(acptr, "Server Terminated by local console"); else if (IsServer(acptr)) sendto_one(acptr, NULL, ":%s ERROR :Terminated by local console", me.name); } (void)s_die(); return 0; } #endif aPendingNet *pendingnet = NULL; void add_pending_net(aClient *sptr, char *str) { aPendingNet *net; aPendingServer *srv; int num = 1; char *p, *name; if (BadPtr(str) || !sptr) return; /* Allocate */ net = MyMallocEx(sizeof(aPendingNet)); net->sptr = sptr; /* Fill in */ if (*str == '*') str++; for (name = strtoken(&p, str, ","); name; name = strtoken(&p, NULL, ",")) { if (!*name) continue; srv = MyMallocEx(sizeof(aPendingServer)); strlcpy(srv->sid, name, sizeof(srv->sid)); AddListItem(srv, net->servers); } AddListItem(net, pendingnet); } void free_pending_net(aClient *sptr) { aPendingNet *net, *net_next; aPendingServer *srv, *srv_next; for (net = pendingnet; net; net = net_next) { net_next = net->next; if (net->sptr == sptr) { for (srv = net->servers; srv; srv = srv_next) { srv_next = srv->next; MyFree(srv); } DelListItem(net, pendingnet); MyFree(net); /* Don't break, there can be multiple objects */ } } } aPendingNet *find_pending_net_by_sid_butone(char *sid, aClient *exempt) { aPendingNet *net; aPendingServer *srv; if (BadPtr(sid)) return NULL; for (net = pendingnet; net; net = net->next) { if (net->sptr == exempt) continue; for (srv = net->servers; srv; srv = srv->next) if (!strcmp(srv->sid, sid)) return net; } return NULL; } /** Search the pending connections list for any identical sids */ aClient *find_pending_net_duplicates(aClient *cptr, aClient **srv, char **sid) { aPendingNet *net, *other; aPendingServer *s; *srv = NULL; *sid = NULL; for (net = pendingnet; net; net = net->next) { if (net->sptr != cptr) continue; /* Ok, found myself */ for (s = net->servers; s; s = s->next) { char *curr_sid = s->sid; other = find_pending_net_by_sid_butone(curr_sid, cptr); if (other) { *srv = net->sptr; *sid = s->sid; return other->sptr; /* Found another (pending) server with identical numeric */ } } } return NULL; } aClient *find_non_pending_net_duplicates(aClient *cptr) { aPendingNet *net; aPendingServer *s; aClient *acptr; for (net = pendingnet; net; net = net->next) { if (net->sptr != cptr) continue; /* Ok, found myself */ for (s = net->servers; s; s = s->next) { acptr = find_server(s->sid, NULL); if (acptr) return acptr; /* Found another (fully CONNECTED) server with identical numeric */ } } 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, NULL, "PROTOCTL NICKCHARS=%s", langsinuse); } strlcpy(previous_langsinuse, langsinuse, sizeof(previous_langsinuse)); }