ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/modules/core/m_sjoin.c
Revision: 8621
Committed: Sat Nov 3 17:46:51 2018 UTC (5 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 19984 byte(s)
Log Message:
- Stylistic changes

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2018 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file m_sjoin.c
23 * \brief Includes required functions for processing the SJOIN command.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "channel.h"
30 #include "channel_mode.h"
31 #include "client.h"
32 #include "hash.h"
33 #include "irc_string.h"
34 #include "ircd.h"
35 #include "numeric.h"
36 #include "send.h"
37 #include "parse.h"
38 #include "modules.h"
39 #include "server.h"
40 #include "conf.h"
41 #include "misc.h"
42
43
44 static char modebuf[MODEBUFLEN];
45 static char parabuf[MODEBUFLEN];
46 static char sendbuf[MODEBUFLEN];
47 static const char *para[MAXMODEPARAMS];
48 static char *mbuf;
49 static int pargs;
50
51 static void set_final_mode(struct Mode *, struct Mode *);
52 static void remove_our_modes(struct Channel *, struct Client *);
53 static void remove_a_mode(struct Channel *, struct Client *, int, char);
54 static void remove_ban_list(struct Channel *, struct Client *, dlink_list *, char);
55
56
57 /* ms_sjoin()
58 *
59 * parv[0] - command
60 * parv[1] - TS
61 * parv[2] - channel
62 * parv[3] - modes + n arguments (key and/or limit)
63 * parv[4+n] - flags+nick list (all in one parameter)
64 *
65 * process a SJOIN, taking the TS's into account to either ignore the
66 * incoming modes or undo the existing ones or merge them, and JOIN
67 * all the specified users while sending JOIN/MODEs to local clients
68 */
69 static int
70 ms_sjoin(struct Client *source_p, int parc, char *parv[])
71 {
72 struct Channel *chptr = NULL;
73 struct Client *target_p = NULL;
74 uintmax_t newts;
75 uintmax_t oldts;
76 uintmax_t tstosend;
77 struct Mode mode, *oldmode;
78 int args = 0;
79 char keep_our_modes = 1;
80 char keep_new_modes = 1;
81 char have_many_uids = 0;
82 int lcount;
83 char uid_prefix[CMEMBER_STATUS_FLAGS_LEN + 1];
84 char *up = NULL;
85 int len_uid = 0;
86 int isnew = 0;
87 int buflen = 0;
88 int slen;
89 unsigned int fl;
90 char *s;
91 char *sptr;
92 char uid_buf[IRCD_BUFSIZE]; /* buffer for modes/prefixes */
93 char *uid_ptr;
94 char *p; /* pointer used making sjbuf */
95 const char *servername = (ConfigServerHide.hide_servers || IsHidden(source_p)) ?
96 me.name : source_p->name;
97
98 if (!IsServer(source_p))
99 return 0;
100
101 if (channel_check_name(parv[2], 0) == 0)
102 {
103 sendto_realops_flags(UMODE_DEBUG, L_ALL, SEND_NOTICE,
104 "*** Too long or invalid channel name from %s(via %s): %s",
105 source_p->name, source_p->from->name, parv[2]);
106 return 0;
107 }
108
109 modebuf[0] = '\0';
110 mbuf = modebuf;
111 pargs = 0;
112 newts = strtoumax(parv[1], NULL, 10);
113
114 mode.mode = 0;
115 mode.limit = 0;
116 mode.key[0] = '\0';
117
118 for (const char *modes = parv[3]; *modes; ++modes)
119 {
120 switch (*modes)
121 {
122 case 'k':
123 strlcpy(mode.key, parv[4 + args], sizeof(mode.key));
124 ++args;
125
126 if (parc < 5 + args)
127 return 0;
128 break;
129
130 case 'l':
131 mode.limit = atoi(parv[4 + args]);
132 ++args;
133
134 if (parc < 5 + args)
135 return 0;
136 break;
137
138 default:
139 {
140 const struct chan_mode *cmode = cmode_map[(unsigned char)*modes];
141
142 if (cmode)
143 mode.mode |= cmode->mode;
144 break;
145 }
146 }
147 }
148
149 if ((chptr = hash_find_channel(parv[2])) == NULL)
150 {
151 isnew = 1;
152 chptr = channel_make(parv[2]);
153 }
154
155 parabuf[0] = '\0';
156 oldts = chptr->creationtime;
157 oldmode = &chptr->mode;
158
159 if (ConfigGeneral.ignore_bogus_ts)
160 {
161 if (newts < 800000000)
162 {
163 sendto_realops_flags(UMODE_DEBUG, L_ALL, SEND_NOTICE,
164 "*** Bogus TS %ju on %s ignored from %s(via %s)",
165 newts, chptr->name,
166 source_p->name, source_p->from->name);
167
168 newts = (oldts == 0) ? 0 : 800000000;
169 }
170 }
171 else
172 {
173 if (newts == 0 && isnew == 0 && oldts)
174 {
175 sendto_channel_local(NULL, chptr, 0, 0, 0,
176 ":%s NOTICE %s :*** Notice -- TS for %s changed from %ju to 0",
177 me.name, chptr->name, chptr->name, oldts);
178 sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE,
179 "Server %s changing TS on %s from %ju to 0",
180 source_p->name, chptr->name, oldts);
181 }
182 }
183
184 if (isnew)
185 chptr->creationtime = tstosend = newts;
186 else if (newts == 0 || oldts == 0)
187 chptr->creationtime = tstosend = 0;
188 else if (newts == oldts)
189 tstosend = oldts;
190 else if (newts < oldts)
191 {
192 keep_our_modes = 0;
193 chptr->creationtime = tstosend = newts;
194 }
195 else
196 {
197 keep_new_modes = 0;
198 tstosend = oldts;
199 }
200
201 if (keep_new_modes == 0)
202 mode = *oldmode;
203 else if (keep_our_modes)
204 {
205 mode.mode |= oldmode->mode;
206
207 if (oldmode->limit > mode.limit)
208 mode.limit = oldmode->limit;
209 if (strcmp(mode.key, oldmode->key) < 0)
210 strlcpy(mode.key, oldmode->key, sizeof(mode.key));
211 }
212
213 set_final_mode(&mode, oldmode);
214 chptr->mode = mode;
215
216 /* Lost the TS, other side wins, so remove modes on this side */
217 if (keep_our_modes == 0)
218 {
219 /* Update channel name to be the correct case */
220 if (isnew == 0)
221 strlcpy(chptr->name, parv[2], sizeof(chptr->name));
222
223 remove_our_modes(chptr, source_p);
224
225 if (dlink_list_length(&chptr->banlist))
226 remove_ban_list(chptr, source_p, &chptr->banlist, 'b');
227
228 if (dlink_list_length(&chptr->exceptlist))
229 remove_ban_list(chptr, source_p, &chptr->exceptlist, 'e');
230
231 if (dlink_list_length(&chptr->invexlist))
232 remove_ban_list(chptr, source_p, &chptr->invexlist, 'I');
233
234 clear_ban_cache_list(&chptr->members_local);
235 clear_invite_list(&chptr->invites);
236
237 if (chptr->topic[0])
238 {
239 channel_set_topic(chptr, "", "", 0, 0);
240 sendto_channel_local(NULL, chptr, 0, 0, 0, ":%s TOPIC %s :",
241 (IsHidden(source_p) ||
242 ConfigServerHide.hide_servers) ?
243 me.name : source_p->name, chptr->name);
244 }
245
246 sendto_channel_local(NULL, chptr, 0, 0, 0,
247 ":%s NOTICE %s :*** Notice -- TS for %s changed from %ju to %ju",
248 me.name, chptr->name, chptr->name,
249 oldts, newts);
250 }
251
252 if (*modebuf)
253 {
254 /* This _SHOULD_ be to ALL_MEMBERS
255 * It contains only +imnpstlk, etc */
256 sendto_channel_local(NULL, chptr, 0, 0, 0, ":%s MODE %s %s %s",
257 servername, chptr->name, modebuf, parabuf);
258 }
259
260 if (*parv[3] != '0' && keep_new_modes)
261 channel_modes(chptr, source_p, modebuf, parabuf);
262 else
263 {
264 modebuf[0] = '0';
265 modebuf[1] = '\0';
266 }
267
268 buflen = snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %ju %s %s %s:",
269 source_p->id, tstosend,
270 chptr->name, modebuf, parabuf);
271 uid_ptr = uid_buf + buflen;
272
273 /*
274 * Check we can fit a nick on the end, as well as \r\n and a prefix "
275 * @%+", and a space.
276 */
277 if (buflen >= (IRCD_BUFSIZE - IDLEN - 2 - CMEMBER_STATUS_FLAGS_LEN - 1))
278 {
279 sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE,
280 "Long SJOIN from server: %s(via %s) (ignored)",
281 source_p->name, source_p->from->name);
282 return 0;
283 }
284
285 mbuf = modebuf;
286 sendbuf[0] = '\0';
287 pargs = 0;
288
289 *mbuf++ = '+';
290
291 s = parv[args + 4];
292 while (*s == ' ')
293 ++s;
294
295 if ((p = strchr(s, ' ')))
296 {
297 *p++ = '\0';
298
299 while (*p == ' ')
300 ++p;
301 have_many_uids = *p;
302 }
303
304 while (*s)
305 {
306 int valid_mode = 1;
307 fl = 0;
308
309 do
310 {
311 switch (*s)
312 {
313 case '@':
314 fl |= CHFL_CHANOP;
315 ++s;
316 break;
317 case '%':
318 fl |= CHFL_HALFOP;
319 ++s;
320 break;
321 case '+':
322 fl |= CHFL_VOICE;
323 ++s;
324 break;
325 default:
326 valid_mode = 0;
327 break;
328 }
329 } while (valid_mode);
330
331 /*
332 * If the client doesn't exist, or if it's fake direction/server, skip.
333 * we cannot send ERR_NOSUCHNICK here because if it's a UID, we cannot
334 * lookup the nick, and it's better to never send the numeric than only
335 * sometimes.
336 */
337 if ((target_p = find_person(source_p, s)) == NULL || target_p->from != source_p->from)
338 goto nextnick;
339
340 len_uid = strlen(target_p->id);
341 up = uid_prefix;
342
343 if (keep_new_modes)
344 {
345 if (fl & CHFL_CHANOP)
346 {
347 *up++ = '@';
348 len_uid++;
349 }
350
351 if (fl & CHFL_HALFOP)
352 {
353 *up++ = '%';
354 len_uid++;
355 }
356
357 if (fl & CHFL_VOICE)
358 {
359 *up++ = '+';
360 len_uid++;
361 }
362 }
363 else
364 fl = 0;
365
366 *up = '\0';
367
368 if ((uid_ptr - uid_buf + len_uid) > (IRCD_BUFSIZE - 2))
369 {
370 sendto_server(source_p, 0, 0, "%s", uid_buf);
371
372 buflen = snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %ju %s %s %s:",
373 source_p->id, tstosend,
374 chptr->name, modebuf, parabuf);
375 uid_ptr = uid_buf + buflen;
376 }
377
378 uid_ptr += sprintf(uid_ptr, "%s%s ", uid_prefix, target_p->id);
379
380 if (!IsMember(target_p, chptr))
381 {
382 add_user_to_channel(chptr, target_p, fl, have_many_uids == 0);
383
384 sendto_channel_local(NULL, chptr, 0, CAP_EXTENDED_JOIN, 0, ":%s!%s@%s JOIN %s %s :%s",
385 target_p->name, target_p->username,
386 target_p->host, chptr->name, target_p->account, target_p->info);
387 sendto_channel_local(NULL, chptr, 0, 0, CAP_EXTENDED_JOIN, ":%s!%s@%s JOIN :%s",
388 target_p->name, target_p->username,
389 target_p->host, chptr->name);
390
391 if (target_p->away[0])
392 sendto_channel_local(target_p, chptr, 0, CAP_AWAY_NOTIFY, 0,
393 ":%s!%s@%s AWAY :%s",
394 target_p->name, target_p->username,
395 target_p->host, target_p->away);
396 }
397
398 if (fl & CHFL_CHANOP)
399 {
400 *mbuf++ = 'o';
401 para[pargs++] = target_p->name;
402
403 if (pargs >= MAXMODEPARAMS)
404 {
405 /*
406 * Ok, the code is now going to "walk" through
407 * sendbuf, filling in para strings. So, I will use sptr
408 * to point into the sendbuf.
409 * Notice, that ircsprintf() returns the number of chars
410 * successfully inserted into string.
411 * - Dianora
412 */
413
414 sptr = sendbuf;
415 *mbuf = '\0';
416
417 for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount)
418 {
419 slen = sprintf(sptr, " %s", para[lcount]); /* see? */
420 sptr += slen; /* ready for next */
421 }
422
423 sendto_channel_local(NULL, chptr, 0, 0, 0, ":%s MODE %s %s%s",
424 servername, chptr->name, modebuf, sendbuf);
425 mbuf = modebuf;
426 *mbuf++ = '+';
427
428 sendbuf[0] = '\0';
429 pargs = 0;
430 }
431 }
432
433 if (fl & CHFL_HALFOP)
434 {
435 *mbuf++ = 'h';
436 para[pargs++] = target_p->name;
437
438 if (pargs >= MAXMODEPARAMS)
439 {
440 sptr = sendbuf;
441 *mbuf = '\0';
442
443 for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount)
444 {
445 slen = sprintf(sptr, " %s", para[lcount]);
446 sptr += slen;
447 }
448
449 sendto_channel_local(NULL, chptr, 0, 0, 0, ":%s MODE %s %s%s",
450 servername, chptr->name, modebuf, sendbuf);
451
452 mbuf = modebuf;
453 *mbuf++ = '+';
454
455 sendbuf[0] = '\0';
456 pargs = 0;
457 }
458 }
459
460 if (fl & CHFL_VOICE)
461 {
462 *mbuf++ = 'v';
463 para[pargs++] = target_p->name;
464
465 if (pargs >= MAXMODEPARAMS)
466 {
467 sptr = sendbuf;
468 *mbuf = '\0';
469
470 for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount)
471 {
472 slen = sprintf(sptr, " %s", para[lcount]);
473 sptr += slen;
474 }
475
476 sendto_channel_local(NULL, chptr, 0, 0, 0, ":%s MODE %s %s%s",
477 servername, chptr->name, modebuf, sendbuf);
478
479 mbuf = modebuf;
480 *mbuf++ = '+';
481
482 sendbuf[0] = '\0';
483 pargs = 0;
484 }
485 }
486
487 nextnick:
488 if ((s = p) == NULL)
489 break;
490
491 while (*s == ' ')
492 ++s;
493
494 if ((p = strchr(s, ' ')))
495 {
496 *p++ = '\0';
497
498 while (*p == ' ')
499 ++p;
500 }
501 }
502
503 *mbuf = '\0';
504 *(uid_ptr - 1) = '\0';
505
506 /*
507 * checking for lcount < MAXMODEPARAMS at this time is wrong
508 * since the code has already verified above that pargs < MAXMODEPARAMS
509 * checking for para[lcount] != '\0' is also wrong, since
510 * there is no place where para[lcount] is set!
511 * - Dianora
512 */
513
514 if (pargs)
515 {
516 sptr = sendbuf;
517
518 for (lcount = 0; lcount < pargs; ++lcount)
519 {
520 slen = sprintf(sptr, " %s", para[lcount]);
521 sptr += slen;
522 }
523
524 sendto_channel_local(NULL, chptr, 0, 0, 0, ":%s MODE %s %s%s",
525 servername, chptr->name, modebuf, sendbuf);
526 }
527
528 /*
529 * If this happens, it's the result of a malformed SJOIN
530 * a remnant from the old persistent channel code. *sigh*
531 * Or it could be the result of a client just leaving
532 * and leaving us with a channel formed just as the client parts.
533 * - Dianora
534 */
535 if (dlink_list_length(&chptr->members) == 0 && isnew)
536 {
537 channel_free(chptr);
538 return 0;
539 }
540
541 if (*parv[4 + args] == '\0')
542 return 0;
543
544 sendto_server(source_p, 0, 0, "%s", uid_buf);
545 return 0;
546 }
547
548 /* set_final_mode
549 *
550 * inputs - channel mode
551 * - old channel mode
552 * output - NONE
553 * side effects - walk through all the channel modes turning off modes
554 * that were on in oldmode but aren't on in mode.
555 * Then walk through turning on modes that are on in mode
556 * but were not set in oldmode.
557 */
558 static void
559 set_final_mode(struct Mode *mode, struct Mode *oldmode)
560 {
561 char *pbuf = parabuf;
562 int len = 0;
563
564 *mbuf++ = '-';
565
566 for (const struct chan_mode *tab = cmode_tab; tab->letter; ++tab)
567 if (tab->mode && (tab->mode & oldmode->mode) && !(tab->mode & mode->mode))
568 *mbuf++ = tab->letter;
569
570 if (oldmode->limit && mode->limit == 0)
571 *mbuf++ = 'l';
572
573 if (oldmode->key[0] && mode->key[0] == '\0')
574 {
575 *mbuf++ = 'k';
576 len = sprintf(pbuf, "%s ", oldmode->key);
577 pbuf += len;
578 pargs++;
579 }
580
581 if (*(mbuf - 1) == '-')
582 *(mbuf - 1) = '+';
583 else
584 *mbuf++ = '+';
585
586 for (const struct chan_mode *tab = cmode_tab; tab->letter; ++tab)
587 if (tab->mode && (tab->mode & mode->mode) && !(tab->mode & oldmode->mode))
588 *mbuf++ = tab->letter;
589
590 if (mode->limit && oldmode->limit != mode->limit)
591 {
592 *mbuf++ = 'l';
593 len = sprintf(pbuf, "%u ", mode->limit);
594 pbuf += len;
595 pargs++;
596 }
597
598 if (mode->key[0] && strcmp(oldmode->key, mode->key))
599 {
600 *mbuf++ = 'k';
601 len = sprintf(pbuf, "%s ", mode->key);
602 pbuf += len;
603 pargs++;
604 }
605
606 if (*(mbuf - 1) == '+')
607 *(mbuf - 1) = '\0';
608 else
609 *mbuf = '\0';
610 }
611
612 /* remove_our_modes()
613 *
614 * inputs - pointer to channel to remove modes from
615 * - client pointer
616 * output - NONE
617 * side effects - Go through the local members, remove all their
618 * chanop modes etc., this side lost the TS.
619 */
620 static void
621 remove_our_modes(struct Channel *chptr, struct Client *source_p)
622 {
623 remove_a_mode(chptr, source_p, CHFL_CHANOP, 'o');
624 remove_a_mode(chptr, source_p, CHFL_HALFOP, 'h');
625 remove_a_mode(chptr, source_p, CHFL_VOICE, 'v');
626 }
627
628 /* remove_a_mode()
629 *
630 * inputs - pointer to channel
631 * - server or client removing the mode
632 * - mask o/h/v mask to be removed
633 * - flag o/h/v to be removed
634 * output - NONE
635 * side effects - remove ONE mode from all members of a channel
636 */
637 static void
638 remove_a_mode(struct Channel *chptr, struct Client *source_p,
639 int mask, char flag)
640 {
641 dlink_node *node = NULL;
642 char lmodebuf[MODEBUFLEN];
643 char *sp = sendbuf;
644 const char *lpara[MAXMODEPARAMS];
645 unsigned int count = 0;
646 int l = 0;
647
648 mbuf = lmodebuf;
649 *mbuf++ = '-';
650 *sp = '\0';
651
652 DLINK_FOREACH(node, chptr->members.head)
653 {
654 struct Membership *member = node->data;
655
656 if ((member->flags & mask) == 0)
657 continue;
658
659 member->flags &= ~mask;
660
661 lpara[count++] = member->client_p->name;
662
663 *mbuf++ = flag;
664
665 if (count >= MAXMODEPARAMS)
666 {
667 for (unsigned int i = 0; i < MAXMODEPARAMS; ++i)
668 {
669 l = sprintf(sp, " %s", lpara[i]);
670 sp += l;
671 }
672
673 *mbuf = '\0';
674 sendto_channel_local(NULL, chptr, 0, 0, 0, ":%s MODE %s %s%s",
675 (IsHidden(source_p) || ConfigServerHide.hide_servers) ?
676 me.name : source_p->name,
677 chptr->name, lmodebuf, sendbuf);
678 mbuf = lmodebuf;
679 *mbuf++ = '-';
680 count = 0;
681 sp = sendbuf;
682 *sp = '\0';
683 }
684 }
685
686 if (count)
687 {
688 *mbuf = '\0';
689
690 for (unsigned int i = 0; i < count; ++i)
691 {
692 l = sprintf(sp, " %s", lpara[i]);
693 sp += l;
694 }
695
696 sendto_channel_local(NULL, chptr, 0, 0, 0, ":%s MODE %s %s%s",
697 (IsHidden(source_p) || ConfigServerHide.hide_servers) ?
698 me.name : source_p->name,
699 chptr->name, lmodebuf, sendbuf);
700 }
701 }
702
703 /* remove_ban_list()
704 *
705 * inputs - channel, source, list to remove, char of mode
706 * outputs - none
707 * side effects - given ban list is removed, modes are sent to local clients
708 */
709 static void
710 remove_ban_list(struct Channel *chptr, struct Client *source_p,
711 dlink_list *list, char c)
712 {
713 char lmodebuf[MODEBUFLEN];
714 char lparabuf[IRCD_BUFSIZE];
715 dlink_node *node = NULL, *node_next = NULL;
716 char *pbuf = NULL;
717 int count = 0;
718 int cur_len, mlen, plen;
719
720 pbuf = lparabuf;
721 cur_len = mlen = snprintf(lmodebuf, sizeof(lmodebuf), ":%s MODE %s -",
722 source_p->name, chptr->name);
723 mbuf = lmodebuf + mlen;
724
725 DLINK_FOREACH_SAFE(node, node_next, list->head)
726 {
727 struct Ban *ban = node->data;
728
729 plen = ban->len + 4; /* another +b and "!@ " */
730
731 if (count >= MAXMODEPARAMS ||
732 (cur_len + 1 /* space between */ + (plen - 1)) > IRCD_BUFSIZE - 2)
733 {
734 /* NUL-terminate and remove trailing space */
735 *mbuf = *(pbuf - 1) = '\0';
736 sendto_channel_local(NULL, chptr, 0, 0, 0, "%s %s", lmodebuf, lparabuf);
737 cur_len = mlen;
738 mbuf = lmodebuf + mlen;
739 pbuf = lparabuf;
740 count = 0;
741 }
742
743 *mbuf++ = c;
744 cur_len += plen;
745 pbuf += sprintf(pbuf, "%s!%s@%s ", ban->name, ban->user, ban->host);
746 ++count;
747
748 remove_ban(ban, list);
749 }
750
751 *mbuf = *(pbuf - 1) = '\0';
752 sendto_channel_local(NULL, chptr, 0, 0, 0, "%s %s", lmodebuf, lparabuf);
753 }
754
755 static struct Message sjoin_msgtab =
756 {
757 .cmd = "SJOIN",
758 .args_min = 5,
759 .args_max = MAXPARA,
760 .handlers[UNREGISTERED_HANDLER] = m_unregistered,
761 .handlers[CLIENT_HANDLER] = m_ignore,
762 .handlers[SERVER_HANDLER] = ms_sjoin,
763 .handlers[ENCAP_HANDLER] = m_ignore,
764 .handlers[OPER_HANDLER] = m_ignore
765 };
766
767 static void
768 module_init(void)
769 {
770 mod_add_cmd(&sjoin_msgtab);
771 }
772
773 static void
774 module_exit(void)
775 {
776 mod_del_cmd(&sjoin_msgtab);
777 }
778
779 struct module module_entry =
780 {
781 .version = "$Revision$",
782 .modinit = module_init,
783 .modexit = module_exit,
784 .flags = MODULE_FLAG_CORE
785 };

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision