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 time_t boot_time
;
58 void received_ping(void);
60 static int replay_read(void);
61 static dict_t irc_func_dict
;
63 typedef void (*foreach_chanfunc
) (struct chanNode
*chan
, void *data
);
64 typedef void (*foreach_nonchan
) (char *name
, void *data
);
65 typedef void (*foreach_userfunc
) (struct userNode
*user
, void *data
);
66 typedef void (*foreach_nonuser
) (char *name
, void *data
);
67 static void parse_foreach(char *target_list
, foreach_chanfunc cf
, foreach_nonchan nc
, foreach_userfunc uf
, foreach_nonuser nu
, void *data
);
68 static void call_channel_mode_funcs(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
);
71 uplink_readable(struct io_fd
*fd
) {
72 static char buffer
[MAXLEN
];
76 pos
= ioset_line_read(fd
, buffer
, sizeof(buffer
));
81 if ((eol
= strpbrk(buffer
, "\r\n")))
83 log_replay(MAIN_LOG
, false, buffer
);
84 if (cManager
.uplink
->state
!= DISCONNECTED
)
85 parse_line(buffer
, 0);
90 socket_destroyed(struct io_fd
*fd
)
92 if (fd
&& fd
->state
!= IO_CONNECTED
)
93 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to server lost.");
95 cManager
.uplink
->state
= DISCONNECTED
;
97 DelServer(self
->uplink
, 0, NULL
);
100 void replay_event_loop(void)
102 while (!quit_services
) {
103 if (!replay_connected
) {
104 /* this time fudging is to get some of the logging right */
105 self
->link_time
= self
->boot
= now
;
106 cManager
.uplink
->state
= AUTHENTICATING
;
107 irc_introduce(cManager
.uplink
->password
);
108 replay_connected
= 1;
109 } else if (!replay_read()) {
110 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to server lost.");
118 create_socket_client(struct uplinkNode
*target
)
120 int port
= target
->port
;
121 const char *addr
= target
->host
;
124 return feof(replay_file
) ? 0 : 1;
127 /* Leave the existing socket open, say we failed. */
128 log_module(MAIN_LOG
, LOG_ERROR
, "Refusing to create second connection to %s:%d.", addr
, port
);
132 log_module(MAIN_LOG
, LOG_INFO
, "Connecting to %s:%i...", addr
, port
);
134 socket_io_fd
= ioset_connect(cManager
.uplink
->bind_addr
, cManager
.uplink
->bind_addr_len
, addr
, port
, 1, 0, NULL
);
136 log_module(MAIN_LOG
, LOG_ERROR
, "Connection to uplink failed: %s (%d)", strerror(errno
), errno
);
137 target
->state
= DISCONNECTED
;
141 socket_io_fd
->readable_cb
= uplink_readable
;
142 socket_io_fd
->destroy_cb
= socket_destroyed
;
143 socket_io_fd
->line_reads
= 1;
144 log_module(MAIN_LOG
, LOG_INFO
, "Connection to server established.");
145 cManager
.uplink
= target
;
146 target
->state
= AUTHENTICATING
;
152 replay_read_line(void)
157 if (replay_line
[0]) return;
159 if (!fgets(replay_line
, sizeof(replay_line
), replay_file
)) {
160 if (feof(replay_file
)) {
162 memset(replay_line
, 0, sizeof(replay_line
));
166 if ((replay_line
[0] != '[')
167 || (replay_line
[3] != ':')
168 || (replay_line
[6] != ':')
169 || (replay_line
[9] != ' ')
170 || (replay_line
[12] != '/')
171 || (replay_line
[15] != '/')
172 || (replay_line
[20] != ']')
173 || (replay_line
[21] != ' ')) {
174 log_module(MAIN_LOG
, LOG_ERROR
, "Unrecognized timestamp in replay file: %s", replay_line
);
177 timestamp
.tm_hour
= strtoul(replay_line
+1, NULL
, 10);
178 timestamp
.tm_min
= strtoul(replay_line
+4, NULL
, 10);
179 timestamp
.tm_sec
= strtoul(replay_line
+7, NULL
, 10);
180 timestamp
.tm_mon
= strtoul(replay_line
+10, NULL
, 10) - 1;
181 timestamp
.tm_mday
= strtoul(replay_line
+13, NULL
, 10);
182 timestamp
.tm_year
= strtoul(replay_line
+16, NULL
, 10) - 1900;
183 timestamp
.tm_isdst
= 0;
184 new_time
= mktime(×tamp
);
185 if (new_time
== -1) {
186 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
);
191 if (strncmp(replay_line
+22, "(info) ", 7))
200 char read_line
[MAXLEN
];
203 /* if it's a sent line, break out to handle it */
204 if (!strncmp(replay_line
+29, " ", 3))
206 if (!strncmp(replay_line
+29, "W: ", 3)) {
207 log_module(MAIN_LOG
, LOG_ERROR
, "Expected response from services: %s", replay_line
+32);
213 log_replay(MAIN_LOG
, false, replay_line
+32);
214 safestrncpy(read_line
, replay_line
+32, sizeof(read_line
));
215 len
= strlen(read_line
);
216 if (read_line
[len
-1] == '\n')
217 read_line
[--len
] = 0;
219 parse_line(read_line
, 0);
225 replay_write(char *text
)
228 if (strncmp(replay_line
+29, "W: ", 3)) {
229 log_module(MAIN_LOG
, LOG_ERROR
, "Unexpected output during replay: %s", text
);
232 if (strcmp(replay_line
+32, text
)) {
233 log_module(MAIN_LOG
, LOG_ERROR
, "Incorrect output during replay:\nReceived: %sExpected: %s", text
, replay_line
+32);
235 log_replay(MAIN_LOG
, true, text
);
241 void putsock(const char *text
, ...) PRINTF_LIKE(1, 2);
244 putsock(const char *text
, ...)
250 if (!cManager
.uplink
|| cManager
.uplink
->state
== DISCONNECTED
) return;
252 va_start(arg_list
, text
);
253 pos
= vsnprintf(buffer
, MAXLEN
- 2, text
, arg_list
);
255 if (pos
< 0 || pos
> (MAXLEN
- 2)) pos
= MAXLEN
- 2;
258 log_replay(MAIN_LOG
, true, buffer
);
259 buffer
[pos
++] = '\n';
261 ioset_write(socket_io_fd
, buffer
, pos
);
263 replay_write(buffer
);
271 replay_connected
= 0;
272 socket_destroyed(socket_io_fd
);
274 ioset_close(socket_io_fd
, 3);
279 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
280 typedef CMD_FUNC(cmd_func_t
);
282 static void timed_ping_timeout(void *data
);
284 /* Ping state is kept in the timeq (only one of these two can be in
285 * the queue at any given time). */
287 timed_send_ping(UNUSED_ARG(void *data
))
289 irc_ping(self
->name
);
290 timeq_add(now
+ ping_timeout
, timed_ping_timeout
, 0);
294 timed_ping_timeout(UNUSED_ARG(void *data
))
296 /* Uplink "health" tracking could be accomplished by counting the
297 number of ping timeouts that happen for each uplink. After the
298 timeouts per time period exceeds some amount, the uplink could
299 be marked as unavalable.*/
300 irc_squit(self
, "Ping timeout.", NULL
);
303 static CMD_FUNC(cmd_pass
)
305 const char *true_pass
;
309 true_pass
= cManager
.uplink
->their_password
;
310 if (true_pass
&& strcmp(true_pass
, argv
[1])) {
311 /* It might be good to mark the uplink as unavailable when
312 this happens, though there should be a way of resetting
314 irc_squit(self
, "Incorrect password received.", NULL
);
318 cManager
.uplink
->state
= BURSTING
;
322 static CMD_FUNC(cmd_dummy
)
324 /* we don't care about these messages */
328 static CMD_FUNC(cmd_error
)
330 if (argv
[1]) log_module(MAIN_LOG
, LOG_ERROR
, "Error: %s", argv
[1]);
331 log_module(MAIN_LOG
, LOG_ERROR
, "Error received from uplink, squitting.");
333 if (cManager
.uplink
->state
!= CONNECTED
) {
334 /* Error messages while connected should be fine. */
335 cManager
.uplink
->flags
|= UPLINK_UNAVAILABLE
;
336 log_module(MAIN_LOG
, LOG_ERROR
, "Disabling uplink.");
343 static CMD_FUNC(cmd_stats
)
349 if (!(un
= GetUserH(origin
)))
351 switch (argv
[1][0]) {
353 unsigned int uptime
= now
- boot_time
;
354 irc_numeric(un
, RPL_STATSUPTIME
, ":Server Up %d days %d:%02d:%02d",
355 uptime
/(24*60*60), (uptime
/(60*60))%24
, (uptime
/60)%60
, uptime%60
);
356 irc_numeric(un
, RPL_MAXCONNECTIONS
, ":Highest connection count: %d (%d clients)",
357 self
->max_clients
+1, self
->max_clients
);
360 default: /* unrecognized/unhandled stats output */ break;
362 irc_numeric(un
, 219, "%s :End of /STATS report", argv
[1]);
366 static CMD_FUNC(cmd_version
)
368 struct userNode
*user
;
369 if (!(user
= GetUserH(origin
))) {
370 log_module(MAIN_LOG
, LOG_ERROR
, "Could not find VERSION origin user %s", origin
);
373 irc_numeric(user
, 351, "%s %s %s", PACKAGE_TARNAME
, PACKAGE_VERSION
, self
->name
);
377 static CMD_FUNC(cmd_admin
)
379 struct userNode
*user
;
380 struct string_list
*slist
;
382 if (!(user
= GetUserH(origin
))) {
383 log_module(MAIN_LOG
, LOG_ERROR
, "Could not find ADMIN origin user %s", origin
);
386 if ((slist
= conf_get_data("server/admin", RECDB_STRING_LIST
)) && slist
->used
) {
389 irc_numeric(user
, 256, ":Administrative info about %s", self
->name
);
390 for (i
= 0; i
< slist
->used
&& i
< 3; i
++)
391 irc_numeric(user
, 257 + i
, ":%s", slist
->list
[i
]);
393 irc_numeric(user
, 423, ":No administrative info available");
399 recalc_bursts(struct server
*eob_server
)
402 eob_server
->burst
= eob_server
->self_burst
;
403 if (eob_server
->uplink
!= self
)
404 eob_server
->burst
= eob_server
->burst
|| eob_server
->uplink
->burst
;
405 for (nn
=0; nn
< eob_server
->children
.used
; nn
++)
406 recalc_bursts(eob_server
->children
.list
[nn
]);
409 static struct chanmsg_func
{
411 struct userNode
*service
;
413 } chanmsg_funcs
[256]; /* indexed by trigger character */
415 static struct allchanmsg_func
{
417 struct userNode
*service
;
418 } allchanmsg_funcs
[ALLCHANMSG_FUNCS_MAX
];
420 struct privmsg_desc
{
421 struct userNode
*user
;
423 unsigned int is_notice
: 1;
424 unsigned int is_qualified
: 1;
428 privmsg_chan_helper(struct chanNode
*cn
, void *data
)
430 struct privmsg_desc
*pd
= data
;
432 struct chanmsg_func
*cf
;
435 /* Don't complain if it can't find the modeNode because the channel might
437 if ((mn
= GetUserMode(cn
, pd
->user
)))
438 mn
->idle_since
= now
;
440 /* Never send a NOTICE to a channel to one of the services */
441 cf
= &chanmsg_funcs
[(unsigned char)pd
->text
[0]];
442 if (!pd
->is_notice
&& cf
->func
)
443 cf
->func(pd
->user
, cn
, pd
->text
+1, cf
->service
, pd
->is_notice
, cf
->extra
);
445 spamserv_channel_message(cn
, pd
->user
, pd
->text
);
447 /* This catches *all* text sent to the channel that the services server sees */
448 for (x
= 0; x
< ALLCHANMSG_FUNCS_MAX
; x
++) {
449 cf
= (struct chanmsg_func
*)&allchanmsg_funcs
[x
];
451 break; /* end of list */
453 cf
->func(pd
->user
, cn
, pd
->text
, cf
->service
, pd
->is_notice
, cf
->extra
);
458 privmsg_invalid(char *name
, void *data
)
460 struct privmsg_desc
*pd
= data
;
464 irc_numeric(pd
->user
, ERR_NOSUCHNICK
, "%s@%s :No such nick", name
, self
->name
);
468 struct userNode
*user
;
473 part_helper(struct chanNode
*cn
, void *data
)
475 struct part_desc
*desc
= data
;
476 DelChannelUser(desc
->user
, cn
, desc
->text
, false);
479 static CMD_FUNC(cmd_part
)
481 struct part_desc desc
;
485 desc
.user
= GetUserH(origin
);
488 desc
.text
= (argc
> 2) ? argv
[argc
- 1] : NULL
;
489 parse_foreach(argv
[1], part_helper
, NULL
, NULL
, NULL
, &desc
);
494 reg_chanmsg_func(unsigned char prefix
, struct userNode
*service
, chanmsg_func_t handler
, void *extra
)
496 if (chanmsg_funcs
[prefix
].func
)
497 log_module(MAIN_LOG
, LOG_WARNING
, "Re-registering new chanmsg handler for character `%c'.", prefix
);
498 chanmsg_funcs
[prefix
].func
= handler
;
499 chanmsg_funcs
[prefix
].service
= service
;
500 chanmsg_funcs
[prefix
].extra
= extra
;
504 reg_allchanmsg_func(struct userNode
*service
, chanmsg_func_t handler
)
507 for (x
= 0; x
< ALLCHANMSG_FUNCS_MAX
; x
++) {
508 if (allchanmsg_funcs
[x
].func
)
510 allchanmsg_funcs
[x
].func
= handler
;
511 allchanmsg_funcs
[x
].service
= service
;
517 get_chanmsg_bot(unsigned char prefix
)
519 return chanmsg_funcs
[prefix
].service
;
522 static mode_change_func_t
*mcf_list
;
523 static void **mcf_list_extra
;
524 static unsigned int mcf_size
= 0, mcf_used
= 0;
527 reg_mode_change_func(mode_change_func_t handler
, void *extra
)
529 if (mcf_used
== mcf_size
) {
532 mcf_list
= realloc(mcf_list
, mcf_size
*sizeof(mode_change_func_t
));
533 mcf_list_extra
= realloc(mcf_list_extra
, mcf_size
*sizeof(void*));
536 mcf_list
= malloc(mcf_size
*sizeof(mode_change_func_t
));
537 mcf_list_extra
= malloc(mcf_size
*sizeof(void*));
540 mcf_list
[mcf_used
] = handler
;
541 mcf_list_extra
[mcf_used
++] = extra
;
544 static oper_func_t
*of_list
;
545 static void **of_list_extra
;
546 static unsigned int of_size
= 0, of_used
= 0;
549 reg_oper_func(oper_func_t handler
, void *extra
)
551 if (of_used
== of_size
) {
554 of_list
= realloc(of_list
, of_size
*sizeof(oper_func_t
));
555 of_list_extra
= realloc(of_list_extra
, of_size
*sizeof(void*));
558 of_list
= malloc(of_size
*sizeof(oper_func_t
));
559 of_list_extra
= malloc(of_size
*sizeof(void*));
562 of_list
[of_used
] = handler
;
563 of_list_extra
[of_used
++] = extra
;
567 call_oper_funcs(struct userNode
*user
)
572 for (n
=0; (n
<of_used
) && !user
->dead
; n
++)
574 of_list
[n
](user
, of_list_extra
[n
]);
578 struct mod_chanmode
*
579 mod_chanmode_alloc(unsigned int argc
)
581 struct mod_chanmode
*res
;
583 res
= calloc(1, sizeof(*res
) + (argc
-1)*sizeof(res
->args
[0]));
585 res
= calloc(1, sizeof(*res
));
588 res
->alloc_argc
= argc
;
595 struct mod_chanmode
*
596 mod_chanmode_dup(struct mod_chanmode
*orig
, unsigned int extra
)
598 struct mod_chanmode
*res
;
599 res
= mod_chanmode_alloc(orig
->argc
+ extra
);
601 res
->modes_set
= orig
->modes_set
;
602 res
->modes_clear
= orig
->modes_clear
;
603 res
->new_limit
= orig
->new_limit
;
604 memcpy(res
->new_key
, orig
->new_key
, sizeof(res
->new_key
));
605 memcpy(res
->new_upass
, orig
->new_upass
, sizeof(res
->new_upass
));
606 memcpy(res
->new_apass
, orig
->new_apass
, sizeof(res
->new_apass
));
607 res
->argc
= orig
->argc
;
608 memcpy(res
->args
, orig
->args
, orig
->argc
*sizeof(orig
->args
[0]));
614 mod_chanmode_apply(struct userNode
*who
, struct chanNode
*channel
, struct mod_chanmode
*change
)
617 struct exemptNode
*en
;
620 assert(change
->argc
<= change
->alloc_argc
);
621 channel
->modes
= (channel
->modes
& ~change
->modes_clear
) | change
->modes_set
;
622 if (change
->modes_set
& MODE_LIMIT
)
623 channel
->limit
= change
->new_limit
;
624 if (change
->modes_set
& MODE_KEY
)
625 strcpy(channel
->key
, change
->new_key
);
626 if (change
->modes_set
& MODE_UPASS
)
627 strcpy(channel
->upass
, change
->new_upass
);
628 if (change
->modes_set
& MODE_APASS
)
629 strcpy(channel
->apass
, change
->new_apass
);
630 for (ii
= 0; ii
< change
->argc
; ++ii
) {
631 switch (change
->args
[ii
].mode
) {
633 /* If any existing ban is a subset of the new ban,
634 * silently remove it. The new ban is not allowed
635 * to be more specific than an existing ban.
637 for (jj
=0; jj
<channel
->banlist
.used
; ++jj
) {
638 bn
= channel
->banlist
.list
[jj
];
639 if (match_ircglobs(change
->args
[ii
].u
.hostmask
, bn
->ban
)) {
640 banList_remove(&channel
->banlist
, bn
);
645 bn
= calloc(1, sizeof(*bn
));
646 safestrncpy(bn
->ban
, change
->args
[ii
].u
.hostmask
, sizeof(bn
->ban
));
648 safestrncpy(bn
->who
, who
->nick
, sizeof(bn
->who
));
650 safestrncpy(bn
->who
, "<unknown>", sizeof(bn
->who
));
652 banList_append(&channel
->banlist
, bn
);
654 case MODE_REMOVE
|MODE_BAN
:
655 for (jj
=0; jj
<channel
->banlist
.used
; ++jj
) {
656 bn
= channel
->banlist
.list
[jj
];
657 if (strcmp(bn
->ban
, change
->args
[ii
].u
.hostmask
))
660 banList_remove(&channel
->banlist
, bn
);
665 /* If any existing exempt is a subset of the new exempt,
666 * silently remove it. The new exempt is not allowed
667 * to be more specific than an existing exempt.
669 for (jj
=0; jj
<channel
->exemptlist
.used
; ++jj
) {
670 if (match_ircglobs(change
->args
[ii
].u
.hostmask
, channel
->exemptlist
.list
[jj
]->exempt
)) {
671 exemptList_remove(&channel
->exemptlist
, channel
->exemptlist
.list
[jj
]);
672 free(channel
->exemptlist
.list
[jj
]);
676 en
= calloc(1, sizeof(*en
));
677 safestrncpy(en
->exempt
, change
->args
[ii
].u
.hostmask
, sizeof(en
->exempt
));
679 safestrncpy(en
->who
, who
->nick
, sizeof(en
->who
));
681 safestrncpy(en
->who
, "<unknown>", sizeof(en
->who
));
683 exemptList_append(&channel
->exemptlist
, en
);
685 case MODE_REMOVE
|MODE_EXEMPT
:
686 for (jj
=0; jj
<channel
->exemptlist
.used
; ++jj
) {
687 if (strcmp(channel
->exemptlist
.list
[jj
]->exempt
, change
->args
[ii
].u
.hostmask
))
689 free(channel
->exemptlist
.list
[jj
]);
690 exemptList_remove(&channel
->exemptlist
, channel
->exemptlist
.list
[jj
]);
694 /* XXX Hack: this is the stupedest use of switch iv ever seen.
695 * you have to compare for EVERY POSSIBLE COMBINATION of bitmask
696 * because switch does only full comparison. This needs redone as if/else.
701 case MODE_VOICE
|MODE_CHANOP
:
702 case MODE_VOICE
|MODE_HALFOP
:
703 case MODE_CHANOP
|MODE_HALFOP
:
704 case MODE_VOICE
|MODE_CHANOP
|MODE_HALFOP
:
705 case MODE_REMOVE
|MODE_CHANOP
:
706 case MODE_REMOVE
|MODE_HALFOP
:
707 case MODE_REMOVE
|MODE_VOICE
:
708 case MODE_REMOVE
|MODE_VOICE
|MODE_CHANOP
:
709 case MODE_REMOVE
|MODE_VOICE
|MODE_HALFOP
:
710 case MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
:
711 case MODE_REMOVE
|MODE_VOICE
|MODE_CHANOP
|MODE_HALFOP
:
712 if (change
->args
[ii
].mode
& MODE_REMOVE
)
713 change
->args
[ii
].u
.member
->modes
&= ~change
->args
[ii
].mode
;
715 change
->args
[ii
].u
.member
->modes
|= change
->args
[ii
].mode
;
718 assert(0 && "Invalid mode argument");
725 mod_chanmode_free(struct mod_chanmode
*change
)
731 mod_chanmode(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
, unsigned int flags
)
733 struct modeNode
*member
;
734 struct mod_chanmode
*change
;
739 if (!modes
|| !modes
[0])
741 if (who
&& (member
= GetUserMode(channel
, who
)))
742 base_oplevel
= member
->oplevel
;
744 base_oplevel
= MAXOPLEVEL
;
745 if (!(change
= mod_chanmode_parse(channel
, modes
, argc
, flags
, base_oplevel
)))
748 call_channel_mode_funcs(who
, channel
, modes
, argc
);
750 if (flags
& MC_ANNOUNCE
)
751 mod_chanmode_announce(who
, channel
, change
);
753 mod_chanmode_apply(who
, channel
, change
);
754 if (flags
& MC_NOTIFY
)
755 for (ii
= 0; ii
< mcf_used
; ++ii
)
756 mcf_list
[ii
](channel
, who
, change
, mcf_list_extra
[ii
]);
757 mod_chanmode_free(change
);
762 irc_make_chanmode(struct chanNode
*chan
, char *out
)
764 struct mod_chanmode change
;
765 mod_chanmode_init(&change
);
766 change
.modes_set
= chan
->modes
;
767 change
.new_limit
= chan
->limit
;
768 safestrncpy(change
.new_key
, chan
->key
, sizeof(change
.new_key
));
769 safestrncpy(change
.new_upass
, chan
->upass
, sizeof(change
.new_upass
));
770 safestrncpy(change
.new_apass
, chan
->apass
, sizeof(change
.new_apass
));
771 return strlen(mod_chanmode_format(&change
, out
));
774 static user_mode_func_t
*um_list
;
775 static void **um_list_extra
;
776 static unsigned int um_size
= 0, um_used
= 0;
779 reg_user_mode_func(user_mode_func_t handler
, void *extra
)
781 if (um_used
== um_size
) {
784 um_list
= realloc(um_list
, um_size
*sizeof(user_mode_func_t
));
785 um_list_extra
= realloc(um_list_extra
, um_size
*sizeof(void*));
788 um_list
= malloc(um_size
*sizeof(user_mode_func_t
));
789 um_list_extra
= malloc(um_size
*sizeof(void*));
792 um_list
[um_used
] = handler
;
793 um_list_extra
[um_used
++] = extra
;
797 unreg_user_mode_func(user_mode_func_t handler
)
800 for (i
=0; i
<um_used
; i
++) {
801 if (um_list
[i
] == handler
) break;
803 if (i
== um_used
) return;
804 memmove(um_list
+i
, um_list
+i
+1, (um_used
-i
-1)*sizeof(um_list
[0]));
809 call_user_mode_funcs(struct userNode
*user
, const char *mode_change
)
812 for (n
=0; n
<um_used
; n
++) {
813 um_list
[n
](user
, mode_change
, um_list_extra
[n
]);
817 static channel_mode_func_t
*cm_list
;
818 static void **cm_list_extra
;
819 static unsigned int cm_size
= 0, cm_used
= 0;
822 reg_channel_mode_func(channel_mode_func_t handler
, void *extra
)
824 if (cm_used
== cm_size
) {
827 cm_list
= realloc(cm_list
, cm_size
*sizeof(channel_mode_func_t
));
828 cm_list_extra
= realloc(cm_list_extra
, cm_size
*sizeof(void*));
831 cm_list
= malloc(cm_size
*sizeof(channel_mode_func_t
));
832 cm_list_extra
= malloc(cm_size
*sizeof(void*));
835 cm_list
[cm_used
] = handler
;
836 cm_list_extra
[cm_used
++] = extra
;
840 unreg_channel_mode_func(channel_mode_func_t handler
)
843 for (i
=0; i
<cm_used
; i
++) {
844 if(cm_list
[i
] == handler
) break;
846 if (i
== cm_used
) return;
847 memmove(cm_list
+i
, cm_list
+i
+1, (cm_used
-i
-1)*sizeof(cm_list
[0]));
852 call_channel_mode_funcs(struct userNode
*who
, struct chanNode
*channel
, char **modes
, unsigned int argc
)
855 for (n
=0; n
<cm_used
; n
++) {
856 cm_list
[n
](who
, channel
, modes
, argc
, cm_list_extra
[n
]);
861 generate_hostmask(struct userNode
*user
, int options
)
864 char *nickname
, *ident
, *hostname
, *mask
;
867 /* figure out string parts */
868 if (options
& GENMASK_OMITNICK
)
870 else if (options
& GENMASK_USENICK
)
871 nickname
= user
->nick
;
874 if (options
& GENMASK_STRICT_IDENT
)
875 // sethost - reed/apples
876 if (IsSetHost(user
)) {
877 ident
= alloca(strcspn(user
->sethost
, "@")+2);
878 safestrncpy(ident
, user
->sethost
, strcspn(user
->sethost
, "@")+1);
882 else if (options
& GENMASK_ANY_IDENT
)
885 // sethost - reed/apples
886 if (IsSetHost(user
)) {
887 ident
= alloca(strcspn(user
->sethost
, "@")+3);
889 safestrncpy(ident
+1, user
->sethost
, strcspn(user
->sethost
, "@")+1);
891 ident
= alloca(strlen(user
->ident
)+2);
893 strcpy(ident
+1, user
->ident
+ ((*user
->ident
== '~')?1:0));
896 hostname
= user
->hostname
;
897 if (IsFakeHost(user
) && IsHiddenHost(user
) && !(options
& GENMASK_NO_HIDING
)) {
898 hostname
= user
->fakehost
;
899 } else if (IsHiddenHost(user
)) {
902 data
= conf_get_data("server/hidden_host_type", RECDB_QSTRING
);
906 if ((style
== 1) && user
->handle_info
&& hidden_host_suffix
&& !(options
& GENMASK_NO_HIDING
)) {
907 hostname
= alloca(strlen(user
->handle_info
->handle
) + strlen(hidden_host_suffix
) + 2);
908 sprintf(hostname
, "%s.%s", user
->handle_info
->handle
, hidden_host_suffix
);
909 } else if ((style
== 2) && !(options
& GENMASK_NO_HIDING
)) {
910 hostname
= alloca(strlen(user
->crypthost
));
911 sprintf(hostname
, "%s", user
->crypthost
);
913 } else if (options
& GENMASK_STRICT_HOST
) {
914 if (options
& GENMASK_BYIP
)
915 hostname
= (char*)irc_ntoa(&user
->ip
);
916 } else if ((options
& GENMASK_BYIP
) || irc_pton(&ip
, NULL
, hostname
)) {
917 /* Should generate an IP-based hostmask. */
918 hostname
= alloca(IRC_NTOP_MAX_SIZE
);
919 hostname
[IRC_NTOP_MAX_SIZE
-1] = '\0';
920 if (irc_in_addr_is_ipv4(user
->ip
)) {
921 /* By popular acclaim, a /16 hostmask is used. */
922 sprintf(hostname
, "%d.%d.*", user
->ip
.in6_8
[12], user
->ip
.in6_8
[13]);
923 } else if (irc_in_addr_is_ipv6(user
->ip
)) {
924 /* Who knows what the default mask should be? Use a /48 to start with. */
925 sprintf(hostname
, "%x:%x:%x:*", user
->ip
.in6
[0], user
->ip
.in6
[1], user
->ip
.in6
[2]);
927 /* Unknown type; just copy IP directly. */
928 irc_ntop(hostname
, IRC_NTOP_MAX_SIZE
, &user
->ip
);
932 /* This heuristic could be made smarter. Is it worth the effort? */
933 for (ii
=cnt
=0; hostname
[ii
]; ii
++)
934 if (hostname
[ii
] == '.')
936 if (cnt
== 0 || cnt
== 1) {
937 /* only a one- or two-level domain name; leave hostname */
938 } else if (cnt
== 2) {
939 for (ii
=0; user
->hostname
[ii
] != '.'; ii
++) ;
940 /* Add 3 to account for the *. and \0. */
941 hostname
= alloca(strlen(user
->hostname
+ii
)+3);
942 sprintf(hostname
, "*.%s", user
->hostname
+ii
+1);
944 for (cnt
=3, ii
--; cnt
; ii
--)
945 if (user
->hostname
[ii
] == '.')
947 /* The loop above will overshoot the dot one character;
948 we skip forward two (the one character and the dot)
949 when printing, so we only add one for the \0. */
950 hostname
= alloca(strlen(user
->hostname
+ii
)+1);
951 sprintf(hostname
, "*.%s", user
->hostname
+ii
+2);
954 // sethost - reed/apples
956 hostname
= strchr(user
->sethost
, '@') + 1;
959 len
= strlen(ident
) + strlen(hostname
) + 2;
961 len
+= strlen(nickname
) + 1;
963 sprintf(mask
, "%s!%s@%s", nickname
, ident
, hostname
);
966 sprintf(mask
, "%s@%s", ident
, hostname
);
972 IsChannelName(const char *name
) {
977 for (ii
=1; name
[ii
]; ++ii
) {
978 if ((name
[ii
] > 0) && (name
[ii
] <= 32))
982 if (name
[ii
] == '\xa0')
990 irc_user_modes(const struct userNode
*user
, char modes
[], size_t length
)
994 for (ii
= jj
= 0; (jj
< length
) && (irc_user_mode_chars
[ii
] != '\0'); ++ii
) {
995 if ((user
->modes
& (1 << ii
)) && (irc_user_mode_chars
[ii
] != ' '))
996 modes
[jj
++] = irc_user_mode_chars
[ii
];