ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hybrid-ircservices-1/messages.c
Revision: 1209
Committed: Thu Aug 25 19:05:49 2011 UTC (12 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 18151 byte(s)
Log Message:
- run everything thru indent
  "-bli0 -di1 -npcs -nut -cdw -bls -nbbo -bap"

File Contents

# Content
1 /* Definitions of IRC message functions and list of messages.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include "services.h"
11 #include "messages.h"
12 #include "language.h"
13 #include "modules.h"
14 #include "version.h"
15 #include "modules/operserv/operserv.h"
16
17 /*************************************************************************/
18
19 /* Enable ignore code for PRIVMSGs? */
20 int allow_ignore = 1;
21
22 /* Callbacks for various messages */
23 static int cb_privmsg = -1;
24 static int cb_whois = -1;
25
26 /*************************************************************************/
27 /************************ Basic message handling *************************/
28 /*************************************************************************/
29
30 static void
31 m_nickcoll(char *source, int ac, char **av)
32 {
33 if (ac < 1)
34 return;
35 if (!readonly)
36 introduce_user(av[0]);
37 }
38
39 /*************************************************************************/
40
41 static void
42 m_ping(char *source, int ac, char **av)
43 {
44 if (ac < 1)
45 return;
46 send_cmd(ServerName, "PONG %s %s", ac > 1 ? av[1] : ServerName, av[0]);
47 }
48
49 /*************************************************************************/
50
51 static void
52 m_info(char *source, int ac, char **av)
53 {
54 int i;
55 struct tm *tm;
56 char timebuf[64];
57
58 if (!*source)
59 {
60 log("Source missing from INFO message");
61 return;
62 }
63
64 tm = localtime(&start_time);
65 strftime(timebuf, sizeof(timebuf), "%a %b %d %H:%M:%S %Y %Z", tm);
66
67 for (i = 0; info_text[i]; i++)
68 send_cmd(ServerName, "371 %s :%s", source, info_text[i]);
69 send_cmd(ServerName, "371 %s :Version %s (%s)", source,
70 version_number, version_build);
71 send_cmd(ServerName, "371 %s :On-line since %s", source, timebuf);
72 send_cmd(ServerName, "374 %s :End of /INFO list.", source);
73 }
74
75 /*************************************************************************/
76
77 static void
78 m_join(char *source, int ac, char **av)
79 {
80 if (!*source)
81 {
82 log("Source missing from JOIN message");
83 return;
84 }
85 else if (ac < 1)
86 {
87 return;
88 }
89 do_join(source, ac, av);
90 }
91
92 /*************************************************************************/
93
94 static void
95 m_kick(char *source, int ac, char **av)
96 {
97 if (!*source)
98 {
99 log("Source missing from KICK message");
100 return;
101 }
102 else if (ac != 3)
103 {
104 return;
105 }
106 do_kick(source, ac, av);
107 }
108
109 /*************************************************************************/
110
111 static void
112 m_kill(char *source, int ac, char **av)
113 {
114 if (!*source)
115 {
116 log("Source missing from KILL message");
117 return;
118 }
119 else if (ac != 2)
120 {
121 return;
122 }
123 /* Recover if someone kills us. If introduce_user() returns 0, then
124 * the user in question isn't a pseudoclient, so pass it on to the
125 * user handling code. */
126 if (!introduce_user(av[0]))
127 do_kill(source, ac, av);
128 }
129
130 /*************************************************************************/
131
132 static void
133 m_mode(char *source, int ac, char **av)
134 {
135 if (!*source)
136 {
137 log("Source missing from MODE message");
138 return;
139 }
140
141 if (*av[0] == '#' || *av[0] == '&')
142 {
143 if (ac < 2)
144 return;
145 do_cmode(source, ac, av);
146 }
147 else
148 {
149 if (ac != 2)
150 {
151 return;
152 }
153 else if (irc_stricmp(source, av[0]) != 0 && strchr(source, '.') == NULL)
154 {
155 log("user: MODE %s %s from different nick %s!", av[0], av[1], source);
156 wallops(NULL, "%s attempted to change mode %s for %s",
157 source, av[1], av[0]);
158 return;
159 }
160 do_umode(source, ac, av);
161 }
162 }
163
164 /*************************************************************************/
165
166 static void
167 m_motd(char *source, int ac, char **av)
168 {
169 FILE *f;
170 char buf[BUFSIZE];
171
172 if (!*source)
173 {
174 log("Source missing from MOTD message");
175 return;
176 }
177
178 f = fopen(MOTDFilename, "r");
179 send_cmd(ServerName, "375 %s :- %s Message of the Day", source, ServerName);
180 if (f)
181 {
182 while (fgets(buf, sizeof(buf), f))
183 {
184 buf[strlen(buf) - 1] = 0;
185 send_cmd(ServerName, "372 %s :- %s", source, buf);
186 }
187 fclose(f);
188 }
189 else
190 {
191 send_cmd(ServerName, "372 %s :- MOTD file not found! Please "
192 "contact your IRC administrator.", source);
193 }
194 }
195
196 /*************************************************************************/
197
198 static void
199 m_part(char *source, int ac, char **av)
200 {
201 if (!*source)
202 {
203 log("Source missing from PART message");
204 return;
205 }
206 else if (ac < 1 || ac > 2)
207 {
208 return;
209 }
210 do_part(source, ac, av);
211 }
212
213 /*************************************************************************/
214
215 static const char msg_up_inactive[] =
216 "Network buffer size exceeded inactive threshold (%d%%), not processing"
217 " PRIVMSGs";
218 static const char msg_up_ignore[] =
219 "Network buffer size exceeded ignore threshold (%d%%), ignoring PRIVMSGs";
220 static const char msg_down_inactive[] =
221 "Network buffer size dropped below ignore threshold (%d%%), not"
222 " processing PRIVMSGs";
223 static const char msg_down_normal[] =
224 "Network buffer size dropped below inactive threshold (%d%%),"
225 " processing PRIVMSGs normally";
226
227 static void
228 m_privmsg(char *source, int ac, char **av)
229 {
230 /* PRIVMSG handling status based on NetBufferLimit settings */
231 static enum
232 { NORMAL, INACTIVE, IGNORE } netbuf_status = NORMAL;
233
234 uint32 start, stop; /* When processing started and finished */
235 User *u = get_user(source);
236 char *s;
237
238
239 if (!*source)
240 {
241 log("Source missing from PRIVMSG message");
242 return;
243 }
244 else if (ac != 2)
245 {
246 return;
247 }
248
249 /* If a server is specified (nick@server format), make sure it matches
250 * us, and strip it off. */
251 s = strchr(av[0], '@');
252 if (s)
253 {
254 *s++ = 0;
255 if (stricmp(s, ServerName) != 0)
256 return;
257 }
258
259 /* Check network buffer status. */
260 if (NetBufferLimitInactive)
261 {
262 int bufstat = sock_bufstat(servsock, NULL, NULL, NULL, NULL);
263 const char *message = NULL;
264 int value = 0;
265 switch (netbuf_status)
266 {
267 case NORMAL:
268 if (NetBufferLimitIgnore && bufstat >= NetBufferLimitIgnore)
269 {
270 message = msg_up_ignore;
271 value = NetBufferLimitIgnore;
272 netbuf_status = IGNORE;
273 }
274 else if (bufstat >= NetBufferLimitInactive)
275 {
276 message = msg_up_inactive;
277 value = NetBufferLimitInactive;
278 netbuf_status = INACTIVE;
279 }
280 break;
281 case INACTIVE:
282 if (NetBufferLimitIgnore && bufstat >= NetBufferLimitIgnore)
283 {
284 message = msg_up_ignore;
285 value = NetBufferLimitIgnore;
286 netbuf_status = IGNORE;
287 }
288 else if (bufstat < NetBufferLimitInactive)
289 {
290 message = msg_down_normal;
291 value = NetBufferLimitInactive;
292 netbuf_status = NORMAL;
293 }
294 break;
295 case IGNORE:
296 if (bufstat < NetBufferLimitInactive)
297 {
298 message = msg_down_normal;
299 value = NetBufferLimitInactive;
300 netbuf_status = NORMAL;
301 }
302 else if (bufstat < NetBufferLimitIgnore)
303 {
304 message = msg_down_inactive;
305 value = NetBufferLimitIgnore;
306 netbuf_status = INACTIVE;
307 }
308 break;
309 } /* switch (netbuf_status) */
310 if (message)
311 {
312 log(message, value);
313 wallops(NULL, message, value);
314 }
315 }
316
317 /* Check if we should ignore. Operators always get through. */
318 if (u)
319 {
320 ignore_update(u, 0);
321 if (!is_oper(u))
322 {
323 if (netbuf_status != NORMAL)
324 {
325 if (netbuf_status == INACTIVE)
326 {
327 if (u)
328 notice_lang(av[0], u, SERVICES_IS_BUSY);
329 else
330 notice(av[0], source, getstring(NULL, SERVICES_IS_BUSY));
331 }
332 return;
333 }
334 else if (allow_ignore && IgnoreDecay && IgnoreThreshold)
335 {
336 if (u->ignore >= IgnoreThreshold)
337 {
338 log("Ignored message from %s: \"%s\"", source, inbuf);
339 return;
340 }
341 }
342 }
343 }
344
345 /* Not ignored; actually execute the command, and update ignore data. */
346 start = time_msec();
347 call_callback_3(cb_privmsg, source, av[0], av[1]);
348 stop = time_msec();
349 if (stop > start && u && !is_oper(u))
350 ignore_update(u, stop - start);
351 }
352
353 /*************************************************************************/
354
355 static void
356 m_quit(char *source, int ac, char **av)
357 {
358 if (!*source)
359 {
360 log("Source missing from QUIT message");
361 return;
362 }
363 else if (ac != 1)
364 {
365 return;
366 }
367 do_quit(source, ac, av);
368 }
369
370 /*************************************************************************/
371
372 static void
373 m_server(char *source, int ac, char **av)
374 {
375 do_server(source, ac, av);
376 }
377
378 /*************************************************************************/
379
380 static void
381 m_squit(char *source, int ac, char **av)
382 {
383 do_squit(source, ac, av);
384 }
385
386 /*************************************************************************/
387
388 static void
389 m_stats(char *source, int ac, char **av)
390 {
391 if (!*source)
392 {
393 log("Source missing from STATS message");
394 return;
395 }
396 else if (ac < 1)
397 {
398 return;
399 }
400
401 switch (*av[0])
402 {
403 case 'u':
404 {
405 int uptime = time(NULL) - start_time;
406 Module *module_operserv;
407 typeof(get_operserv_data) * p_get_operserv_data;
408 int32 maxusercnt;
409
410 send_cmd(NULL, "242 %s :Services up %d day%s, %02d:%02d:%02d",
411 source, uptime / 86400, (uptime / 86400 == 1) ? "" : "s",
412 (uptime / 3600) % 24, (uptime / 60) % 60, uptime % 60);
413 if ((module_operserv = find_module("operserv/main")) != NULL
414 && (p_get_operserv_data =
415 get_module_symbol(module_operserv, "get_operserv_data"))
416 && p_get_operserv_data(OSDATA_MAXUSERCNT, &maxusercnt))
417 {
418 send_cmd(NULL, "250 %s :Current users: %d (%d ops); maximum %d",
419 source, usercnt, opcnt, maxusercnt);
420 }
421 else
422 {
423 send_cmd(NULL, "250 %s :Current users: %d (%d ops)",
424 source, usercnt, opcnt);
425 }
426 send_cmd(NULL, "219 %s u :End of /STATS report.", source);
427 break;
428 } /* case 'u' */
429
430 case 'l':
431 {
432 uint64 read, written;
433 sock_rwstat(servsock, &read, &written);
434 send_cmd(NULL, "211 %s Server SendBuf SentBytes SentMsgs RecvBuf "
435 "RecvBytes RecvMsgs ConnTime", source);
436 #if SIZEOF_LONG >= 8
437 send_cmd(NULL, "211 %s %s %u %lu %d %u %lu %d %ld",
438 source, RemoteServer,
439 read_buffer_len(servsock), (unsigned long) read, -1,
440 write_buffer_len(servsock), (unsigned long) written, -1,
441 (long) start_time);
442 #else // assume long long is available
443 send_cmd(NULL, "211 %s %s %u %llu %d %u %llu %d %ld",
444 source, RemoteServer,
445 read_buffer_len(servsock), (unsigned long long) read, -1,
446 write_buffer_len(servsock), (unsigned long long) written, -1,
447 (long) start_time);
448 #endif
449 send_cmd(NULL, "219 %s l :End of /STATS report.", source);
450 break;
451 }
452
453 case 'c':
454 case 'h':
455 case 'i':
456 case 'k':
457 case 'm':
458 case 'o':
459 case 'y':
460 send_cmd(NULL, "219 %s %c :/STATS %c not applicable or not supported.",
461 source, *av[0], *av[0]);
462 break;
463 }
464 }
465
466 /*************************************************************************/
467
468 static void
469 m_time(char *source, int ac, char **av)
470 {
471 time_t t;
472 struct tm *tm;
473 char buf[64];
474
475 if (!*source)
476 {
477 log("Source missing from TIME message");
478 return;
479 }
480
481 time(&t);
482 tm = localtime(&t);
483 strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y %Z", tm);
484 send_cmd(NULL, "391 %s %s :%s", source, ServerName, buf);
485 }
486
487 /*************************************************************************/
488
489 static void
490 m_topic(char *source, int ac, char **av)
491 {
492 if (ac != 4)
493 return;
494 do_topic(source, ac, av);
495 }
496
497 /*************************************************************************/
498
499 static void
500 m_version(char *source, int ac, char **av)
501 {
502 if (!*source)
503 {
504 log("Source missing from VERSION message");
505 return;
506 }
507 send_cmd(ServerName, "351 %s %s-%s %s :%s", source,
508 program_name, version_number, ServerName, version_build);
509 }
510
511 /*************************************************************************/
512
513 static void
514 m_whois(char *source, int ac, char **av)
515 {
516 if (!*source)
517 {
518 log("Source missing from WHOIS message");
519 return;
520 }
521 else if (ac < 1)
522 {
523 return;
524 }
525
526 if (call_callback_3(cb_whois, source, av[0], ac > 1 ? av[1] : NULL) <= 0)
527 {
528 send_cmd(ServerName, "401 %s %s :No such service.", source, av[0]);
529 }
530 }
531
532 /*************************************************************************/
533
534 /* Basic messages (defined above). Note that NICK and USER are left to the
535 * protocol modules, since their usage varies widely between protocols. */
536
537 static Message base_messages[] = {
538
539 {"401", NULL},
540 {"436", m_nickcoll},
541 {"AWAY", NULL},
542 {"INFO", m_info},
543 {"JOIN", m_join},
544 {"KICK", m_kick},
545 {"KILL", m_kill},
546 {"MODE", m_mode},
547 {"MOTD", m_motd},
548 {"NOTICE", NULL},
549 {"PART", m_part},
550 {"PASS", NULL},
551 {"PING", m_ping},
552 {"PONG", NULL},
553 {"PRIVMSG", m_privmsg},
554 {"QUIT", m_quit},
555 {"SERVER", m_server},
556 {"SQUIT", m_squit},
557 {"STATS", m_stats},
558 {"TIME", m_time},
559 {"TOPIC", m_topic},
560 {"VERSION", m_version},
561 {"WALLOPS", NULL},
562 {"WHOIS", m_whois},
563
564 {NULL}
565
566 };
567
568 /*************************************************************************/
569 /******************** Message registration and lookup ********************/
570 /*************************************************************************/
571
572 /* Structure to link tables together */
573 typedef struct messagetable_ MessageTable;
574 struct messagetable_
575 {
576 MessageTable *next, *prev;
577 Message *table;
578 };
579 static MessageTable *msgtable = NULL;
580
581 /* List of known messages (for speed-lookup list) */
582 typedef struct messagenode_ MessageNode;
583 struct messagenode_
584 {
585 MessageNode *next, *prev;
586 Message *msg;
587 };
588 static MessageNode *msglist = NULL;
589
590 /*************************************************************************/
591 /*************************************************************************/
592
593 /* (Re)generate the speed-lookup list. This list is used to reduce the
594 * time spent searching for messages; every time a message is seen, its
595 * entry in this list is moved one place closer to the head of the list,
596 * allowing frequently-seen messages to "percolate" to the top of the list
597 * so that they will be found more quickly by searches.
598 */
599
600 static void
601 init_message_list(void)
602 {
603 MessageNode *mn, *mn2;
604 MessageTable *mt;
605 Message *m;
606
607 LIST_FOREACH_SAFE(mn, msglist, mn2) free(mn);
608 msglist = NULL;
609
610 LIST_FOREACH(mt, msgtable)
611 {
612 for (m = mt->table; m->name; m++)
613 {
614 LIST_SEARCH(msglist, msg->name, m->name, stricmp, mn);
615 if (!mn)
616 {
617 mn = smalloc(sizeof(*mn));
618 mn->msg = m;
619 LIST_INSERT(mn, msglist);
620 }
621 }
622 }
623 }
624
625 /*************************************************************************/
626
627 /* Register the given table of messages. Returns 1 on success, 0 on
628 * failure (`table' == NULL, `table' already registered, or out of memory).
629 */
630
631 int
632 register_messages(Message * table)
633 {
634 MessageTable *mt;
635
636 if (!table)
637 return 0;
638 LIST_SEARCH_SCALAR(msgtable, table, table, mt);
639 if (mt) /* if it's already on the list, abort */
640 return 0;
641 mt = malloc(sizeof(*mt));
642 if (!mt) /* out of memory */
643 return 0;
644 mt->table = table;
645 LIST_INSERT(mt, msgtable);
646 init_message_list();
647 return 1;
648 }
649
650 /*************************************************************************/
651
652 /* Unregister the given table of messages. Returns 1 on success, 0 on
653 * failure (`table' not registered).
654 */
655
656 int
657 unregister_messages(Message * table)
658 {
659 MessageTable *mt;
660
661 LIST_SEARCH_SCALAR(msgtable, table, table, mt);
662 if (!mt)
663 return 0;
664 LIST_REMOVE(mt, msgtable);
665 free(mt);
666 init_message_list();
667 return 1;
668 }
669
670 /*************************************************************************/
671
672 /* Return the Message structure for the given message name, or NULL if none
673 * exists. If there are multiple tables with entries for the message,
674 * returns the entry in the most recently registered table.
675 */
676
677 Message *
678 find_message(const char *name)
679 {
680 MessageNode *mn;
681
682 LIST_SEARCH(msglist, msg->name, name, stricmp, mn);
683 if (mn)
684 {
685 MessageNode *prev = mn->prev;
686 if (prev)
687 {
688 MessageNode *pprev = prev->prev;
689 MessageNode *next = mn->next;
690 /* Current order: pprev -> prev -> mn -> next */
691 /* New order: pprev -> mn -> prev -> next */
692 if (pprev)
693 pprev->next = mn;
694 else
695 msglist = mn;
696 mn->prev = pprev;
697 mn->next = prev;
698 prev->prev = mn;
699 prev->next = next;
700 if (next)
701 next->prev = prev;
702 }
703 return mn->msg;
704 }
705 return NULL;
706 }
707
708 /*************************************************************************/
709 /************************ Initialization/cleanup *************************/
710 /*************************************************************************/
711
712 int
713 messages_init(int ac, char **av)
714 {
715 if (!register_messages(base_messages))
716 {
717 log("messages_init: Unable to register base messages\n");
718 return 0;
719 }
720 cb_privmsg = register_callback("m_privmsg");
721 cb_whois = register_callback("m_whois");
722 if (cb_privmsg < 0 || cb_whois < 0)
723 {
724 log("messages_init: register_callback() failed\n");
725 return 0;
726 }
727 return 1;
728 }
729
730 /*************************************************************************/
731
732 void
733 messages_cleanup(void)
734 {
735 unregister_callback(cb_whois);
736 unregister_callback(cb_privmsg);
737 unregister_messages(base_messages);
738 }
739
740 /*************************************************************************/
741
742 /*
743 * Local variables:
744 * c-file-style: "stroustrup"
745 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
746 * indent-tabs-mode: nil
747 * End:
748 *
749 * vim: expandtab shiftwidth=4:
750 */