diff options
-rw-r--r-- | src/common.c | 11 | ||||
-rw-r--r-- | src/kike.c | 313 |
2 files changed, 164 insertions, 160 deletions
diff --git a/src/common.c b/src/common.c index 5e4b130..782572d 100644 --- a/src/common.c +++ b/src/common.c @@ -1815,6 +1815,17 @@ irc_strcmp (const char *a, const char *b) return 0; } +static int +irc_fnmatch (const char *pattern, const char *string) +{ + size_t pattern_size = strlen (pattern) + 1; + size_t string_size = strlen (string) + 1; + char x_pattern[pattern_size], x_string[string_size]; + irc_strxfrm (x_pattern, pattern, pattern_size); + irc_strxfrm (x_string, string, string_size); + return fnmatch (x_pattern, x_string, 0); +} + // --- Configuration ----------------------------------------------------------- // The keys are stripped of surrounding whitespace, the values are not. @@ -236,7 +236,7 @@ irc_is_valid_fingerprint (const char *fp) return irc_regex_match ("^[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){19}$", fp); } -// --- Application data -------------------------------------------------------- +// --- Clients (equals users) -------------------------------------------------- #define IRC_SUPPORTED_USER_MODES "aiwros" @@ -307,26 +307,14 @@ client_free (struct client *self) free (self->away_message); } -static void -client_mode_to_str (unsigned m, struct str *out) -{ - if (m & IRC_USER_MODE_INVISIBLE) str_append_c (out, 'i'); - if (m & IRC_USER_MODE_RX_WALLOPS) str_append_c (out, 'w'); - if (m & IRC_USER_MODE_RESTRICTED) str_append_c (out, 'r'); - if (m & IRC_USER_MODE_OPERATOR) str_append_c (out, 'o'); - if (m & IRC_USER_MODE_RX_SERVER_NOTICES) str_append_c (out, 's'); -} +static void client_close_link (struct client *, const char *); +static void client_send (struct client *, const char *, ...) + ATTRIBUTE_PRINTF (2, 3); +static void client_cancel_timers (struct client *); +static void client_set_kill_timer (struct client *); +static void client_update_poller (struct client *, const struct pollfd *); -static char * -client_get_mode (struct client *self) -{ - struct str mode; - str_init (&mode); - if (self->away_message) - str_append_c (&mode, 'a'); - client_mode_to_str (self->mode, &mode); - return str_steal (&mode); -} +// --- Channels ---------------------------------------------------------------- #define IRC_SUPPORTED_CHAN_MODES "ov" "beI" "imnqpst" "kl" @@ -434,6 +422,42 @@ channel_get_mode (struct channel *self, bool disclose_secrets) return str_steal (&mode); } +static struct channel_user * +channel_get_user (const struct channel *chan, const struct client *c) +{ + for (struct channel_user *iter = chan->users; iter; iter = iter->next) + if (iter->c == c) + return iter; + return NULL; +} + +static struct channel_user * +channel_add_user (struct channel *chan, struct client *c) +{ + struct channel_user *link = xcalloc (1, sizeof *link); + link->c = c; + LIST_PREPEND (chan->users, link); + return link; +} + +static void +channel_remove_user (struct channel *chan, struct channel_user *user) +{ + LIST_UNLINK (chan->users, user); + free (user); +} + +static size_t +channel_user_count (const struct channel *chan) +{ + size_t result = 0; + for (struct channel_user *iter = chan->users; iter; iter = iter->next) + result++; + return result; +} + +// --- IRC server context ------------------------------------------------------ + struct server_context { int listen_fds[1]; ///< Listening socket FD's @@ -523,13 +547,6 @@ server_context_free (struct server_context *self) str_map_free (&self->operators); } -static void -irc_try_finish_quit (struct server_context *ctx) -{ - if (!ctx->n_clients && ctx->quitting) - ctx->polling = false; -} - static const char * irc_get_text (struct server_context *ctx, int id, const char *def) { @@ -540,55 +557,37 @@ irc_get_text (struct server_context *ctx, int id, const char *def) return catgets (ctx->catalog, 1, id, def); } -static int -irc_fnmatch (const char *pattern, const char *string) +static void +irc_try_finish_quit (struct server_context *ctx) { - size_t pattern_size = strlen (pattern) + 1; - size_t string_size = strlen (string) + 1; - char x_pattern[pattern_size], x_string[string_size]; - irc_strxfrm (x_pattern, pattern, pattern_size); - irc_strxfrm (x_string, string, string_size); - return fnmatch (x_pattern, x_string, 0); + if (!ctx->n_clients && ctx->quitting) + ctx->polling = false; } -// --- Channels ---------------------------------------------------------------- - -static struct channel_user * -channel_get_user (const struct channel *chan, const struct client *c) +static void +irc_initiate_quit (struct server_context *ctx) { - for (struct channel_user *iter = chan->users; iter; iter = iter->next) - if (iter->c == c) - return iter; - return NULL; -} + print_status ("shutting down"); -static struct channel_user * -channel_add_user (struct channel *chan, struct client *c) -{ - struct channel_user *link = xcalloc (1, sizeof *link); - link->c = c; - LIST_PREPEND (chan->users, link); - return link; -} + for (struct client *iter = ctx->clients; iter; iter = iter->next) + if (!iter->closing_link) + client_close_link (iter, "Shutting down"); -static void -channel_remove_user (struct channel *chan, struct channel_user *user) -{ - LIST_UNLINK (chan->users, user); - free (user); -} + for (size_t i = 0; i < ctx->n_listen_fds; i++) + { + ssize_t index = poller_find_by_fd (&ctx->poller, ctx->listen_fds[i]); + if (soft_assert (index != -1)) + poller_remove_at_index (&ctx->poller, index); + xclose (ctx->listen_fds[i]); + } + ctx->n_listen_fds = 0; -static size_t -channel_user_count (const struct channel *chan) -{ - size_t result = 0; - for (struct channel_user *iter = chan->users; iter; iter = iter->next) - result++; - return result; + ctx->quitting = true; + irc_try_finish_quit (ctx); } static struct channel * -channel_create (struct server_context *ctx, const char *name) +irc_channel_create (struct server_context *ctx, const char *name) { struct channel *chan = channel_new (); chan->ctx = ctx; @@ -598,20 +597,66 @@ channel_create (struct server_context *ctx, const char *name) } static void -channel_destroy_if_empty (struct server_context *ctx, struct channel *chan) +irc_channel_destroy_if_empty (struct server_context *ctx, struct channel *chan) { if (!chan->users) str_map_set (&ctx->channels, chan->name, NULL); } -// --- Clients ----------------------------------------------------------------- +static void +irc_send_to_roommates (struct client *c, const char *message) +{ + struct str_map targets; + str_map_init (&targets); + targets.key_xfrm = irc_strxfrm; -static void client_cancel_timers (struct client *); -static void client_set_kill_timer (struct client *); -static void client_update_poller (struct client *, const struct pollfd *); + struct str_map_iter iter; + str_map_iter_init (&iter, &c->ctx->channels); + struct channel *chan; + while ((chan = str_map_iter_next (&iter))) + { + if (chan->modes & IRC_CHAN_MODE_QUIET + || !channel_get_user (chan, c)) + continue; + + // When we're unregistering, the str_map_find() will return zero, + // which will prevent sending the QUIT message to ourselves. + for (struct channel_user *iter = chan->users; iter; iter = iter->next) + str_map_set (&targets, iter->c->nickname, + str_map_find (&c->ctx->users, iter->c->nickname)); + } + + str_map_iter_init (&iter, &targets); + struct client *target; + while ((target = str_map_iter_next (&iter))) + client_send (target, "%s", message); +} + +// --- Clients (continued) ----------------------------------------------------- + +static void +client_mode_to_str (unsigned m, struct str *out) +{ + if (m & IRC_USER_MODE_INVISIBLE) str_append_c (out, 'i'); + if (m & IRC_USER_MODE_RX_WALLOPS) str_append_c (out, 'w'); + if (m & IRC_USER_MODE_RESTRICTED) str_append_c (out, 'r'); + if (m & IRC_USER_MODE_OPERATOR) str_append_c (out, 'o'); + if (m & IRC_USER_MODE_RX_SERVER_NOTICES) str_append_c (out, 's'); +} + +static char * +client_get_mode (struct client *self) +{ + struct str mode; + str_init (&mode); + if (self->away_message) + str_append_c (&mode, 'a'); + client_mode_to_str (self->mode, &mode); + return str_steal (&mode); +} static void -irc_send_str (struct client *c, const struct str *s) +client_send_str (struct client *c, const struct str *s) { hard_assert (c->initialized && !c->closing_link); @@ -624,11 +669,8 @@ irc_send_str (struct client *c, const struct str *s) client_update_poller (c, NULL); } -static void irc_send (struct client *c, - const char *format, ...) ATTRIBUTE_PRINTF (2, 3); - static void -irc_send (struct client *c, const char *format, ...) +client_send (struct client *c, const char *format, ...) { struct str tmp; str_init (&tmp); @@ -638,40 +680,11 @@ irc_send (struct client *c, const char *format, ...) str_append_vprintf (&tmp, format, ap); va_end (ap); - irc_send_str (c, &tmp); + client_send_str (c, &tmp); str_free (&tmp); } static void -client_send_to_roommates (struct client *c, const char *message) -{ - struct str_map targets; - str_map_init (&targets); - targets.key_xfrm = irc_strxfrm; - - struct str_map_iter iter; - str_map_iter_init (&iter, &c->ctx->channels); - struct channel *chan; - while ((chan = str_map_iter_next (&iter))) - { - if (chan->modes & IRC_CHAN_MODE_QUIET - || !channel_get_user (chan, c)) - continue; - - // When we're unregistering, the str_map_find() will return zero, - // which will prevent sending the QUIT message to ourselves. - for (struct channel_user *iter = chan->users; iter; iter = iter->next) - str_map_set (&targets, iter->c->nickname, - str_map_find (&c->ctx->users, iter->c->nickname)); - } - - str_map_iter_init (&iter, &targets); - struct client *target; - while ((target = str_map_iter_next (&iter))) - irc_send (target, "%s", message); -} - -static void client_unregister (struct client *c, const char *reason) { if (!c->registered) @@ -682,7 +695,7 @@ client_unregister (struct client *c, const char *reason) char *message = xstrdup_printf (":%s!%s@%s QUIT :%s", c->nickname, c->username, c->hostname, reason); - client_send_to_roommates (c, message); + irc_send_to_roommates (c, message); free (message); struct str_map_iter iter; @@ -695,7 +708,7 @@ client_unregister (struct client *c, const char *reason) if (!(user = channel_get_user (chan, c))) continue; channel_remove_user (chan, user); - channel_destroy_if_empty (c->ctx, chan); + irc_channel_destroy_if_empty (c->ctx, chan); } free (c->nickname); @@ -727,7 +740,7 @@ client_kill (struct client *c, const char *reason) } static void -irc_close_link (struct client *c, const char *reason) +client_close_link (struct client *c, const char *reason) { if (!soft_assert (!c->closing_link)) return; @@ -736,7 +749,7 @@ irc_close_link (struct client *c, const char *reason) // it, with some arbitrary timeout. The `closing_link' state makes sure // that a/ we ignore any successive messages, and b/ that the connection // is killed after the write buffer is transferred and emptied. - irc_send (c, "ERROR :Closing Link: %s[%s] (%s)", c->nickname, + client_send (c, "ERROR :Closing Link: %s[%s] (%s)", c->nickname, c->hostname /* TODO host IP? */, reason); c->closing_link = true; @@ -760,28 +773,6 @@ client_in_mask_list (const struct client *c, const struct str_vector *mask) return result; } -static void -irc_initiate_quit (struct server_context *ctx) -{ - print_status ("shutting down"); - - for (struct client *iter = ctx->clients; iter; iter = iter->next) - if (!iter->closing_link) - irc_close_link (iter, "Shutting down"); - - for (size_t i = 0; i < ctx->n_listen_fds; i++) - { - ssize_t index = poller_find_by_fd (&ctx->poller, ctx->listen_fds[i]); - if (soft_assert (index != -1)) - poller_remove_at_index (&ctx->poller, index); - xclose (ctx->listen_fds[i]); - } - ctx->n_listen_fds = 0; - - ctx->quitting = true; - irc_try_finish_quit (ctx); -} - static char * client_get_ssl_cert_fingerprint (struct client *c) { @@ -830,7 +821,7 @@ client_set_timer (struct client *c, poller_timer_func fn, unsigned interval) } static void -on_irc_client_kill_timer (void *user_data) +on_client_kill_timer (void *user_data) { struct client *c = user_data; hard_assert (!c->initialized || c->closing_link); @@ -840,32 +831,32 @@ on_irc_client_kill_timer (void *user_data) static void client_set_kill_timer (struct client *c) { - client_set_timer (c, on_irc_client_kill_timer, c->ctx->ping_interval); + client_set_timer (c, on_client_kill_timer, c->ctx->ping_interval); } static void -on_irc_client_timeout_timer (void *user_data) +on_client_timeout_timer (void *user_data) { struct client *c = user_data; char *reason = xstrdup_printf ("Ping timeout: >%u seconds", c->ctx->ping_interval); - irc_close_link (c, reason); + client_close_link (c, reason); free (reason); } static void -on_irc_client_ping_timer (void *user_data) +on_client_ping_timer (void *user_data) { struct client *c = user_data; hard_assert (!c->closing_link); - irc_send (c, "PING :%s", c->ctx->server_name); - client_set_timer (c, on_irc_client_timeout_timer, c->ctx->ping_interval); + client_send (c, "PING :%s", c->ctx->server_name); + client_set_timer (c, on_client_timeout_timer, c->ctx->ping_interval); } static void client_set_ping_timer (struct client *c) { - client_set_timer (c, on_irc_client_ping_timer, c->ctx->ping_interval); + client_set_timer (c, on_client_ping_timer, c->ctx->ping_interval); } // --- IRC command handling ---------------------------------------------------- @@ -1045,7 +1036,7 @@ irc_send_reply (struct client *c, int id, ...) irc_get_text (c->ctx, id, g_default_replies[id]), ap); va_end (ap); - irc_send_str (c, &tmp); + client_send_str (c, &tmp); str_free (&tmp); } @@ -1132,12 +1123,12 @@ irc_try_finish_registration (struct client *c) char *mode = client_get_mode (c); if (*mode) - irc_send (c, ":%s MODE %s :+%s", c->nickname, c->nickname, mode); + client_send (c, ":%s MODE %s :+%s", c->nickname, c->nickname, mode); free (mode); hard_assert (c->ssl_cert_fingerprint == NULL); if ((c->ssl_cert_fingerprint = client_get_ssl_cert_fingerprint (c))) - irc_send (c, ":%s NOTICE %s :" + client_send (c, ":%s NOTICE %s :" "Your SSL client certificate fingerprint is %s", ctx->server_name, c->nickname, c->ssl_cert_fingerprint); } @@ -1173,7 +1164,7 @@ irc_handle_nick (const struct irc_message *msg, struct client *c) { char *message = xstrdup_printf (":%s!%s@%s NICK :%s", c->nickname, c->username, c->hostname, nickname); - client_send_to_roommates (c, message); + irc_send_to_roommates (c, message); free (message); } @@ -1276,7 +1267,7 @@ irc_handle_ping (const struct irc_message *msg, struct client *c) else if (msg->params.len < 1) irc_send_reply (c, IRC_ERR_NOORIGIN); else - irc_send (c, ":%s PONG :%s", + client_send (c, ":%s PONG :%s", c->ctx->server_name, msg->params.vector[0]); } @@ -1296,7 +1287,7 @@ irc_handle_quit (const struct irc_message *msg, struct client *c) { char *reason = xstrdup_printf ("Quit: %s", msg->params.len > 0 ? msg->params.vector[0] : c->nickname); - irc_close_link (c, reason); + client_close_link (c, reason); free (reason); } @@ -1330,7 +1321,7 @@ irc_channel_multicast (struct channel *chan, const char *message, for (struct channel_user *iter = chan->users; iter; iter = iter->next) { if (iter->c != except) - irc_send (iter->c, "%s", message); + client_send (iter->c, "%s", message); } } @@ -1369,7 +1360,7 @@ irc_update_user_mode (struct client *c, unsigned new_mode) } if (diff.len) - irc_send (c, ":%s MODE %s :%s", + client_send (c, ":%s MODE %s :%s", c->nickname, c->nickname, diff.str); str_free (&diff); } @@ -1407,7 +1398,7 @@ irc_handle_user_mode_change (struct client *c, const char *mode_string) && str_map_find (&c->ctx->operators, c->ssl_cert_fingerprint)) new_mode |= IRC_USER_MODE_OPERATOR; else - irc_send (c, ":%s NOTICE %s :Either you're not using an SSL" + client_send (c, ":%s NOTICE %s :Either you're not using an SSL" " client certificate, or the fingerprint doesn't match", c->ctx->server_name, c->nickname); break; @@ -1736,7 +1727,7 @@ irc_handle_user_message (const struct irc_message *msg, struct client *c, struct client *client = str_map_find (&c->ctx->users, target); if (client) { - irc_send (client, ":%s!%s@%s %s %s :%s", + client_send (client, ":%s!%s@%s %s %s :%s", c->nickname, c->username, c->hostname, command, target, text); if (allow_away_reply && client->away_message) irc_send_reply (c, IRC_RPL_AWAY, target, client->away_message); @@ -2143,11 +2134,11 @@ irc_try_part (struct client *c, const char *channel_name, const char *reason) if (!(chan->modes & IRC_CHAN_MODE_QUIET)) irc_channel_multicast (chan, message, NULL); else - irc_send (c, "%s", message); + client_send (c, "%s", message); free (message); channel_remove_user (chan, user); - channel_destroy_if_empty (c->ctx, chan); + irc_channel_destroy_if_empty (c->ctx, chan); } static void @@ -2205,11 +2196,11 @@ irc_try_kick (struct client *c, const char *channel_name, const char *nick, if (!(chan->modes & IRC_CHAN_MODE_QUIET)) irc_channel_multicast (chan, message, NULL); else - irc_send (c, "%s", message); + client_send (c, "%s", message); free (message); channel_remove_user (chan, user); - channel_destroy_if_empty (c->ctx, chan); + irc_channel_destroy_if_empty (c->ctx, chan); } static void @@ -2249,7 +2240,7 @@ irc_try_join (struct client *c, const char *channel_name, const char *key) { if (irc_validate_channel_name (channel_name) != VALIDATION_OK) RETURN_WITH_REPLY (c, IRC_ERR_BADCHANMASK, channel_name); - chan = channel_create (c->ctx, channel_name); + chan = irc_channel_create (c->ctx, channel_name); user_mode = IRC_CHAN_MODE_OPERATOR; } else if (channel_get_user (chan, c)) @@ -2275,7 +2266,7 @@ irc_try_join (struct client *c, const char *channel_name, const char *key) if (!(chan->modes & IRC_CHAN_MODE_QUIET)) irc_channel_multicast (chan, message, NULL); else - irc_send (c, "%s", message); + client_send (c, "%s", message); free (message); irc_send_rpl_topic (c, chan); @@ -2383,7 +2374,7 @@ irc_handle_kill (const struct irc_message *msg, struct client *c) RETURN_WITH_REPLY (c, IRC_ERR_NOSUCHNICK, msg->params.vector[0]); char *reason = xstrdup_printf ("Killed by %s: %s", c->nickname, msg->params.vector[1]); - irc_close_link (target, reason); + client_close_link (target, reason); free (reason); } @@ -2684,7 +2675,7 @@ error_ssl_1: } static void -on_irc_client_ready (const struct pollfd *pfd, void *user_data) +on_client_ready (const struct pollfd *pfd, void *user_data) { struct client *c = user_data; if (!c->initialized) @@ -2735,7 +2726,7 @@ client_update_poller (struct client *c, const struct pollfd *pfd) hard_assert (new_events != 0); if (!pfd || pfd->events != new_events) poller_set (&c->ctx->poller, c->socket_fd, new_events, - (poller_dispatcher_func) on_irc_client_ready, c); + (poller_dispatcher_func) on_client_ready, c); } static void @@ -2794,7 +2785,7 @@ on_irc_client_available (const struct pollfd *pfd, void *user_data) } } -// ----------------------------------------------------------------------------- +// --- Application setup ------------------------------------------------------- static int irc_ssl_verify_callback (int verify_ok, X509_STORE_CTX *ctx) @@ -3107,6 +3098,8 @@ irc_setup_listen_fds (struct server_context *ctx, struct error **e) return true; } +// --- Main -------------------------------------------------------------------- + static void on_signal_pipe_readable (const struct pollfd *fd, struct server_context *ctx) { |