1 /* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * x3 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.
29 #ifdef HAVE_SYS_SOCKET_H
30 #include <sys/socket.h>
32 #ifdef HAVE_NETINET_IN_H
33 #include <netinet/in.h>
35 #ifdef HAVE_ARPA_INET_H
36 #include <arpa/inet.h>
39 unsigned int lines_processed
;
41 struct io_fd
*socket_io_fd
;
43 const char *hidden_host_suffix
;
45 static char replay_line
[MAXLEN
+80];
47 static int ping_timeout
;
48 static int replay_connected
;
49 static unsigned int nicklen
= NICKLEN
; /* how long do we think servers allow nicks to be? */
50 static struct userList dead_users
;
52 extern struct cManagerNode cManager
;
53 extern unsigned long burst_length
;
54 extern struct cManagerNode cManager
;
55 extern struct policer_params
*oper_policer_params
, *luser_policer_params
;
56 extern server_link_func_t
*slf_list
;
57 extern unsigned int slf_size
, slf_used
;
58 extern new_user_func_t
*nuf_list
;
59 extern unsigned int nuf_size
, nuf_used
;
60 extern del_user_func_t
*duf_list
;
61 extern unsigned int duf_size
, duf_used
;
62 extern time_t boot_time
;
64 void received_ping(void);
66 static int replay_read(void);
67 static dict_t irc_func_dict
;
69 typedef void (*foreach_chanfunc
) (struct chanNode
*chan
, void *data
);
70 typedef void (*foreach_nonchan
) (char *name
, void *data
);
71 typedef void (*foreach_userfunc
) (struct userNode
*user
, void *data
);
72 typedef void (*foreach_nonuser
) (char *name
, void *data
);
73 static void parse_foreach(char *target_list
, foreach_chanfunc cf
, foreach_nonchan nc
, foreach_userfunc uf
, foreach_nonuser nu
, void *data
);
74 static void call_channel_mode_funcs(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
);
77 uplink_readable(struct io_fd
*fd
) {
78 static char buffer
[MAXLEN
];
82 pos
= ioset_line_read(fd
, buffer
, sizeof(buffer
));
87 if ((eol
= strpbrk(buffer
, "\r\n")))
89 log_replay(MAIN_LOG
, false, buffer
);
90 if (cManager
.uplink
->state
!= DISCONNECTED
)
91 parse_line(buffer
, 0);
96 socket_destroyed(struct io_fd
*fd
)
99 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to server lost.");
101 cManager
.uplink
->state
= DISCONNECTED
;
103 DelServer(self
->uplink
, 0, NULL
);
106 void replay_event_loop(void)
108 while (!quit_services
) {
109 if (!replay_connected
) {
110 /* this time fudging is to get some of the logging right */
111 self
->link
= self
->boot
= now
;
112 cManager
.uplink
->state
= AUTHENTICATING
;
113 irc_introduce(cManager
.uplink
->password
);
114 replay_connected
= 1;
115 } else if (!replay_read()) {
116 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to server lost.");
124 create_socket_client(struct uplinkNode
*target
)
126 int port
= target
->port
;
127 const char *addr
= target
->host
;
130 return feof(replay_file
) ? 0 : 1;
133 /* Leave the existing socket open, say we failed. */
134 log_module(MAIN_LOG
, LOG_ERROR
, "Refusing to create second connection to %s:%d.", addr
, port
);
138 log_module(MAIN_LOG
, LOG_INFO
, "Connecting to %s:%i...", addr
, port
);
140 socket_io_fd
= ioset_connect(cManager
.uplink
->bind_addr
, cManager
.uplink
->bind_addr_len
, addr
, port
, 1, 0, NULL
);
142 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to uplink failed: %s (%d)", strerror(errno
), errno
);
143 target
->state
= DISCONNECTED
;
147 socket_io_fd
->readable_cb
= uplink_readable
;
148 socket_io_fd
->destroy_cb
= socket_destroyed
;
149 socket_io_fd
->line_reads
= 1;
150 socket_io_fd
->wants_reads
= 1;
151 log_module(MAIN_LOG
, LOG_INFO
, "Connection to server established.");
152 cManager
.uplink
= target
;
153 target
->state
= AUTHENTICATING
;
159 replay_read_line(void)
164 if (replay_line
[0]) return;
166 if (!fgets(replay_line
, sizeof(replay_line
), replay_file
)) {
167 if (feof(replay_file
)) {
169 memset(replay_line
, 0, sizeof(replay_line
));
173 if ((replay_line
[0] != '[')
174 || (replay_line
[3] != ':')
175 || (replay_line
[6] != ':')
176 || (replay_line
[9] != ' ')
177 || (replay_line
[12] != '/')
178 || (replay_line
[15] != '/')
179 || (replay_line
[20] != ']')
180 || (replay_line
[21] != ' ')) {
181 log_module(MAIN_LOG
, LOG_ERROR
, "Unrecognized timestamp in replay file: %s", replay_line
);
184 timestamp
.tm_hour
= strtoul(replay_line
+1, NULL
, 10);
185 timestamp
.tm_min
= strtoul(replay_line
+4, NULL
, 10);
186 timestamp
.tm_sec
= strtoul(replay_line
+7, NULL
, 10);
187 timestamp
.tm_mon
= strtoul(replay_line
+10, NULL
, 10) - 1;
188 timestamp
.tm_mday
= strtoul(replay_line
+13, NULL
, 10);
189 timestamp
.tm_year
= strtoul(replay_line
+16, NULL
, 10) - 1900;
190 timestamp
.tm_isdst
= 0;
191 new_time
= mktime(×tamp
);
192 if (new_time
== -1) {
193 log_module(MAIN_LOG
, LOG_ERROR
, "Unable to parse time struct tm_sec=%d tm_min=%d tm_hour=%d tm_mday=%d tm_mon=%d tm_year=%d", timestamp
.tm_sec
, timestamp
.tm_min
, timestamp
.tm_hour
, timestamp
.tm_mday
, timestamp
.tm_mon
, timestamp
.tm_year
);
198 if (strncmp(replay_line
+22, "(info) ", 7))
207 char read_line
[MAXLEN
];
210 /* if it's a sent line, break out to handle it */
211 if (!strncmp(replay_line
+29, " ", 3))
213 if (!strncmp(replay_line
+29, "W: ", 3)) {
214 log_module(MAIN_LOG
, LOG_ERROR
, "Expected response from services: %s", replay_line
+32);
220 log_replay(MAIN_LOG
, false, replay_line
+32);
221 safestrncpy(read_line
, replay_line
+32, sizeof(read_line
));
222 len
= strlen(read_line
);
223 if (read_line
[len
-1] == '\n')
224 read_line
[--len
] = 0;
226 parse_line(read_line
, 0);
232 replay_write(char *text
)
235 if (strncmp(replay_line
+29, "W: ", 3)) {
236 log_module(MAIN_LOG
, LOG_ERROR
, "Unexpected output during replay: %s", text
);
239 if (strcmp(replay_line
+32, text
)) {
240 log_module(MAIN_LOG
, LOG_ERROR
, "Incorrect output during replay:\nReceived: %sExpected: %s", text
, replay_line
+32);
242 log_replay(MAIN_LOG
, true, text
);
248 void putsock(const char *text
, ...) PRINTF_LIKE(1, 2);
251 putsock(const char *text
, ...)
257 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
259 va_start(arg_list
, text
);
260 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
262 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
265 log_replay(MAIN_LOG
, true, buffer
);
266 buffer
[pos
++] = '\n';
268 ioset_write(socket_io_fd
, buffer
, pos
);
270 replay_write(buffer
);
278 replay_connected
= 0;
279 socket_destroyed(socket_io_fd
);
281 ioset_close(socket_io_fd
->fd
, 1);
285 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
286 typedef CMD_FUNC(cmd_func_t
);
288 static void timed_ping_timeout(void *data
);
290 /* Ping state is kept in the timeq (only one of these two can be in
291 * the queue at any given time). */
293 timed_send_ping(UNUSED_ARG(void *data
))
295 irc_ping(self
->name
);
296 timeq_add(now
+ ping_timeout
, timed_ping_timeout
, 0);
300 timed_ping_timeout(UNUSED_ARG(void *data
))
302 /* Uplink "health" tracking could be accomplished by counting the
303 number of ping timeouts that happen for each uplink. After the
304 timeouts per time period exceeds some amount, the uplink could
305 be marked as unavalable.*/
306 irc_squit(self
, "Ping timeout.", NULL
);
309 static CMD_FUNC(cmd_pass
)
311 const char *true_pass
;
315 true_pass
= cManager
.uplink
->their_password
;
316 if (true_pass
&& strcmp(true_pass
, argv
[1])) {
317 /* It might be good to mark the uplink as unavailable when
318 this happens, though there should be a way of resetting
320 irc_squit(self
, "Incorrect password received.", NULL
);
324 cManager
.uplink
->state
= BURSTING
;
328 static CMD_FUNC(cmd_dummy
)
330 /* we don't care about these messages */
334 static CMD_FUNC(cmd_error
)
336 if (argv
[1]) log_module(MAIN_LOG
, LOG_ERROR
, "Error: %s", argv
[1]);
337 log_module(MAIN_LOG
, LOG_ERROR
, "Error received from uplink, squitting.");
339 if (cManager
.uplink
->state
!= CONNECTED
) {
340 /* Error messages while connected should be fine. */
341 cManager
.uplink
->flags
|= UPLINK_UNAVAILABLE
;
342 log_module(MAIN_LOG
, LOG_ERROR
, "Disabling uplink.");
349 static CMD_FUNC(cmd_stats
)
355 if (!(un
= GetUserH(origin
)))
357 switch (argv
[1][0]) {
359 unsigned int uptime
= now
- boot_time
;
360 irc_numeric(un
, RPL_STATSUPTIME
, ":Server Up %d days %d:%02d:%02d",
361 uptime
/(24*60*60), (uptime
/(60*60))%24
, (uptime
/60)%60
, uptime%60
);
362 irc_numeric(un
, RPL_MAXCONNECTIONS
, ":Highest connection count: %d (%d clients)",
363 self
->max_clients
+1, self
->max_clients
);
366 default: /* unrecognized/unhandled stats output */ break;
368 irc_numeric(un
, 219, "%s :End of /STATS report", argv
[1]);
372 static CMD_FUNC(cmd_version
)
374 struct userNode
*user
;
375 if (!(user
= GetUserH(origin
))) {
376 log_module(MAIN_LOG
, LOG_ERROR
, "Could not find VERSION origin user %s", origin
);
379 irc_numeric(user
, 351, "%s %s %s", PACKAGE_TARNAME
, PACKAGE_VERSION
, self
->name
);
383 static CMD_FUNC(cmd_admin
)
385 struct userNode
*user
;
386 struct string_list
*slist
;
388 if (!(user
= GetUserH(origin
))) {
389 log_module(MAIN_LOG
, LOG_ERROR
, "Could not find ADMIN origin user %s", origin
);
392 if ((slist
= conf_get_data("server/admin", RECDB_STRING_LIST
)) && slist
->used
) {
395 irc_numeric(user
, 256, ":Administrative info about %s", self
->name
);
396 for (i
= 0; i
< slist
->used
&& i
< 3; i
++)
397 irc_numeric(user
, 257 + i
, ":%s", slist
->list
[i
]);
399 irc_numeric(user
, 423, ":No administrative info available");
405 recalc_bursts(struct server
*eob_server
)
408 eob_server
->burst
= eob_server
->self_burst
;
409 if (eob_server
->uplink
!= self
)
410 eob_server
->burst
= eob_server
->burst
|| eob_server
->uplink
->burst
;
411 for (nn
=0; nn
< eob_server
->children
.used
; nn
++)
412 recalc_bursts(eob_server
->children
.list
[nn
]);
415 static struct chanmsg_func
{
417 struct userNode
*service
;
418 } chanmsg_funcs
[256]; /* indexed by trigger character */
420 static struct allchanmsg_func
{
422 struct userNode
*service
;
423 } allchanmsg_funcs
[ALLCHANMSG_FUNCS_MAX
];
425 struct privmsg_desc
{
426 struct userNode
*user
;
428 unsigned int is_notice
: 1;
429 unsigned int is_qualified
: 1;
433 privmsg_chan_helper(struct chanNode
*cn
, void *data
)
435 struct privmsg_desc
*pd
= data
;
437 struct chanmsg_func
*cf
= &chanmsg_funcs
[(unsigned char)pd
->text
[0]];
440 /* Don't complain if it can't find the modeNode because the channel might
442 if ((mn
= GetUserMode(cn
, pd
->user
)))
443 mn
->idle_since
= now
;
445 /* Never send a NOTICE to a channel to one of the services */
446 if (!pd
->is_notice
&& cf
->func
447 && ((cn
->modes
& MODE_REGISTERED
) || GetUserMode(cn
, cf
->service
)))
448 cf
->func(pd
->user
, cn
, pd
->text
+1, cf
->service
);
450 spamserv_channel_message(cn
, pd
->user
, pd
->text
);
452 /* This catches *all* text sent to the channel that the services server sees */
453 for (x
= 0; x
< ALLCHANMSG_FUNCS_MAX
; x
++) {
454 cf
= (struct chanmsg_func
*)&allchanmsg_funcs
[x
];
456 break; /* end of list */
458 cf
->func(pd
->user
, cn
, pd
->text
, cf
->service
);
463 privmsg_invalid(char *name
, void *data
)
465 struct privmsg_desc
*pd
= data
;
469 irc_numeric(pd
->user
, ERR_NOSUCHNICK
, "%s@%s :No such nick", name
, self
->name
);
473 part_helper(struct chanNode
*cn
, void *data
)
475 DelChannelUser(data
, cn
, false, 0);
479 reg_chanmsg_func(unsigned char prefix
, struct userNode
*service
, chanmsg_func_t handler
)
481 if (chanmsg_funcs
[prefix
].func
)
482 log_module(MAIN_LOG
, LOG_WARNING
, "Re-registering new chanmsg handler for character `%c'.", prefix
);
483 chanmsg_funcs
[prefix
].func
= handler
;
484 chanmsg_funcs
[prefix
].service
= service
;
488 reg_allchanmsg_func(struct userNode
*service
, chanmsg_func_t handler
)
491 for (x
= 0; x
< ALLCHANMSG_FUNCS_MAX
; x
++) {
492 if (allchanmsg_funcs
[x
].func
)
494 allchanmsg_funcs
[x
].func
= handler
;
495 allchanmsg_funcs
[x
].service
= service
;
501 get_chanmsg_bot(unsigned char prefix
)
503 return chanmsg_funcs
[prefix
].service
;
506 static mode_change_func_t
*mcf_list
;
507 static unsigned int mcf_size
= 0, mcf_used
= 0;
510 reg_mode_change_func(mode_change_func_t handler
)
512 if (mcf_used
== mcf_size
) {
515 mcf_list
= realloc(mcf_list
, mcf_size
*sizeof(mode_change_func_t
));
518 mcf_list
= malloc(mcf_size
*sizeof(mode_change_func_t
));
521 mcf_list
[mcf_used
++] = handler
;
524 struct mod_chanmode
*
525 mod_chanmode_alloc(unsigned int argc
)
527 struct mod_chanmode
*res
;
529 res
= calloc(1, sizeof(*res
) + (argc
-1)*sizeof(res
->args
[0]));
531 res
= calloc(1, sizeof(*res
));
534 res
->alloc_argc
= argc
;
541 struct mod_chanmode
*
542 mod_chanmode_dup(struct mod_chanmode
*orig
, unsigned int extra
)
544 struct mod_chanmode
*res
;
545 res
= mod_chanmode_alloc(orig
->argc
+ extra
);
547 res
->modes_set
= orig
->modes_set
;
548 res
->modes_clear
= orig
->modes_clear
;
549 res
->new_limit
= orig
->new_limit
;
550 memcpy(res
->new_key
, orig
->new_key
, sizeof(res
->new_key
));
551 memcpy(res
->new_upass
, orig
->new_upass
, sizeof(res
->new_upass
));
552 memcpy(res
->new_apass
, orig
->new_apass
, sizeof(res
->new_apass
));
553 res
->argc
= orig
->argc
;
554 memcpy(res
->args
, orig
->args
, orig
->argc
*sizeof(orig
->args
[0]));
560 mod_chanmode_apply(struct userNode
*who
, struct chanNode
*channel
, struct mod_chanmode
*change
)
563 struct exemptNode
*en
;
566 assert(change
->argc
<= change
->alloc_argc
);
567 channel
->modes
= (channel
->modes
& ~change
->modes_clear
) | change
->modes_set
;
568 if (change
->modes_set
& MODE_LIMIT
)
569 channel
->limit
= change
->new_limit
;
570 if (change
->modes_set
& MODE_KEY
)
571 strcpy(channel
->key
, change
->new_key
);
572 if (change
->modes_set
& MODE_UPASS
)
573 strcpy(channel
->upass
, change
->new_upass
);
574 if (change
->modes_set
& MODE_APASS
)
575 strcpy(channel
->apass
, change
->new_apass
);
576 for (ii
= 0; ii
< change
->argc
; ++ii
) {
577 switch (change
->args
[ii
].mode
) {
579 /* If any existing ban is a subset of the new ban,
580 * silently remove it. The new ban is not allowed
581 * to be more specific than an existing ban.
583 for (jj
=0; jj
<channel
->banlist
.used
; ++jj
) {
584 bn
= channel
->banlist
.list
[jj
];
585 if (match_ircglobs(change
->args
[ii
].u
.hostmask
, bn
->ban
)) {
586 banList_remove(&channel
->banlist
, bn
);
591 bn
= calloc(1, sizeof(*bn
));
592 safestrncpy(bn
->ban
, change
->args
[ii
].u
.hostmask
, sizeof(bn
->ban
));
594 safestrncpy(bn
->who
, who
->nick
, sizeof(bn
->who
));
596 safestrncpy(bn
->who
, "<unknown>", sizeof(bn
->who
));
598 banList_append(&channel
->banlist
, bn
);
600 case MODE_REMOVE
|MODE_BAN
:
601 for (jj
=0; jj
<channel
->banlist
.used
; ++jj
) {
602 bn
= channel
->banlist
.list
[jj
];
603 if (strcmp(bn
->ban
, change
->args
[ii
].u
.hostmask
))
606 banList_remove(&channel
->banlist
, bn
);
611 /* If any existing exempt is a subset of the new exempt,
612 * silently remove it. The new exempt is not allowed
613 * to be more specific than an existing exempt.
615 for (jj
=0; jj
<channel
->exemptlist
.used
; ++jj
) {
616 if (match_ircglobs(change
->args
[ii
].u
.hostmask
, channel
->exemptlist
.list
[jj
]->exempt
)) {
617 exemptList_remove(&channel
->exemptlist
, channel
->exemptlist
.list
[jj
]);
618 free(channel
->exemptlist
.list
[jj
]);
622 en
= calloc(1, sizeof(*en
));
623 safestrncpy(en
->exempt
, change
->args
[ii
].u
.hostmask
, sizeof(en
->exempt
));
625 safestrncpy(en
->who
, who
->nick
, sizeof(en
->who
));
627 safestrncpy(en
->who
, "<unknown>", sizeof(en
->who
));
629 exemptList_append(&channel
->exemptlist
, en
);
631 case MODE_REMOVE
|MODE_EXEMPT
:
632 for (jj
=0; jj
<channel
->exemptlist
.used
; ++jj
) {
633 if (strcmp(channel
->exemptlist
.list
[jj
]->exempt
, change
->args
[ii
].u
.hostmask
))
635 free(channel
->exemptlist
.list
[jj
]);
636 exemptList_remove(&channel
->exemptlist
, channel
->exemptlist
.list
[jj
]);
640 /* XXX Hack: this is the stupedest use of switch iv ever seen.
641 * you have to compare for EVERY POSSIBLE COMBINATION of bitmask
642 * because switch does only full comparison. This needs redone as if/else.
647 case MODE_VOICE
|MODE_CHANOP
:
648 case MODE_VOICE
|MODE_HALFOP
:
649 case MODE_CHANOP
|MODE_HALFOP
:
650 case MODE_VOICE
|MODE_CHANOP
|MODE_HALFOP
:
651 case MODE_REMOVE
|MODE_CHANOP
:
652 case MODE_REMOVE
|MODE_HALFOP
:
653 case MODE_REMOVE
|MODE_VOICE
:
654 case MODE_REMOVE
|MODE_VOICE
|MODE_CHANOP
:
655 case MODE_REMOVE
|MODE_VOICE
|MODE_HALFOP
:
656 case MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
:
657 case MODE_REMOVE
|MODE_VOICE
|MODE_CHANOP
|MODE_HALFOP
:
658 if (change
->args
[ii
].mode
& MODE_REMOVE
)
659 change
->args
[ii
].u
.member
->modes
&= ~change
->args
[ii
].mode
;
661 change
->args
[ii
].u
.member
->modes
|= change
->args
[ii
].mode
;
664 assert(0 && "Invalid mode argument");
671 mod_chanmode_free(struct mod_chanmode
*change
)
677 mod_chanmode(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
, unsigned int flags
)
679 struct modeNode
*member
;
680 struct mod_chanmode
*change
;
685 if (!modes
|| !modes
[0])
687 if (who
&& (member
= GetUserMode(channel
, who
)))
688 base_oplevel
= member
->oplevel
;
690 base_oplevel
= MAXOPLEVEL
;
691 if (!(change
= mod_chanmode_parse(channel
, modes
, argc
, flags
, base_oplevel
)))
694 call_channel_mode_funcs(who
, channel
, modes
, argc
);
696 if (flags
& MC_ANNOUNCE
)
697 mod_chanmode_announce(who
, channel
, change
);
699 mod_chanmode_apply(who
, channel
, change
);
700 if (flags
& MC_NOTIFY
)
701 for (ii
= 0; ii
< mcf_used
; ++ii
)
702 mcf_list
[ii
](channel
, who
, change
);
703 mod_chanmode_free(change
);
708 irc_make_chanmode(struct chanNode
*chan
, char *out
)
710 struct mod_chanmode change
;
711 mod_chanmode_init(&change
);
712 change
.modes_set
= chan
->modes
;
713 change
.new_limit
= chan
->limit
;
714 safestrncpy(change
.new_key
, chan
->key
, sizeof(change
.new_key
));
715 safestrncpy(change
.new_upass
, chan
->upass
, sizeof(change
.new_upass
));
716 safestrncpy(change
.new_apass
, chan
->apass
, sizeof(change
.new_apass
));
717 return strlen(mod_chanmode_format(&change
, out
));
720 static user_mode_func_t
*um_list
;
721 static unsigned int um_size
= 0, um_used
= 0;
724 reg_user_mode_func(user_mode_func_t handler
)
726 if (um_used
== um_size
) {
729 um_list
= realloc(um_list
, um_size
*sizeof(user_mode_func_t
));
732 um_list
= malloc(um_size
*sizeof(user_mode_func_t
));
735 um_list
[um_used
++] = handler
;
739 unreg_user_mode_func(user_mode_func_t handler
)
742 for (i
=0; i
<um_used
; i
++) {
743 if (um_list
[i
] == handler
) break;
745 if (i
== um_used
) return;
746 memmove(um_list
+i
, um_list
+i
+1, (um_used
-i
-1)*sizeof(um_list
[0]));
751 call_user_mode_funcs(struct userNode
*user
, const char *mode_change
)
754 for (n
=0; n
<um_used
; n
++) {
755 um_list
[n
](user
, mode_change
);
759 static channel_mode_func_t
*cm_list
;
760 static unsigned int cm_size
= 0, cm_used
= 0;
763 reg_channel_mode_func(channel_mode_func_t handler
)
765 if (cm_used
== cm_size
) {
768 cm_list
= realloc(cm_list
, cm_size
*sizeof(channel_mode_func_t
));
771 cm_list
= malloc(cm_size
*sizeof(channel_mode_func_t
));
774 cm_list
[cm_used
++] = handler
;
778 unreg_channel_mode_func(channel_mode_func_t handler
)
781 for (i
=0; i
<cm_used
; i
++) {
782 if(cm_list
[i
] == handler
) break;
784 if (i
== cm_used
) return;
785 memmove(cm_list
+i
, cm_list
+i
+1, (cm_used
-i
-1)*sizeof(cm_list
[0]));
790 call_channel_mode_funcs(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
)
793 for (n
=0; n
<cm_used
; n
++) {
794 cm_list
[n
](who
, channel
, modes
, argc
);
799 generate_hostmask(struct userNode
*user
, int options
)
802 char *nickname
, *ident
, *hostname
, *mask
;
805 /* figure out string parts */
806 if (options
& GENMASK_OMITNICK
)
808 else if (options
& GENMASK_USENICK
)
809 nickname
= user
->nick
;
812 if (options
& GENMASK_STRICT_IDENT
)
813 // sethost - reed/apples
814 if (IsSetHost(user
)) {
815 ident
= alloca(strcspn(user
->sethost
, "@")+2);
816 safestrncpy(ident
, user
->sethost
, strcspn(user
->sethost
, "@")+1);
820 else if (options
& GENMASK_ANY_IDENT
)
823 // sethost - reed/apples
824 if (IsSetHost(user
)) {
825 ident
= alloca(strcspn(user
->sethost
, "@")+3);
827 safestrncpy(ident
+1, user
->sethost
, strcspn(user
->sethost
, "@")+1);
829 ident
= alloca(strlen(user
->ident
)+2);
831 strcpy(ident
+1, user
->ident
+ ((*user
->ident
== '~')?1:0));
834 hostname
= user
->hostname
;
835 if (IsFakeHost(user
) && IsHiddenHost(user
) && !(options
& GENMASK_NO_HIDING
)) {
836 hostname
= user
->fakehost
;
837 } else if (IsHiddenHost(user
)) {
840 data
= conf_get_data("server/hidden_host_type", RECDB_QSTRING
);
844 if ((style
== 1) && user
->handle_info
&& hidden_host_suffix
&& !(options
& GENMASK_NO_HIDING
)) {
845 hostname
= alloca(strlen(user
->handle_info
->handle
) + strlen(hidden_host_suffix
) + 2);
846 sprintf(hostname
, "%s.%s", user
->handle_info
->handle
, hidden_host_suffix
);
847 } else if ((style
== 2) && !(options
& GENMASK_NO_HIDING
)) {
848 hostname
= alloca(strlen(user
->crypthost
));
849 sprintf(hostname
, "%s", user
->crypthost
);
851 } else if (options
& GENMASK_STRICT_HOST
) {
852 if (options
& GENMASK_BYIP
)
853 hostname
= (char*)irc_ntoa(&user
->ip
);
854 } else if ((options
& GENMASK_BYIP
) || irc_pton(&ip
, NULL
, hostname
)) {
855 /* Should generate an IP-based hostmask. */
856 hostname
= alloca(IRC_NTOP_MAX_SIZE
);
857 hostname
[IRC_NTOP_MAX_SIZE
-1] = '\0';
858 if (irc_in_addr_is_ipv4(user
->ip
)) {
859 /* By popular acclaim, a /16 hostmask is used. */
860 sprintf(hostname
, "%d.%d.*", user
->ip
.in6_8
[12], user
->ip
.in6_8
[13]);
861 } else if (irc_in_addr_is_ipv6(user
->ip
)) {
862 /* Who knows what the default mask should be? Use a /48 to start with. */
863 sprintf(hostname
, "%x:%x:%x:*", user
->ip
.in6
[0], user
->ip
.in6
[1], user
->ip
.in6
[2]);
865 /* Unknown type; just copy IP directly. */
866 irc_ntop(hostname
, IRC_NTOP_MAX_SIZE
, &user
->ip
);
870 /* This heuristic could be made smarter. Is it worth the effort? */
871 for (ii
=cnt
=0; hostname
[ii
]; ii
++)
872 if (hostname
[ii
] == '.')
875 /* only a two-level domain name; leave hostname */
876 } else if (cnt
== 2) {
877 for (ii
=0; user
->hostname
[ii
] != '.'; ii
++) ;
878 /* Add 3 to account for the *. and \0. */
879 hostname
= alloca(strlen(user
->hostname
+ii
)+3);
880 sprintf(hostname
, "*.%s", user
->hostname
+ii
+1);
882 for (cnt
=3, ii
--; cnt
; ii
--)
883 if (user
->hostname
[ii
] == '.')
885 /* The loop above will overshoot the dot one character;
886 we skip forward two (the one character and the dot)
887 when printing, so we only add one for the \0. */
888 hostname
= alloca(strlen(user
->hostname
+ii
)+1);
889 sprintf(hostname
, "*.%s", user
->hostname
+ii
+2);
892 // sethost - reed/apples
894 hostname
= strchr(user
->sethost
, '@') + 1;
897 len
= strlen(ident
) + strlen(hostname
) + 2;
899 len
+= strlen(nickname
) + 1;
901 sprintf(mask
, "%s!%s@%s", nickname
, ident
, hostname
);
904 sprintf(mask
, "%s@%s", ident
, hostname
);
910 IsChannelName(const char *name
) {
915 for (ii
=1; name
[ii
]; ++ii
) {
916 if ((name
[ii
] > 0) && (name
[ii
] <= 32))
920 if (name
[ii
] == '\xa0')