jcs
/wallops
/amendments
/88
irc: Implement /op, /deop, /voice, and /devoice
jcs made amendment 88 2 months ago
--- irc.c Wed Sep 11 16:25:38 2024
+++ irc.c Wed Sep 11 17:36:11 2024
@@ -45,8 +45,10 @@ void irc_remove_nick_from_channel(struct irc_channel *
bool irc_nick_is_in_channel(struct irc_channel *channel, char *nick);
void irc_change_user_nick(struct irc_channel *channel,
struct irc_user *user, char *nick);
-void irc_parse_channel_mode_change(struct irc_channel *channel, char *mode,
+bool irc_parse_channel_mode_change(struct irc_channel *channel, char *mode,
char *args);
+void irc_do_mode_to_users(struct irc_connection *conn, char *channel_name,
+ char *mode, char *nicks);
#define IRC_CAN_SEND(conn) ((conn)->send_pb.ioResult <= 0)
@@ -643,13 +645,14 @@ irc_process_server(struct irc_connection *conn)
"$B***$0 Mode change ($B%s %s$0) on $B%s$0 by $B%s$0",
msg.arg[1], msg.msg, msg.arg[0], user->nick);
if (channel) {
- irc_parse_channel_mode_change(channel, msg.arg[1],
- msg.msg);
- /*
- * To simplify parsing mode changes like "+ik blah",
- * just ask for the full mode now to get a 324
- */
- irc_printf(conn, "MODE %s\r\n", channel->name);
+ bool others = irc_parse_channel_mode_change(channel,
+ msg.arg[1], msg.msg);
+ if (others)
+ /*
+ * To simplify parsing mode changes like "+ik blah",
+ * just ask for the full mode now to get a 324
+ */
+ irc_printf(conn, "MODE %s\r\n", channel->name);
}
}
return true;
@@ -946,9 +949,22 @@ irc_process_server(struct irc_connection *conn)
case 475:
/* can't join channel */
chatter_printf(conn->chatter, conn, NULL,
- "$B***$0 Cannot join $B%s$0$/: %s",
+ "$B***$0 Can't join $B%s$0$/: %s",
msg.arg[1], msg.msg);
return true;
+ case 481:
+ /* not oper */
+ chatter_printf(conn->chatter, conn, NULL,
+ "$B***$0 You can't do that thing if you ain't got that "
+ "swing:$/: %s",
+ msg.msg);
+ return true;
+ case 482:
+ /* not op */
+ chatter_printf(conn->chatter, conn, NULL,
+ "$B***$0 Can't do that on $B%s$0$/: %s",
+ msg.arg[1], msg.msg);
+ return true;
case 502:
/* error */
chatter_printf(conn->chatter, conn, NULL,
@@ -1050,6 +1066,22 @@ irc_process_input(struct irc_connection *conn, struct
arg0);
return;
}
+ if (strcasecmp(arg0, "deop") == 0) {
+ if (conn == NULL)
+ goto not_connected;
+ if (channel_name == NULL)
+ goto not_in_channel;
+ irc_do_mode_to_users(conn, channel_name, "-o", str);
+ return;
+ }
+ if (strcasecmp(arg0, "devoice") == 0) {
+ if (conn == NULL)
+ goto not_connected;
+ if (channel_name == NULL)
+ goto not_in_channel;
+ irc_do_mode_to_users(conn, channel_name, "-v", str);
+ return;
+ }
if (strcasecmp(arg0, "disco") == 0 ||
strcasecmp(arg0, "discon") == 0 ||
strcasecmp(arg0, "disconnect") == 0) {
@@ -1119,6 +1151,14 @@ irc_process_input(struct irc_connection *conn, struct
irc_printf(conn, "NICK %s\r\n", str);
return;
}
+ if (strcasecmp(arg0, "op") == 0) {
+ if (conn == NULL)
+ goto not_connected;
+ if (channel_name == NULL)
+ goto not_in_channel;
+ irc_do_mode_to_users(conn, channel_name, "+o", str);
+ return;
+ }
if (strcasecmp(arg0, "part") == 0) {
if (conn == NULL)
goto not_connected;
@@ -1165,6 +1205,14 @@ irc_process_input(struct irc_connection *conn, struct
(str ? str : ""));
return;
}
+ if (strcasecmp(arg0, "voice") == 0) {
+ if (conn == NULL)
+ goto not_connected;
+ if (channel_name == NULL)
+ goto not_in_channel;
+ irc_do_mode_to_users(conn, channel_name, "+v", str);
+ return;
+ }
if (strcasecmp(arg0, "who") == 0) {
if (conn == NULL)
goto not_connected;
@@ -1488,7 +1536,7 @@ irc_change_user_nick(struct irc_channel *channel, stru
}
}
-void
+bool
irc_parse_channel_mode_change(struct irc_channel *channel, char *mode,
char *args)
{
@@ -1497,6 +1545,7 @@ irc_parse_channel_mode_change(struct irc_channel *chan
char *user;
short n, j, flags;
bool add = false;
+ bool others = false;
len = strlen(mode);
@@ -1546,7 +1595,48 @@ irc_parse_channel_mode_change(struct irc_channel *chan
break;
default:
/* some other channel mode */
+ others = true;
break;
+ }
+ }
+
+ return others;
+}
+
+void
+irc_do_mode_to_users(struct irc_connection *conn, char *channel_name,
+ char *mode, char *nicks)
+{
+#define MAX_MODES_AT_ONCE 4
+ /* assume we can only do 4 at a time */
+ char modes[(2 * MAX_MODES_AT_ONCE) + 1];
+ char mnicks[((member_size(struct irc_user, nick) + 1) *
+ MAX_MODES_AT_ONCE) + 1];
+ char *nick;
+ short count;
+
+ /* TODO: support +b by looking up user in nick table for hostmask */
+
+ modes[0] = '\0';
+ mnicks[0] = '\0';
+ count = 0;
+
+ while (nicks != NULL) {
+ strlcat(modes, mode, sizeof(modes));
+
+ nick = strsep(&nicks, " ");
+ strlcat(mnicks, nick, sizeof(mnicks));
+ count++;
+
+ if (nicks != NULL && count != MAX_MODES_AT_ONCE)
+ strlcat(mnicks, " ", sizeof(mnicks));
+
+ if (nicks == NULL || count == 4) {
+ irc_printf(conn, "MODE %s %s %s\r\n", channel_name, modes,
+ mnicks);
+ mnicks[0] = '\0';
+ modes[0] = '\0';
+ count = 0;
}
}
}
\ No newline at end of file