diff --git a/degesch.c b/degesch.c index da80577..52414de 100644 --- a/degesch.c +++ b/degesch.c @@ -4741,6 +4741,25 @@ cut_word (char **s) return start; } +static char * +maybe_cut_word (char **s, bool (*validator) (void *, char *), void *user_data) +{ + char *start = *s; + size_t word_len = strcspn (*s, WORD_BREAKING_CHARS); + + char *word = xstrndup (start, word_len); + bool ok = validator (user_data, word); + free (word); + + if (!ok) + return NULL; + + char *end = start + word_len; + *s = end + strspn (end, WORD_BREAKING_CHARS); + *end = '\0'; + return start; +} + static bool try_handle_buffer_goto (struct app_context *ctx, const char *word) { @@ -4790,6 +4809,25 @@ server_command_check (struct app_context *ctx, const char *action, return false; } +static bool +validate_channel_name (void *user_data, char *word) +{ + struct server *s = user_data; + return irc_is_channel (s, word); +} + +static char * +try_get_channel (struct app_context *ctx, char **arguments) +{ + struct server *s = ctx->current_buffer->server; + char *channel_name = maybe_cut_word (arguments, validate_channel_name, s); + if (channel_name) + return channel_name; + if (ctx->current_buffer->type == BUFFER_CHANNEL) + return ctx->current_buffer->channel->name; + return NULL; +} + static void show_buffers_list (struct app_context *ctx) { @@ -5261,6 +5299,131 @@ handle_command_cycle (struct app_context *ctx, char *arguments) return true; } +static bool +handle_command_mode (struct app_context *ctx, char *arguments) +{ + if (!server_command_check (ctx, "mode", true)) + return true; + + // FIXME: allow usernames as well, not only channels + // FIXME: +channels collide with setting modes + struct server *s = ctx->current_buffer->server; + char *channel_name = try_get_channel (ctx, &arguments); + if (!channel_name) + buffer_send_error (ctx, ctx->current_buffer, + "%s: %s", "Can't set mode", + "no channel name given and this buffer is not a channel"); + else if (*arguments) + irc_send (s, "MODE %s %s", channel_name, arguments); + else + irc_send (s, "MODE %s", channel_name); + return true; +} + +static bool +handle_command_topic (struct app_context *ctx, char *arguments) +{ + if (!server_command_check (ctx, "topic", true)) + return true; + + // FIXME: currently the topic can't start with a channel name + struct server *s = ctx->current_buffer->server; + char *channel_name = try_get_channel (ctx, &arguments); + if (!channel_name) + buffer_send_error (ctx, ctx->current_buffer, + "%s: %s", "Can't change topic", + "no channel name given and this buffer is not a channel"); + else if (*arguments) + // FIXME: there's no way to unset the topic + irc_send (s, "TOPIC %s :%s", channel_name, arguments); + else + irc_send (s, "TOPIC %s", channel_name); + return true; +} + +static bool +handle_command_kick (struct app_context *ctx, char *arguments) +{ + if (!server_command_check (ctx, "kick", true)) + return true; + + struct server *s = ctx->current_buffer->server; + char *channel_name = try_get_channel (ctx, &arguments); + if (!channel_name) + buffer_send_error (ctx, ctx->current_buffer, + "%s: %s", "Can't kick", + "no channel name given and this buffer is not a channel"); + else if (*arguments) + // FIXME: the reason should be one argument + irc_send (s, "KICK %s %s", channel_name, arguments); + else + return false; + return true; +} + +static bool +handle_command_kickban (struct app_context *ctx, char *arguments) +{ + if (!server_command_check (ctx, "kickban", true)) + return true; + + struct server *s = ctx->current_buffer->server; + char *channel_name = try_get_channel (ctx, &arguments); + if (!channel_name) + buffer_send_error (ctx, ctx->current_buffer, + "%s: %s", "Can't kickban", + "no channel name given and this buffer is not a channel"); + else if (*arguments) + { + // FIXME: don't include the reason + irc_send (s, "MODE %s +b %s", channel_name, arguments); + // FIXME: the reason should be one argument + irc_send (s, "KICK %s %s", channel_name, arguments); + } + else + return false; + return true; +} + +static bool +handle_command_ban (struct app_context *ctx, char *arguments) +{ + if (!server_command_check (ctx, "ban", true)) + return true; + + struct server *s = ctx->current_buffer->server; + char *channel_name = try_get_channel (ctx, &arguments); + if (!channel_name) + buffer_send_error (ctx, ctx->current_buffer, + "%s: %s", "Can't ban", + "no channel name given and this buffer is not a channel"); + else if (*arguments) + irc_send (s, "MODE %s +b %s", channel_name, arguments); + else + return false; + return true; +} + +static bool +handle_command_invite (struct app_context *ctx, char *arguments) +{ + if (!server_command_check (ctx, "invite", true)) + return true; + + // XXX: the order of arguments should probably be reverse + struct server *s = ctx->current_buffer->server; + char *channel_name = try_get_channel (ctx, &arguments); + if (!channel_name) + buffer_send_error (ctx, ctx->current_buffer, + "%s: %s", "Can't invite", + "no channel name given and this buffer is not a channel"); + else if (*arguments) + irc_send (s, "INVITE %s %s", arguments, channel_name); + else + return false; + return true; +} + static bool handle_command_connect (struct app_context *ctx, char *arguments) { @@ -5410,18 +5573,30 @@ g_command_handlers[] = "[[,...]]", handle_command_join }, { "part", "Leave channels", - "[[,...]] [reason]", + "[[,...]] []", handle_command_part }, { "cycle", "Rejoin channels", - "[[,...]] [reason]", + "[[,...]] []", handle_command_cycle }, - NOT_IMPLEMENTED (mode) - NOT_IMPLEMENTED (topic) - NOT_IMPLEMENTED (kick) - NOT_IMPLEMENTED (kickban) - NOT_IMPLEMENTED (ban) - NOT_IMPLEMENTED (invite) + { "mode", "Change mode", + "[] [...]", + handle_command_mode }, + { "topic", "Change topic", + "[] []", + handle_command_topic }, + { "kick", "Kick user from channel", + "[] []", + handle_command_kick }, + { "kickban", "Kick and ban user from channel", + "[] []", + handle_command_kickban }, + { "ban", "Ban user from channel", + "[] ", + handle_command_ban }, + { "invite", "Invite user to channel", + "[] ", + handle_command_invite }, { "connect", "Connect to the server", "[server]", @@ -5430,7 +5605,7 @@ g_command_handlers[] = "[reason]", handle_command_disconnect }, { "list", "List channels and their topic", - "[[,...]] [server]", + "[[,...]] []", handle_command_list }, NOT_IMPLEMENTED (names) NOT_IMPLEMENTED (who)