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 3 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
)
98 if (fd
&& fd
->state
!= IO_CONNECTED
)
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_time
= 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 log_module(MAIN_LOG
, LOG_INFO
, "Connection to server established.");
151 cManager
.uplink
= target
;
152 target
->state
= AUTHENTICATING
;
158 replay_read_line(void)
163 if (replay_line
[0]) return;
165 if (!fgets(replay_line
, sizeof(replay_line
), replay_file
)) {
166 if (feof(replay_file
)) {
168 memset(replay_line
, 0, sizeof(replay_line
));
172 if ((replay_line
[0] != '[')
173 || (replay_line
[3] != ':')
174 || (replay_line
[6] != ':')
175 || (replay_line
[9] != ' ')
176 || (replay_line
[12] != '/')
177 || (replay_line
[15] != '/')
178 || (replay_line
[20] != ']')
179 || (replay_line
[21] != ' ')) {
180 log_module(MAIN_LOG
, LOG_ERROR
, "Unrecognized timestamp in replay file: %s", replay_line
);
183 timestamp
.tm_hour
= strtoul(replay_line
+1, NULL
, 10);
184 timestamp
.tm_min
= strtoul(replay_line
+4, NULL
, 10);
185 timestamp
.tm_sec
= strtoul(replay_line
+7, NULL
, 10);
186 timestamp
.tm_mon
= strtoul(replay_line
+10, NULL
, 10) - 1;
187 timestamp
.tm_mday
= strtoul(replay_line
+13, NULL
, 10);
188 timestamp
.tm_year
= strtoul(replay_line
+16, NULL
, 10) - 1900;
189 timestamp
.tm_isdst
= 0;
190 new_time
= mktime(×tamp
);
191 if (new_time
== -1) {
192 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
);
197 if (strncmp(replay_line
+22, "(info) ", 7))
206 char read_line
[MAXLEN
];
209 /* if it's a sent line, break out to handle it */
210 if (!strncmp(replay_line
+29, " ", 3))
212 if (!strncmp(replay_line
+29, "W: ", 3)) {
213 log_module(MAIN_LOG
, LOG_ERROR
, "Expected response from services: %s", replay_line
+32);
219 log_replay(MAIN_LOG
, false, replay_line
+32);
220 safestrncpy(read_line
, replay_line
+32, sizeof(read_line
));
221 len
= strlen(read_line
);
222 if (read_line
[len
-1] == '\n')
223 read_line
[--len
] = 0;
225 parse_line(read_line
, 0);
231 replay_write(char *text
)
234 if (strncmp(replay_line
+29, "W: ", 3)) {
235 log_module(MAIN_LOG
, LOG_ERROR
, "Unexpected output during replay: %s", text
);
238 if (strcmp(replay_line
+32, text
)) {
239 log_module(MAIN_LOG
, LOG_ERROR
, "Incorrect output during replay:\nReceived: %sExpected: %s", text
, replay_line
+32);
241 log_replay(MAIN_LOG
, true, text
);
247 void putsock(const char *text
, ...) PRINTF_LIKE(1, 2);
250 putsock(const char *text
, ...)
256 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
258 va_start(arg_list
, text
);
259 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
261 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
264 log_replay(MAIN_LOG
, true, buffer
);
265 buffer
[pos
++] = '\n';
267 ioset_write(socket_io_fd
, buffer
, pos
);
269 replay_write(buffer
);
277 replay_connected
= 0;
278 socket_destroyed(socket_io_fd
);
280 ioset_close(socket_io_fd
, 3);
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
;
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 cf
= &chanmsg_funcs
[(unsigned char)pd
->text
[0]];
447 if (!pd
->is_notice
&& cf
->func
448 && ((cn
->modes
& MODE_REGISTERED
) || GetUserMode(cn
, cf
->service
)))
449 cf
->func(pd
->user
, cn
, pd
->text
+1, cf
->service
, pd
->is_notice
);
451 spamserv_channel_message(cn
, pd
->user
, pd
->text
);
453 /* This catches *all* text sent to the channel that the services server sees */
454 for (x
= 0; x
< ALLCHANMSG_FUNCS_MAX
; x
++) {
455 cf
= (struct chanmsg_func
*)&allchanmsg_funcs
[x
];
457 break; /* end of list */
459 cf
->func(pd
->user
, cn
, pd
->text
, cf
->service
, pd
->is_notice
);
464 privmsg_invalid(char *name
, void *data
)
466 struct privmsg_desc
*pd
= data
;
470 irc_numeric(pd
->user
, ERR_NOSUCHNICK
, "%s@%s :No such nick", name
, self
->name
);
474 struct userNode
*user
;
479 part_helper(struct chanNode
*cn
, void *data
)
481 struct part_desc
*desc
= data
;
482 DelChannelUser(desc
->user
, cn
, desc
->text
, false);
485 static CMD_FUNC(cmd_part
)
487 struct part_desc desc
;
491 desc
.user
= GetUserH(origin
);
494 desc
.text
= (argc
> 2) ? argv
[argc
- 1] : NULL
;
495 parse_foreach(argv
[1], part_helper
, NULL
, NULL
, NULL
, &desc
);
500 reg_chanmsg_func(unsigned char prefix
, struct userNode
*service
, chanmsg_func_t handler
)
502 if (chanmsg_funcs
[prefix
].func
)
503 log_module(MAIN_LOG
, LOG_WARNING
, "Re-registering new chanmsg handler for character `%c'.", prefix
);
504 chanmsg_funcs
[prefix
].func
= handler
;
505 chanmsg_funcs
[prefix
].service
= service
;
509 reg_allchanmsg_func(struct userNode
*service
, chanmsg_func_t handler
)
512 for (x
= 0; x
< ALLCHANMSG_FUNCS_MAX
; x
++) {
513 if (allchanmsg_funcs
[x
].func
)
515 allchanmsg_funcs
[x
].func
= handler
;
516 allchanmsg_funcs
[x
].service
= service
;
522 get_chanmsg_bot(unsigned char prefix
)
524 return chanmsg_funcs
[prefix
].service
;
527 static mode_change_func_t
*mcf_list
;
528 static unsigned int mcf_size
= 0, mcf_used
= 0;
531 reg_mode_change_func(mode_change_func_t handler
)
533 if (mcf_used
== mcf_size
) {
536 mcf_list
= realloc(mcf_list
, mcf_size
*sizeof(mode_change_func_t
));
539 mcf_list
= malloc(mcf_size
*sizeof(mode_change_func_t
));
542 mcf_list
[mcf_used
++] = handler
;
545 static oper_func_t
*of_list
;
547 static unsigned int of_size
= 0, of_used
= 0;
550 reg_oper_func(oper_func_t handler
)
552 if (of_used
== of_size
) {
555 of_list
= realloc(of_list
, of_size
*sizeof(oper_func_t
));
558 of_list
= malloc(of_size
*sizeof(oper_func_t
));
561 of_list
[of_used
++] = handler
;
565 call_oper_funcs(struct userNode
*user
)
570 for (n
=0; (n
<of_used
) && !user
->dead
; n
++)
576 struct mod_chanmode
*
577 mod_chanmode_alloc(unsigned int argc
)
579 struct mod_chanmode
*res
;
581 res
= calloc(1, sizeof(*res
) + (argc
-1)*sizeof(res
->args
[0]));
583 res
= calloc(1, sizeof(*res
));
586 res
->alloc_argc
= argc
;
593 struct mod_chanmode
*
594 mod_chanmode_dup(struct mod_chanmode
*orig
, unsigned int extra
)
596 struct mod_chanmode
*res
;
597 res
= mod_chanmode_alloc(orig
->argc
+ extra
);
599 res
->modes_set
= orig
->modes_set
;
600 res
->modes_clear
= orig
->modes_clear
;
601 res
->new_limit
= orig
->new_limit
;
602 memcpy(res
->new_key
, orig
->new_key
, sizeof(res
->new_key
));
603 memcpy(res
->new_upass
, orig
->new_upass
, sizeof(res
->new_upass
));
604 memcpy(res
->new_apass
, orig
->new_apass
, sizeof(res
->new_apass
));
605 res
->argc
= orig
->argc
;
606 memcpy(res
->args
, orig
->args
, orig
->argc
*sizeof(orig
->args
[0]));
612 mod_chanmode_apply(struct userNode
*who
, struct chanNode
*channel
, struct mod_chanmode
*change
)
615 struct exemptNode
*en
;
618 assert(change
->argc
<= change
->alloc_argc
);
619 channel
->modes
= (channel
->modes
& ~change
->modes_clear
) | change
->modes_set
;
620 if (change
->modes_set
& MODE_LIMIT
)
621 channel
->limit
= change
->new_limit
;
622 if (change
->modes_set
& MODE_KEY
)
623 strcpy(channel
->key
, change
->new_key
);
624 if (change
->modes_set
& MODE_UPASS
)
625 strcpy(channel
->upass
, change
->new_upass
);
626 if (change
->modes_set
& MODE_APASS
)
627 strcpy(channel
->apass
, change
->new_apass
);
628 for (ii
= 0; ii
< change
->argc
; ++ii
) {
629 switch (change
->args
[ii
].mode
) {
631 /* If any existing ban is a subset of the new ban,
632 * silently remove it. The new ban is not allowed
633 * to be more specific than an existing ban.
635 for (jj
=0; jj
<channel
->banlist
.used
; ++jj
) {
636 bn
= channel
->banlist
.list
[jj
];
637 if (match_ircglobs(change
->args
[ii
].u
.hostmask
, bn
->ban
)) {
638 banList_remove(&channel
->banlist
, bn
);
643 bn
= calloc(1, sizeof(*bn
));
644 safestrncpy(bn
->ban
, change
->args
[ii
].u
.hostmask
, sizeof(bn
->ban
));
646 safestrncpy(bn
->who
, who
->nick
, sizeof(bn
->who
));
648 safestrncpy(bn
->who
, "<unknown>", sizeof(bn
->who
));
650 banList_append(&channel
->banlist
, bn
);
652 case MODE_REMOVE
|MODE_BAN
:
653 for (jj
=0; jj
<channel
->banlist
.used
; ++jj
) {
654 bn
= channel
->banlist
.list
[jj
];
655 if (strcmp(bn
->ban
, change
->args
[ii
].u
.hostmask
))
658 banList_remove(&channel
->banlist
, bn
);
663 /* If any existing exempt is a subset of the new exempt,
664 * silently remove it. The new exempt is not allowed
665 * to be more specific than an existing exempt.
667 for (jj
=0; jj
<channel
->exemptlist
.used
; ++jj
) {
668 if (match_ircglobs(change
->args
[ii
].u
.hostmask
, channel
->exemptlist
.list
[jj
]->exempt
)) {
669 exemptList_remove(&channel
->exemptlist
, channel
->exemptlist
.list
[jj
]);
670 free(channel
->exemptlist
.list
[jj
]);
674 en
= calloc(1, sizeof(*en
));
675 safestrncpy(en
->exempt
, change
->args
[ii
].u
.hostmask
, sizeof(en
->exempt
));
677 safestrncpy(en
->who
, who
->nick
, sizeof(en
->who
));
679 safestrncpy(en
->who
, "<unknown>", sizeof(en
->who
));
681 exemptList_append(&channel
->exemptlist
, en
);
683 case MODE_REMOVE
|MODE_EXEMPT
:
684 for (jj
=0; jj
<channel
->exemptlist
.used
; ++jj
) {
685 if (strcmp(channel
->exemptlist
.list
[jj
]->exempt
, change
->args
[ii
].u
.hostmask
))
687 free(channel
->exemptlist
.list
[jj
]);
688 exemptList_remove(&channel
->exemptlist
, channel
->exemptlist
.list
[jj
]);
692 /* XXX Hack: this is the stupedest use of switch iv ever seen.
693 * you have to compare for EVERY POSSIBLE COMBINATION of bitmask
694 * because switch does only full comparison. This needs redone as if/else.
699 case MODE_VOICE
|MODE_CHANOP
:
700 case MODE_VOICE
|MODE_HALFOP
:
701 case MODE_CHANOP
|MODE_HALFOP
:
702 case MODE_VOICE
|MODE_CHANOP
|MODE_HALFOP
:
703 case MODE_REMOVE
|MODE_CHANOP
:
704 case MODE_REMOVE
|MODE_HALFOP
:
705 case MODE_REMOVE
|MODE_VOICE
:
706 case MODE_REMOVE
|MODE_VOICE
|MODE_CHANOP
:
707 case MODE_REMOVE
|MODE_VOICE
|MODE_HALFOP
:
708 case MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
:
709 case MODE_REMOVE
|MODE_VOICE
|MODE_CHANOP
|MODE_HALFOP
:
710 if (change
->args
[ii
].mode
& MODE_REMOVE
)
711 change
->args
[ii
].u
.member
->modes
&= ~change
->args
[ii
].mode
;
713 change
->args
[ii
].u
.member
->modes
|= change
->args
[ii
].mode
;
716 assert(0 && "Invalid mode argument");
723 mod_chanmode_free(struct mod_chanmode
*change
)
729 mod_chanmode(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
, unsigned int flags
)
731 struct modeNode
*member
;
732 struct mod_chanmode
*change
;
737 if (!modes
|| !modes
[0])
739 if (who
&& (member
= GetUserMode(channel
, who
)))
740 base_oplevel
= member
->oplevel
;
742 base_oplevel
= MAXOPLEVEL
;
743 if (!(change
= mod_chanmode_parse(channel
, modes
, argc
, flags
, base_oplevel
)))
746 call_channel_mode_funcs(who
, channel
, modes
, argc
);
748 if (flags
& MC_ANNOUNCE
)
749 mod_chanmode_announce(who
, channel
, change
);
751 mod_chanmode_apply(who
, channel
, change
);
752 if (flags
& MC_NOTIFY
)
753 for (ii
= 0; ii
< mcf_used
; ++ii
)
754 mcf_list
[ii
](channel
, who
, change
);
755 mod_chanmode_free(change
);
760 irc_make_chanmode(struct chanNode
*chan
, char *out
)
762 struct mod_chanmode change
;
763 mod_chanmode_init(&change
);
764 change
.modes_set
= chan
->modes
;
765 change
.new_limit
= chan
->limit
;
766 safestrncpy(change
.new_key
, chan
->key
, sizeof(change
.new_key
));
767 safestrncpy(change
.new_upass
, chan
->upass
, sizeof(change
.new_upass
));
768 safestrncpy(change
.new_apass
, chan
->apass
, sizeof(change
.new_apass
));
769 return strlen(mod_chanmode_format(&change
, out
));
772 static user_mode_func_t
*um_list
;
773 static unsigned int um_size
= 0, um_used
= 0;
776 reg_user_mode_func(user_mode_func_t handler
)
778 if (um_used
== um_size
) {
781 um_list
= realloc(um_list
, um_size
*sizeof(user_mode_func_t
));
784 um_list
= malloc(um_size
*sizeof(user_mode_func_t
));
787 um_list
[um_used
++] = handler
;
791 unreg_user_mode_func(user_mode_func_t handler
)
794 for (i
=0; i
<um_used
; i
++) {
795 if (um_list
[i
] == handler
) break;
797 if (i
== um_used
) return;
798 memmove(um_list
+i
, um_list
+i
+1, (um_used
-i
-1)*sizeof(um_list
[0]));
803 call_user_mode_funcs(struct userNode
*user
, const char *mode_change
)
806 for (n
=0; n
<um_used
; n
++) {
807 um_list
[n
](user
, mode_change
);
811 static channel_mode_func_t
*cm_list
;
812 static unsigned int cm_size
= 0, cm_used
= 0;
815 reg_channel_mode_func(channel_mode_func_t handler
)
817 if (cm_used
== cm_size
) {
820 cm_list
= realloc(cm_list
, cm_size
*sizeof(channel_mode_func_t
));
823 cm_list
= malloc(cm_size
*sizeof(channel_mode_func_t
));
826 cm_list
[cm_used
++] = handler
;
830 unreg_channel_mode_func(channel_mode_func_t handler
)
833 for (i
=0; i
<cm_used
; i
++) {
834 if(cm_list
[i
] == handler
) break;
836 if (i
== cm_used
) return;
837 memmove(cm_list
+i
, cm_list
+i
+1, (cm_used
-i
-1)*sizeof(cm_list
[0]));
842 call_channel_mode_funcs(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
)
845 for (n
=0; n
<cm_used
; n
++) {
846 cm_list
[n
](who
, channel
, modes
, argc
);
851 generate_hostmask(struct userNode
*user
, int options
)
854 char *nickname
, *ident
, *hostname
, *mask
;
857 /* figure out string parts */
858 if (options
& GENMASK_OMITNICK
)
860 else if (options
& GENMASK_USENICK
)
861 nickname
= user
->nick
;
864 if (options
& GENMASK_STRICT_IDENT
)
865 // sethost - reed/apples
866 if (IsSetHost(user
)) {
867 ident
= alloca(strcspn(user
->sethost
, "@")+2);
868 safestrncpy(ident
, user
->sethost
, strcspn(user
->sethost
, "@")+1);
872 else if (options
& GENMASK_ANY_IDENT
)
875 // sethost - reed/apples
876 if (IsSetHost(user
)) {
877 ident
= alloca(strcspn(user
->sethost
, "@")+3);
879 safestrncpy(ident
+1, user
->sethost
, strcspn(user
->sethost
, "@")+1);
881 ident
= alloca(strlen(user
->ident
)+2);
883 strcpy(ident
+1, user
->ident
+ ((*user
->ident
== '~')?1:0));
886 hostname
= user
->hostname
;
887 if (IsFakeHost(user
) && IsHiddenHost(user
) && !(options
& GENMASK_NO_HIDING
)) {
888 hostname
= user
->fakehost
;
889 } else if (IsHiddenHost(user
)) {
892 data
= conf_get_data("server/hidden_host_type", RECDB_QSTRING
);
896 if ((style
== 1) && user
->handle_info
&& hidden_host_suffix
&& !(options
& GENMASK_NO_HIDING
)) {
897 hostname
= alloca(strlen(user
->handle_info
->handle
) + strlen(hidden_host_suffix
) + 2);
898 sprintf(hostname
, "%s.%s", user
->handle_info
->handle
, hidden_host_suffix
);
899 } else if ((style
== 2) && !(options
& GENMASK_NO_HIDING
)) {
900 hostname
= alloca(strlen(user
->crypthost
));
901 sprintf(hostname
, "%s", user
->crypthost
);
903 } else if (options
& GENMASK_STRICT_HOST
) {
904 if (options
& GENMASK_BYIP
)
905 hostname
= (char*)irc_ntoa(&user
->ip
);
906 } else if ((options
& GENMASK_BYIP
) || irc_pton(&ip
, NULL
, hostname
)) {
907 /* Should generate an IP-based hostmask. */
908 hostname
= alloca(IRC_NTOP_MAX_SIZE
);
909 hostname
[IRC_NTOP_MAX_SIZE
-1] = '\0';
910 if (irc_in_addr_is_ipv4(user
->ip
)) {
911 /* By popular acclaim, a /16 hostmask is used. */
912 sprintf(hostname
, "%d.%d.*", user
->ip
.in6_8
[12], user
->ip
.in6_8
[13]);
913 } else if (irc_in_addr_is_ipv6(user
->ip
)) {
914 /* Who knows what the default mask should be? Use a /48 to start with. */
915 sprintf(hostname
, "%x:%x:%x:*", user
->ip
.in6
[0], user
->ip
.in6
[1], user
->ip
.in6
[2]);
917 /* Unknown type; just copy IP directly. */
918 irc_ntop(hostname
, IRC_NTOP_MAX_SIZE
, &user
->ip
);
922 /* This heuristic could be made smarter. Is it worth the effort? */
923 for (ii
=cnt
=0; hostname
[ii
]; ii
++)
924 if (hostname
[ii
] == '.')
926 if (cnt
== 0 || cnt
== 1) {
927 /* only a one- or two-level domain name; leave hostname */
928 } else if (cnt
== 2) {
929 for (ii
=0; user
->hostname
[ii
] != '.'; ii
++) ;
930 /* Add 3 to account for the *. and \0. */
931 hostname
= alloca(strlen(user
->hostname
+ii
)+3);
932 sprintf(hostname
, "*.%s", user
->hostname
+ii
+1);
934 for (cnt
=3, ii
--; cnt
; ii
--)
935 if (user
->hostname
[ii
] == '.')
937 /* The loop above will overshoot the dot one character;
938 we skip forward two (the one character and the dot)
939 when printing, so we only add one for the \0. */
940 hostname
= alloca(strlen(user
->hostname
+ii
)+1);
941 sprintf(hostname
, "*.%s", user
->hostname
+ii
+2);
944 // sethost - reed/apples
946 hostname
= strchr(user
->sethost
, '@') + 1;
949 len
= strlen(ident
) + strlen(hostname
) + 2;
951 len
+= strlen(nickname
) + 1;
953 sprintf(mask
, "%s!%s@%s", nickname
, ident
, hostname
);
956 sprintf(mask
, "%s@%s", ident
, hostname
);
962 IsChannelName(const char *name
) {
967 for (ii
=1; name
[ii
]; ++ii
) {
968 if ((name
[ii
] > 0) && (name
[ii
] <= 32))
972 if (name
[ii
] == '\xa0')