source: roaraudio/plugins/roard/protocol-irc.c @ 5308:90bb00b0ea22

Last change on this file since 5308:90bb00b0ea22 was 5308:90bb00b0ea22, checked in by phi, 12 years ago

added support for LIST command

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