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 *modes
, 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
, modes
? modes
: "+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
, time_t timestamp
)
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
);
405 irc_umode(struct userNode
*target
, const char *modes
)
407 putsock(":%s MODE %s %s ",self
->numeric
,target
->nick
, modes
);
412 irc_svsmode(struct userNode
*target
, char *modes
, unsigned long stamp
) {
413 extern struct userNode
*nickserv
;
415 putsock(":%s SVSMODE %s "FMT_TIME_T
" %s %lu", nickserv
->nick
, target
->nick
, target
->timestamp
, modes
, stamp
);
417 putsock(":%s SVSMODE %s "FMT_TIME_T
" %s", nickserv
->nick
, target
->nick
, target
->timestamp
, modes
);
422 irc_kick(struct userNode
*who
, struct userNode
*target
, struct chanNode
*from
, const char *msg
) {
423 putsock(":%s KICK %s %s :%s", who
->nick
, from
->name
, target
->nick
, msg
);
424 ChannelUserKicked(who
, target
, from
);
428 irc_part(struct userNode
*who
, struct chanNode
*what
, const char *reason
) {
430 putsock(":%s PART %s :%s", who
->nick
, what
->name
, reason
);
432 putsock(":%s PART %s", who
->nick
, what
->name
);
437 irc_topic(struct userNode
*who
, struct chanNode
*what
, const char *topic
) {
438 putsock(":%s TOPIC %s :%s", who
->nick
, what
->name
, topic
);
442 irc_fetchtopic(struct userNode
*from
, const char *to
) {
443 if (!from
|| !to
) return;
444 putsock(":%s TOPIC %s", from
->nick
, to
);
448 irc_gline(struct server
*srv
, struct gline
*gline
) {
449 char host
[HOSTLEN
+1], ident
[USERLEN
+1], *sep
;
452 log_module(MAIN_LOG
, LOG_WARNING
, "%s tried to send a targeted G-line for %s (not supported by protocol!)", gline
->issuer
, gline
->target
);
455 if (!(sep
= strchr(gline
->target
, '@'))) {
456 log_module(MAIN_LOG
, LOG_ERROR
, "%s tried to add G-line with bad mask %s", gline
->issuer
, gline
->target
);
459 len
= sep
- gline
->target
+ 1;
460 if (len
> ArrayLength(ident
)) len
= ArrayLength(ident
);
461 safestrncpy(ident
, gline
->target
, len
);
462 safestrncpy(host
, sep
+1, ArrayLength(host
));
463 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
);
467 irc_settime(UNUSED_ARG(const char *srv_name_mask
), UNUSED_ARG(time_t new_time
))
469 /* Bahamut has nothing like this, so ignore it. */
473 irc_ungline(const char *mask
) {
474 char host
[HOSTLEN
+1], ident
[USERLEN
+1], *sep
;
476 if (!(sep
= strchr(mask
, '@'))) {
477 log_module(MAIN_LOG
, LOG_ERROR
, "Tried to remove G-line with bad mask %s", mask
);
480 len
= sep
- mask
+ 1;
481 if (len
> ArrayLength(ident
)) len
= ArrayLength(ident
);
482 safestrncpy(ident
, mask
, len
);
483 safestrncpy(host
, sep
+1, ArrayLength(host
));
484 putsock(":%s RAKILL %s %s", self
->name
, host
, ident
);
488 irc_error(const char *to
, const char *message
) {
490 putsock("%s ERROR :%s", to
, message
);
492 putsock(":%s ERROR :%s", self
->name
, message
);
497 irc_kill(struct userNode
*from
, struct userNode
*target
, const char *message
) {
499 putsock(":%s KILL %s :%s!%s (%s)", from
->nick
, target
->nick
, self
->name
, from
->nick
, message
);
501 putsock(":%s KILL %s :%s (%s)", self
->name
, target
->nick
, self
->name
, message
);
506 irc_raw(const char *what
) {
511 irc_stats(struct userNode
*from
, struct server
*target
, char type
) {
512 putsock(":%s STATS %c :%s", from
->nick
, type
, target
->name
);
516 irc_svsnick(struct userNode
*from
, struct userNode
*target
, const char *newnick
)
518 putsock(":%s SVSNICK %s %s :"FMT_TIME_T
, from
->nick
, target
->nick
, newnick
, now
);
522 irc_numeric(struct userNode
*user
, unsigned int num
, const char *format
, ...) {
525 va_start(arg_list
, format
);
526 vsnprintf(buffer
, MAXLEN
-2, format
, arg_list
);
527 buffer
[MAXLEN
-1] = 0;
528 putsock(":%s %03d %s %s", self
->name
, num
, user
->nick
, buffer
);
532 parse_foreach(char *target_list
, foreach_chanfunc cf
, foreach_nonchan nc
, foreach_userfunc uf
, foreach_nonuser nu
, void *data
) {
536 while (*j
!= 0 && *j
!= ',') j
++;
539 if (IsChannelName(target_list
)) {
540 struct chanNode
*chan
= GetChannel(target_list
);
542 if (cf
) cf(chan
, data
);
544 if (nc
) nc(target_list
, data
);
547 struct userNode
*user
;
548 struct privmsg_desc
*pd
= data
;
550 pd
->is_qualified
= 0;
551 if (*target_list
== '@') {
553 } else if (strchr(target_list
, '@')) {
554 struct server
*server
;
556 pd
->is_qualified
= 1;
557 user
= GetUserH(strtok(target_list
, "@"));
558 server
= GetServerH(strtok(NULL
, "@"));
560 if (user
&& (user
->uplink
!= server
)) {
561 /* Don't attempt to index into any arrays
562 using a user's numeric on another server. */
566 user
= GetUserH(target_list
);
570 if (uf
) uf(user
, data
);
572 if (nu
) nu(target_list
, data
);
576 } while (old
== ',');
579 static CMD_FUNC(cmd_notice
) {
580 struct privmsg_desc pd
;
581 if ((argc
< 3) || !origin
) return 0;
582 if (!(pd
.user
= GetUserH(origin
))) return 1;
583 if (IsGagged(pd
.user
) && !IsOper(pd
.user
)) return 1;
586 parse_foreach(argv
[1], privmsg_chan_helper
, NULL
, privmsg_user_helper
, privmsg_invalid
, &pd
);
590 static CMD_FUNC(cmd_privmsg
) {
591 struct privmsg_desc pd
;
592 if ((argc
< 3) || !origin
) return 0;
593 if (!(pd
.user
= GetUserH(origin
))) return 1;
594 if (IsGagged(pd
.user
) && !IsOper(pd
.user
)) return 1;
597 parse_foreach(argv
[1], privmsg_chan_helper
, NULL
, privmsg_user_helper
, privmsg_invalid
, &pd
);
601 static CMD_FUNC(cmd_whois
) {
602 struct userNode
*from
;
603 struct userNode
*who
;
607 if (!(from
= GetUserH(origin
))) {
608 log_module(MAIN_LOG
, LOG_ERROR
, "Could not find WHOIS origin user %s", origin
);
611 if(!(who
= GetUserH(argv
[2]))) {
612 irc_numeric(from
, ERR_NOSUCHNICK
, "%s@%s :No such nick", argv
[2], self
->name
);
615 if (IsHiddenHost(who
) && !IsOper(from
)) {
616 /* Just stay quiet. */
619 irc_numeric(from
, RPL_WHOISUSER
, "%s %s %s * :%s", who
->nick
, who
->ident
, who
->hostname
, who
->info
);
620 #ifdef WITH_PROTOCOL_P10
621 if (his_servername
&& his_servercomment
)
622 irc_numeric(from
, RPL_WHOISSERVER
, "%s %s :%s", who
->nick
, his_servername
, his_servercomment
);
625 irc_numeric(from
, RPL_WHOISSERVER
, "%s %s :%s", who
->nick
, who
->uplink
->name
, who
->uplink
->description
);
628 irc_numeric(from
, RPL_WHOISOPERATOR
, "%s :is a megalomaniacal power hungry tyrant", who
->nick
);
630 irc_numeric(from
, RPL_ENDOFWHOIS
, "%s :End of /WHOIS list", who
->nick
);
634 static CMD_FUNC(cmd_capab
) {
635 static const struct {
639 { "TS3", CAPAB_TS3
},
640 { "NOQUIT", CAPAB_NOQUIT
},
641 { "SSJOIN", CAPAB_SSJOIN
},
642 { "BURST", CAPAB_BURST
},
643 { "UNCONNECT", CAPAB_UNCONNECT
},
644 { "NICKIP", CAPAB_NICKIP
},
645 { "TSMODE", CAPAB_TSMODE
},
646 { "ZIP", CAPAB_ZIP
},
652 for(nn
=1; nn
<argc
; nn
++) {
653 for (mm
=0; capabs
[mm
].name
&& irccasecmp(capabs
[mm
].name
, argv
[nn
]); mm
++) ;
654 if (capabs
[mm
].name
) {
655 uplink_capab
|= capabs
[mm
].mask
;
657 log_module(MAIN_LOG
, LOG_INFO
, "Saw unrecognized/unhandled capability %s. Please notify X3 developers so they can add it.", argv
[nn
]);
663 static void burst_channel(struct chanNode
*chan
) {
665 int pos
, base_len
, len
, queued
;
668 if (!chan
->members
.used
) return;
669 /* send list of users in the channel.. */
670 base_len
= sprintf(line
, ":%s SJOIN "FMT_TIME_T
" %s ", self
->name
, chan
->timestamp
, chan
->name
);
671 len
= irc_make_chanmode(chan
, line
+base_len
);
672 pos
= base_len
+ len
;
675 for (nn
=0; nn
<chan
->members
.used
; nn
++) {
676 struct modeNode
*mn
= chan
->members
.list
[nn
];
677 len
= strlen(mn
->user
->nick
);
678 if (pos
+ len
> 500) {
686 if (mn
->modes
& MODE_CHANOP
) line
[pos
++] = '@';
687 if (mn
->modes
& MODE_VOICE
) line
[pos
++] = '+';
688 memcpy(line
+pos
, mn
->user
->nick
, len
);
692 /* print the last line */
695 /* now send the bans.. */
696 base_len
= sprintf(line
, ":%s MODE "FMT_TIME_T
" %s +", self
->name
, chan
->timestamp
, chan
->name
);
697 pos
= sizeof(line
)-1;
699 for (nn
=queued
=0; nn
<chan
->banlist
.used
; nn
++) {
700 struct banNode
*bn
= chan
->banlist
.list
[nn
];
701 len
= strlen(bn
->ban
);
702 if (pos
< base_len
+queued
+len
+4) {
707 putsock("%s%s", line
, line
+pos
);
708 pos
= sizeof(line
)-1;
711 memcpy(line
+pos
, bn
->ban
, len
);
720 putsock("%s%s", line
, line
+pos
);
724 static void send_burst() {
726 for (it
= dict_first(servers
); it
; it
= iter_next(it
)) {
727 struct server
*serv
= iter_data(it
);
728 if ((serv
!= self
) && (serv
!= self
->uplink
)) irc_server(serv
);
731 for (it
= dict_first(clients
); it
; it
= iter_next(it
)) {
732 irc_user(iter_data(it
));
734 for (it
= dict_first(channels
); it
; it
= iter_next(it
)) {
735 burst_channel(iter_data(it
));
737 /* Uplink will do AWAY and TOPIC bursts before sending BURST 0, but we don't */
741 static CMD_FUNC(cmd_server
) {
742 if (argc
< 4) return 0;
744 AddServer(GetServerH(origin
), argv
[1], atoi(argv
[2]), 0, now
, 0, argv
[3]);
746 self
->uplink
= AddServer(self
, argv
[1], atoi(argv
[2]), 0, now
, 0, argv
[3]);
751 static CMD_FUNC(cmd_svinfo
) {
752 if (argc
< 5) return 0;
753 if ((atoi(argv
[1]) < 3) || (atoi(argv
[2]) > 3)) return 0;
754 /* TODO: something with the timestamp we get from the other guy */
759 static CMD_FUNC(cmd_ping
)
761 irc_pong(self
->name
, argc
> 1 ? argv
[1] : origin
);
762 timeq_del(0, timed_send_ping
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
763 timeq_del(0, timed_ping_timeout
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
764 timeq_add(now
+ ping_freq
, timed_send_ping
, 0);
769 static CMD_FUNC(cmd_burst
) {
770 struct server
*sender
= GetServerH(origin
);
771 if (!sender
) return 0;
772 if (argc
== 1) return 1;
773 if (sender
== self
->uplink
) {
774 cManager
.uplink
->state
= CONNECTED
;
776 sender
->self_burst
= 0;
777 recalc_bursts(sender
);
781 static CMD_FUNC(cmd_nick
) {
783 if ((un
= GetUserH(origin
))) {
785 if (argc
< 2) return 0;
786 NickChange(un
, argv
[1], 1);
788 /* new nick from a server */
793 if (argc
< 10) return 0;
794 stamp
= strtoul(argv
[8], NULL
, 0);
795 if (stamp
) inttobase64(id
, stamp
, IDLEN
);
796 ip
.s_addr
= (argc
> 10) ? atoi(argv
[9]) : 0;
797 un
= AddUser(GetServerH(argv
[7]), argv
[1], argv
[5], argv
[6], argv
[4], argv
[argc
-1], atoi(argv
[3]), ip
, (stamp
? id
: 0));
802 static CMD_FUNC(cmd_sjoin
) {
803 struct chanNode
*cNode
;
804 struct userNode
*uNode
;
805 struct modeNode
*mNode
;
806 unsigned int next
= 4, last
;
807 char *nick
, *nickend
;
809 if ((argc
== 3) && (uNode
= GetUserH(origin
))) {
811 if (!(cNode
= GetChannel(argv
[2]))) {
812 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find SJOIN target %s", argv
[2]);
815 AddChannelUser(uNode
, cNode
);
818 if (argc
< 5) return 0;
819 if (argv
[3][0] == '+') {
820 char modes
[MAXLEN
], *pos
;
822 for (pos
= argv
[3], n_modes
= 1; *pos
; pos
++) {
823 if ((*pos
== 'k') || (*pos
== 'l')) n_modes
++;
825 unsplit_string(argv
+3, n_modes
, modes
);
826 cNode
= AddChannel(argv
[2], atoi(argv
[1]), modes
, NULL
);
827 } else if (argv
[3][0] == '0') {
828 cNode
= GetChannel(argv
[2]);
830 log_module(MAIN_LOG
, LOG_ERROR
, "Unsure how to handle SJOIN when arg 3 is %s", argv
[3]);
834 /* argv[next] is now the space-delimited, @+-prefixed list of
835 * nicks in the channel. Split it and add the users. */
836 for (last
= 0, nick
= argv
[next
]; !last
; nick
= nickend
+ 1) {
838 for (nickend
= nick
; *nickend
&& (*nickend
!= ' '); nickend
++) ;
839 if (!*nickend
) last
= 1;
841 if (*nick
== '@') { mode
|= MODE_CHANOP
; nick
++; }
842 if (*nick
== '+') { mode
|= MODE_VOICE
; nick
++; }
843 if ((uNode
= GetUserH(nick
)) && (mNode
= AddChannelUser(uNode
, cNode
))) {
850 static CMD_FUNC(cmd_mode
) {
854 log_module(MAIN_LOG
, LOG_ERROR
, "Illegal MODE from %s (no arguments).", origin
);
856 } else if (IsChannelName(argv
[1])) {
860 if (!(cn
= GetChannel(argv
[1]))) {
861 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find channel %s whose mode is changing", argv
[1]);
865 if ((un
= GetUserH(origin
))) {
866 /* Update idle time for the user */
867 if ((mn
= GetUserMode(cn
, un
)))
868 mn
->idle_since
= now
;
870 /* Must be a server in burst or something. Make sure we're using the right timestamp. */
871 cn
->timestamp
= atoi(argv
[2]);
874 return mod_chanmode(un
, cn
, argv
+3, argc
-3, MCP_ALLOW_OVB
|MCP_FROM_SERVER
|MC_ANNOUNCE
);
875 } else if ((un
= GetUserH(argv
[1]))) {
876 mod_usermode(un
, argv
[2]);
879 log_module(MAIN_LOG
, LOG_ERROR
, "Not sure what MODE %s is affecting (not a channel name and no such user)", argv
[1]);
884 static CMD_FUNC(cmd_topic
) {
886 if (argc
< 5) return 0;
887 if (!(cn
= GetChannel(argv
[1]))) {
888 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find channel %s whose topic is being set", argv
[1]);
891 if (irccasecmp(origin
, argv
[2])) {
892 /* coming from a topic burst; the origin is a server */
893 safestrncpy(cn
->topic
, argv
[4], sizeof(cn
->topic
));
894 safestrncpy(cn
->topic_nick
, argv
[2], sizeof(cn
->topic_nick
));
895 cn
->topic_time
= atoi(argv
[3]);
897 SetChannelTopic(cn
, GetUserH(argv
[2]), argv
[4], 0);
902 static CMD_FUNC(cmd_part
) {
903 if (argc
< 2) return 0;
904 parse_foreach(argv
[1], part_helper
, NULL
, NULL
, NULL
, GetUserH(origin
));
908 static CMD_FUNC(cmd_away
) {
911 if (!(un
= GetUserH(origin
))) {
912 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find user %s sending AWAY", origin
);
916 un
->modes
|= FLAGS_AWAY
;
918 un
->modes
&= ~FLAGS_AWAY
;
923 static CMD_FUNC(cmd_kick
) {
924 if (argc
< 3) return 0;
925 ChannelUserKicked(GetUserH(origin
), GetUserH(argv
[2]), GetChannel(argv
[1]));
929 static CMD_FUNC(cmd_kill
) {
930 struct userNode
*user
;
931 if (argc
< 3) return 0;
932 if (!(user
= GetUserH(argv
[1]))) {
933 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find kill victim %s", argv
[1]);
936 if (IsLocal(user
) && IsService(user
)) {
937 ReintroduceUser(user
);
939 DelUser(user
, GetUserH(origin
), false, ((argc
>= 3) ? argv
[2] : NULL
));
944 static CMD_FUNC(cmd_pong
)
946 if (argc
< 3) return 0;
947 if (!strcmp(argv
[2], self
->name
)) {
948 timeq_del(0, timed_send_ping
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
949 timeq_del(0, timed_ping_timeout
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
950 timeq_add(now
+ ping_freq
, timed_send_ping
, 0);
956 static CMD_FUNC(cmd_num_topic
)
958 static struct chanNode
*cn
;
963 cn
= GetChannel(argv
[2]);
965 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to find channel %s in topic reply", argv
[2]);
971 switch (atoi(argv
[0])) {
974 break; /* no topic */
978 safestrncpy(cn
->topic
, unsplit_string(argv
+3, argc
-3, NULL
), sizeof(cn
->topic
));
983 safestrncpy(cn
->topic_nick
, argv
[3], sizeof(cn
->topic_nick
));
984 cn
->topic_time
= atoi(argv
[4]);
987 return 0; /* should never happen */
992 static CMD_FUNC(cmd_quit
)
994 struct userNode
*user
;
995 if (argc
< 2) return 0;
996 /* Sometimes we get a KILL then a QUIT or the like, so we don't want to
997 * call DelUser unless we have the user in our grasp. */
998 if ((user
= GetUserH(origin
))) {
999 DelUser(user
, NULL
, false, argv
[1]);
1004 static CMD_FUNC(cmd_squit
)
1006 struct server
*server
;
1009 if (!(server
= GetServerH(argv
[1])))
1011 if (server
== self
->uplink
) {
1012 /* Force a reconnect to the currently selected server. */
1013 cManager
.uplink
->tries
= 0;
1014 log_module(MAIN_LOG
, LOG_INFO
, "Squitting from uplink: %s", argv
[3]);
1019 DelServer(server
, 0, argv
[3]);
1023 static CMD_FUNC(cmd_svsnick
)
1025 struct userNode
*target
, *dest
;
1026 if (argc
< 4) return 0;
1027 if (!(target
= GetUserH(argv
[1]))) return 0;
1028 if (!IsLocal(target
)) return 0;
1029 if ((dest
= GetUserH(argv
[2]))) return 0; /* Note: Bahamut will /KILL instead. */
1030 NickChange(target
, argv
[2], 0);
1034 static oper_func_t
*of_list
;
1035 static unsigned int of_size
= 0, of_used
= 0;
1037 void parse_cleanup(void) {
1039 if (of_list
) free(of_list
);
1040 dict_delete(irc_func_dict
);
1041 dict_delete(service_msginfo_dict
);
1043 for (nn
=0; nn
<dead_users
.used
; nn
++) free_user(dead_users
.list
[nn
]);
1044 userList_clean(&dead_users
);
1047 void init_parse(void) {
1048 const char *str
, *desc
;
1050 str
= conf_get_data("server/ping_freq", RECDB_QSTRING
);
1051 ping_freq
= str
? ParseInterval(str
) : 120;
1052 str
= conf_get_data("server/ping_timeout", RECDB_QSTRING
);
1053 ping_timeout
= str
? ParseInterval(str
) : 30;
1054 str
= conf_get_data("server/hostname", RECDB_QSTRING
);
1055 desc
= conf_get_data("server/description", RECDB_QSTRING
);
1056 if (!str
|| !desc
) {
1057 log_module(MAIN_LOG
, LOG_ERROR
, "No server/hostname entry in config file.");
1060 self
= AddServer(NULL
, str
, 0, boot_time
, now
, NULL
, desc
);
1062 str
= conf_get_data("server/ping_freq", RECDB_QSTRING
);
1063 ping_freq
= str
? ParseInterval(str
) : 120;
1064 str
= conf_get_data("server/ping_timeout", RECDB_QSTRING
);
1065 ping_timeout
= str
? ParseInterval(str
) : 30;
1067 service_msginfo_dict
= dict_new();
1068 dict_set_free_data(service_msginfo_dict
, free
);
1069 irc_func_dict
= dict_new();
1070 dict_insert(irc_func_dict
, "ADMIN", cmd_admin
);
1071 dict_insert(irc_func_dict
, "AWAY", cmd_away
);
1072 dict_insert(irc_func_dict
, "BURST", cmd_burst
);
1073 dict_insert(irc_func_dict
, "CAPAB", cmd_capab
);
1074 dict_insert(irc_func_dict
, "ERROR", cmd_error
);
1075 dict_insert(irc_func_dict
, "GNOTICE", cmd_dummy
);
1076 dict_insert(irc_func_dict
, "INVITE", cmd_dummy
);
1077 dict_insert(irc_func_dict
, "KICK", cmd_kick
);
1078 dict_insert(irc_func_dict
, "KILL", cmd_kill
);
1079 dict_insert(irc_func_dict
, "LUSERSLOCK", cmd_dummy
);
1080 dict_insert(irc_func_dict
, "MODE", cmd_mode
);
1081 dict_insert(irc_func_dict
, "NICK", cmd_nick
);
1082 dict_insert(irc_func_dict
, "NOTICE", cmd_notice
);
1083 dict_insert(irc_func_dict
, "PART", cmd_part
);
1084 dict_insert(irc_func_dict
, "PASS", cmd_pass
);
1085 dict_insert(irc_func_dict
, "PING", cmd_ping
);
1086 dict_insert(irc_func_dict
, "PONG", cmd_pong
);
1087 dict_insert(irc_func_dict
, "PRIVMSG", cmd_privmsg
);
1088 dict_insert(irc_func_dict
, "QUIT", cmd_quit
);
1089 dict_insert(irc_func_dict
, "SERVER", cmd_server
);
1090 dict_insert(irc_func_dict
, "SJOIN", cmd_sjoin
);
1091 dict_insert(irc_func_dict
, "SQUIT", cmd_squit
);
1092 dict_insert(irc_func_dict
, "STATS", cmd_stats
);
1093 dict_insert(irc_func_dict
, "SVSNICK", cmd_svsnick
);
1094 dict_insert(irc_func_dict
, "SVINFO", cmd_svinfo
);
1095 dict_insert(irc_func_dict
, "TOPIC", cmd_topic
);
1096 dict_insert(irc_func_dict
, "VERSION", cmd_version
);
1097 dict_insert(irc_func_dict
, "WHOIS", cmd_whois
);
1098 dict_insert(irc_func_dict
, "331", cmd_num_topic
);
1099 dict_insert(irc_func_dict
, "332", cmd_num_topic
);
1100 dict_insert(irc_func_dict
, "333", cmd_num_topic
);
1101 dict_insert(irc_func_dict
, "413", cmd_num_topic
);
1103 userList_init(&dead_users
);
1104 reg_exit_func(parse_cleanup
);
1107 int parse_line(char *line
, int recursive
) {
1108 char *argv
[MAXNUMPARAMS
];
1112 argc
= split_line(line
, true, ArrayLength(argv
), argv
);
1113 cmd
= line
[0] == ':';
1114 if ((argc
> cmd
) && (func
= dict_find(irc_func_dict
, argv
[cmd
], NULL
))) {
1117 origin
= argv
[0] + 1;
1118 } else if (self
->uplink
) {
1119 origin
= self
->uplink
->name
;
1123 res
= func(origin
, argc
-cmd
, argv
+cmd
);
1128 log_module(MAIN_LOG
, LOG_ERROR
, "PARSE ERROR on line: %s", unsplit_string(argv
, argc
, NULL
));
1129 } else if (!recursive
) {
1131 for (i
=0; i
<dead_users
.used
; i
++) {
1132 free_user(dead_users
.list
[i
]);
1134 dead_users
.used
= 0;
1140 privmsg_user_helper(struct userNode
*un
, void *data
)
1142 struct privmsg_desc
*pd
= data
;
1143 struct service_message_info
*info
= dict_find(service_msginfo_dict
, un
->nick
, 0);
1145 if (pd
->is_notice
) {
1146 if (info
->on_notice
) info
->on_notice(pd
->user
, un
, pd
->text
, pd
->is_qualified
);
1148 if (info
->on_privmsg
) info
->on_privmsg(pd
->user
, un
, pd
->text
, pd
->is_qualified
);
1154 reg_privmsg_func(struct userNode
*user
, privmsg_func_t handler
) {
1155 struct service_message_info
*info
= dict_find(service_msginfo_dict
, user
->nick
, NULL
);
1157 info
= calloc(1, sizeof(*info
));
1158 dict_insert(service_msginfo_dict
, user
->nick
, info
);
1160 info
->on_privmsg
= handler
;
1164 reg_notice_func(struct userNode
*user
, privmsg_func_t handler
) {
1165 struct service_message_info
*info
= dict_find(service_msginfo_dict
, user
->nick
, NULL
);
1167 info
= calloc(1, sizeof(*info
));
1168 dict_insert(service_msginfo_dict
, user
->nick
, info
);
1170 info
->on_notice
= handler
;
1174 reg_oper_func(oper_func_t handler
)
1176 if (of_used
== of_size
) {
1179 of_list
= realloc(of_list
, of_size
*sizeof(oper_func_t
));
1182 of_list
= malloc(of_size
*sizeof(oper_func_t
));
1185 of_list
[of_used
++] = handler
;
1189 call_oper_funcs(struct userNode
*user
)
1192 if (IsLocal(user
)) return;
1193 for (n
=0; n
<of_used
; n
++)
1199 void mod_usermode(struct userNode
*user
, const char *mode_change
) {
1202 if (!user
|| !mode_change
) return;
1204 #define do_user_mode(FLAG) do { if (add) user->modes |= FLAG; else user->modes &= ~FLAG; } while (0)
1205 switch (*mode_change
++) {
1207 case '+': add
= 1; break;
1208 case '-': add
= 0; break;
1210 do_user_mode(FLAGS_OPER
);
1212 userList_append(&curr_opers
, user
);
1213 call_oper_funcs(user
);
1215 userList_remove(&curr_opers
, user
);
1218 case 'O': do_user_mode(FLAGS_LOCOP
); break;
1219 case 'i': do_user_mode(FLAGS_INVISIBLE
);
1220 if (add
) invis_clients
++; else invis_clients
--;
1222 case 'w': do_user_mode(FLAGS_WALLOP
); break;
1223 case 's': do_user_mode(FLAGS_SERVNOTICE
); break;
1224 case 'd': do_user_mode(FLAGS_DEAF
); break;
1225 case 'r': do_user_mode(FLAGS_REGNICK
); break;
1226 case 'k': do_user_mode(FLAGS_SERVICE
); break;
1227 case 'g': do_user_mode(FLAGS_GLOBAL
); break;
1228 case 'h': do_user_mode(FLAGS_HELPER
); break;
1234 struct mod_chanmode
*
1235 mod_chanmode_parse(struct chanNode
*channel
, char **modes
, unsigned int argc
, unsigned int flags
)
1237 struct mod_chanmode
*change
;
1238 unsigned int ii
, in_arg
, ch_arg
, add
;
1242 if (!(change
= mod_chanmode_alloc(argc
)))
1245 for (ii
= ch_arg
= 0, in_arg
= add
= 1;
1246 (modes
[0][ii
] != '\0') && (modes
[0][ii
] != ' ');
1248 switch (modes
[0][ii
]) {
1255 #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)
1256 case 'R': do_chan_mode(MODE_REGONLY
); break;
1257 case 'D': do_chan_mode(MODE_DELAYJOINS
); break;
1258 case 'c': do_chan_mode(MODE_NOCOLORS
); break;
1259 case 'i': do_chan_mode(MODE_INVITEONLY
); break;
1260 case 'm': do_chan_mode(MODE_MODERATED
); break;
1261 case 'n': do_chan_mode(MODE_NOPRIVMSGS
); break;
1262 case 'p': do_chan_mode(MODE_PRIVATE
); break;
1263 case 's': do_chan_mode(MODE_SECRET
); break;
1264 case 't': do_chan_mode(MODE_TOPICLIMIT
); break;
1266 if (!(flags
& MCP_REGISTERED
)) {
1267 do_chan_mode(MODE_REGISTERED
);
1269 mod_chanmode_free(change
);
1278 change
->modes_set
|= MODE_LIMIT
;
1279 change
->new_limit
= atoi(modes
[in_arg
++]);
1281 change
->modes_clear
|= MODE_LIMIT
;
1288 change
->modes_set
|= MODE_KEY
;
1289 safestrncpy(change
->new_key
, modes
[in_arg
++], sizeof(change
->new_key
));
1291 change
->modes_clear
|= MODE_KEY
;
1292 if (!(flags
& MCP_KEY_FREE
)) {
1300 if (!(flags
& MCP_ALLOW_OVB
))
1304 change
->args
[ch_arg
].mode
= MODE_BAN
;
1306 change
->args
[ch_arg
].mode
|= MODE_REMOVE
;
1307 change
->args
[ch_arg
++].u
.hostmask
= modes
[in_arg
++];
1311 struct userNode
*victim
;
1312 if (!(flags
& MCP_ALLOW_OVB
))
1316 change
->args
[ch_arg
].mode
= (modes
[0][ii
] == 'o') ? MODE_CHANOP
: MODE_VOICE
;
1318 change
->args
[ch_arg
].mode
|= MODE_REMOVE
;
1319 victim
= GetUserH(modes
[in_arg
++]);
1322 if ((change
->args
[ch_arg
].u
.member
= GetUserMode(channel
, victim
)))
1327 if (!(flags
& MCP_FROM_SERVER
))
1332 change
->argc
= ch_arg
; /* in case any turned out to be ignored */
1333 if (change
->modes_set
& MODE_SECRET
) {
1334 change
->modes_set
&= ~(MODE_PRIVATE
);
1335 change
->modes_clear
|= MODE_PRIVATE
;
1336 } else if (change
->modes_set
& MODE_PRIVATE
) {
1337 change
->modes_set
&= ~(MODE_SECRET
);
1338 change
->modes_clear
|= MODE_SECRET
;
1342 mod_chanmode_free(change
);
1346 struct chanmode_buffer
{
1349 struct chanNode
*channel
;
1350 struct userNode
*actor
;
1351 unsigned int modes_used
;
1352 unsigned int args_used
;
1354 unsigned int is_add
: 1;
1358 mod_chanmode_append(struct chanmode_buffer
*buf
, char ch
, const char *arg
)
1360 size_t arg_len
= strlen(arg
);
1361 if (buf
->modes_used
+ buf
->args_used
+ buf
->chname_len
+ arg_len
> 450) {
1362 memcpy(buf
->modes
+ buf
->modes_used
, buf
->args
, buf
->args_used
);
1363 buf
->modes
[buf
->modes_used
+ buf
->args_used
] = '\0';
1364 irc_mode(buf
->actor
, buf
->channel
, buf
->modes
);
1365 buf
->modes
[0] = buf
->is_add
? '+' : '-';
1366 buf
->modes_used
= 1;
1369 buf
->modes
[buf
->modes_used
++] = ch
;
1370 buf
->args
[buf
->args_used
++] = ' ';
1371 memcpy(buf
->args
+ buf
->args_used
, arg
, arg_len
);
1372 buf
->args_used
+= arg_len
;
1376 mod_chanmode_announce(struct userNode
*who
, struct chanNode
*channel
, struct mod_chanmode
*change
)
1378 struct chanmode_buffer chbuf
;
1382 assert(change
->argc
<= change
->alloc_argc
);
1383 memset(&chbuf
, 0, sizeof(chbuf
));
1384 chbuf
.channel
= channel
;
1386 chbuf
.chname_len
= strlen(channel
->name
);
1388 /* First remove modes */
1390 if (change
->modes_clear
) {
1391 chbuf
.modes
[chbuf
.modes_used
++] = '-';
1392 #define DO_MODE_CHAR(BIT, CHAR) if (change->modes_clear & MODE_##BIT) chbuf.modes[chbuf.modes_used++] = CHAR;
1393 DO_MODE_CHAR(PRIVATE
, 'p');
1394 DO_MODE_CHAR(SECRET
, 's');
1395 DO_MODE_CHAR(MODERATED
, 'm');
1396 DO_MODE_CHAR(TOPICLIMIT
, 't');
1397 DO_MODE_CHAR(INVITEONLY
, 'i');
1398 DO_MODE_CHAR(NOPRIVMSGS
, 'n');
1399 DO_MODE_CHAR(LIMIT
, 'l');
1400 DO_MODE_CHAR(DELAYJOINS
, 'D');
1401 DO_MODE_CHAR(REGONLY
, 'R');
1402 DO_MODE_CHAR(NOCOLORS
, 'c');
1403 DO_MODE_CHAR(REGISTERED
, 'r');
1405 if (change
->modes_clear
& channel
->modes
& MODE_KEY
)
1406 mod_chanmode_append(&chbuf
, 'k', channel
->key
);
1408 for (arg
= 0; arg
< change
->argc
; ++arg
) {
1409 if (!(change
->args
[arg
].mode
& MODE_REMOVE
))
1411 switch (change
->args
[arg
].mode
& ~MODE_REMOVE
) {
1413 mod_chanmode_append(&chbuf
, 'b', change
->args
[arg
].u
.hostmask
);
1416 if (change
->args
[arg
].mode
& MODE_CHANOP
)
1417 mod_chanmode_append(&chbuf
, 'o', change
->args
[arg
].u
.member
->user
->nick
);
1418 if (change
->args
[arg
].mode
& MODE_VOICE
)
1419 mod_chanmode_append(&chbuf
, 'v', change
->args
[arg
].u
.member
->user
->nick
);
1426 if (change
->modes_set
) {
1427 chbuf
.modes
[chbuf
.modes_used
++] = '+';
1428 #define DO_MODE_CHAR(BIT, CHAR) if (change->modes_set & MODE_##BIT) chbuf.modes[chbuf.modes_used++] = CHAR;
1429 DO_MODE_CHAR(PRIVATE
, 'p');
1430 DO_MODE_CHAR(SECRET
, 's');
1431 DO_MODE_CHAR(MODERATED
, 'm');
1432 DO_MODE_CHAR(TOPICLIMIT
, 't');
1433 DO_MODE_CHAR(INVITEONLY
, 'i');
1434 DO_MODE_CHAR(NOPRIVMSGS
, 'n');
1435 DO_MODE_CHAR(DELAYJOINS
, 'D');
1436 DO_MODE_CHAR(REGONLY
, 'R');
1437 DO_MODE_CHAR(NOCOLORS
, 'c');
1438 DO_MODE_CHAR(REGISTERED
, 'r');
1440 if (change
->modes_set
& MODE_KEY
)
1441 mod_chanmode_append(&chbuf
, 'k', change
->new_key
);
1442 if (change
->modes_set
& MODE_LIMIT
)
1444 sprintf(int_buff
, "%d", change
->new_limit
);
1445 mod_chanmode_append(&chbuf
, 'l', int_buff
);
1448 for (arg
= 0; arg
< change
->argc
; ++arg
) {
1449 if (change
->args
[arg
].mode
& MODE_REMOVE
)
1451 switch (change
->args
[arg
].mode
) {
1453 mod_chanmode_append(&chbuf
, 'b', change
->args
[arg
].u
.hostmask
);
1456 if (change
->args
[arg
].mode
& MODE_CHANOP
)
1457 mod_chanmode_append(&chbuf
, 'o', change
->args
[arg
].u
.member
->user
->nick
);
1458 if (change
->args
[arg
].mode
& MODE_VOICE
)
1459 mod_chanmode_append(&chbuf
, 'v', change
->args
[arg
].u
.member
->user
->nick
);
1464 /* Flush the buffer and apply changes locally */
1465 if (chbuf
.modes_used
> 0) {
1466 memcpy(chbuf
.modes
+ chbuf
.modes_used
, chbuf
.args
, chbuf
.args_used
);
1467 chbuf
.modes
[chbuf
.modes_used
+ chbuf
.args_used
] = '\0';
1468 irc_mode(chbuf
.actor
, chbuf
.channel
, chbuf
.modes
);
1470 mod_chanmode_apply(who
, channel
, change
);
1474 mod_chanmode_format(struct mod_chanmode
*change
, char *outbuff
)
1476 unsigned int used
= 0;
1477 assert(change
->argc
<= change
->alloc_argc
);
1478 if (change
->modes_clear
) {
1479 outbuff
[used
++] = '-';
1480 #define DO_MODE_CHAR(BIT, CHAR) if (change->modes_clear & MODE_##BIT) outbuff[used++] = CHAR
1481 DO_MODE_CHAR(PRIVATE
, 'p');
1482 DO_MODE_CHAR(SECRET
, 's');
1483 DO_MODE_CHAR(MODERATED
, 'm');
1484 DO_MODE_CHAR(TOPICLIMIT
, 't');
1485 DO_MODE_CHAR(INVITEONLY
, 'i');
1486 DO_MODE_CHAR(NOPRIVMSGS
, 'n');
1487 DO_MODE_CHAR(LIMIT
, 'l');
1488 DO_MODE_CHAR(KEY
, 'k');
1489 DO_MODE_CHAR(DELAYJOINS
, 'D');
1490 DO_MODE_CHAR(REGONLY
, 'R');
1491 DO_MODE_CHAR(NOCOLORS
, 'c');
1492 DO_MODE_CHAR(REGISTERED
, 'r');
1495 if (change
->modes_set
) {
1496 outbuff
[used
++] = '+';
1497 #define DO_MODE_CHAR(BIT, CHAR) if (change->modes_set & MODE_##BIT) outbuff[used++] = CHAR
1498 DO_MODE_CHAR(PRIVATE
, 'p');
1499 DO_MODE_CHAR(SECRET
, 's');
1500 DO_MODE_CHAR(MODERATED
, 'm');
1501 DO_MODE_CHAR(TOPICLIMIT
, 't');
1502 DO_MODE_CHAR(INVITEONLY
, 'i');
1503 DO_MODE_CHAR(NOPRIVMSGS
, 'n');
1504 DO_MODE_CHAR(DELAYJOINS
, 'D');
1505 DO_MODE_CHAR(REGONLY
, 'R');
1506 DO_MODE_CHAR(NOCOLORS
, 'c');
1507 DO_MODE_CHAR(REGISTERED
, 'r');
1509 switch (change
->modes_set
& (MODE_KEY
|MODE_LIMIT
)) {
1510 case MODE_KEY
|MODE_LIMIT
:
1511 used
+= sprintf(outbuff
+used
, "lk %d %s", change
->new_limit
, change
->new_key
);
1514 used
+= sprintf(outbuff
+used
, "k %s", change
->new_key
);
1517 used
+= sprintf(outbuff
+used
, "l %d", change
->new_limit
);