ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/messages.c
Revision: 1171
Committed: Fri Aug 12 20:00:46 2011 UTC (12 years, 8 months ago) by michael
Content type: text/x-csrc
File size: 19384 byte(s)
Log Message:
- Import ircservices-5.1.24. Don't ever think about modifying anything in this
  folder!
  Since Andrew Church has discontinued his services project in April 2011, the
  ircd-hybrid team has been given permissions to officially continue and
  maintain the already mentioned project.
  The name of this project will be changed for the reason being that the current
  name "IRC Services" is way too generic these days.

  Remember: Don't ever modify anything in here. This folder is kept for reference.

File Contents

# User Rev Content
1 michael 1171 /* 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     */