1 /*
2  * nincache.c: this parses and caches date from the server...
3  *
4  * this was created because:
5  *  1. didn't want to make a big mess in parse.c
6  *  2. wanted to cache data before do_hook(RAW_IRC_LIST...
7  *
8  * written by Joshua J. Drake <jduck@EFNet>
9  */
10 
11 #include "irc.h"
12 #include "parse.h"
13 #include "channels.h"
14 #include "dma.h"
15 #include "server.h"
16 #include "ircaux.h"
17 #include "funny.h"
18 #include "notify.h"
19 #include "window.h"
20 #include "ctcp.h"
21 #include "vars.h"
22 #include "server.h"
23 #include "output.h"
24 
25 #include "bans.h"
26 #include "orignick.h"
27 #include "friends.h"
28 #include "ndcc.h"
29 #include "nicks.h"
30 
31 /*
32  * this idea would cause joining race conditions resulting in
33  * people not getting cached when they joined..
34  *
35  * well this is only used for whether or not to display the who line now..
36  *
37 static	u_char	*who_hack_nick = UNULL;
38 static	int	who_hack_warned = 0;
39  *
40  * and its not used anymore.. server[index].who_nicks instead keeps track
41  * of multiple people...
42  */
43 
44 extern	int	chk_channel_cached _((u_char *));
45 static	int	check_who_nicks _((u_char *, int));
46 
47 int
parse_into_cache(line,flag,str_flag)48 parse_into_cache(line, flag, str_flag)
49    u_char *line;
50    int *flag;
51    u_char **str_flag;
52 {
53    u_char *copy = UNULL, *comm, *from = UNULL;
54    u_char *chk_chan = UNULL;
55    u_char **ArgList, *TrueArgs[MAXPARA+1];
56    int numeric, ret = 1;
57 
58    /* make a copy and break it up */
59    dma_strcpy(&copy, line);
60    ArgList = TrueArgs;
61    BreakArgs(copy, &from, ArgList);
62 
63    /* if no command, return */
64    if (!(comm = (*ArgList++)))
65      return ret;
66 
67    /* is it numeric? */
68    numeric = my_atoi(comm);
69    if (numeric > 0)
70      {
71 	ArgList++; /* skip past "user" field, it should be me! */
72 	switch (numeric)
73 	  {
74 	   case 004:	/* allowed channel and user modes for server */
75 	       {
76 		  if (ArgList[0] && ArgList[1] && ArgList[2] && ArgList[3])
77 		    {
78 		       if (my_strncmp(ArgList[1], "2.8/hybrid-", 11) == 0)
79 			 set_server_version(parsing_server_index, Server2_8_hyb);
80 		       dma_strcpy(&(server_list[parsing_server_index].umodes), ArgList[2]);
81 		       dma_strcpy(&(server_list[parsing_server_index].cmodes), ArgList[3]);
82 		       goto out;
83 		    }
84 	       }
85 	     break;
86 	   case 324:   	/* update channel mode */
87 	       {
88 		  u_char *mode, *channel;
89 		  Channel *chan;
90 
91 		  if (!ArgList[0])
92 		    goto out;
93 
94 		  /* backwards compatability sucks! */
95 		  if (get_server_version(parsing_server_index) < Server2_6)
96 		    {
97 		       channel = NULL;
98 		       mode = ArgList[0];
99 		       PasteArgs(ArgList, 0);
100 		    }
101 		  else
102 		    {
103 		       channel = ArgList[0];
104 		       mode = ArgList[1];
105 		       PasteArgs(ArgList, 1);
106 		    }
107 
108 		  /* lookup the channel */
109 		  chan = lookup_channel(channel, parsing_server_index, CHAN_NOUNLINK);
110 		  if (!chan)
111 		    goto out;
112 		  /* the channel to check for complete sync at the end */
113 		  dma_strcpy(&chk_chan, chan->channel);
114 		  /* upate the mode! */
115 		  update_channel_mode(UNULL, parsing_server_index, mode, from, chan);
116 
117 		  /* the mode is sync'd now! */
118 		  if (!(chan->status & CHAN_MODE))
119 		    {
120 		       ret = 0;
121 		       chan->status |= CHAN_MODE;
122 		       update_all_status();
123 		    }
124 	       }
125 	     break;
126 	   case 367:	/* ban entry */
127 	       {
128 		  Channel *chan;
129 
130 		  /* lookup the channel */
131 		  if (!ArgList[0])
132 		    goto out;
133 		  chan = lookup_channel(ArgList[0], parsing_server_index, CHAN_NOUNLINK);
134 
135 		  if (!chan || (chan->status & CHAN_BANS))
136 		    goto out;
137 
138 		  /* the channel to check for complete sync at the end */
139 		  dma_strcpy(&chk_chan, chan->channel);
140 
141 		  if (ArgList[1] && ArgList[2] && ArgList[3])
142 		    add_ban(chan, ArgList[1], ArgList[2], ArgList[3]);
143 		  else if (ArgList[1] && ArgList[2])
144 		    add_ban(chan, ArgList[1], ArgList[2], UNULL);
145 		  else if (ArgList[1])
146 		    add_ban(chan, ArgList[1], UNULL, UNULL);
147 		  ret = 0;
148 	       }
149 	     break;
150 	   case 348:	/* ban exception entry */
151 	       {
152 		  Channel *chan;
153 
154 		  /* lookup the channel */
155 		  chan = lookup_channel(ArgList[0], parsing_server_index, CHAN_NOUNLINK);
156 		  if (!chan || (chan->status & CHAN_EXCEPT))
157 		    goto out;
158 
159 		  /* the channel to check for complete sync at the end */
160 		  dma_strcpy(&chk_chan, chan->channel);
161 
162 		  /* add_exception(chan, ArgList[2], ArgList[3], ArgList[4]); */
163 		  ret = 0;
164 	       }
165 	     break;
166 	   case 315:	/* end of who */
167 	   case 349:	/* end of ban exceptions */
168 	   case 366:	/* end of names */
169 	   case 368:	/* end of bans */
170 	   case 329:	/* channel timestamp... not cached yet */
171 	       {
172 		  Channel *chan;
173 		  int tmp = 0;
174 
175 		  if (numeric == 315)
176 		    tmp |= CHAN_WHO;
177 		  else if (numeric == 349)
178 		    tmp |= CHAN_EXCEPT;
179 		  else if (numeric == 368)
180 		    tmp |= CHAN_BANS;
181 		  else if (numeric == 329)
182 		    tmp |= CHAN_START;
183 		  else if (numeric == 366)
184 		    tmp |= CHAN_NAMES;
185 
186 		  /* it must be a nick instead of a channel, eh? */
187 		  if (numeric == 315
188 		      && !is_channel(ArgList[0]))
189 		    {
190 		       ret = check_who_nicks(ArgList[0], 1);
191 		       goto out;
192 		    }
193 
194 		  /* lookup the channel */
195 		  chan = lookup_channel(ArgList[0], parsing_server_index, CHAN_NOUNLINK);
196 		  if (!chan || (chan->status & tmp))
197 		    goto out;
198 
199 		  /* the channel to check for complete sync at the end */
200 		  dma_strcpy(&chk_chan, chan->channel);
201 
202 		  chan->status |= tmp;
203 		  if (numeric != 366 ||		/* show then end maybe */
204 		      ((numeric == 366)
205 		       && !get_int_var(SHOW_CHANNEL_NAMES_VAR)))
206 		    ret = 0;
207 	       }
208 	     break;
209 	   case 352:	/* who entry */
210 	       {
211 		  u_char  *channel, *user, *host, *server, *nick, *stat, *name, *hopcount;
212 		  int i = 0, chop, oper, voice, hops, here;
213 		  Channel *tmp, *c2 = NULL;
214 		  Nick *n;
215 
216 		  /* get args */
217 		  channel = user = host = server = nick = stat = hopcount = name = UNULL;
218 		  chop = oper = voice = hops = here = 0;
219 		  if (ArgList[i])
220 		    channel = ArgList[i++];
221 
222 		  /* lookup the channel (if we aren't on it, go to the whois_who stuff! */
223 		  tmp = lookup_channel(channel, parsing_server_index, CHAN_NOUNLINK);
224 		  c2 = tmp;
225 
226 		  /* i'm there, go on... */
227 		  if (ArgList[i])
228 		    user = ArgList[i++];
229 		  if (ArgList[i])
230 		    host = ArgList[i++];
231 		  if (ArgList[i])
232 		    server = ArgList[i++];
233 		  if (ArgList[i])
234 		    nick = ArgList[i++];
235 		  if (ArgList[i])
236 		    stat = ArgList[i++];
237 		  PasteArgs(ArgList, i);
238 		  if (ArgList[i])
239 		    name = ArgList[i];
240 
241 		  /* who header?! wtf */
242 		  if (stat && *stat == 'S')
243 		    goto out;
244 
245 		  /* parse up the name field for a possible hop count */
246 		  hops = -1;
247 		  if (name)
248 		    {
249 		       if (*name == ':')
250 			 name++;
251 		       if (isdigit(*name) && my_index(name, ' '))
252 			 hopcount = next_arg(name, &name);
253 		       if (hopcount)
254 			 hops = my_atoi(hopcount);
255 		    }
256 
257 		  /* parse out stat stuff */
258 		  if (stat)
259 		    {
260 		       here = (*stat == 'H');
261 		       voice = (my_index(stat, '+') != NULL);
262 		       chop = (my_index(stat, '@') != NULL);
263 		       oper = (my_index(stat, '*') != NULL);
264 		    }
265 
266 		  /* update global stuff for all other channels they're on with me
267 		   * and if its a channel that shows in the who line, then update the chan
268 		   * specific stuff too..
269 		   */
270 		  if (c2)
271 		    {
272 		       add_to_channel(tmp->channel, nick, parsing_server_index,
273 				      oper, chop, voice,
274 				      user, host, name,
275 				      hops, here, server);
276 		       if (!(c2->status & CHAN_WHO))
277 			 {
278 			    ret = 0;
279 			    goto out;
280 			 }
281 
282 		       /* the channel to check for complete sync at the end */
283 		       dma_strcpy(&chk_chan, c2->channel);
284 		    }
285 		  else /* i'm not on that channel! */
286 		    {
287 		       /* we'll always update them on all channnels they are on... */
288 		       for (tmp = server_list[parsing_server_index].chan_list; tmp; tmp = tmp->next)
289 			 if ((n = find_nick(nick, UNULL, parsing_server_index, tmp)))
290 			   add_to_channel(tmp->channel, nick, parsing_server_index,
291 					  oper, -1, -1,
292 					  user, host, name,
293 					  hops, here, server);
294 		    }
295 		  /* BUT,
296 		   * if we are /who'n them on a join, we must ignore the line..
297 		   *
298 		   * we don't adjust the list here, we do that in ENDOFWHO stuff
299 		   */
300 		  ret = check_who_nicks(nick, 0);
301 		  goto out;
302 	       }
303 	     break;
304 	   case 353: /* names line.. this is here for compatability..
305 		      * and since most servers send these on join already!
306 		      */
307 	       {
308 		  u_char *nick, *p;
309 		  Channel *ch;
310 
311 		  ch = lookup_channel(ArgList[1], parsing_server_index, CHAN_NOUNLINK);
312 		  if (!ch || (ch->status & CHAN_NAMES))
313 		    goto out;
314 		  PasteArgs(ArgList, 2);
315 		  p = ArgList[2];
316 		  while ((nick = next_arg(p, &p)) != NULL)
317 		    add_to_channel(ArgList[1], nick, parsing_server_index,
318 				   -1, -1, -1,
319 				   UNULL, UNULL, UNULL,
320 				   -1, -1, UNULL);
321 	       }
322 	     break;
323 	   default:
324 	     break;
325 	  }
326      }
327    else
328      {
329 	/* non-numeric thing needing to be parsed into cache
330 	 *
331 	 * this stuff is mostly maintaining the cache, the above numeric stuff
332 	 * mostly does join cacheing..
333 	 */
334 
335 	/* part's must be handled silently here (for channel cache) */
336 	if (my_strcmp(comm, "PART") == 0)
337 	  {
338 	     if (my_stricmp(from, get_server_nickname(parsing_server_index)) == 0)
339 	       remove_channel(ArgList[0], parsing_server_index);
340 	     else
341 	       remove_from_channel(ArgList[0], from, from_server);
342 	     goto out;
343 	  }
344 
345 	/*
346 	 * I had to move this back to parse.c due to signoffs not showing up and not as
347 	 * easy to hack it as it was with NICK
348 	 *
349 	 * someone quit, remove them from all channels!
350 	if (my_strcmp(comm, "QUIT") == 0)
351 	  {
352 	     remove_from_channel(NULL, from, from_server);
353 	     notify_mark(from, 0, 0, UNULL, UNULL, 0);
354 	     nchk_orignick(from);
355 	     goto out;
356 	  }
357 	 */
358 
359 	/* someone entered a channel (or exited for old style) */
360 	if (my_strcmp(comm, "CHANNEL") == 0
361 	    || my_strcmp(comm, "JOIN") == 0)
362 	  {
363 	     int join, chop = 0, voice = 0;
364 	     u_char *channel, *s;
365 
366 	     if (!from)
367 	       goto out;
368 	     /* are we joining 0? */
369 	     if (my_strcmp(ArgList[0], zero) != 0)
370 	       {
371 		  join = 1;
372 		  channel = ArgList[0];
373 		  if ((s = my_index(channel, '\007')))
374 		    {
375 		       *s = '\0';
376 		       while (*++s)
377 			 {
378 			    if (*s == 'o')
379 			      chop = 1;
380 			    if (*s == 'v')
381 			      voice = 1;
382 			 }
383 		    }
384 		  dma_strcpy(&joined_nick, from);
385 	       }
386 	     else
387 	       {
388 		  join = 0;
389 		  channel = zero;
390 	       }
391 
392 	     /* is it me? */
393 	     if (!my_stricmp(from, get_server_nickname(parsing_server_index)))
394 	       {
395 		  if (join)
396 		    {
397 		       add_channel(channel, parsing_server_index, CHAN_JOINED, (Channel *)0);
398 		       if (get_server_version(parsing_server_index) == Server2_5)
399 			 send_to_server("NAMES %s", channel);
400 		       send_to_server("WHO %s", channel);
401 		       send_to_server("MODE %s", channel);
402 		       send_to_server("MODE %s b", channel);
403 		       /*
404 			* MISC server modes, not really implemented yet..
405 			*
406 		       if (my_index(server_list[parsing_server_index].cmodes, 'e'))
407 		       send_to_server("MODE %s e", channel);
408 		       if (my_index(server_list[parsing_server_index].cmodes, 'd'))
409 		       send_to_server("MODE %s d", channel);
410 			*/
411 		    }
412 		  else
413 		    remove_channel(channel, parsing_server_index);
414 	       }
415 	     else
416 	       {
417 		  if (join)
418 		    {
419 		       Channel *chan;
420 
421 		       /* XXX: check for netjoins */
422 
423 		       chan = lookup_channel(channel, parsing_server_index, CHAN_NOUNLINK);
424 		       if (!chan)
425 			 {
426 			    /* little error message to see if this is ever reached */
427 			    put_error("odd, someone joined %s on server #%d which i know nothing about...",
428 				      channel, parsing_server_index+1);
429 			 }
430 
431 		       /* blah but we have to add them with minor stuff for the next thing to work.. */
432 		       add_to_channel(channel, from, parsing_server_index,
433 				      -1, 0, 0,
434 				      FromUser, FromHost, UNULL,
435 				      -1, -1, UNULL);
436 
437 		       /*
438 			* we'll just /who the user... this will update the cache..
439 			*/
440 		       if (get_int_var(EXTENDED_CACHE_VAR))
441 			 {
442 			    if (server_list[parsing_server_index].who_nicks)
443 			      dma_strcat(&server_list[parsing_server_index].who_nicks, ",");
444 			    dma_strcat(&server_list[parsing_server_index].who_nicks, from);
445 			    send_to_server("WHO %s", from);
446 			 }
447 
448 		       /* if i'm a channel operator, then do friends list stuff */
449 		       if ((chan->status & CHAN_CHOP))
450 			 check_friend_join(chan, from, FromUser, FromHost, 3+(int) (4.0*rand()/(RAND_MAX+1.0)));
451 		    }
452 		  else
453 		    remove_from_channel(channel, from, parsing_server_index);
454 	       }
455 	     goto out;
456 	  }
457 
458 	/* nick changes */
459 	if (my_strcmp(comm, "NICK") == 0)
460 	  {
461 	     dma_strcpy(str_flag, ArgList[0]);
462 	     /* am i changing my nickname? */
463 	     if (my_stricmp(from, get_server_nickname(parsing_server_index)) == 0)
464 	       {
465 		  if (parsing_server_index == primary_server)
466 		    malloc_strcpy(&nickname, ArgList[0]);
467 		  set_server_nickname(parsing_server_index, ArgList[0]);
468 		  *flag = 1;
469 	       }
470 	     else
471 	       nchk_orignick(from);
472 	     rename_nick(from, ArgList[0], parsing_server_index);
473 	     if (my_stricmp(from, ArgList[0]) != 0)
474 	       {
475 		  notify_mark(from, 0, 0, FromUser, FromHost, 0);
476 		  notify_mark(ArgList[0], 1, 0, FromUser, FromHost, 0);
477 	       }
478 	     goto out;
479 	  }
480 
481 	/* kick's */
482 	if (my_strcmp(comm, "KICK") == 0)
483 	  {
484 	     /* me? */
485 	     if (my_stricmp(ArgList[1], get_server_nickname(parsing_server_index)) == 0)
486 	       remove_channel(ArgList[0], parsing_server_index);
487 	     else
488 	       {
489 		  /* this friends list check must be done before removing
490 		   * them from the channel
491 		   */
492 		  check_friend_kick(ArgList[0], from, FromUser, FromHost, ArgList[1]);
493 		  remove_from_channel(ArgList[0], ArgList[1], parsing_server_index);
494 	       }
495 	     goto out;
496 	  }
497 
498 	/* mode changes */
499 	if (my_strcmp(comm, "MODE") == 0)
500 	  {
501 	     PasteArgs(ArgList, 1);
502 	     if (ArgList[0] && ArgList[1])
503 	       {
504 		  if (is_channel(ArgList[0]))
505 		    update_channel_mode(ArgList[0], parsing_server_index, ArgList[1], from, (Channel *)0);
506 		  else
507 		    update_user_mode(ArgList[1]);
508 	       }
509 	     goto out;
510 	  }
511 
512 	/* pings, server seeing if we're alive */
513 	if (my_strcmp(comm, "PING") == 0)
514 	  {
515 	     PasteArgs(ArgList, 0);
516 	     send_to_server("PONG :%s", ArgList[0]);
517 	     ret = 0;
518 	     goto out;
519 	  }
520 
521 	/* pongs, lag check */
522 	if (my_strcmp(comm, "PONG") == 0)
523 	  {
524 	     Server *srv = (Server *)&(server_list[parsing_server_index]);
525 
526 	     if (my_stricmp(from, srv->itsname) == 0
527 		 && srv->in_ping
528 		 /* && my_strncmp("LAG:", ArgList[1], 4) == 0 */ )
529 	       {
530 		  set_server_lag(parsing_server_index);
531 		  ret = 0;
532 		  goto out;
533 	       }
534 	  }
535 
536 	/* privmsg, anti-idle?! */
537 	if (my_strcmp(comm, "PRIVMSG") == 0)
538 	  {
539 	     Server *srv = (Server *)&(server_list[parsing_server_index]);
540 	     u_char *ptr = ArgList[1];
541 
542 	     if (my_strncmp("LAG CHECK ", ArgList[1], 10) == 0
543 		 && !my_stricmp(from, srv->nickname)
544 		 && srv->in_ping)
545 	       {
546 		  set_server_lag(parsing_server_index);
547 		  ret = 0;
548 		  goto out;
549 	       }
550 	     /* allow NDCC calls via /msg as well as ctcp */
551 	     if (get_int_var(NDCC_OFFERING_VAR))
552 	       {
553 		  if (*ptr == CTCP_DELIM_CHAR)
554 		    {
555 		       u_char *p;
556 
557 		       ptr++;
558 		       p = my_index(ptr, CTCP_DELIM_CHAR);
559 		       if (p)
560 			 *p = '\0';
561 		    }
562 		  if (my_strnicmp(ptr, "NDCC ", 5) == 0
563 		      || my_strnicmp(ptr, "XDCC ", 5) == 0
564 		      || my_strnicmp(ptr, "CDCC ", 5) == 0)
565 		    {
566 		       process_remote_ndcc(from, FromUser, FromHost, ArgList[0], ptr+5);
567 		       ret = 0;
568 		       goto out;
569 		    }
570 	       }
571 	  }
572 
573 	/* invite, auto join due to friend?! */
574 	if (my_strcmp(comm, "INVITE") == 0)
575 	  check_friend_invite(ArgList[1], from, FromUser, FromHost);
576 
577 	/* MORE! */
578      }
579    /* okay, all parsed up, RIGHT?!  you didn't forget anything, did you?!?!! */
580 out:
581    if (chk_chan)
582      {
583 	chk_channel_cached(chk_chan);
584 	dma_Free(&chk_chan);
585      }
586    dma_Free(&copy);
587    return ret;
588 }
589 
590 
591 /*
592  * check to see if we just /who'd a person on join...
593  *
594  * update specifies whether or not to remove them from the list..
595  *
596  * process:
597  * srv: JOIN person
598  * you: WHO person
599  * srv: 352 person stuff...
600  * you: (nothing sent, information saved and line ignored if this code returns true)
601  * srv: 315 person end of /who
602  * you: (nothing sent, remove person from the list)
603  *
604  * returns: 1 (person not found, show the line)
605  *      or: 0 (person found, don't show it, we may have removed it)
606  */
607 int
check_who_nicks(nick,update)608 check_who_nicks(nick, update)
609    u_char *nick;
610    int update;
611 {
612    u_char *who_nicks, *otp, *tp;
613 
614    who_nicks = server_list[parsing_server_index].who_nicks;
615    if (!who_nicks)
616      return 1;
617 
618    otp = who_nicks;
619    while (otp)
620      {
621 	tp = my_index(otp, ',');
622 	if (tp) /* not the last spot */
623 	  *tp = '\0';
624 	if (my_stricmp(otp, nick) == 0) /* found it ? */
625 	  {
626 	     if (update)	/* shift the list back from tp.. */
627 	       {
628 		  u_char *nwn = UNULL;
629 
630 		  /* copy from beginning to otp */
631 		  if (otp != who_nicks)
632 		    *(otp-1) = '\0';
633 		  dma_strcat(&nwn, who_nicks);
634 		  if (otp != who_nicks)
635 		    *(otp-1) = ',';
636 
637 		  /* copy from after otp nick to end (if we're not at the end) */
638 		  if (tp)
639 		    {
640 		       dma_strcat(&nwn, UP(","));
641 		       dma_strcat(&nwn, tp+1);
642 		    }
643 
644 		  /* free the list and point it to our new list*/
645 		  dma_Free(&server_list[parsing_server_index].who_nicks);
646 		  server_list[parsing_server_index].who_nicks = nwn;
647 
648 		  /* ignore it!*/
649 		  return 0;
650 	       }
651 	     else	/* found it, but no update! */
652 	       {
653 		  if (tp)
654 		    *tp = ',';
655 		  return 0;
656 	       }
657 	  }
658 	if (tp)
659 	  {
660 	     *tp = ',';
661 	     otp = tp+1;
662 	  }
663 	else
664 	  otp = tp;
665      }
666    return 1;
667 }
668