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.
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
33 #ifdef HAVE_NETINET_IN_H
34 #include <netinet/in.h>
36 #ifdef HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
40 unsigned int lines_processed
;
42 struct io_fd
*socket_io_fd
;
44 const char *hidden_host_suffix
;
46 static char replay_line
[MAXLEN
+80];
48 static int ping_timeout
;
49 static int replay_connected
;
50 static unsigned int nicklen
= NICKLEN
; /* how long do we think servers allow nicks to be? */
51 static struct userList dead_users
;
53 extern struct cManagerNode cManager
;
54 extern unsigned long burst_length
;
55 extern struct cManagerNode cManager
;
56 extern struct policer_params
*oper_policer_params
, *luser_policer_params
;
57 extern time_t boot_time
;
59 void received_ping(void);
61 static int replay_read(void);
62 static dict_t irc_func_dict
;
64 typedef void (*foreach_chanfunc
) (struct chanNode
*chan
, void *data
);
65 typedef void (*foreach_nonchan
) (char *name
, void *data
);
66 typedef void (*foreach_userfunc
) (struct userNode
*user
, void *data
);
67 typedef void (*foreach_nonuser
) (char *name
, void *data
);
68 static void parse_foreach(char *target_list
, foreach_chanfunc cf
, foreach_nonchan nc
, foreach_userfunc uf
, foreach_nonuser nu
, void *data
);
69 static void call_channel_mode_funcs(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
);
72 uplink_readable(struct io_fd
*fd
) {
73 static char buffer
[MAXLEN
];
77 pos
= ioset_line_read(fd
, buffer
, sizeof(buffer
));
82 if ((eol
= strpbrk(buffer
, "\r\n")))
84 log_replay(MAIN_LOG
, false, buffer
);
85 if (cManager
.uplink
->state
!= DISCONNECTED
)
86 parse_line(buffer
, 0);
91 socket_destroyed(struct io_fd
*fd
)
93 if (fd
&& fd
->state
!= IO_CONNECTED
)
94 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to server lost.");
96 cManager
.uplink
->state
= DISCONNECTED
;
98 DelServer(self
->uplink
, 0, NULL
);
101 void replay_event_loop(void)
103 while (!quit_services
) {
104 if (!replay_connected
) {
105 /* this time fudging is to get some of the logging right */
106 self
->link_time
= self
->boot
= now
;
107 cManager
.uplink
->state
= AUTHENTICATING
;
108 irc_introduce(cManager
.uplink
->password
);
109 replay_connected
= 1;
110 } else if (!replay_read()) {
111 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to server lost.");
119 create_socket_client(struct uplinkNode
*target
)
121 int port
= target
->port
;
122 const char *addr
= target
->host
;
125 return feof(replay_file
) ? 0 : 1;
128 /* Leave the existing socket open, say we failed. */
129 log_module(MAIN_LOG
, LOG_ERROR
, "Refusing to create second connection to %s:%d.", addr
, port
);
133 log_module(MAIN_LOG
, LOG_INFO
, "Connecting to %s:%i...", addr
, port
);
135 socket_io_fd
= ioset_connect(cManager
.uplink
->bind_addr
, cManager
.uplink
->bind_addr_len
, addr
, port
, 1, 0, NULL
);
137 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to uplink failed: %s (%d)", strerror(errno
), errno
);
138 target
->state
= DISCONNECTED
;
142 socket_io_fd
->readable_cb
= uplink_readable
;
143 socket_io_fd
->destroy_cb
= socket_destroyed
;
144 socket_io_fd
->line_reads
= 1;
145 log_module(MAIN_LOG
, LOG_INFO
, "Connection to server established.");
146 cManager
.uplink
= target
;
147 target
->state
= AUTHENTICATING
;
153 replay_read_line(void)
158 if (replay_line
[0]) return;
160 if (!fgets(replay_line
, sizeof(replay_line
), replay_file
)) {
161 if (feof(replay_file
)) {
163 memset(replay_line
, 0, sizeof(replay_line
));
167 if ((replay_line
[0] != '[')
168 || (replay_line
[3] != ':')
169 || (replay_line
[6] != ':')
170 || (replay_line
[9] != ' ')
171 || (replay_line
[12] != '/')
172 || (replay_line
[15] != '/')
173 || (replay_line
[20] != ']')
174 || (replay_line
[21] != ' ')) {
175 log_module(MAIN_LOG
, LOG_ERROR
, "Unrecognized timestamp in replay file: %s", replay_line
);
178 timestamp
.tm_hour
= strtoul(replay_line
+1, NULL
, 10);
179 timestamp
.tm_min
= strtoul(replay_line
+4, NULL
, 10);
180 timestamp
.tm_sec
= strtoul(replay_line
+7, NULL
, 10);
181 timestamp
.tm_mon
= strtoul(replay_line
+10, NULL
, 10) - 1;
182 timestamp
.tm_mday
= strtoul(replay_line
+13, NULL
, 10);
183 timestamp
.tm_year
= strtoul(replay_line
+16, NULL
, 10) - 1900;
184 timestamp
.tm_isdst
= 0;
185 new_time
= mktime(×tamp
);
186 if (new_time
== -1) {
187 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
);
192 if (strncmp(replay_line
+22, "(info) ", 7))
201 char read_line
[MAXLEN
];
204 /* if it's a sent line, break out to handle it */
205 if (!strncmp(replay_line
+29, " ", 3))
207 if (!strncmp(replay_line
+29, "W: ", 3)) {
208 log_module(MAIN_LOG
, LOG_ERROR
, "Expected response from services: %s", replay_line
+32);
214 log_replay(MAIN_LOG
, false, replay_line
+32);
215 safestrncpy(read_line
, replay_line
+32, sizeof(read_line
));
216 len
= strlen(read_line
);
217 if (read_line
[len
-1] == '\n')
218 read_line
[--len
] = 0;
220 parse_line(read_line
, 0);
226 replay_write(char *text
)
229 if (strncmp(replay_line
+29, "W: ", 3)) {
230 log_module(MAIN_LOG
, LOG_ERROR
, "Unexpected output during replay: %s", text
);
233 if (strcmp(replay_line
+32, text
)) {
234 log_module(MAIN_LOG
, LOG_ERROR
, "Incorrect output during replay:\nReceived: %sExpected: %s", text
, replay_line
+32);
236 log_replay(MAIN_LOG
, true, text
);
242 void putsock(const char *text
, ...) PRINTF_LIKE(1, 2);
245 putsock(const char *text
, ...)
251 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
253 va_start(arg_list
, text
);
254 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
256 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
259 log_replay(MAIN_LOG
, true, buffer
);
260 buffer
[pos
++] = '\n';
262 ioset_write(socket_io_fd
, buffer
, pos
);
264 replay_write(buffer
);
272 replay_connected
= 0;
273 socket_destroyed(socket_io_fd
);
275 ioset_close(socket_io_fd
, 3);
280 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
281 typedef CMD_FUNC(cmd_func_t
);
283 static void timed_ping_timeout(void *data
);
285 /* Ping state is kept in the timeq (only one of these two can be in
286 * the queue at any given time). */
288 timed_send_ping(UNUSED_ARG(void *data
))
290 irc_ping(self
->name
);
291 timeq_add(now
+ ping_timeout
, timed_ping_timeout
, 0);
295 timed_ping_timeout(UNUSED_ARG(void *data
))
297 /* Uplink "health" tracking could be accomplished by counting the
298 number of ping timeouts that happen for each uplink. After the
299 timeouts per time period exceeds some amount, the uplink could
300 be marked as unavalable.*/
301 irc_squit(self
, "Ping timeout.", NULL
);
304 static CMD_FUNC(cmd_pass
)
306 const char *true_pass
;
310 true_pass
= cManager
.uplink
->their_password
;
311 if (true_pass
&& strcmp(true_pass
, argv
[1])) {
312 /* It might be good to mark the uplink as unavailable when
313 this happens, though there should be a way of resetting
315 irc_squit(self
, "Incorrect password received.", NULL
);
319 cManager
.uplink
->state
= BURSTING
;
323 static CMD_FUNC(cmd_dummy
)
325 /* we don't care about these messages */
329 static CMD_FUNC(cmd_error
)
331 if (argv
[1]) log_module(MAIN_LOG
, LOG_ERROR
, "Error: %s", argv
[1]);
332 log_module(MAIN_LOG
, LOG_ERROR
, "Error received from uplink, squitting.");
334 if (cManager
.uplink
->state
!= CONNECTED
) {
335 /* Error messages while connected should be fine. */
336 cManager
.uplink
->flags
|= UPLINK_UNAVAILABLE
;
337 log_module(MAIN_LOG
, LOG_ERROR
, "Disabling uplink.");
344 static CMD_FUNC(cmd_stats
)
350 if (!(un
= GetUserH(origin
)))
352 switch (argv
[1][0]) {
354 unsigned int uptime
= now
- boot_time
;
355 irc_numeric(un
, RPL_STATSUPTIME
, ":Server Up %d days %d:%02d:%02d",
356 uptime
/(24*60*60), (uptime
/(60*60))%24
, (uptime
/60)%60
, uptime%60
);
357 irc_numeric(un
, RPL_MAXCONNECTIONS
, ":Highest connection count: %d (%d clients)",
358 self
->max_clients
+1, self
->max_clients
);
361 default: /* unrecognized/unhandled stats output */ break;
363 irc_numeric(un
, 219, "%s :End of /STATS report", argv
[1]);
367 static CMD_FUNC(cmd_version
)
369 struct userNode
*user
;
370 if (!(user
= GetUserH(origin
))) {
371 log_module(MAIN_LOG
, LOG_ERROR
, "Could not find VERSION origin user %s", origin
);
374 irc_numeric(user
, 351, "%s %s+[%s] %s", PACKAGE_TARNAME
, PACKAGE_VERSION
, cvs_version
, self
->name
);
378 static CMD_FUNC(cmd_admin
)
380 struct userNode
*user
;
381 struct string_list
*slist
;
383 if (!(user
= GetUserH(origin
))) {
384 log_module(MAIN_LOG
, LOG_ERROR
, "Could not find ADMIN origin user %s", origin
);
387 if ((slist
= conf_get_data("server/admin", RECDB_STRING_LIST
)) && slist
->used
) {
390 irc_numeric(user
, 256, ":Administrative info about %s", self
->name
);
391 for (i
= 0; i
< slist
->used
&& i
< 3; i
++)
392 irc_numeric(user
, 257 + i
, ":%s", slist
->list
[i
]);
394 irc_numeric(user
, 423, ":No administrative info available");
400 recalc_bursts(struct server
*eob_server
)
403 eob_server
->burst
= eob_server
->self_burst
;
404 if (eob_server
->uplink
!= self
)
405 eob_server
->burst
= eob_server
->burst
|| eob_server
->uplink
->burst
;
406 for (nn
=0; nn
< eob_server
->children
.used
; nn
++)
407 recalc_bursts(eob_server
->children
.list
[nn
]);
410 static struct chanmsg_func
{
412 struct userNode
*service
;
414 } chanmsg_funcs
[256]; /* indexed by trigger character */
416 static struct allchanmsg_func
{
418 struct userNode
*service
;
419 } allchanmsg_funcs
[ALLCHANMSG_FUNCS_MAX
];
421 struct privmsg_desc
{
422 struct userNode
*user
;
424 unsigned int is_notice
: 1;
425 unsigned int is_qualified
: 1;
429 privmsg_chan_helper(struct chanNode
*cn
, void *data
)
431 struct privmsg_desc
*pd
= data
;
433 struct chanmsg_func
*cf
;
436 /* Don't complain if it can't find the modeNode because the channel might
438 if ((mn
= GetUserMode(cn
, pd
->user
)))
439 mn
->idle_since
= now
;
441 /* Never send a NOTICE to a channel to one of the services */
442 cf
= &chanmsg_funcs
[(unsigned char)pd
->text
[0]];
443 if (!pd
->is_notice
&& cf
->func
)
444 cf
->func(pd
->user
, cn
, pd
->text
+1, cf
->service
, pd
->is_notice
, cf
->extra
);
446 spamserv_channel_message(cn
, pd
->user
, pd
->text
);
448 /* This catches *all* text sent to the channel that the services server sees */
449 for (x
= 0; x
< ALLCHANMSG_FUNCS_MAX
; x
++) {
450 cf
= (struct chanmsg_func
*)&allchanmsg_funcs
[x
];
452 break; /* end of list */
454 cf
->func(pd
->user
, cn
, pd
->text
, cf
->service
, pd
->is_notice
, cf
->extra
);
459 privmsg_invalid(char *name
, void *data
)
461 struct privmsg_desc
*pd
= data
;
465 irc_numeric(pd
->user
, ERR_NOSUCHNICK
, "%s@%s :No such nick", name
, self
->name
);
469 struct userNode
*user
;
474 part_helper(struct chanNode
*cn
, void *data
)
476 struct part_desc
*desc
= data
;
477 DelChannelUser(desc
->user
, cn
, desc
->text
, false);
480 static CMD_FUNC(cmd_part
)
482 struct part_desc desc
;
486 desc
.user
= GetUserH(origin
);
489 desc
.text
= (argc
> 2) ? argv
[argc
- 1] : NULL
;
490 parse_foreach(argv
[1], part_helper
, NULL
, NULL
, NULL
, &desc
);
495 reg_chanmsg_func(unsigned char prefix
, struct userNode
*service
, chanmsg_func_t handler
, void *extra
)
497 if (chanmsg_funcs
[prefix
].func
)
498 log_module(MAIN_LOG
, LOG_WARNING
, "Re-registering new chanmsg handler for character `%c'.", prefix
);
499 chanmsg_funcs
[prefix
].func
= handler
;
500 chanmsg_funcs
[prefix
].service
= service
;
501 chanmsg_funcs
[prefix
].extra
= extra
;
505 reg_allchanmsg_func(struct userNode
*service
, chanmsg_func_t handler
)
508 for (x
= 0; x
< ALLCHANMSG_FUNCS_MAX
; x
++) {
509 if (allchanmsg_funcs
[x
].func
)
511 allchanmsg_funcs
[x
].func
= handler
;
512 allchanmsg_funcs
[x
].service
= service
;
518 get_chanmsg_bot(unsigned char prefix
)
520 return chanmsg_funcs
[prefix
].service
;
523 static mode_change_func_t
*mcf_list
;
524 static void **mcf_list_extra
;
525 static unsigned int mcf_size
= 0, mcf_used
= 0;
528 reg_mode_change_func(mode_change_func_t handler
, void *extra
)
530 if (mcf_used
== mcf_size
) {
533 mcf_list
= realloc(mcf_list
, mcf_size
*sizeof(mode_change_func_t
));
534 mcf_list_extra
= realloc(mcf_list_extra
, mcf_size
*sizeof(void*));
537 mcf_list
= malloc(mcf_size
*sizeof(mode_change_func_t
));
538 mcf_list_extra
= malloc(mcf_size
*sizeof(void*));
541 mcf_list
[mcf_used
] = handler
;
542 mcf_list_extra
[mcf_used
++] = extra
;
545 static oper_func_t
*of_list
;
546 static void **of_list_extra
;
547 static unsigned int of_size
= 0, of_used
= 0;
550 reg_oper_func(oper_func_t handler
, void *extra
)
552 if (of_used
== of_size
) {
555 of_list
= realloc(of_list
, of_size
*sizeof(oper_func_t
));
556 of_list_extra
= realloc(of_list_extra
, of_size
*sizeof(void*));
559 of_list
= malloc(of_size
*sizeof(oper_func_t
));
560 of_list_extra
= malloc(of_size
*sizeof(void*));
563 of_list
[of_used
] = handler
;
564 of_list_extra
[of_used
++] = extra
;
568 call_oper_funcs(struct userNode
*user
)
573 for (n
=0; (n
<of_used
) && !user
->dead
; n
++)
575 of_list
[n
](user
, of_list_extra
[n
]);
579 struct mod_chanmode
*
580 mod_chanmode_alloc(unsigned int argc
)
582 struct mod_chanmode
*res
;
584 res
= calloc(1, sizeof(*res
) + (argc
-1)*sizeof(res
->args
[0]));
586 res
= calloc(1, sizeof(*res
));
589 res
->alloc_argc
= argc
;
596 struct mod_chanmode
*
597 mod_chanmode_dup(struct mod_chanmode
*orig
, unsigned int extra
)
599 struct mod_chanmode
*res
;
600 res
= mod_chanmode_alloc(orig
->argc
+ extra
);
602 res
->modes_set
= orig
->modes_set
;
603 res
->modes_clear
= orig
->modes_clear
;
604 res
->new_limit
= orig
->new_limit
;
605 memcpy(res
->new_key
, orig
->new_key
, sizeof(res
->new_key
));
606 memcpy(res
->new_upass
, orig
->new_upass
, sizeof(res
->new_upass
));
607 memcpy(res
->new_apass
, orig
->new_apass
, sizeof(res
->new_apass
));
608 res
->argc
= orig
->argc
;
609 memcpy(res
->args
, orig
->args
, orig
->argc
*sizeof(orig
->args
[0]));
615 mod_chanmode_apply(struct userNode
*who
, struct chanNode
*channel
, struct mod_chanmode
*change
)
618 struct exemptNode
*en
;
621 assert(change
->argc
<= change
->alloc_argc
);
622 channel
->modes
= (channel
->modes
& ~change
->modes_clear
) | change
->modes_set
;
623 if (change
->modes_set
& MODE_LIMIT
)
624 channel
->limit
= change
->new_limit
;
625 if (change
->modes_set
& MODE_KEY
)
626 strcpy(channel
->key
, change
->new_key
);
627 if (change
->modes_set
& MODE_UPASS
)
628 strcpy(channel
->upass
, change
->new_upass
);
629 if (change
->modes_set
& MODE_APASS
)
630 strcpy(channel
->apass
, change
->new_apass
);
631 for (ii
= 0; ii
< change
->argc
; ++ii
) {
632 switch (change
->args
[ii
].mode
) {
634 /* If any existing ban is a subset of the new ban,
635 * silently remove it. The new ban is not allowed
636 * to be more specific than an existing ban.
638 for (jj
=0; jj
<channel
->banlist
.used
; ++jj
) {
639 bn
= channel
->banlist
.list
[jj
];
640 if (match_ircglobs(change
->args
[ii
].u
.hostmask
, bn
->ban
)) {
641 banList_remove(&channel
->banlist
, bn
);
646 bn
= calloc(1, sizeof(*bn
));
647 safestrncpy(bn
->ban
, change
->args
[ii
].u
.hostmask
, sizeof(bn
->ban
));
649 safestrncpy(bn
->who
, who
->nick
, sizeof(bn
->who
));
651 safestrncpy(bn
->who
, "<unknown>", sizeof(bn
->who
));
653 banList_append(&channel
->banlist
, bn
);
655 case MODE_REMOVE
|MODE_BAN
:
656 for (jj
=0; jj
<channel
->banlist
.used
; ++jj
) {
657 bn
= channel
->banlist
.list
[jj
];
658 if (strcmp(bn
->ban
, change
->args
[ii
].u
.hostmask
))
661 banList_remove(&channel
->banlist
, bn
);
666 /* If any existing exempt is a subset of the new exempt,
667 * silently remove it. The new exempt is not allowed
668 * to be more specific than an existing exempt.
670 for (jj
=0; jj
<channel
->exemptlist
.used
; ++jj
) {
671 if (match_ircglobs(change
->args
[ii
].u
.hostmask
, channel
->exemptlist
.list
[jj
]->exempt
)) {
672 exemptList_remove(&channel
->exemptlist
, channel
->exemptlist
.list
[jj
]);
673 free(channel
->exemptlist
.list
[jj
]);
677 en
= calloc(1, sizeof(*en
));
678 safestrncpy(en
->exempt
, change
->args
[ii
].u
.hostmask
, sizeof(en
->exempt
));
680 safestrncpy(en
->who
, who
->nick
, sizeof(en
->who
));
682 safestrncpy(en
->who
, "<unknown>", sizeof(en
->who
));
684 exemptList_append(&channel
->exemptlist
, en
);
686 case MODE_REMOVE
|MODE_EXEMPT
:
687 for (jj
=0; jj
<channel
->exemptlist
.used
; ++jj
) {
688 if (strcmp(channel
->exemptlist
.list
[jj
]->exempt
, change
->args
[ii
].u
.hostmask
))
690 free(channel
->exemptlist
.list
[jj
]);
691 exemptList_remove(&channel
->exemptlist
, channel
->exemptlist
.list
[jj
]);
695 /* XXX Hack: this is the stupedest use of switch iv ever seen.
696 * you have to compare for EVERY POSSIBLE COMBINATION of bitmask
697 * because switch does only full comparison. This needs redone as if/else.
702 case MODE_VOICE
|MODE_CHANOP
:
703 case MODE_VOICE
|MODE_HALFOP
:
704 case MODE_CHANOP
|MODE_HALFOP
:
705 case MODE_VOICE
|MODE_CHANOP
|MODE_HALFOP
:
706 case MODE_REMOVE
|MODE_CHANOP
:
707 case MODE_REMOVE
|MODE_HALFOP
:
708 case MODE_REMOVE
|MODE_VOICE
:
709 case MODE_REMOVE
|MODE_VOICE
|MODE_CHANOP
:
710 case MODE_REMOVE
|MODE_VOICE
|MODE_HALFOP
:
711 case MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
:
712 case MODE_REMOVE
|MODE_VOICE
|MODE_CHANOP
|MODE_HALFOP
:
713 if (change
->args
[ii
].mode
& MODE_REMOVE
)
714 change
->args
[ii
].u
.member
->modes
&= ~change
->args
[ii
].mode
;
716 change
->args
[ii
].u
.member
->modes
|= change
->args
[ii
].mode
;
719 assert(0 && "Invalid mode argument");
726 mod_chanmode_free(struct mod_chanmode
*change
)
732 mod_chanmode(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
, unsigned int flags
)
734 struct modeNode
*member
;
735 struct mod_chanmode
*change
;
740 if (!modes
|| !modes
[0])
742 if (who
&& (member
= GetUserMode(channel
, who
)))
743 base_oplevel
= member
->oplevel
;
745 base_oplevel
= MAXOPLEVEL
;
746 if (!(change
= mod_chanmode_parse(channel
, modes
, argc
, flags
, base_oplevel
)))
749 call_channel_mode_funcs(who
, channel
, modes
, argc
);
751 if (flags
& MC_ANNOUNCE
)
752 mod_chanmode_announce(who
, channel
, change
);
754 mod_chanmode_apply(who
, channel
, change
);
755 if (flags
& MC_NOTIFY
)
756 for (ii
= 0; ii
< mcf_used
; ++ii
)
757 mcf_list
[ii
](channel
, who
, change
, mcf_list_extra
[ii
]);
758 mod_chanmode_free(change
);
763 irc_make_chanmode(struct chanNode
*chan
, char *out
)
765 struct mod_chanmode change
;
766 mod_chanmode_init(&change
);
767 change
.modes_set
= chan
->modes
;
768 change
.new_limit
= chan
->limit
;
769 safestrncpy(change
.new_key
, chan
->key
, sizeof(change
.new_key
));
770 safestrncpy(change
.new_upass
, chan
->upass
, sizeof(change
.new_upass
));
771 safestrncpy(change
.new_apass
, chan
->apass
, sizeof(change
.new_apass
));
772 return strlen(mod_chanmode_format(&change
, out
));
775 static user_mode_func_t
*um_list
;
776 static void **um_list_extra
;
777 static unsigned int um_size
= 0, um_used
= 0;
780 reg_user_mode_func(user_mode_func_t handler
, void *extra
)
782 if (um_used
== um_size
) {
785 um_list
= realloc(um_list
, um_size
*sizeof(user_mode_func_t
));
786 um_list_extra
= realloc(um_list_extra
, um_size
*sizeof(void*));
789 um_list
= malloc(um_size
*sizeof(user_mode_func_t
));
790 um_list_extra
= malloc(um_size
*sizeof(void*));
793 um_list
[um_used
] = handler
;
794 um_list_extra
[um_used
++] = extra
;
798 unreg_user_mode_func(user_mode_func_t handler
)
801 for (i
=0; i
<um_used
; i
++) {
802 if (um_list
[i
] == handler
) break;
804 if (i
== um_used
) return;
805 memmove(um_list
+i
, um_list
+i
+1, (um_used
-i
-1)*sizeof(um_list
[0]));
810 call_user_mode_funcs(struct userNode
*user
, const char *mode_change
)
813 for (n
=0; n
<um_used
; n
++) {
814 um_list
[n
](user
, mode_change
, um_list_extra
[n
]);
818 static channel_mode_func_t
*cm_list
;
819 static void **cm_list_extra
;
820 static unsigned int cm_size
= 0, cm_used
= 0;
823 reg_channel_mode_func(channel_mode_func_t handler
, void *extra
)
825 if (cm_used
== cm_size
) {
828 cm_list
= realloc(cm_list
, cm_size
*sizeof(channel_mode_func_t
));
829 cm_list_extra
= realloc(cm_list_extra
, cm_size
*sizeof(void*));
832 cm_list
= malloc(cm_size
*sizeof(channel_mode_func_t
));
833 cm_list_extra
= malloc(cm_size
*sizeof(void*));
836 cm_list
[cm_used
] = handler
;
837 cm_list_extra
[cm_used
++] = extra
;
841 unreg_channel_mode_func(channel_mode_func_t handler
)
844 for (i
=0; i
<cm_used
; i
++) {
845 if(cm_list
[i
] == handler
) break;
847 if (i
== cm_used
) return;
848 memmove(cm_list
+i
, cm_list
+i
+1, (cm_used
-i
-1)*sizeof(cm_list
[0]));
853 call_channel_mode_funcs(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
)
856 for (n
=0; n
<cm_used
; n
++) {
857 cm_list
[n
](who
, channel
, modes
, argc
, cm_list_extra
[n
]);
862 generate_hostmask(struct userNode
*user
, int options
)
865 char *nickname
, *ident
, *hostname
, *mask
;
868 /* figure out string parts */
869 if (options
& GENMASK_OMITNICK
)
871 else if (options
& GENMASK_USENICK
)
872 nickname
= user
->nick
;
875 if (options
& GENMASK_STRICT_IDENT
)
876 // sethost - reed/apples
877 if (IsSetHost(user
)) {
878 ident
= alloca(strcspn(user
->sethost
, "@")+2);
879 safestrncpy(ident
, user
->sethost
, strcspn(user
->sethost
, "@")+1);
883 else if (options
& GENMASK_ANY_IDENT
)
886 // sethost - reed/apples
887 if (IsSetHost(user
)) {
888 ident
= alloca(strcspn(user
->sethost
, "@")+3);
890 safestrncpy(ident
+1, user
->sethost
, strcspn(user
->sethost
, "@")+1);
892 ident
= alloca(strlen(user
->ident
)+2);
894 strcpy(ident
+1, user
->ident
+ ((*user
->ident
== '~')?1:0));
897 hostname
= user
->hostname
;
898 if (IsFakeHost(user
) && IsHiddenHost(user
) && !(options
& GENMASK_NO_HIDING
)) {
899 hostname
= user
->fakehost
;
900 } else if (IsHiddenHost(user
)) {
903 data
= conf_get_data("server/hidden_host_type", RECDB_QSTRING
);
907 if (((style
== 1) || (style
== 3)) && user
->handle_info
&& hidden_host_suffix
&& !(options
& GENMASK_NO_HIDING
)) {
908 hostname
= alloca(strlen(user
->handle_info
->handle
) + strlen(hidden_host_suffix
) + 2);
909 sprintf(hostname
, "%s.%s", user
->handle_info
->handle
, hidden_host_suffix
);
910 } else if (((style
== 2) || (style
== 3)) && !(options
& GENMASK_NO_HIDING
)) {
911 hostname
= alloca(strlen(user
->crypthost
));
912 sprintf(hostname
, "%s", user
->crypthost
);
914 } else if (options
& GENMASK_STRICT_HOST
) {
915 if (options
& GENMASK_BYIP
)
916 hostname
= (char*)irc_ntoa(&user
->ip
);
917 } else if ((options
& GENMASK_BYIP
) || irc_pton(&ip
, NULL
, hostname
)) {
918 /* Should generate an IP-based hostmask. */
919 hostname
= alloca(IRC_NTOP_MAX_SIZE
);
920 hostname
[IRC_NTOP_MAX_SIZE
-1] = '\0';
921 if (irc_in_addr_is_ipv4(user
->ip
)) {
922 /* By popular acclaim, a /16 hostmask is used. */
923 sprintf(hostname
, "%d.%d.*", user
->ip
.in6_8
[12], user
->ip
.in6_8
[13]);
924 } else if (irc_in_addr_is_ipv6(user
->ip
)) {
925 /* Who knows what the default mask should be? Use a /48 to start with. */
926 sprintf(hostname
, "%x:%x:%x:*", user
->ip
.in6
[0], user
->ip
.in6
[1], user
->ip
.in6
[2]);
928 /* Unknown type; just copy IP directly. */
929 irc_ntop(hostname
, IRC_NTOP_MAX_SIZE
, &user
->ip
);
933 /* This heuristic could be made smarter. Is it worth the effort? */
934 for (ii
=cnt
=0; hostname
[ii
]; ii
++)
935 if (hostname
[ii
] == '.')
937 if (cnt
== 0 || cnt
== 1) {
938 /* only a one- or two-level domain name; leave hostname */
939 } else if (cnt
== 2) {
940 for (ii
=0; user
->hostname
[ii
] != '.'; ii
++) ;
941 /* Add 3 to account for the *. and \0. */
942 hostname
= alloca(strlen(user
->hostname
+ii
)+3);
943 sprintf(hostname
, "*.%s", user
->hostname
+ii
+1);
945 for (cnt
=3, ii
--; cnt
; ii
--)
946 if (user
->hostname
[ii
] == '.')
948 /* The loop above will overshoot the dot one character;
949 we skip forward two (the one character and the dot)
950 when printing, so we only add one for the \0. */
951 hostname
= alloca(strlen(user
->hostname
+ii
)+1);
952 sprintf(hostname
, "*.%s", user
->hostname
+ii
+2);
955 // sethost - reed/apples
957 hostname
= strchr(user
->sethost
, '@') + 1;
960 len
= strlen(ident
) + strlen(hostname
) + 2;
962 len
+= strlen(nickname
) + 1;
964 sprintf(mask
, "%s!%s@%s", nickname
, ident
, hostname
);
967 sprintf(mask
, "%s@%s", ident
, hostname
);
973 IsChannelName(const char *name
) {
978 for (ii
=1; name
[ii
]; ++ii
) {
979 if ((name
[ii
] > 0) && (name
[ii
] <= 32))
983 if (name
[ii
] == '\xa0')
991 irc_user_modes(const struct userNode
*user
, char modes
[], size_t length
)
995 for (ii
= jj
= 0; (jj
< length
) && (irc_user_mode_chars
[ii
] != '\0'); ++ii
) {
996 if ((user
->modes
& (1 << ii
)) && (irc_user_mode_chars
[ii
] != ' '))
997 modes
[jj
++] = irc_user_mode_chars
[ii
];