1 /* proto-bahamut.c - IRC protocol output
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx 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.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 #include "proto-common.c"
23 #define CAPAB_TS3 0x01
24 #define CAPAB_NOQUIT 0x02
25 #define CAPAB_SSJOIN 0x04
26 #define CAPAB_BURST 0x08
27 #define CAPAB_UNCONNECT 0x10
28 #define CAPAB_NICKIP 0x20
29 #define CAPAB_TSMODE 0x40
30 #define CAPAB_ZIP 0x80
32 struct service_message_info
{
33 privmsg_func_t on_privmsg
;
34 privmsg_func_t on_notice
;
37 static dict_t service_msginfo_dict
; /* holds service_message_info structs */
38 static int uplink_capab
;
39 static void privmsg_user_helper(struct userNode
*un
, void *data
);
41 void irc_svsmode(struct userNode
*target
, char *modes
, unsigned long stamp
);
44 AddServer(struct server
*uplink
, const char *name
, int hops
, time_t boot
, time_t link
, UNUSED_ARG(const char *numeric
), const char *description
) {
47 sNode
= calloc(1, sizeof(*sNode
));
48 sNode
->uplink
= uplink
;
49 safestrncpy(sNode
->name
, name
, sizeof(sNode
->name
));
53 sNode
->users
= dict_new();
54 safestrncpy(sNode
->description
, description
, sizeof(sNode
->description
));
55 serverList_init(&sNode
->children
);
57 /* uplink may be NULL if we're just building ourself */
58 serverList_append(&sNode
->uplink
->children
, sNode
);
60 dict_insert(servers
, sNode
->name
, sNode
);
62 if (hops
&& !self
->burst
) {
64 for (n
=0; n
<slf_used
; n
++) {
73 DelServer(struct server
* serv
, int announce
, const char *message
) {
75 dict_iterator_t it
, next
;
78 if (announce
&& (serv
->uplink
== self
) && (serv
!= self
->uplink
)) {
79 irc_squit(serv
, message
, NULL
);
81 for (nn
=serv
->children
.used
; nn
>0;) {
82 if (serv
->children
.list
[--nn
] != self
) {
83 DelServer(serv
->children
.list
[nn
], false, "uplink delinking");
86 for (it
=dict_first(serv
->users
); it
; it
=next
) {
88 DelUser(iter_data(it
), NULL
, false, "server delinking");
90 if (serv
->uplink
) serverList_remove(&serv
->uplink
->children
, serv
);
91 if (serv
== self
->uplink
) self
->uplink
= NULL
;
92 dict_remove(servers
, serv
->name
);
93 serverList_clean(&serv
->children
);
94 dict_delete(serv
->users
);
99 is_valid_nick(const char *nick
) {
100 /* IRC has some of The Most Fucked-Up ideas about character sets
102 if ((*nick
< 'A') || (*nick
>= '~')) return 0;
103 for (++nick
; *nick
; ++nick
) {
104 if (!((*nick
>= 'A') && (*nick
< '~'))
110 if (strlen(nick
) > nicklen
) return 0;
115 AddUser(struct server
* uplink
, const char *nick
, const char *ident
, const char *hostname
, const char *modes
, const char *userinfo
, time_t timestamp
, struct in_addr realip
, const char *stamp
) {
116 struct userNode
*uNode
, *oldUser
;
120 log_module(MAIN_LOG
, LOG_WARNING
, "AddUser(%p, %s, ...): server does not exist!", uplink
, nick
);
124 if (!is_valid_nick(nick
)) {
125 log_module(MAIN_LOG
, LOG_WARNING
, "AddUser(%p, %s, ...): invalid nickname detected.", uplink
, nick
);
129 if ((oldUser
= GetUserH(nick
))) {
130 if (IsLocal(oldUser
) && IsService(oldUser
)) {
131 /* The service should collide the new user off. */
132 oldUser
->timestamp
= timestamp
- 1;
135 if (oldUser
->timestamp
> timestamp
) {
136 /* "Old" user is really newer; remove them */
137 DelUser(oldUser
, 0, 1, "Overruled by older nick");
139 /* User being added is too new */
144 uNode
= calloc(1, sizeof(*uNode
));
145 uNode
->nick
= strdup(nick
);
146 safestrncpy(uNode
->ident
, ident
, sizeof(uNode
->ident
));
147 safestrncpy(uNode
->info
, userinfo
, sizeof(uNode
->info
));
148 safestrncpy(uNode
->hostname
, hostname
, sizeof(uNode
->hostname
));
150 uNode
->timestamp
= timestamp
;
151 modeList_init(&uNode
->channels
);
152 uNode
->uplink
= uplink
;
153 dict_insert(uplink
->users
, uNode
->nick
, uNode
);
154 if (++uNode
->uplink
->clients
> uNode
->uplink
->max_clients
) {
155 uNode
->uplink
->max_clients
= uNode
->uplink
->clients
;
158 dict_insert(clients
, uNode
->nick
, uNode
);
159 if (dict_size(clients
) > max_clients
) {
160 max_clients
= dict_size(clients
);
161 max_clients_time
= now
;
164 mod_usermode(uNode
, modes
);
165 if (stamp
) call_account_func(uNode
, stamp
);
166 if (IsLocal(uNode
)) irc_user(uNode
);
167 for (nn
=0; nn
<nuf_used
; nn
++) {
168 if (nuf_list
[nn
](uNode
)) break;
174 AddService(const char *nick
, const char *desc
, const char *hostname
) {
175 time_t timestamp
= now
;
176 struct userNode
*old_user
= GetUserH(nick
);
177 struct in_addr ipaddr
= { INADDR_LOOPBACK
};
179 if (IsLocal(old_user
))
181 timestamp
= old_user
->timestamp
- 1;
184 hostname
= self
->name
;
185 return AddUser(self
, nick
, nick
, hostname
, "+oikr", desc
, timestamp
, ipaddr
, 0);
189 AddClone(const char *nick
, const char *ident
, const char *hostname
, const char *desc
) {
190 time_t timestamp
= now
;
191 struct userNode
*old_user
= GetUserH(nick
);
192 struct in_addr ipaddr
= { INADDR_LOOPBACK
};
194 if (IsLocal(old_user
))
196 timestamp
= old_user
->timestamp
- 1;
198 return AddUser(self
, nick
, ident
, hostname
, "+ir", desc
, timestamp
, ipaddr
, 0);
202 free_user(struct userNode
*user
)
209 DelUser(struct userNode
* user
, struct userNode
*killer
, int announce
, const char *why
) {
212 for (nn
=user
->channels
.used
; nn
>0;) {
213 DelChannelUser(user
, user
->channels
.list
[--nn
]->channel
, false, 0);
215 for (nn
=duf_used
; nn
>0; ) duf_list
[--nn
](user
, killer
, why
);
216 user
->uplink
->clients
--;
217 dict_remove(user
->uplink
->users
, user
->nick
);
218 if (IsOper(user
)) userList_remove(&curr_opers
, user
);
219 if (IsInvisible(user
)) invis_clients
--;
220 if (user
== dict_find(clients
, user
->nick
, NULL
)) dict_remove(clients
, user
->nick
);
225 irc_kill(killer
, user
, why
);
228 modeList_clean(&user
->channels
);
230 if (dead_users
.size
) {
231 userList_append(&dead_users
, user
);
238 irc_server(struct server
*srv
) {
240 putsock("SERVER %s %d :%s", srv
->name
, srv
->hops
, srv
->description
);
242 putsock(":%s SERVER %s %d :%s", self
->name
, srv
->name
, srv
->hops
, srv
->description
);
247 irc_user(struct userNode
*user
) {
250 if (IsOper(user
)) modes
[modelen
++] = 'o';
251 if (IsInvisible(user
)) modes
[modelen
++] = 'i';
252 if (IsWallOp(user
)) modes
[modelen
++] = 'w';
253 if (IsService(user
)) modes
[modelen
++] = 'k';
254 if (IsServNotice(user
)) modes
[modelen
++] = 's';
255 if (IsDeaf(user
)) modes
[modelen
++] = 'd';
256 if (IsReggedNick(user
)) modes
[modelen
++] = 'r';
257 if (IsGlobal(user
)) modes
[modelen
++] = 'g';
259 putsock("NICK %s %d "FMT_TIME_T
" +%s %s %s %s %d %u :%s",
260 user
->nick
, user
->uplink
->hops
+2, user
->timestamp
, modes
,
261 user
->ident
, user
->hostname
, user
->uplink
->name
, 0, ntohl(user
->ip
.s_addr
), user
->info
);
265 irc_account(struct userNode
*user
, const char *stamp
)
267 if (IsReggedNick(user
)) {
268 irc_svsmode(user
, "+rd", base64toint(stamp
, IDLEN
));
270 irc_svsmode(user
, "+d", base64toint(stamp
, IDLEN
));
275 irc_fakehost(UNUSED_ARG(struct userNode
*user
), UNUSED_ARG(const char *host
))
277 /* not supported in bahamut */
281 irc_regnick(struct userNode
*user
)
283 if (IsReggedNick(user
)) {
284 irc_svsmode(user
, "+r", 0);
286 irc_svsmode(user
, "-r", 0);
291 irc_nick(struct userNode
*user
, const char *old_nick
) {
292 if (user
->uplink
== self
) {
293 /* update entries in PRIVMSG/NOTICE handlers (if they exist) */
294 struct service_message_info
*smi
= dict_find(service_msginfo_dict
, user
->nick
, NULL
);
296 dict_remove2(service_msginfo_dict
, old_nick
, 1);
297 dict_insert(service_msginfo_dict
, user
->nick
, smi
);
300 putsock(":%s NICK %s :"FMT_TIME_T
, old_nick
, user
->nick
, user
->timestamp
);
304 irc_pass(const char *passwd
) {
305 putsock("PASS %s :TS", passwd
);
310 putsock("CAPAB TS3 NOQUIT SSJOIN BURST UNCONNECT NICKIP TSMODE");
315 putsock("SVINFO 3 3 0 :"FMT_TIME_T
, now
);
319 irc_introduce(const char *passwd
) {
320 extern time_t burst_begin
;
328 timeq_add(now
+ ping_freq
, timed_send_ping
, 0);
332 irc_ping(const char *something
) {
333 putsock("PING :%s", something
);
337 irc_pong(const char *who
, const char *data
) {
338 putsock(":%s PONG %s :%s", self
->name
, who
, data
);
342 irc_quit(struct userNode
*user
, const char *message
) {
343 putsock(":%s QUIT :%s", user
->nick
, message
);
347 irc_squit(struct server
*srv
, const char *message
, const char *service_message
) {
348 if (!service_message
) service_message
= message
;
349 /* If we're leaving the network, QUIT all our clients. */
350 if ((srv
== self
) && (cManager
.uplink
->state
== CONNECTED
)) {
352 for (it
= dict_first(srv
->users
); it
; it
= iter_next(it
)) {
353 irc_quit(iter_data(it
), service_message
);
356 putsock(":%s SQUIT %s 0 :%s", self
->name
, srv
->name
, message
);
358 /* Reconnect to the currently selected server. */
359 cManager
.uplink
->tries
= 0;
360 log_module(MAIN_LOG
, LOG_INFO
, "Squitting from uplink: %s", message
);
366 irc_privmsg(struct userNode
*from
, const char *to
, const char *message
) {
367 putsock(":%s PRIVMSG %s :%s", from
->nick
, to
, message
);
371 irc_notice(struct userNode
*from
, const char *to
, const char *message
) {
372 putsock(":%s NOTICE %s :%s", from
->nick
, to
, message
);
376 irc_notice_user(struct userNode
*from
, struct userNode
*to
, const char *message
) {
377 putsock(":%s NOTICE %s :%s", from
->nick
, to
->nick
, message
);
381 irc_wallchops(UNUSED_ARG(struct userNode
*from
), UNUSED_ARG(const char *to
), UNUSED_ARG(const char *message
)) {
385 irc_join(struct userNode
*who
, struct chanNode
*what
) {
386 if (what
->members
.used
== 1) {
387 putsock(":%s SJOIN "FMT_TIME_T
" %s + :@%s", self
->name
, what
->timestamp
, what
->name
, who
->nick
);
389 putsock(":%s SJOIN "FMT_TIME_T
" %s", who
->nick
, what
->timestamp
, what
->name
);
394 irc_invite(struct userNode
*from
, struct userNode
*who
, struct chanNode
*to
) {
395 putsock(":%s INVITE %s %s", from
->nick
, who
->nick
, to
->name
);
399 irc_mode(struct userNode
*who
, struct chanNode
*target
, const char *modes
) {
400 putsock(":%s MODE %s "FMT_TIME_T
" %s", who
->nick
, target
->name
, target
->timestamp
, modes
);
404 irc_svsmode(struct userNode
*target
, char *modes
, unsigned long stamp
) {
405 extern struct userNode
*nickserv
;
407 putsock(":%s SVSMODE %s "FMT_TIME_T
" %s %lu", nickserv
->nick
, target
->nick
, target
->timestamp
, modes
, stamp
);
409 putsock(":%s SVSMODE %s "FMT_TIME_T
" %s", nickserv
->nick
, target
->nick
, target
->timestamp
, modes
);
414 irc_kick(struct userNode
*who
, struct userNode
*target
, struct chanNode
*from
, const char *msg
) {
415 putsock(":%s KICK %s %s :%s", who
->nick
, from
->name
, target
->nick
, msg
);
416 ChannelUserKicked(who
, target
, from
);
420 irc_part(struct userNode
*who
, struct chanNode
*what
, const char *reason
) {
422 putsock(":%s PART %s :%s", who
->nick
, what
->name
, reason
);
424 putsock(":%s PART %s", who
->nick
, what
->name
);
429 irc_topic(struct userNode
*who
, struct chanNode
*what
, const char *topic
) {
430 putsock(":%s TOPIC %s :%s", who
->nick
, what
->name
, topic
);
434 irc_fetchtopic(struct userNode
*from
, const char *to
) {
435 if (!from
|| !to
) return;
436 putsock(":%s TOPIC %s", from
->nick
, to
);
440 irc_gline(struct server
*srv
, struct gline
*gline
) {
441 char host
[HOSTLEN
+1], ident
[USERLEN
+1], *sep
;
444 log_module(MAIN_LOG
, LOG_WARNING
, "%s tried to send a targeted G-line for %s (not supported by protocol!)", gline
->issuer
, gline
->target
);
447 if (!(sep
= strchr(gline
->target
, '@'))) {
448 log_module(MAIN_LOG
, LOG_ERROR
, "%s tried to add G-line with bad mask %s", gline
->issuer
, gline
->target
);
451 len
= sep
- gline
->target
+ 1;
452 if (len
> ArrayLength(ident
)) len
= ArrayLength(ident
);
453 safestrncpy(ident
, gline
->target
, len
);
454 safestrncpy(host
, sep
+1, ArrayLength(host
));
455 putsock(":%s AKILL %s %s "FMT_TIME_T
" %s "FMT_TIME_T
" :%s", self
->name
, host
, ident
, gline
->expires
-gline
->issued
, gline
->issuer
, gline
->issued
, gline
->reason
);
459 irc_settime(UNUSED_ARG(const char *srv_name_mask
), UNUSED_ARG(time_t new_time
))
461 /* Bahamut has nothing like this, so ignore it. */
465 irc_ungline(const char *mask
) {
466 char host
[HOSTLEN
+1], ident
[USERLEN
+1], *sep
;
468 if (!(sep
= strchr(mask
, '@'))) {
469 log_module(MAIN_LOG
, LOG_ERROR
, "Tried to remove G-line with bad mask %s", mask
);
472 len
= sep
- mask
+ 1;
473 if (len
> ArrayLength(ident
)) len
= ArrayLength(ident
);
474 safestrncpy(ident
, mask
, len
);
475 safestrncpy(host
, sep
+1, ArrayLength(host
));
476 putsock(":%s RAKILL %s %s", self
->name
, host
, ident
);
480 irc_error(const char *to
, const char *message
) {
482 putsock("%s ERROR :%s", to
, message
);
484 putsock(":%s ERROR :%s", self
->name
, message
);
489 irc_kill(struct userNode
*from
, struct userNode
*target
, const char *message
) {
491 putsock(":%s KILL %s :%s!%s (%s)", from
->nick
, target
->nick
, self
->name
, from
->nick
, message
);
493 putsock(":%s KILL %s :%s (%s)", self
->name
, target
->nick
, self
->name
, message
);
498 irc_raw(const char *what
) {
503 irc_stats(struct userNode
*from
, struct server
*target
, char type
) {
504 putsock(":%s STATS %c :%s", from
->nick
, type
, target
->name
);
508 irc_svsnick(struct userNode
*from
, struct userNode
*target
, const char *newnick
)
510 putsock(":%s SVSNICK %s %s :"FMT_TIME_T
, from
->nick
, target
->nick
, newnick
, now
);
514 irc_numeric(struct userNode
*user
, unsigned int num
, const char *format
, ...) {
517 va_start(arg_list
, format
);
518 vsnprintf(buffer
, MAXLEN
-2, format
, arg_list
);
519 buffer
[MAXLEN
-1] = 0;
520 putsock(":%s %03d %s %s", self
->name
, num
, user
->nick
, buffer
);
524 parse_foreach(char *target_list
, foreach_chanfunc cf
, foreach_nonchan nc
, foreach_userfunc uf
, foreach_nonuser nu
, void *data
) {
528 while (*j
!= 0 && *j
!= ',') j
++;
531 if (IsChannelName(target_list
)) {
532 struct chanNode
*chan
= GetChannel(target_list
);
534 if (cf
) cf(chan
, data
);
536 if (nc
) nc(target_list
, data
);
539 struct userNode
*user
;
540 struct privmsg_desc
*pd
= data
;
542 pd
->is_qualified
= 0;
543 if (*target_list
== '@') {
545 } else if (strchr(target_list
, '@')) {
546 struct server
*server
;
548 pd
->is_qualified
= 1;
549 user
= GetUserH(strtok(target_list
, "@"));
550 server
= GetServerH(strtok(NULL
, "@"));
552 if (user
&& (user
->uplink
!= server
)) {
553 /* Don't attempt to index into any arrays
554 using a user's numeric on another server. */
558 user
= GetUserH(target_list
);
562 if (uf
) uf(user
, data
);
564 if (nu
) nu(target_list
, data
);
568 } while (old
== ',');
571 static CMD_FUNC(cmd_notice
) {
572 struct privmsg_desc pd
;
573 if ((argc
< 3) || !origin
) return 0;
574 if (!(pd
.user
= GetUserH(origin
))) return 1;
575 if (IsGagged(pd
.user
) && !IsOper(pd
.user
)) return 1;
578 parse_foreach(argv
[1], privmsg_chan_helper
, NULL
, privmsg_user_helper
, privmsg_invalid
, &pd
);
582 static CMD_FUNC(cmd_privmsg
) {
583 struct privmsg_desc pd
;
584 if ((argc
< 3) || !origin
) return 0;
585 if (!(pd
.user
= GetUserH(origin
))) return 1;
586 if (IsGagged(pd
.user
) && !IsOper(pd
.user
)) return 1;
589 parse_foreach(argv
[1], privmsg_chan_helper
, NULL
, privmsg_user_helper
, privmsg_invalid
, &pd
);
593 static CMD_FUNC(cmd_whois
) {
594 struct userNode
*from
;
595 struct userNode
*who
;
599 if (!(from
= GetUserH(origin
))) {
600 log_module(MAIN_LOG
, LOG_ERROR
, "Could not find WHOIS origin user %s", origin
);
603 if(!(who
= GetUserH(argv
[2]))) {
604 irc_numeric(from
, ERR_NOSUCHNICK
, "%s@%s :No such nick", argv
[2], self
->name
);
607 if (IsHiddenHost(who
) && !IsOper(from
)) {
608 /* Just stay quiet. */
611 irc_numeric(from
, RPL_WHOISUSER
, "%s %s %s * :%s", who
->nick
, who
->ident
, who
->hostname
, who
->info
);
612 #ifdef WITH_PROTOCOL_P10
613 if (his_servername
&& his_servercomment
)
614 irc_numeric(from
, RPL_WHOISSERVER
, "%s %s :%s", who
->nick
, his_servername
, his_servercomment
);
617 irc_numeric(from
, RPL_WHOISSERVER
, "%s %s :%s", who
->nick
, who
->uplink
->name
, who
->uplink
->description
);
620 irc_numeric(from
, RPL_WHOISOPERATOR
, "%s :is a megalomaniacal power hungry tyrant", who
->nick
);
622 irc_numeric(from
, RPL_ENDOFWHOIS
, "%s :End of /WHOIS list", who
->nick
);
626 static CMD_FUNC(cmd_capab
) {
627 static const struct {
631 { "TS3", CAPAB_TS3
},
632 { "NOQUIT", CAPAB_NOQUIT
},
633 { "SSJOIN", CAPAB_SSJOIN
},
634 { "BURST", CAPAB_BURST
},
635 { "UNCONNECT", CAPAB_UNCONNECT
},
636 { "NICKIP", CAPAB_NICKIP
},
637 { "TSMODE", CAPAB_TSMODE
},
638 { "ZIP", CAPAB_ZIP
},
644 for(nn
=1; nn
<argc
; nn
++) {
645 for (mm
=0; capabs
[mm
].name
&& irccasecmp(capabs
[mm
].name
, argv
[nn
]); mm
++) ;
646 if (capabs
[mm
].name
) {
647 uplink_capab
|= capabs
[mm
].mask
;
649 log_module(MAIN_LOG
, LOG_INFO
, "Saw unrecognized/unhandled capability %s. Please notify srvx developers so they can add it.", argv
[nn
]);
655 static void burst_channel(struct chanNode
*chan
) {
657 int pos
, base_len
, len
, queued
;
660 if (!chan
->members
.used
) return;
661 /* send list of users in the channel.. */
662 base_len
= sprintf(line
, ":%s SJOIN "FMT_TIME_T
" %s ", self
->name
, chan
->timestamp
, chan
->name
);
663 len
= irc_make_chanmode(chan
, line
+base_len
);
664 pos
= base_len
+ len
;
667 for (nn
=0; nn
<chan
->members
.used
; nn
++) {
668 struct modeNode
*mn
= chan
->members
.list
[nn
];
669 len
= strlen(mn
->user
->nick
);
670 if (pos
+ len
> 500) {
678 if (mn
->modes
& MODE_CHANOP
) line
[pos
++] = '@';
679 if (mn
->modes
& MODE_VOICE
) line
[pos
++] = '+';
680 memcpy(line
+pos
, mn
->user
->nick
, len
);
684 /* print the last line */
687 /* now send the bans.. */
688 base_len
= sprintf(line
, ":%s MODE "FMT_TIME_T
" %s +", self
->name
, chan
->timestamp
, chan
->name
);
689 pos
= sizeof(line
)-1;
691 for (nn
=queued
=0; nn
<chan
->banlist
.used
; nn
++) {
692 struct banNode
*bn
= chan
->banlist
.list
[nn
];
693 len
= strlen(bn
->ban
);
694 if (pos
< base_len
+queued
+len
+4) {
699 putsock("%s%s", line
, line
+pos
);
700 pos
= sizeof(line
)-1;
703 memcpy(line
+pos
, bn
->ban
, len
);
712 putsock("%s%s", line
, line
+pos
);
716 static void send_burst() {
718 for (it
= dict_first(servers
); it
; it
= iter_next(it
)) {
719 struct server
*serv
= iter_data(it
);
720 if ((serv
!= self
) && (serv
!= self
->uplink
)) irc_server(serv
);
723 for (it
= dict_first(clients
); it
; it
= iter_next(it
)) {
724 irc_user(iter_data(it
));
726 for (it
= dict_first(channels
); it
; it
= iter_next(it
)) {
727 burst_channel(iter_data(it
));
729 /* Uplink will do AWAY and TOPIC bursts before sending BURST 0, but we don't */
733 static CMD_FUNC(cmd_server
) {
734 if (argc
< 4) return 0;
736 AddServer(GetServerH(origin
), argv
[1], atoi(argv
[2]), 0, now
, 0, argv
[3]);
738 self
->uplink
= AddServer(self
, argv
[1], atoi(argv
[2]), 0, now
, 0, argv
[3]);
743 static CMD_FUNC(cmd_svinfo
) {
744 if (argc
< 5) return 0;
745 if ((atoi(argv
[1]) < 3) || (atoi(argv
[2]) > 3)) return 0;
746 /* TODO: something with the timestamp we get from the other guy */
751 static CMD_FUNC(cmd_ping
)
753 irc_pong(self
->name
, argc
> 1 ? argv
[1] : origin
);
754 timeq_del(0, timed_send_ping
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
755 timeq_del(0, timed_ping_timeout
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
756 timeq_add(now
+ ping_freq
, timed_send_ping
, 0);
761 static CMD_FUNC(cmd_burst
) {
762 struct server
*sender
= GetServerH(origin
);
763 if (!sender
) return 0;
764 if (argc
== 1) return 1;
765 if (sender
== self
->uplink
) {
766 cManager
.uplink
->state
= CONNECTED
;
768 sender
->self_burst
= 0;
769 recalc_bursts(sender
);
773 static CMD_FUNC(cmd_nick
) {
775 if ((un
= GetUserH(origin
))) {
777 if (argc
< 2) return 0;
778 NickChange(un
, argv
[1], 1);
780 /* new nick from a server */
785 if (argc
< 10) return 0;
786 stamp
= strtoul(argv
[8], NULL
, 0);
787 if (stamp
) inttobase64(id
, stamp
, IDLEN
);
788 ip
.s_addr
= (argc
> 10) ? atoi(argv
[9]) : 0;
789 un
= AddUser(GetServerH(argv
[7]), argv
[1], argv
[5], argv
[6], argv
[4], argv
[argc
-1], atoi(argv
[3]), ip
, (stamp
? id
: 0));
794 static CMD_FUNC(cmd_sjoin
) {
795 struct chanNode
*cNode
;
796 struct userNode
*uNode
;
797 struct modeNode
*mNode
;
798 unsigned int next
= 4, last
;
799 char *nick
, *nickend
;
801 if ((argc
== 3) && (uNode
= GetUserH(origin
))) {
803 if (!(cNode
= GetChannel(argv
[2]))) {
804 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find SJOIN target %s", argv
[2]);
807 AddChannelUser(uNode
, cNode
);
810 if (argc
< 5) return 0;
811 if (argv
[3][0] == '+') {
812 char modes
[MAXLEN
], *pos
;
814 for (pos
= argv
[3], n_modes
= 1; *pos
; pos
++) {
815 if ((*pos
== 'k') || (*pos
== 'l')) n_modes
++;
817 unsplit_string(argv
+3, n_modes
, modes
);
818 cNode
= AddChannel(argv
[2], atoi(argv
[1]), modes
, NULL
);
819 } else if (argv
[3][0] == '0') {
820 cNode
= GetChannel(argv
[2]);
822 log_module(MAIN_LOG
, LOG_ERROR
, "Unsure how to handle SJOIN when arg 3 is %s", argv
[3]);
826 /* argv[next] is now the space-delimited, @+-prefixed list of
827 * nicks in the channel. Split it and add the users. */
828 for (last
= 0, nick
= argv
[next
]; !last
; nick
= nickend
+ 1) {
830 for (nickend
= nick
; *nickend
&& (*nickend
!= ' '); nickend
++) ;
831 if (!*nickend
) last
= 1;
833 if (*nick
== '@') { mode
|= MODE_CHANOP
; nick
++; }
834 if (*nick
== '+') { mode
|= MODE_VOICE
; nick
++; }
835 if ((uNode
= GetUserH(nick
)) && (mNode
= AddChannelUser(uNode
, cNode
))) {
842 static CMD_FUNC(cmd_mode
) {
846 log_module(MAIN_LOG
, LOG_ERROR
, "Illegal MODE from %s (no arguments).", origin
);
848 } else if (IsChannelName(argv
[1])) {
852 if (!(cn
= GetChannel(argv
[1]))) {
853 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find channel %s whose mode is changing", argv
[1]);
857 if ((un
= GetUserH(origin
))) {
858 /* Update idle time for the user */
859 if ((mn
= GetUserMode(cn
, un
)))
860 mn
->idle_since
= now
;
862 /* Must be a server in burst or something. Make sure we're using the right timestamp. */
863 cn
->timestamp
= atoi(argv
[2]);
866 return mod_chanmode(un
, cn
, argv
+3, argc
-3, MCP_ALLOW_OVB
|MCP_FROM_SERVER
|MC_ANNOUNCE
);
867 } else if ((un
= GetUserH(argv
[1]))) {
868 mod_usermode(un
, argv
[2]);
871 log_module(MAIN_LOG
, LOG_ERROR
, "Not sure what MODE %s is affecting (not a channel name and no such user)", argv
[1]);
876 static CMD_FUNC(cmd_topic
) {
878 if (argc
< 5) return 0;
879 if (!(cn
= GetChannel(argv
[1]))) {
880 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find channel %s whose topic is being set", argv
[1]);
883 if (irccasecmp(origin
, argv
[2])) {
884 /* coming from a topic burst; the origin is a server */
885 safestrncpy(cn
->topic
, argv
[4], sizeof(cn
->topic
));
886 safestrncpy(cn
->topic_nick
, argv
[2], sizeof(cn
->topic_nick
));
887 cn
->topic_time
= atoi(argv
[3]);
889 SetChannelTopic(cn
, GetUserH(argv
[2]), argv
[4], 0);
894 static CMD_FUNC(cmd_part
) {
895 if (argc
< 2) return 0;
896 parse_foreach(argv
[1], part_helper
, NULL
, NULL
, NULL
, GetUserH(origin
));
900 static CMD_FUNC(cmd_away
) {
903 if (!(un
= GetUserH(origin
))) {
904 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find user %s sending AWAY", origin
);
908 un
->modes
|= FLAGS_AWAY
;
910 un
->modes
&= ~FLAGS_AWAY
;
915 static CMD_FUNC(cmd_kick
) {
916 if (argc
< 3) return 0;
917 ChannelUserKicked(GetUserH(origin
), GetUserH(argv
[2]), GetChannel(argv
[1]));
921 static CMD_FUNC(cmd_kill
) {
922 struct userNode
*user
;
923 if (argc
< 3) return 0;
924 if (!(user
= GetUserH(argv
[1]))) {
925 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find kill victim %s", argv
[1]);
928 if (IsLocal(user
) && IsService(user
)) {
929 ReintroduceUser(user
);
931 DelUser(user
, GetUserH(origin
), false, ((argc
>= 3) ? argv
[2] : NULL
));
936 static CMD_FUNC(cmd_pong
)
938 if (argc
< 3) return 0;
939 if (!strcmp(argv
[2], self
->name
)) {
940 timeq_del(0, timed_send_ping
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
941 timeq_del(0, timed_ping_timeout
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
942 timeq_add(now
+ ping_freq
, timed_send_ping
, 0);
948 static CMD_FUNC(cmd_num_topic
)
950 static struct chanNode
*cn
;
955 cn
= GetChannel(argv
[2]);
957 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find channel %s in topic reply", argv
[2]);
963 switch (atoi(argv
[0])) {
966 break; /* no topic */
970 safestrncpy(cn
->topic
, unsplit_string(argv
+3, argc
-3, NULL
), sizeof(cn
->topic
));
975 safestrncpy(cn
->topic_nick
, argv
[3], sizeof(cn
->topic_nick
));
976 cn
->topic_time
= atoi(argv
[4]);
979 return 0; /* should never happen */
984 static CMD_FUNC(cmd_quit
)
986 struct userNode
*user
;
987 if (argc
< 2) return 0;
988 /* Sometimes we get a KILL then a QUIT or the like, so we don't want to
989 * call DelUser unless we have the user in our grasp. */
990 if ((user
= GetUserH(origin
))) {
991 DelUser(user
, NULL
, false, argv
[1]);
996 static CMD_FUNC(cmd_squit
)
998 struct server
*server
;
1001 if (!(server
= GetServerH(argv
[1])))
1003 if (server
== self
->uplink
) {
1004 /* Force a reconnect to the currently selected server. */
1005 cManager
.uplink
->tries
= 0;
1006 log_module(MAIN_LOG
, LOG_INFO
, "Squitting from uplink: %s", argv
[3]);
1011 DelServer(server
, 0, argv
[3]);
1015 static CMD_FUNC(cmd_svsnick
)
1017 struct userNode
*target
, *dest
;
1018 if (argc
< 4) return 0;
1019 if (!(target
= GetUserH(argv
[1]))) return 0;
1020 if (!IsLocal(target
)) return 0;
1021 if ((dest
= GetUserH(argv
[2]))) return 0; /* Note: Bahamut will /KILL instead. */
1022 NickChange(target
, argv
[2], 0);
1026 static oper_func_t
*of_list
;
1027 static unsigned int of_size
= 0, of_used
= 0;
1029 void parse_cleanup(void) {
1031 if (of_list
) free(of_list
);
1032 dict_delete(irc_func_dict
);
1033 dict_delete(service_msginfo_dict
);
1035 for (nn
=0; nn
<dead_users
.used
; nn
++) free_user(dead_users
.list
[nn
]);
1036 userList_clean(&dead_users
);
1039 void init_parse(void) {
1040 const char *str
, *desc
;
1042 str
= conf_get_data("server/ping_freq", RECDB_QSTRING
);
1043 ping_freq
= str
? ParseInterval(str
) : 120;
1044 str
= conf_get_data("server/ping_timeout", RECDB_QSTRING
);
1045 ping_timeout
= str
? ParseInterval(str
) : 30;
1046 str
= conf_get_data("server/hostname", RECDB_QSTRING
);
1047 desc
= conf_get_data("server/description", RECDB_QSTRING
);
1048 if (!str
|| !desc
) {
1049 log_module(MAIN_LOG
, LOG_ERROR
, "No server/hostname entry in config file.");
1052 self
= AddServer(NULL
, str
, 0, boot_time
, now
, NULL
, desc
);
1054 str
= conf_get_data("server/ping_freq", RECDB_QSTRING
);
1055 ping_freq
= str
? ParseInterval(str
) : 120;
1056 str
= conf_get_data("server/ping_timeout", RECDB_QSTRING
);
1057 ping_timeout
= str
? ParseInterval(str
) : 30;
1059 service_msginfo_dict
= dict_new();
1060 dict_set_free_data(service_msginfo_dict
, free
);
1061 irc_func_dict
= dict_new();
1062 dict_insert(irc_func_dict
, "ADMIN", cmd_admin
);
1063 dict_insert(irc_func_dict
, "AWAY", cmd_away
);
1064 dict_insert(irc_func_dict
, "BURST", cmd_burst
);
1065 dict_insert(irc_func_dict
, "CAPAB", cmd_capab
);
1066 dict_insert(irc_func_dict
, "ERROR", cmd_error
);
1067 dict_insert(irc_func_dict
, "GNOTICE", cmd_dummy
);
1068 dict_insert(irc_func_dict
, "INVITE", cmd_dummy
);
1069 dict_insert(irc_func_dict
, "KICK", cmd_kick
);
1070 dict_insert(irc_func_dict
, "KILL", cmd_kill
);
1071 dict_insert(irc_func_dict
, "LUSERSLOCK", cmd_dummy
);
1072 dict_insert(irc_func_dict
, "MODE", cmd_mode
);
1073 dict_insert(irc_func_dict
, "NICK", cmd_nick
);
1074 dict_insert(irc_func_dict
, "NOTICE", cmd_notice
);
1075 dict_insert(irc_func_dict
, "PART", cmd_part
);
1076 dict_insert(irc_func_dict
, "PASS", cmd_pass
);
1077 dict_insert(irc_func_dict
, "PING", cmd_ping
);
1078 dict_insert(irc_func_dict
, "PONG", cmd_pong
);
1079 dict_insert(irc_func_dict
, "PRIVMSG", cmd_privmsg
);
1080 dict_insert(irc_func_dict
, "QUIT", cmd_quit
);
1081 dict_insert(irc_func_dict
, "SERVER", cmd_server
);
1082 dict_insert(irc_func_dict
, "SJOIN", cmd_sjoin
);
1083 dict_insert(irc_func_dict
, "SQUIT", cmd_squit
);
1084 dict_insert(irc_func_dict
, "STATS", cmd_stats
);
1085 dict_insert(irc_func_dict
, "SVSNICK", cmd_svsnick
);
1086 dict_insert(irc_func_dict
, "SVINFO", cmd_svinfo
);
1087 dict_insert(irc_func_dict
, "TOPIC", cmd_topic
);
1088 dict_insert(irc_func_dict
, "VERSION", cmd_version
);
1089 dict_insert(irc_func_dict
, "WHOIS", cmd_whois
);
1090 dict_insert(irc_func_dict
, "331", cmd_num_topic
);
1091 dict_insert(irc_func_dict
, "332", cmd_num_topic
);
1092 dict_insert(irc_func_dict
, "333", cmd_num_topic
);
1093 dict_insert(irc_func_dict
, "413", cmd_num_topic
);
1095 userList_init(&dead_users
);
1096 reg_exit_func(parse_cleanup
);
1099 int parse_line(char *line
, int recursive
) {
1100 char *argv
[MAXNUMPARAMS
];
1104 argc
= split_line(line
, true, ArrayLength(argv
), argv
);
1105 cmd
= line
[0] == ':';
1106 if ((argc
> cmd
) && (func
= dict_find(irc_func_dict
, argv
[cmd
], NULL
))) {
1109 origin
= argv
[0] + 1;
1110 } else if (self
->uplink
) {
1111 origin
= self
->uplink
->name
;
1115 res
= func(origin
, argc
-cmd
, argv
+cmd
);
1120 log_module(MAIN_LOG
, LOG_ERROR
, "PARSE ERROR on line: %s", unsplit_string(argv
, argc
, NULL
));
1121 } else if (!recursive
) {
1123 for (i
=0; i
<dead_users
.used
; i
++) {
1124 free_user(dead_users
.list
[i
]);
1126 dead_users
.used
= 0;
1132 privmsg_user_helper(struct userNode
*un
, void *data
)
1134 struct privmsg_desc
*pd
= data
;
1135 struct service_message_info
*info
= dict_find(service_msginfo_dict
, un
->nick
, 0);
1137 if (pd
->is_notice
) {
1138 if (info
->on_notice
) info
->on_notice(pd
->user
, un
, pd
->text
, pd
->is_qualified
);
1140 if (info
->on_privmsg
) info
->on_privmsg(pd
->user
, un
, pd
->text
, pd
->is_qualified
);
1146 reg_privmsg_func(struct userNode
*user
, privmsg_func_t handler
) {
1147 struct service_message_info
*info
= dict_find(service_msginfo_dict
, user
->nick
, NULL
);
1149 info
= calloc(1, sizeof(*info
));
1150 dict_insert(service_msginfo_dict
, user
->nick
, info
);
1152 info
->on_privmsg
= handler
;
1156 reg_notice_func(struct userNode
*user
, privmsg_func_t handler
) {
1157 struct service_message_info
*info
= dict_find(service_msginfo_dict
, user
->nick
, NULL
);
1159 info
= calloc(1, sizeof(*info
));
1160 dict_insert(service_msginfo_dict
, user
->nick
, info
);
1162 info
->on_notice
= handler
;
1166 reg_oper_func(oper_func_t handler
)
1168 if (of_used
== of_size
) {
1171 of_list
= realloc(of_list
, of_size
*sizeof(oper_func_t
));
1174 of_list
= malloc(of_size
*sizeof(oper_func_t
));
1177 of_list
[of_used
++] = handler
;
1181 call_oper_funcs(struct userNode
*user
)
1184 if (IsLocal(user
)) return;
1185 for (n
=0; n
<of_used
; n
++)
1191 void mod_usermode(struct userNode
*user
, const char *mode_change
) {
1194 if (!user
|| !mode_change
) return;
1196 #define do_user_mode(FLAG) do { if (add) user->modes |= FLAG; else user->modes &= ~FLAG; } while (0)
1197 switch (*mode_change
++) {
1199 case '+': add
= 1; break;
1200 case '-': add
= 0; break;
1202 do_user_mode(FLAGS_OPER
);
1204 userList_append(&curr_opers
, user
);
1205 call_oper_funcs(user
);
1207 userList_remove(&curr_opers
, user
);
1210 case 'O': do_user_mode(FLAGS_LOCOP
); break;
1211 case 'i': do_user_mode(FLAGS_INVISIBLE
);
1212 if (add
) invis_clients
++; else invis_clients
--;
1214 case 'w': do_user_mode(FLAGS_WALLOP
); break;
1215 case 's': do_user_mode(FLAGS_SERVNOTICE
); break;
1216 case 'd': do_user_mode(FLAGS_DEAF
); break;
1217 case 'r': do_user_mode(FLAGS_REGNICK
); break;
1218 case 'k': do_user_mode(FLAGS_SERVICE
); break;
1219 case 'g': do_user_mode(FLAGS_GLOBAL
); break;
1220 case 'h': do_user_mode(FLAGS_HELPER
); break;
1226 struct mod_chanmode
*
1227 mod_chanmode_parse(struct chanNode
*channel
, char **modes
, unsigned int argc
, unsigned int flags
)
1229 struct mod_chanmode
*change
;
1230 unsigned int ii
, in_arg
, ch_arg
, add
;
1234 if (!(change
= mod_chanmode_alloc(argc
)))
1237 for (ii
= ch_arg
= 0, in_arg
= add
= 1;
1238 (modes
[0][ii
] != '\0') && (modes
[0][ii
] != ' ');
1240 switch (modes
[0][ii
]) {
1247 #define do_chan_mode(FLAG) do { if (add) change->modes_set |= FLAG, change->modes_clear &= ~FLAG; else change->modes_clear |= FLAG, change->modes_set &= ~FLAG; } while(0)
1248 case 'R': do_chan_mode(MODE_REGONLY
); break;
1249 case 'r': do_chan_mode(MODE_REGISTERED
); break;
1250 case 'D': do_chan_mode(MODE_DELAYJOINS
); break;
1251 case 'c': do_chan_mode(MODE_NOCOLORS
); break;
1252 case 'i': do_chan_mode(MODE_INVITEONLY
); break;
1253 case 'm': do_chan_mode(MODE_MODERATED
); break;
1254 case 'n': do_chan_mode(MODE_NOPRIVMSGS
); break;
1255 case 'p': do_chan_mode(MODE_PRIVATE
); break;
1256 case 's': do_chan_mode(MODE_SECRET
); break;
1257 case 't': do_chan_mode(MODE_TOPICLIMIT
); break;
1263 change
->modes_set
|= MODE_LIMIT
;
1264 change
->new_limit
= atoi(modes
[in_arg
++]);
1266 change
->modes_clear
|= MODE_LIMIT
;
1273 change
->modes_set
|= MODE_KEY
;
1274 safestrncpy(change
->new_key
, modes
[in_arg
++], sizeof(change
->new_key
));
1276 change
->modes_clear
|= MODE_KEY
;
1277 if (!(flags
& MCP_KEY_FREE
)) {
1285 if (!(flags
& MCP_ALLOW_OVB
))
1289 change
->args
[ch_arg
].mode
= MODE_BAN
;
1291 change
->args
[ch_arg
].mode
|= MODE_REMOVE
;
1292 change
->args
[ch_arg
++].hostmask
= modes
[in_arg
++];
1296 struct userNode
*victim
;
1297 if (!(flags
& MCP_ALLOW_OVB
))
1301 change
->args
[ch_arg
].mode
= (modes
[0][ii
] == 'o') ? MODE_CHANOP
: MODE_VOICE
;
1303 change
->args
[ch_arg
].mode
|= MODE_REMOVE
;
1304 victim
= GetUserH(modes
[in_arg
++]);
1307 if ((change
->args
[ch_arg
].member
= GetUserMode(channel
, victim
)))
1312 if (!(flags
& MCP_FROM_SERVER
))
1317 change
->argc
= ch_arg
; /* in case any turned out to be ignored */
1318 if (change
->modes_set
& MODE_SECRET
) {
1319 change
->modes_set
&= ~(MODE_PRIVATE
);
1320 change
->modes_clear
|= MODE_PRIVATE
;
1321 } else if (change
->modes_set
& MODE_PRIVATE
) {
1322 change
->modes_set
&= ~(MODE_SECRET
);
1323 change
->modes_clear
|= MODE_SECRET
;
1327 mod_chanmode_free(change
);
1331 struct chanmode_buffer
{
1334 struct chanNode
*channel
;
1335 struct userNode
*actor
;
1336 unsigned int modes_used
;
1337 unsigned int args_used
;
1339 unsigned int is_add
: 1;
1343 mod_chanmode_append(struct chanmode_buffer
*buf
, char ch
, const char *arg
)
1345 size_t arg_len
= strlen(arg
);
1346 if (buf
->modes_used
+ buf
->args_used
+ buf
->chname_len
+ arg_len
> 450) {
1347 memcpy(buf
->modes
+ buf
->modes_used
, buf
->args
, buf
->args_used
);
1348 buf
->modes
[buf
->modes_used
+ buf
->args_used
] = '\0';
1349 irc_mode(buf
->actor
, buf
->channel
, buf
->modes
);
1350 buf
->modes
[0] = buf
->is_add
? '+' : '-';
1351 buf
->modes_used
= 1;
1354 buf
->modes
[buf
->modes_used
++] = ch
;
1355 buf
->args
[buf
->args_used
++] = ' ';
1356 memcpy(buf
->args
+ buf
->args_used
, arg
, arg_len
);
1357 buf
->args_used
+= arg_len
;
1361 mod_chanmode_announce(struct userNode
*who
, struct chanNode
*channel
, struct mod_chanmode
*change
)
1363 struct chanmode_buffer chbuf
;
1367 assert(change
->argc
<= change
->alloc_argc
);
1368 memset(&chbuf
, 0, sizeof(chbuf
));
1369 chbuf
.channel
= channel
;
1371 chbuf
.chname_len
= strlen(channel
->name
);
1373 /* First remove modes */
1375 if (change
->modes_clear
) {
1376 chbuf
.modes
[chbuf
.modes_used
++] = '-';
1377 #define DO_MODE_CHAR(BIT, CHAR) if (change->modes_clear & MODE_##BIT) chbuf.modes[chbuf.modes_used++] = CHAR;
1378 DO_MODE_CHAR(PRIVATE
, 'p');
1379 DO_MODE_CHAR(SECRET
, 's');
1380 DO_MODE_CHAR(MODERATED
, 'm');
1381 DO_MODE_CHAR(TOPICLIMIT
, 't');
1382 DO_MODE_CHAR(INVITEONLY
, 'i');
1383 DO_MODE_CHAR(NOPRIVMSGS
, 'n');
1384 DO_MODE_CHAR(LIMIT
, 'l');
1385 DO_MODE_CHAR(DELAYJOINS
, 'D');
1386 DO_MODE_CHAR(REGONLY
, 'R');
1387 DO_MODE_CHAR(NOCOLORS
, 'c');
1388 DO_MODE_CHAR(REGISTERED
, 'r');
1390 if (change
->modes_clear
& channel
->modes
& MODE_KEY
)
1391 mod_chanmode_append(&chbuf
, 'k', channel
->key
);
1393 for (arg
= 0; arg
< change
->argc
; ++arg
) {
1394 if (!(change
->args
[arg
].mode
& MODE_REMOVE
))
1396 switch (change
->args
[arg
].mode
& ~MODE_REMOVE
) {
1398 mod_chanmode_append(&chbuf
, 'b', change
->args
[arg
].hostmask
);
1401 if (change
->args
[arg
].mode
& MODE_CHANOP
)
1402 mod_chanmode_append(&chbuf
, 'o', change
->args
[arg
].member
->user
->nick
);
1403 if (change
->args
[arg
].mode
& MODE_VOICE
)
1404 mod_chanmode_append(&chbuf
, 'v', change
->args
[arg
].member
->user
->nick
);
1411 if (change
->modes_set
) {
1412 chbuf
.modes
[chbuf
.modes_used
++] = '+';
1413 #define DO_MODE_CHAR(BIT, CHAR) if (change->modes_set & MODE_##BIT) chbuf.modes[chbuf.modes_used++] = CHAR;
1414 DO_MODE_CHAR(PRIVATE
, 'p');
1415 DO_MODE_CHAR(SECRET
, 's');
1416 DO_MODE_CHAR(MODERATED
, 'm');
1417 DO_MODE_CHAR(TOPICLIMIT
, 't');
1418 DO_MODE_CHAR(INVITEONLY
, 'i');
1419 DO_MODE_CHAR(NOPRIVMSGS
, 'n');
1420 DO_MODE_CHAR(DELAYJOINS
, 'D');
1421 DO_MODE_CHAR(REGONLY
, 'R');
1422 DO_MODE_CHAR(NOCOLORS
, 'c');
1423 DO_MODE_CHAR(REGISTERED
, 'r');
1425 if (change
->modes_set
& MODE_KEY
)
1426 mod_chanmode_append(&chbuf
, 'k', change
->new_key
);
1427 if (change
->modes_set
& MODE_LIMIT
)
1429 sprintf(int_buff
, "%d", change
->new_limit
);
1430 mod_chanmode_append(&chbuf
, 'l', int_buff
);
1433 for (arg
= 0; arg
< change
->argc
; ++arg
) {
1434 if (change
->args
[arg
].mode
& MODE_REMOVE
)
1436 switch (change
->args
[arg
].mode
) {
1438 mod_chanmode_append(&chbuf
, 'b', change
->args
[arg
].hostmask
);
1441 if (change
->args
[arg
].mode
& MODE_CHANOP
)
1442 mod_chanmode_append(&chbuf
, 'o', change
->args
[arg
].member
->user
->nick
);
1443 if (change
->args
[arg
].mode
& MODE_VOICE
)
1444 mod_chanmode_append(&chbuf
, 'v', change
->args
[arg
].member
->user
->nick
);
1449 /* Flush the buffer and apply changes locally */
1450 if (chbuf
.modes_used
> 0) {
1451 memcpy(chbuf
.modes
+ chbuf
.modes_used
, chbuf
.args
, chbuf
.args_used
);
1452 chbuf
.modes
[chbuf
.modes_used
+ chbuf
.args_used
] = '\0';
1453 irc_mode(chbuf
.actor
, chbuf
.channel
, chbuf
.modes
);
1455 mod_chanmode_apply(who
, channel
, change
);
1459 mod_chanmode_format(struct mod_chanmode
*change
, char *outbuff
)
1461 unsigned int used
= 0;
1462 assert(change
->argc
<= change
->alloc_argc
);
1463 if (change
->modes_clear
) {
1464 outbuff
[used
++] = '-';
1465 #define DO_MODE_CHAR(BIT, CHAR) if (change->modes_clear & MODE_##BIT) outbuff[used++] = CHAR
1466 DO_MODE_CHAR(PRIVATE
, 'p');
1467 DO_MODE_CHAR(SECRET
, 's');
1468 DO_MODE_CHAR(MODERATED
, 'm');
1469 DO_MODE_CHAR(TOPICLIMIT
, 't');
1470 DO_MODE_CHAR(INVITEONLY
, 'i');
1471 DO_MODE_CHAR(NOPRIVMSGS
, 'n');
1472 DO_MODE_CHAR(LIMIT
, 'l');
1473 DO_MODE_CHAR(KEY
, 'k');
1474 DO_MODE_CHAR(DELAYJOINS
, 'D');
1475 DO_MODE_CHAR(REGONLY
, 'R');
1476 DO_MODE_CHAR(NOCOLORS
, 'c');
1477 DO_MODE_CHAR(REGISTERED
, 'r');
1480 if (change
->modes_set
) {
1481 outbuff
[used
++] = '+';
1482 #define DO_MODE_CHAR(BIT, CHAR) if (change->modes_set & MODE_##BIT) outbuff[used++] = CHAR
1483 DO_MODE_CHAR(PRIVATE
, 'p');
1484 DO_MODE_CHAR(SECRET
, 's');
1485 DO_MODE_CHAR(MODERATED
, 'm');
1486 DO_MODE_CHAR(TOPICLIMIT
, 't');
1487 DO_MODE_CHAR(INVITEONLY
, 'i');
1488 DO_MODE_CHAR(NOPRIVMSGS
, 'n');
1489 DO_MODE_CHAR(DELAYJOINS
, 'D');
1490 DO_MODE_CHAR(REGONLY
, 'R');
1491 DO_MODE_CHAR(NOCOLORS
, 'c');
1492 DO_MODE_CHAR(REGISTERED
, 'r');
1494 switch (change
->modes_set
& (MODE_KEY
|MODE_LIMIT
)) {
1495 case MODE_KEY
|MODE_LIMIT
:
1496 used
+= sprintf(outbuff
+used
, "lk %d %s", change
->new_limit
, change
->new_key
);
1499 used
+= sprintf(outbuff
+used
, "k %s", change
->new_key
);
1502 used
+= sprintf(outbuff
+used
, "l %d", change
->new_limit
);