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(©, 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(©);
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