/* * 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. */ #ifndef CLEAN_COMPILE static char sccsid[] = "@(#)s_user.c 2.74 2/8/94 (C) 1988 University of Oulu, \ Computing Center and Jarkko Oikarinen"; #endif #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 STRIPBADWORDS #include "badwords.h" #endif #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) { DYN_LOCAL(char, did_parts, sptr->user->joined); if (!sptr->user) { DYN_FREE(did_parts); return; } if (UHOST_ALLOWED == UHALLOW_REJOIN) rejoin_doparts(sptr, did_parts); if (sptr->user->virthost) { MyFree(sptr->user->virthost); sptr->user->virthost = NULL; } sptr->user->virthost = strdup(host); if (MyConnect(sptr)) sendto_serv_butone_token(&me, sptr->name, MSG_SETHOST, TOK_SETHOST, "%s", sptr->user->virthost); sptr->umodes |= UMODE_SETHOST; if (UHOST_ALLOWED == UHALLOW_REJOIN) rejoin_dojoinandmode(sptr, did_parts); DYN_FREE(did_parts); } 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[0], sender (prefix string), if not present ** this points to an empty string. ** parv[1]...parv[parc-1] ** pointers to additional parameters ** parv[parc] == NULL, *always* ** ** note: it is guaranteed that parv[0]..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 && tmp->prev == next) return NULL; if (next != tmp) return next; for (; next; next = next->next) { 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) */ int hunt_server(aClient *cptr, aClient *sptr, char *command, int server, int parc, char *parv[]) { aClient *acptr; /* ** Assume it's me, if no server */ if (parc <= server || BadPtr(parv[server]) || match(me.name, parv[server]) == 0 || match(parv[server], me.name) == 0) return (HUNTED_ISME); /* ** These are to pickup matches that would cause the following ** message to go in the wrong direction while doing quick fast ** non-matching lookups. */ if ((acptr = find_client(parv[server], NULL))) if (acptr->from == sptr->from && !MyConnect(acptr)) acptr = NULL; if (!acptr && (acptr = find_server_quick(parv[server]))) if (acptr->from == sptr->from && !MyConnect(acptr)) acptr = NULL; if (!acptr) for (acptr = client, (void)collapse(parv[server]); (acptr = next_client(acptr, parv[server])); acptr = acptr->next) { if (acptr->from == sptr->from && !MyConnect(acptr)) continue; /* * Fix to prevent looping in case the parameter for * some reason happens to match someone from the from * link --jto */ if (IsRegistered(acptr) && (acptr != cptr)) break; } /* Fix for unregistered client receiving msgs: */ if (acptr && MyConnect(acptr) && IsUnknown(acptr)) acptr = NULL; if (acptr) { if (IsMe(acptr) || MyClient(acptr)) return HUNTED_ISME; if (match(acptr->name, parv[server])) parv[server] = acptr->name; sendto_one(acptr, command, parv[0], parv[1], parv[2], parv[3], parv[4], parv[5], parv[6], parv[7], parv[8]); return (HUNTED_PASS); } sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[server]); return (HUNTED_NOSUCH); } /* ** hunt_server_token ** ** Do the basic thing in delivering the message (command) ** across the relays to the specific server (server) for ** actions. This works like hunt_server, except if the ** server supports tokens, the token is used. ** ** command specifies the command name ** token specifies the token name ** params is a formated parameter string ** 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) */ int hunt_server_token(aClient *cptr, aClient *sptr, char *command, char *token, char *params, int server, int parc, char *parv[]) { aClient *acptr; /* ** Assume it's me, if no server */ if (parc <= server || BadPtr(parv[server]) || match(me.name, parv[server]) == 0 || match(parv[server], me.name) == 0) return (HUNTED_ISME); /* ** These are to pickup matches that would cause the following ** message to go in the wrong direction while doing quick fast ** non-matching lookups. */ if ((acptr = find_client(parv[server], NULL))) if (acptr->from == sptr->from && !MyConnect(acptr)) acptr = NULL; if (!acptr && (acptr = find_server_quick(parv[server]))) if (acptr->from == sptr->from && !MyConnect(acptr)) acptr = NULL; if (!acptr) for (acptr = client, (void)collapse(parv[server]); (acptr = next_client(acptr, parv[server])); acptr = acptr->next) { if (acptr->from == sptr->from && !MyConnect(acptr)) continue; /* * Fix to prevent looping in case the parameter for * some reason happens to match someone from the from * link --jto */ if (IsRegistered(acptr) && (acptr != cptr)) break; } /* Fix for unregistered client receiving msgs: */ if (acptr && MyConnect(acptr) && IsUnknown(acptr)) acptr = NULL; if (acptr) { char buff[1024]; if (IsMe(acptr) || MyClient(acptr)) return HUNTED_ISME; if (match(acptr->name, parv[server])) parv[server] = acptr->name; if (IsToken(acptr->from)) { sprintf(buff, ":%s %s ", parv[0], token); strcat(buff, params); sendto_one(acptr, buff, parv[1], parv[2], parv[3], parv[4], parv[5], parv[6], parv[7], parv[8]); } else { sprintf(buff, ":%s %s ", parv[0], command); strcat(buff, params); sendto_one(acptr, buff, parv[1], parv[2], parv[3], parv[4], parv[5], parv[6], parv[7], parv[8]); } return (HUNTED_PASS); } sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[server]); return (HUNTED_NOSUCH); } int hunt_server_token_quiet(aClient *cptr, aClient *sptr, char *command, char *token, char *params, int server, int parc, char *parv[]) { aClient *acptr; /* ** Assume it's me, if no server */ if (parc <= server || BadPtr(parv[server]) || match(me.name, parv[server]) == 0 || match(parv[server], me.name) == 0) return (HUNTED_ISME); /* ** These are to pickup matches that would cause the following ** message to go in the wrong direction while doing quick fast ** non-matching lookups. */ if ((acptr = find_client(parv[server], NULL))) if (acptr->from == sptr->from && !MyConnect(acptr)) acptr = NULL; if (!acptr && (acptr = find_server_quick(parv[server]))) if (acptr->from == sptr->from && !MyConnect(acptr)) acptr = NULL; if (!acptr) for (acptr = client, (void)collapse(parv[server]); (acptr = next_client(acptr, parv[server])); acptr = acptr->next) { if (acptr->from == sptr->from && !MyConnect(acptr)) continue; /* * Fix to prevent looping in case the parameter for * some reason happens to match someone from the from * link --jto */ if (IsRegistered(acptr) && (acptr != cptr)) break; } if (acptr) { char buff[1024]; if (IsMe(acptr) || MyClient(acptr)) return HUNTED_ISME; if (match(acptr->name, parv[server])) parv[server] = acptr->name; if (IsToken(acptr->from)) { sprintf(buff, ":%s %s ", parv[0], token); strcat(buff, params); sendto_one(acptr, buff, parv[1], parv[2], parv[3], parv[4], parv[5], parv[6], parv[7], parv[8]); } else { sprintf(buff, ":%s %s ", parv[0], command); strcat(buff, params); sendto_one(acptr, buff, parv[1], parv[2], parv[3], parv[4], parv[5], parv[6], parv[7], parv[8]); } return (HUNTED_PASS); } return (HUNTED_NOSUCH); } /* ** 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 (IsAnOper(sptr)) return 0; if (sptr->targets[0] == hash) return 0; for (p = sptr->targets; p < &sptr->targets[MAXTARGETS - 1];) if (*++p == hash) { /* move targethash to first position... */ memmove(&sptr->targets[1], &sptr->targets[0], p - sptr->targets); sptr->targets[0] = hash; return 0; } if (TStime() < sptr->nexttarget) { sptr->since += TARGET_DELAY; /* lag them up */ sptr->nexttarget += TARGET_DELAY; sendto_one(sptr, err_str(ERR_TARGETTOOFAST), me.name, sptr->name, name, sptr->nexttarget - TStime()); return 1; } if (TStime() > sptr->nexttarget + TARGET_DELAY*MAXTARGETS) { sptr->nexttarget = TStime() - TARGET_DELAY*MAXTARGETS; } sptr->nexttarget += TARGET_DELAY; memmove(&sptr->targets[1], &sptr->targets[0], MAXTARGETS - 1); sptr->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[BUFSIZ]; char *s, *t, *cp = cbuf; int l = 0; char *p = NULL, *p2; *cp = '\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 %s %s :%s", sptr->name, (IsToken(cptr) ? TOK_MODE : MSG_MODE), sptr->name, umode_buf); } /* * added Sat Jul 25 07:30:42 EST 1992 */ void send_umode_out(aClient *cptr, aClient *sptr, long old) { int i; aClient *acptr; send_umode(NULL, sptr, old, SEND_UMODES, buf); for (i = LastSlot; i >= 0; i--) if ((acptr = local[i]) && IsServer(acptr) && (acptr != cptr) && (acptr != sptr) && *buf) { if (!SupportUMODE2(acptr)) { sendto_one(acptr, ":%s MODE %s :%s", sptr->name, sptr->name, buf); } else { sendto_one(acptr, ":%s %s %s", sptr->name, (IsToken(acptr) ? TOK_UMODE2 : MSG_UMODE2), buf); } } if (cptr && MyClient(cptr)) send_umode(cptr, sptr, old, ALL_UMODES, buf); } void send_umode_out_nickv2(aClient *cptr, aClient *sptr, long old) { int i; aClient *acptr; send_umode(NULL, sptr, old, SEND_UMODES, buf); for (i = LastSlot; i >= 0; i--) if ((acptr = local[i]) && IsServer(acptr) && !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; }