/* * Unreal Internet Relay Chat Daemon, src/s_user.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_user.c 2.74 2/8/94 (C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen */ #include "macros.h" #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" #include "proto.h" #ifdef _WIN32 #include "version.h" #endif void send_umode_out(aClient *, aClient *, long); void send_umode_out_nickv2(aClient *, aClient *, long); void send_umode(aClient *, aClient *, long, long, char *); void set_snomask(aClient *, char *); void create_snomask(aClient *, anUser *, char *); extern int short_motd(aClient *sptr); extern aChannel *get_channel(aClient *cptr, char *chname, int flag); /* static Link *is_banned(aClient *, aChannel *); */ int dontspread = 0; extern char *me_hash; extern char backupbuf[]; static char buf[BUFSIZE]; void iNAH_host(aClient *sptr, char *host) { if (!sptr->user) return; userhost_save_current(sptr); if (sptr->user->virthost) { MyFree(sptr->user->virthost); sptr->user->virthost = NULL; } sptr->user->virthost = strdup(host); if (MyConnect(sptr)) sendto_server(&me, 0, 0, ":%s SETHOST :%s", sptr->name, sptr->user->virthost); sptr->umodes |= UMODE_SETHOST; userhost_changed(sptr); sendto_one(sptr, err_str(RPL_HOSTHIDDEN), me.name, sptr->name, sptr->user->virthost); } long set_usermode(char *umode) { int newumode; int what; char *m; int i; newumode = 0; what = MODE_ADD; for (m = umode; *m; m++) switch (*m) { case '+': what = MODE_ADD; break; case '-': what = MODE_DEL; break; case ' ': case '\n': case '\r': case '\t': break; default: for (i = 0; i <= Usermode_highest; i++) { if (!Usermode_Table[i].flag) continue; if (*m == Usermode_Table[i].flag) { if (what == MODE_ADD) newumode |= Usermode_Table[i].mode; else newumode &= ~Usermode_Table[i].mode; } } } return (newumode); } /* ** m_functions execute protocol messages on this server: ** ** 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. */ /* ** next_client ** Local function to find the next matching client. The search ** can be continued from the specified client entry. Normal ** usage loop is: ** ** for (x = client; x = next_client(x,mask); x = x->next) ** HandleMatchingClient; ** */ aClient *next_client(aClient *next, char *ch) { aClient *tmp = next; next = find_client(ch, tmp); if (tmp && list_empty(&next->client_node)) return NULL; if (next != tmp) return next; tmp = next; list_for_each_entry(next, &next->client_node, client_node) { if (!match(ch, next->name) || !match(next->name, ch)) break; } return next; } /* ** hunt_server ** ** Do the basic thing in delivering the message (command) ** across the relays to the specific server (server) for ** actions. ** ** Note: The command is a format string and *MUST* be ** of prefixed style (e.g. ":%s COMMAND %s ..."). ** Command can have only max 8 parameters. ** ** server parv[server] is the parameter identifying the ** target server. ** ** *WARNING* ** parv[server] is replaced with the pointer to the ** real servername from the matched client (I'm lazy ** now --msa). ** ** returns: (see #defines) ** ** Rewritten by Syzop / Oct 2015. This function was rather ** complex and no longer understandable. It also was responsible ** for mysterious issues and crashes. Hence rewritten. */ int hunt_server(aClient *cptr, aClient *sptr, char *command, int server, int parc, char *parv[]) { aClient *acptr; char *saved; /* This would be strange and bad. Previous version assumed "it's for me". Hmm.. okay. */ if (parc <= server || BadPtr(parv[server])) return HUNTED_ISME; acptr = find_client(parv[server], NULL); /* find_client() may find a variety of clients. Only servers/persons please, no 'unknowns'. */ if (acptr && MyConnect(acptr) && !IsMe(acptr) && !IsPerson(acptr) && !IsServer(acptr)) acptr = NULL; if (!acptr) { sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, sptr->name, parv[server]); return HUNTED_NOSUCH; } if (IsMe(acptr) || MyClient(acptr)) return HUNTED_ISME; /* Never send the message back from where it came from */ if (acptr->from == sptr->from) { sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, sptr->name, parv[server]); return HUNTED_NOSUCH; } /* Replace "server" part with actual servername (eg: 'User' -> 'x.y.net') * Ugly. Previous version didn't even restore the state, now we do. */ saved = parv[server]; parv[server] = acptr->name; sendto_one(acptr, command, sptr->name, parv[1], parv[2], parv[3], parv[4], parv[5], parv[6], parv[7], parv[8]); parv[server] = saved; return HUNTED_PASS; } /* ** check_for_target_limit ** ** Return Values: ** True(1) == too many targets are addressed ** False(0) == ok to send message ** */ int check_for_target_limit(aClient *sptr, void *target, const char *name) { #ifndef _WIN32 /* This is not windows compatible */ u_char *p; #ifndef __alpha u_int tmp = ((u_int)(intptr_t)target & 0xffff00) >> 8; #else u_int tmp = ((u_long)target & 0xffff00) >> 8; #endif u_char hash = (tmp * tmp) >> 12; if (ValidatePermissionsForPath("immune:limits",sptr,NULL,NULL,NULL)) return 0; if (sptr->local->targets[0] == hash) return 0; for (p = sptr->local->targets; p < &sptr->local->targets[MAXTARGETS - 1];) if (*++p == hash) { /* move targethash to first position... */ memmove(&sptr->local->targets[1], &sptr->local->targets[0], p - sptr->local->targets); sptr->local->targets[0] = hash; return 0; } if (TStime() < sptr->local->nexttarget) { sptr->local->since += TARGET_DELAY; /* lag them up */ sptr->local->nexttarget += TARGET_DELAY; sendto_one(sptr, err_str(ERR_TARGETTOOFAST), me.name, sptr->name, name, sptr->local->nexttarget - TStime()); return 1; } if (TStime() > sptr->local->nexttarget + TARGET_DELAY*MAXTARGETS) { sptr->local->nexttarget = TStime() - TARGET_DELAY*MAXTARGETS; } sptr->local->nexttarget += TARGET_DELAY; memmove(&sptr->local->targets[1], &sptr->local->targets[0], MAXTARGETS - 1); sptr->local->targets[0] = hash; #endif return 0; } /* ** canonize ** ** reduce a string of duplicate list entries to contain only the unique ** items. Unavoidably O(n^2). */ extern char *canonize(char *buffer) { static char cbuf[2048]; char *s, *t, *cp = cbuf; int l = 0; char *p = NULL, *p2; *cp = '\0'; if (!buffer) return NULL; /* Ohh.. so lazy. But then again, this should never happen with a 2K buffer anyway. */ if (strlen(buffer) >= sizeof(cbuf)) buffer[sizeof(cbuf)-1] = '\0'; for (s = strtoken(&p, buffer, ","); s; s = strtoken(&p, NULL, ",")) { if (l) { for (p2 = NULL, t = strtoken(&p2, cbuf, ","); t; t = strtoken(&p2, NULL, ",")) if (!mycmp(s, t)) break; else if (p2) p2[-1] = ','; } else t = NULL; if (!t) { if (l) *(cp - 1) = ','; else l = 1; (void)strcpy(cp, s); if (p) cp += (p - s); } else if (p2) p2[-1] = ','; } return cbuf; } /* ** get_mode_str ** by vmlinuz ** returns an ascii string of modes */ char *get_sno_str(aClient *sptr) { int i; char *m; m = buf; *m++ = '+'; for (i = 0; i <= Snomask_highest && (m - buf < BUFSIZE - 4); i++) if (Snomask_Table[i].flag && sptr->user->snomask & Snomask_Table[i].mode) *m++ = Snomask_Table[i].flag; *m = 0; return buf; } char *get_mode_str(aClient *acptr) { int i; char *m; m = buf; *m++ = '+'; for (i = 0; (i <= Usermode_highest) && (m - buf < BUFSIZE - 4); i++) if (Usermode_Table[i].flag && (acptr->umodes & Usermode_Table[i].mode)) *m++ = Usermode_Table[i].flag; *m = '\0'; return buf; } char *get_modestr(long umodes) { int i; char *m; m = buf; *m++ = '+'; for (i = 0; (i <= Usermode_highest) && (m - buf < BUFSIZE - 4); i++) if (Usermode_Table[i].flag && (umodes & Usermode_Table[i].mode)) *m++ = Usermode_Table[i].flag; *m = '\0'; return buf; } char *get_snostr(long sno) { int i; char *m; m = buf; *m++ = '+'; for (i = 0; i <= Snomask_highest && (m - buf < BUFSIZE - 4); i++) if (Snomask_Table[i].flag && sno & Snomask_Table[i].mode) *m++ = Snomask_Table[i].flag; *m = 0; return buf; } void set_snomask(aClient *sptr, char *snomask) { int what = MODE_ADD; /* keep this an int. -- Syzop */ char *p; int i; if (snomask == NULL) { sptr->user->snomask = 0; return; } for (p = snomask; p && *p; p++) { switch (*p) { case '+': what = MODE_ADD; break; case '-': what = MODE_DEL; break; default: for (i = 0; i <= Snomask_highest; i++) { if (!Snomask_Table[i].flag) continue; if (*p == Snomask_Table[i].flag) { if (Snomask_Table[i].allowed && !Snomask_Table[i].allowed(sptr,what)) continue; if (what == MODE_ADD) sptr->user->snomask |= Snomask_Table[i].mode; else sptr->user->snomask &= ~Snomask_Table[i].mode; } } } } } void create_snomask(aClient *sptr, anUser *user, char *snomask) { int what = MODE_ADD; /* keep this an int. -- Syzop */ char *p; int i; if (snomask == NULL) { user->snomask = 0; return; } for (p = snomask; p && *p; p++) { switch (*p) { case '+': what = MODE_ADD; break; case '-': what = MODE_DEL; break; default: for (i = 0; i <= Snomask_highest; i++) { if (!Snomask_Table[i].flag) continue; if (*p == Snomask_Table[i].flag) { if (Snomask_Table[i].allowed && !Snomask_Table[i].allowed(sptr,what)) continue; if (what == MODE_ADD) user->snomask |= Snomask_Table[i].mode; else user->snomask &= ~Snomask_Table[i].mode; } } } } } /* * send the MODE string for user (user) to connection cptr * -avalon */ void send_umode(aClient *cptr, aClient *sptr, long old, long sendmask, char *umode_buf) { int i; long flag; char *m; int what = MODE_NULL; /* * build a string in umode_buf to represent the change in the user's * mode between the new (sptr->flag) and 'old'. */ m = umode_buf; *m = '\0'; for (i = 0; i <= Usermode_highest; i++) { if (!Usermode_Table[i].flag) continue; flag = Usermode_Table[i].mode; if (MyClient(sptr) && !(flag & sendmask)) continue; if ((flag & old) && !(sptr->umodes & flag)) { if (what == MODE_DEL) *m++ = Usermode_Table[i].flag; else { what = MODE_DEL; *m++ = '-'; *m++ = Usermode_Table[i].flag; } } else if (!(flag & old) && (sptr->umodes & flag)) { if (what == MODE_ADD) *m++ = Usermode_Table[i].flag; else { what = MODE_ADD; *m++ = '+'; *m++ = Usermode_Table[i].flag; } } } *m = '\0'; if (*umode_buf && cptr) sendto_one(cptr, ":%s MODE %s :%s", sptr->name, sptr->name, umode_buf); } /* * added Sat Jul 25 07:30:42 EST 1992 */ void send_umode_out(aClient *cptr, aClient *sptr, long old) { aClient *acptr; send_umode(NULL, sptr, old, SEND_UMODES, buf); list_for_each_entry(acptr, &server_list, special_node) { if ((acptr != cptr) && (acptr != sptr) && *buf) { sendto_one(acptr, ":%s UMODE2 %s", sptr->name, buf); } } if (cptr && MyClient(cptr)) send_umode(cptr, sptr, old, ALL_UMODES, buf); } void send_umode_out_nickv2(aClient *cptr, aClient *sptr, long old) { aClient *acptr; send_umode(NULL, sptr, old, SEND_UMODES, buf); list_for_each_entry(acptr, &server_list, special_node) { if (!SupportNICKv2(acptr) && (acptr != cptr) && (acptr != sptr) && *buf) sendto_one(acptr, ":%s MODE %s :%s", sptr->name, sptr->name, buf); } if (cptr && MyClient(cptr)) send_umode(cptr, sptr, old, ALL_UMODES, buf); } int del_silence(aClient *sptr, char *mask) { Link **lp; Link *tmp; for (lp = &(sptr->user->silence); *lp; lp = &((*lp)->next)) if (mycmp(mask, (*lp)->value.cp) == 0) { tmp = *lp; *lp = tmp->next; MyFree(tmp->value.cp); free_link(tmp); return 0; } return -1; } int add_silence(aClient *sptr, char *mask, int senderr) { Link *lp; int cnt = 0; for (lp = sptr->user->silence; lp; lp = lp->next) { if (MyClient(sptr)) if ((strlen(lp->value.cp) > MAXSILELENGTH) || (++cnt >= SILENCE_LIMIT)) { if (senderr) sendto_one(sptr, err_str(ERR_SILELISTFULL), me.name, sptr->name, mask); return -1; } else { if (!match(lp->value.cp, mask)) return -1; } else if (!mycmp(lp->value.cp, mask)) return -1; } lp = make_link(); bzero((char *)lp, sizeof(Link)); lp->next = sptr->user->silence; lp->value.cp = (char *)MyMalloc(strlen(mask) + 1); (void)strcpy(lp->value.cp, mask); sptr->user->silence = lp; return 0; }