source: roaraudio/plugins/roard/protocol-irc.c @ 5306:8f1d1d2d3ca9

Last change on this file since 5306:8f1d1d2d3ca9 was 5306:8f1d1d2d3ca9, checked in by phi, 12 years ago

added IRC plugin

File size: 18.2 KB
Line 
1//irc.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2011
5 *
6 *  This file is part of roard a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include <roard/include/roard.h>
27#include <ctype.h>
28
29#define MAX_CHANNELS 8
30
31static int on_nick(int client, const char * cmd, char * args, char * text);
32static int on_user(int client, const char * cmd, char * args, char * text);
33static int on_quit(int client, const char * cmd, char * args, char * text);
34static int on_ping(int client, const char * cmd, char * args, char * text);
35static int on_whois(int client, const char * cmd, char * args, char * text);
36static int on_privmsg(int client, const char * cmd, char * args, char * text);
37
38static int on_join(int client, const char * cmd, char * args, char * text);
39static int on_part(int client, const char * cmd, char * args, char * text);
40static int on_names(int client, const char * cmd, char * args, char * text);
41
42static int on_topic(int client, const char * cmd, char * args, char * text);
43
44static int do_join(int client, const char * channel);
45static int do_part(int client, const char * channel);
46static int do_names(int client, const char * channel);
47
48static void cb_client_delete(struct roar_notify_core * core, struct roar_event * event, void * userdata);
49
50static struct roar_subscriber * subscription_client_delete = NULL;
51
52static char * server_name     = "irc.roard";
53static char * server_fullname = "RoarAudio roard IRC Server plugin";
54
55static char * quit_msg = NULL;
56
57static struct channel {
58 char * name;
59 struct {
60  char * text;
61  char * user;
62  time_t ts;
63 } topic;
64 size_t client_count;
65 int clients[ROAR_CLIENTS_MAX];
66} g_channels[MAX_CHANNELS];
67
68static struct command {
69 const char * name;
70 int (*func)(int client, const char * cmd, char * args, char * text);
71} g_commands[] = {
72 {"NICK", on_nick},
73 {"USER", on_user},
74 {"QUIT", on_quit},
75 {"PING", on_ping},
76 {"WHOIS", on_whois},
77 {"PRIVMSG", on_privmsg},
78 {"NOTICE", on_privmsg},
79 {"JOIN", on_join},
80 {"PART", on_part},
81 {"NAMES", on_names},
82 {"TOPIC", on_topic},
83 {NULL, NULL}
84};
85
86static void init(void) {
87 struct roar_event event;
88
89 memset(&event, 0, sizeof(event));
90
91 event.event = ROAR_OE_BASICS_DELETE;
92
93 event.emitter = -1;
94 event.target = -1;
95 event.target_type = ROAR_OT_CLIENT;
96
97 memset(g_channels, 0, sizeof(g_channels));
98
99 subscription_client_delete = roar_notify_core_subscribe(NULL, &event, cb_client_delete, NULL);
100}
101
102static int is_valid_name (const char * name) {
103 register char c;
104
105 for (; (c = *name) != 0; name++) {
106  if ( !(isalnum(c) || c == '-' || c == '_') )
107   return 0;
108 }
109
110 return 1;
111}
112
113static void strip_nl(char * str) {
114 register char c;
115
116 for (; (c = *str) != 0; str++) {
117  if ( c == '\r' || c == '\n' ) {
118   *str = 0;
119   return;
120  }
121 }
122}
123
124static char * get_nick(int client) {
125 struct roar_client * c;
126 clients_get(client, &c);
127 return c->name;
128}
129
130static const char * get_ident(int client) {
131 static char buf[80];
132 struct roar_client * c;
133 clients_get(client, &c);
134
135 if ( c->uid == -1 ) {
136  buf[0] = '~';
137  buf[1] = 0;
138 } else {
139  snprintf(buf, sizeof(buf)-1, "uid%i~", c->uid);
140  buf[sizeof(buf)-1] = 0;
141 }
142
143 return buf;
144}
145
146static const char * get_node(int client) {
147 struct roar_client * c;
148 char buf_nnode[80];
149 char * nnode;
150
151 clients_get(client, &c);
152
153 roar_nnode_to_str(&(c->nnode), buf_nnode, sizeof(buf_nnode));
154
155 nnode  = strstr(buf_nnode, ": ");
156 if ( nnode == NULL ) {
157  nnode  = "unknown~";
158 } else {
159  nnode += 2;
160 }
161
162 return nnode;
163}
164
165static const char * get_ufull(int client) {
166 struct roar_client * c;
167 static char buf[80];
168 const char * ident = get_ident(client);
169 const char * nnode = get_node(client);
170
171 clients_get(client, &c);
172
173 snprintf(buf, sizeof(buf)-1, "%s!%s@%s", c->name, ident, nnode);
174
175 buf[sizeof(buf)-1] = 0;
176
177 return buf;
178}
179
180static const char * get_realname(int client) {
181 (void)client;
182 return "(no realname)";
183}
184
185static int get_client_by_nick(const char * nick) {
186 struct roar_client * c;
187 int i;
188
189 for (i = 0; i < ROAR_CLIENTS_MAX; i++) {
190  if ( clients_get(i, &c) != 0 )
191   continue;
192
193  if ( !!strcasecmp(c->name, nick) )
194   continue;
195
196  return i;
197 }
198
199 return -1;
200}
201
202static struct channel * get_channel(const char * name) {
203 struct channel * c;
204 size_t i;
205
206 for (i = 0; i < MAX_CHANNELS; i++) {
207  c = &(g_channels[i]);
208
209  if ( !c->client_count )
210   continue;
211
212  if ( !!strcasecmp(c->name, name) )
213   continue;
214
215  return c;
216 }
217
218 return NULL;
219}
220
221static size_t get_listener_list(int client, const char * channel, int ** listener) {
222 struct channel * c;
223 static int clients[ROAR_CLIENTS_MAX];
224 size_t ret = 0;
225 size_t i, k;
226 int j;
227 int found;
228
229 for (i = 0; i < MAX_CHANNELS; i++) {
230  c = &(g_channels[i]);
231
232  if ( !c->client_count )
233   continue;
234
235  if ( c->clients[client] < 1 )
236   continue;
237
238  if ( channel != NULL && !!strcasecmp(c->name, channel) )
239   continue;
240
241  for (j = 0; j < ROAR_CLIENTS_MAX; j++) {
242   if ( c->clients[j] > 0 ) {
243    found = 0;
244    for (k = 0; k < ret; k++) {
245     if ( clients[k] == j ) {
246      found = 1;
247     }
248    }
249    if ( !found ) {
250     clients[ret] = j;
251     ret++;
252    }
253   }
254  }
255 }
256
257 *listener = clients;
258
259 return ret;
260}
261
262static void put_printf(int client, const char *format, ...) {
263 va_list ap;
264 struct roar_buffer * buf;
265 size_t len = 2048;
266 void * data;
267 int ret;
268
269 if ( roar_buffer_new_data(&buf, len, &data) == -1 )
270  return;
271
272 va_start(ap, format);
273 ret = vsnprintf(data, len-1, format, ap);
274 va_end(ap);
275
276 if ( roar_buffer_set_len(buf, strlen(data)) == -1 ) {
277  roar_buffer_free(buf);
278  return;
279 }
280
281 clients_add_output(client, &buf);
282}
283
284static int do_join(int client, const char * channel) {
285 struct channel * c = NULL;
286 size_t i;
287
288 if ( channel[0] != '#' )
289  return -1;
290
291 if ( !is_valid_name(channel+1) )
292  return -1;
293
294 for (i = 0; i < MAX_CHANNELS; i++) {
295  if ( !g_channels[i].client_count )
296   continue;
297
298  if ( !!strcasecmp(g_channels[i].name, channel) )
299   continue;
300
301  c = &(g_channels[i]);
302  break;
303 }
304
305 if ( c == NULL ) {
306  for (i = 0; i < MAX_CHANNELS; i++) {
307   if ( g_channels[i].client_count )
308    continue;
309
310   c = &(g_channels[i]);
311   break;
312  }
313
314  if ( c == NULL )
315   return -1;
316
317  memset(c, 0, sizeof(*c));
318
319  c->name = roar_mm_strdup(channel);
320 }
321
322 if ( c->clients[client] )
323  return -1;
324
325 c->clients[client] = 1;
326 c->client_count++;
327
328 return 0;
329}
330
331static int do_part(int client, const char * channel) {
332 struct channel * c = NULL;
333 size_t i;
334
335 for (i = 0; i < MAX_CHANNELS; i++) {
336  if ( !g_channels[i].client_count )
337   continue;
338
339  if ( !!strcasecmp(g_channels[i].name, channel) )
340   continue;
341
342  if ( !g_channels[i].clients[client] )
343   return -1;
344
345  c = &(g_channels[i]);
346  break;
347 }
348
349 c->clients[client] = 0;
350 c->client_count--;
351
352 if ( !c->client_count ) {
353  roar_mm_free(c->name);
354
355  if ( c->topic.text != NULL )
356   roar_mm_free(c->topic.text);
357  if ( c->topic.user != NULL )
358   roar_mm_free(c->topic.user);
359 }
360
361 return 0;
362}
363
364static int do_names(int client, const char * channel) {
365 const char * nick = get_nick(client);
366 size_t i;
367 char buf[256];
368 size_t len, offset;
369 char * c_nick;
370 int j;
371
372 for (i = 0; i < MAX_CHANNELS; i++) {
373  if ( !g_channels[i].client_count )
374   continue;
375
376  if ( !!strcasecmp(g_channels[i].name, channel) )
377   continue;
378
379  offset = 0;
380
381  for (j = 0; j < ROAR_CLIENTS_MAX; j++) {
382   if ( g_channels[i].clients[j] == 0 )
383    continue;
384
385   c_nick = get_nick(j);
386   len = strlen(c_nick);
387   if ( (offset + len + 3) > sizeof(buf) ) {
388    buf[offset] = 0;
389    put_printf(client, ":%s 353 %s = %s :%s\n", server_name, nick, channel, buf);
390    offset = 0;
391   }
392
393   memcpy(buf + offset, c_nick, len);
394   offset += len;
395   buf[offset] = ' ';
396   offset++;
397   buf[offset] = 0;
398  }
399
400  if ( offset ) {
401   buf[offset] = 0;
402   put_printf(client, ":%s 353 %s = %s :%s\n", server_name, nick, channel, buf);
403  }
404  put_printf(client, ":%s 366 %s %s :End of /NAMES list.\n", server_name, nick, channel);
405
406  return 0;
407 }
408
409 return -1;
410}
411
412static void cb_client_delete(struct roar_notify_core * core, struct roar_event * event, void * userdata) {
413 int * listener;
414 size_t count;
415 struct channel * c;
416 char * text = quit_msg;
417 int client = event->target;
418 const char * ufull = get_ufull(client);
419 size_t i;
420
421 (void)core, (void)userdata;
422
423 if ( text == NULL ) {
424  text = "Client deleted. Died, kicked or internal error.";
425 }
426
427 put_printf(client, "ERROR :Closing Link: %s (Quit: %s)\n", ufull, text);
428
429  count = get_listener_list(client, NULL, &listener);
430  for (; count; count--, listener++)
431   put_printf(*listener, ":%s QUIT :Quit: %s\n", ufull, text);
432
433 for (i = 0; i < MAX_CHANNELS; i++) {
434  c = &(g_channels[i]);
435
436  if ( !c->client_count )
437   continue;
438
439  if ( !c->clients[client] )
440   continue;
441
442  c->clients[client] = 0;
443  c->client_count--;
444
445  if ( !c->client_count ) {
446   roar_mm_free(c->name);
447
448   if ( c->topic.text != NULL )
449    roar_mm_free(c->topic.text);
450   if ( c->topic.user != NULL )
451    roar_mm_free(c->topic.user);
452  }
453 }
454
455 clients_flush(client);
456}
457
458static int new_client(int client, struct roar_vio_calls * vio, struct roard_listen * lsock) {
459 struct roar_client_server * cs;
460 char * name;
461
462 (void)client, (void)vio, (void)lsock;
463
464 clients_get_server(client, &cs);
465
466 name = ROAR_CLIENT(cs)->name;
467 snprintf(name, sizeof(ROAR_CLIENT(cs)->name), "Client%i~", client);
468
469/*
470:ph7.ph.sft NOTICE AUTH :*** Looking up your hostname...
471:ph7.ph.sft NOTICE AUTH :*** Couldn't resolve your hostname; using your IP address instead
472NICK nick
473USER A B C D
474:ph7.ph.sft NOTICE AUTH :*** Couldn't resolve your hostname; using your IP address instead
475:ph7.ph.sft 002 nick :Your host is ph7.ph.sft, running version Unreal3.2.7
476:ph7.ph.sft 003 nick :This server was created Tue Aug 28 2007 at 10:02:00 CEST
477:ph7.ph.sft 004 nick ph7.ph.sft Unreal3.2.7 iowghraAsORTVSxNCWqBzvdHtGp lvhopsmntikrRcaqOALQbSeIKVfMCuzNTGj
478:ph7.ph.sft 005 nick NAMESX SAFELIST HCN MAXCHANNELS=10 CHANLIMIT=#:10 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 MAXTARGETS=20 WALLCHOPS :are supported by this server
479:ph7.ph.sft 005 nick WATCH=128 SILENCE=15 MODES=12 CHANTYPES=# PREFIX=(ohv)@%+ CHANMODES=beIqa,kfL,lj,psmntirRcOAQKVCuzNSMTG NETWORK=PH2 CASEMAPPING=ascii EXTBAN=~,cqnr ELIST=MNUCT STATUSMSG=@%+ EXCEPTS INVEX :are supported by this server
480:ph7.ph.sft 005 nick CMDS=KNOCK,MAP,DCCALLOW,USERIP :are supported by this server
481:ph7.ph.sft 251 nick :There are 1 users and 0 invisible on 1 servers
482:ph7.ph.sft 253 nick 1 :unknown connection(s)
483:ph7.ph.sft 255 nick :I have 1 clients and 0 servers
484:ph7.ph.sft 265 nick :Current Local Users: 1  Max: 4
485:ph7.ph.sft 266 nick :Current Global Users: 1  Max: 1
486*/
487
488 put_printf(client,
489      ":%s 001 %s :Welcome to the roard based IRC server.\n"
490      ":%s 375 %s :- %s Message of the Day -\n"
491      ":%s 372 %s :- MotD goes here...\n"
492      ":%s 376 %s :End of /MOTD command.\n",
493   server_name, name,
494   server_name, name, server_name,
495   server_name, name,
496   server_name, name
497 );
498
499 return 0;
500}
501
502static int check_client(int client, struct roar_vio_calls * vio) {
503 struct roar_vio_calls     rvio;
504 char cmd[1024*2];
505 char * args;
506 char * text;
507 ssize_t len;
508 size_t i;
509 int found = 0;
510
511 if ( vio == NULL ) {
512  vio = &rvio;
513  roar_vio_open_fh_socket(vio, clients_get_fh(client));
514 }
515
516 len = roar_vio_read(vio, cmd, sizeof(cmd)-1);
517 if ( len < 1 ) {
518  clients_delete(client);
519  return -1;
520 }
521
522 cmd[len] = 0;
523
524 strip_nl(cmd);
525
526 if ( cmd[0] == 0 )
527  return 0;
528
529 args = strstr(cmd, " ");
530
531 if ( args != NULL ) {
532  *args = 0;
533  args++;
534  if ( *args == ':' ) {
535   text = args + 1;
536   args = NULL;
537  } else {
538   text = strstr(args, " :");
539   if ( text != NULL ) {
540    *text = 0;
541    text += 2;
542   }
543  }
544 } else {
545  text = NULL;
546 }
547
548 for (i = 0; g_commands[i].name != NULL; i++) {
549  if ( !strcasecmp(g_commands[i].name, cmd) ) {
550   found = 1;
551   g_commands[i].func(client, cmd, args, text);
552  }
553 }
554
555 if ( !found ) {
556  put_printf(client, ":%s 421 %s %s :Unknown command\n", server_name, get_nick(client), cmd);
557 }
558
559 return 0;
560}
561
562// commands:
563static int on_nick(int client, const char * cmd, char * args, char * text) {
564 char * nick = get_nick(client);
565 int * listener;
566 size_t count;
567 const char * ufull;
568
569 (void)cmd, (void)text;
570
571 if ( args != NULL && args[0] != 0 && is_valid_name(args) && strlen(args) < ROAR_BUFFER_NAME ) {
572  if ( get_client_by_nick(args) != -1 ) {
573   put_printf(client, ":%s 433 %s %s :Nickname is already in use.\n", server_name, nick, args);
574  } else {
575   ufull = get_ufull(client);
576   put_printf(client, ":%s NICK :%s\n", ufull, args);
577   count = get_listener_list(client, NULL, &listener);
578   for (; count; count--, listener++)
579    if ( *listener != client )
580     put_printf(*listener, ":%s NICK :%s\n", ufull, args);
581
582   strcpy(nick, args);
583  }
584 } else {
585  put_printf(client, ":%s 432 %s %s :Erroneous Nickname: Illegal characters\n", server_name, nick, args);
586 }
587
588 return 0;
589}
590
591static int on_user(int client, const char * cmd, char * args, char * text) {
592 (void)client, (void)cmd, (void)args, (void)text;
593 return 0;
594}
595
596static int on_quit(int client, const char * cmd, char * args, char * text) {
597 int ret;
598
599 (void)cmd, (void)args;
600
601 if ( text == NULL )
602  text = "... have a cuddle ...";
603
604 quit_msg = text;
605 ret = clients_delete(client);
606 quit_msg = NULL;
607
608 return ret;
609}
610
611static int on_ping(int client, const char * cmd, char * args, char * text) {
612 (void)cmd, (void)args;
613
614 put_printf(client, "PONG :%s\n", text);
615
616 return 0;
617}
618
619static int on_whois(int client, const char * cmd, char * args, char * text) {
620 const char * clientnick = get_nick(client);
621 const char * tnick = args;
622 int tclient;
623
624 (void)cmd, (void)text;
625
626/*
627:ph7.ph.sft 317 nick phi 131 1322456381 :seconds idle, signon time
628*/
629
630 if ( (tclient = get_client_by_nick(tnick)) == -1 ) {
631  put_printf(client, ":%s 401 %s %s :No such nick/channel\n", server_name, clientnick, tnick);
632 } else {
633  put_printf(client, ":%s 311 %s %s %s %s * :%s\n", server_name, clientnick, tnick,
634             get_ident(tclient), get_node(tclient), get_realname(tclient));
635  put_printf(client, ":%s 312 %s %s %s :%s\n", server_name, clientnick, tnick, server_name, server_fullname);
636 }
637 put_printf(client, ":%s 318 %s %s :End of /WHOIS list.\n", server_name, clientnick, tnick);
638
639 return 0;
640}
641
642static int on_privmsg(int client, const char * cmd, char * args, char * text) {
643 const char * ufull = get_ufull(client);
644 int * listener;
645 size_t count;
646 char * next;
647 int tmp;
648
649 if ( args == NULL || text == NULL )
650  return -1;
651
652 if ( text[0] == 0 )
653  return 0;
654
655 while (args != NULL) {
656  next = strstr(args, ",");
657  if ( next != NULL ) {
658   *next = 0;
659   next++;
660  }
661
662  if ( args[0] == '#' ) {
663   count = get_listener_list(client, args, &listener);
664   for (; count; count--, listener++)
665    if ( *listener != client )
666     put_printf(*listener, ":%s %s %s :%s\n", ufull, cmd, args, text);
667  } else {
668   if ( (tmp = get_client_by_nick(args)) == -1 ) {
669    put_printf(client, ":%s 401 %s %s :No such nick/channel\n", server_name, get_nick(client), args);
670   } else {
671    put_printf(tmp, ":%s %s %s :%s\n", ufull, cmd, args, text);
672   }
673  }
674
675  args = next;
676 }
677
678 return 0;
679}
680
681
682static int on_join(int client, const char * cmd, char * args, char * text) {
683 struct channel * c;
684 const char * ufull = get_ufull(client);
685 int * listener;
686 size_t count;
687 const char * nick;
688
689 (void)cmd, (void)text;
690
691 if ( args == NULL )
692  return -1;
693
694 if ( do_join(client, args) != 0 ) {
695  return -1;
696 }
697
698 count = get_listener_list(client, args, &listener);
699 for (; count; count--, listener++)
700  put_printf(*listener, ":%s JOIN :%s\n", ufull, args);
701
702 c = get_channel(args);
703
704 if ( c->topic.text != NULL ) {
705  nick = get_nick(client);
706  put_printf(client, ":%s 332 %s %s :%s\n"
707                     ":%s 333 %s %s %s %li\n",
708         server_name, nick, c->name, c->topic.text,
709         server_name, nick, c->name, c->topic.user, (long int)c->topic.ts
710  );
711 }
712
713 do_names(client, args);
714
715 return 0;
716}
717
718static int on_part(int client, const char * cmd, char * args, char * text) {
719 const char * ufull = get_ufull(client);
720 int * listener;
721 size_t count;
722
723 (void)cmd;
724
725 if ( args == NULL )
726  return -1;
727
728 if ( text == NULL )
729  text = "Dejoined.";
730
731 count = get_listener_list(client, args, &listener);
732 for (; count; count--, listener++)
733  put_printf(*listener, ":%s PART %s :%s\n", ufull, args, text);
734
735 do_part(client, args);
736
737 return 0;
738}
739
740static int on_names(int client, const char * cmd, char * args, char * text) {
741 (void)cmd, (void)text;
742
743 if ( args == NULL )
744  return -1;
745
746 return do_names(client, args);
747}
748
749static int on_topic(int client, const char * cmd, char * args, char * text) {
750 struct channel * c;
751 const char * ufull = get_ufull(client);
752 int * listener;
753 size_t count;
754 char * nick = get_nick(client);
755
756 (void)cmd;
757
758 if ( args == NULL )
759  return -1;
760
761 c = get_channel(args);
762
763 if ( c == NULL )
764  return -1;
765
766 if ( !c->clients[client] ) {
767  return -1;
768 }
769
770 if ( c->topic.text != NULL )
771  roar_mm_free(c->topic.text);
772 if ( c->topic.user != NULL )
773  roar_mm_free(c->topic.user);
774
775 c->topic.text = NULL;
776 c->topic.user = roar_mm_strdup(nick);
777 c->topic.ts   = time(NULL);
778
779 if ( text != NULL )
780  c->topic.text = roar_mm_strdup(text);
781
782 if ( text == NULL )
783  text = "";
784
785 count = get_listener_list(client, c->name, &listener);
786 for (; count; count--, listener++)
787  put_printf(*listener, ":%s TOPIC %s :%s\n", ufull, c->name, text);
788
789 return 0;
790}
791
792// plugin handling suff:
793
794static int unload(struct roar_dl_librarypara * para, struct roar_dl_libraryinst * lib) {
795 (void)para, (void)lib;
796
797 roar_notify_core_unsubscribe(NULL, subscription_client_delete);
798
799 return 0;
800}
801
802static struct roard_proto proto[1] = {
803 {ROAR_PROTO_IRC, ROAR_SUBSYS_NONE, "Internet Relay Chat", new_client, check_client, NULL, NULL}
804};
805
806ROARD_DL_REG_PROTO(proto)
807
808ROAR_DL_PLUGIN_START(roard_irc_protocol) {
809 ROARD_DL_CHECK_VERSIONS();
810
811 libname.license = "GPL-3.0";
812
813 ROAR_DL_PLUGIN_REG_UNLOAD(unload);
814 ROARD_DL_REGFN_PROTO();
815 init();
816} ROAR_DL_PLUGIN_END
817
818//ll
Note: See TracBrowser for help on using the repository browser.