ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/messages.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (9 years, 10 months ago) by michael
Content type: text/x-csrc
File size: 19384 byte(s)
Log Message:
- Imported ircservices-5.1.24

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