]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/irc-cap.c
Rework check for number of parameters
[ngircd-alex.git] / src / ngircd / irc-cap.c
index 926943c859ab5f265ac3dda585e62ea2ff2bb4cb..42b0e4da9aa112b464d9754bb2f2632a5f41fedf 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@
 #include "conn.h"
 #include "channel.h"
 #include "client-cap.h"
+#include "irc-macros.h"
 #include "irc-write.h"
 #include "log.h"
 #include "login.h"
 #include "exp.h"
 #include "irc-cap.h"
 
-bool Handle_CAP_LS PARAMS((CLIENT *Client, char *Arg));
-bool Handle_CAP_LIST PARAMS((CLIENT *Client, char *Arg));
-bool Handle_CAP_REQ PARAMS((CLIENT *Client, char *Arg));
-bool Handle_CAP_ACK PARAMS((CLIENT *Client, char *Arg));
-bool Handle_CAP_CLEAR PARAMS((CLIENT *Client));
-bool Handle_CAP_END PARAMS((CLIENT *Client));
+/* Local functions */
 
 /**
- * Handler for the IRCv3 "CAP" command.
+ * Set CAP negotiation status and mark client as "supports capabilities".
  *
- * @param Client The client from which this command has been received.
- * @param Req Request structure with prefix and all parameters.
- * @returns CONNECTED or DISCONNECTED.
+ * @param Client The client to handle.
  */
-GLOBAL bool
-IRC_CAP(CLIENT *Client, REQUEST *Req)
+static void
+Set_CAP_Negotiation(CLIENT *Client)
 {
        assert(Client != NULL);
-       assert(Req != NULL);
 
-       /* Bad number of prameters? */
-       if (Req->argc < 1 || Req->argc > 2)
-               return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-                                         Client_ID(Client), Req->command);
+       if (Client_Type(Client) != CLIENT_USER)
+               Client_CapAdd(Client, CLIENT_CAP_PENDING);
+       Client_CapAdd(Client, CLIENT_CAP_SUPPORTED);
+}
 
-       LogDebug("Got \"%s %s\" command from \"%s\" ...",
-                Req->command, Req->argv[0], Client_ID(Client));
+/**
+ * Parse capability string and return numeric flag value.
+ *
+ * @param Args The string containing space-separated capability names.
+ * @return Changed capability flags or 0 on error.
+ */
+static int
+Parse_CAP(int Capabilities, char *Args)
+{
+       static char tmp[COMMAND_LEN];
+       char *ptr;
 
-       if (Req->argc == 1) {
-               if (strcasecmp(Req->argv[0], "CLEAR") == 0)
-                       return Handle_CAP_CLEAR(Client);
-               if (strcasecmp(Req->argv[0], "END") == 0)
-                       return Handle_CAP_END(Client);
-       }
-       if (Req->argc >= 1 && Req->argc <= 2) {
-               if (strcasecmp(Req->argv[0], "LS") == 0)
-                       return Handle_CAP_LS(Client, Req->argv[1]);
-               if (strcasecmp(Req->argv[0], "LIST") == 0)
-                       return Handle_CAP_LIST(Client, Req->argv[1]);
-       }
-       if (Req->argc == 2) {
-               if (strcasecmp(Req->argv[0], "REQ") == 0)
-                       return Handle_CAP_REQ(Client, Req->argv[1]);
-               if (strcasecmp(Req->argv[0], "ACK") == 0)
-                       return Handle_CAP_ACK(Client, Req->argv[1]);
+       assert(Args != NULL);
+
+       strlcpy(tmp, Args, sizeof(tmp));
+
+       ptr = strtok(tmp, " ");
+       while (ptr) {
+               if (*ptr == '-') {
+                       /* drop capabilities */
+                       ptr++;
+                       if (strcmp(ptr, "multi-prefix") == 0)
+                               Capabilities &= ~CLIENT_CAP_MULTI_PREFIX;
+                       else
+                               return -1;
+               } else {
+                       /* request capabilities */
+                       if (strcmp(ptr, "multi-prefix") == 0)
+                               Capabilities |= CLIENT_CAP_MULTI_PREFIX;
+                       else
+                               return -1;
+               }
+               ptr = strtok(NULL, " ");
        }
 
-       return IRC_WriteStrClient(Client, ERR_INVALIDCAP_MSG,
-                                 Client_ID(Client), Req->argv[0]);
+       return Capabilities;
+}
+
+/**
+ * Return textual representation of capability flags.
+ *
+ * Please note: this function returns a pointer to a global buffer and
+ * therefore isn't thread safe!
+ *
+ * @param Capabilities Capability flags (bitmask).
+ * @return Pointer to textual representation.
+ */
+static char *
+Get_CAP_String(int Capabilities)
+{
+       static char txt[COMMAND_LEN];
+
+       txt[0] = '\0';
+
+       if (Capabilities & CLIENT_CAP_MULTI_PREFIX)
+               strlcat(txt, "multi-prefix ", sizeof(txt));
+
+       return txt;
 }
 
 /**
- * Handler for the "CAP LS" command.
+ * Handler for the IRCv3 sub-command "CAP LS".
  *
  * @param Client The client from which this command has been received.
  * @param Arg Command argument or NULL.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_LS(CLIENT *Client, UNUSED char *Arg)
 {
        assert(Client != NULL);
 
-       if (Client_Type(Client) != CLIENT_USER)
-               Client_CapAdd(Client, CLIENT_CAP_PENDING);
+       Set_CAP_Negotiation(Client);
 
-       Client_CapAdd(Client, CLIENT_CAP_SUPPORTED);
-       return IRC_WriteStrClient(Client, "CAP %s LS :", Client_ID(Client));
+       return IRC_WriteStrClient(Client,
+                                 "CAP %s LS :multi-prefix",
+                                 Client_ID(Client));
 }
 
 /**
- * Handler for the "CAP LIST" command.
+ * Handler for the IRCv3 sub-command "CAP LIST".
  *
  * @param Client The client from which this command has been received.
  * @param Arg Command argument or NULL.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_LIST(CLIENT *Client, UNUSED char *Arg)
 {
        assert(Client != NULL);
 
-       return IRC_WriteStrClient(Client, "CAP %s LIST :", Client_ID(Client));
+       return IRC_WriteStrClient(Client, "CAP %s LIST :%s", Client_ID(Client),
+                                 Get_CAP_String(Client_Cap(Client)));
 }
 
 /**
- * Handler for the "CAP REQ" command.
+ * Handler for the IRCv3 sub-command "CAP REQ".
  *
  * @param Client The client from which this command has been received.
  * @param Arg Command argument.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_REQ(CLIENT *Client, char *Arg)
 {
+       int new_cap;
+
        assert(Client != NULL);
        assert(Arg != NULL);
 
-       return IRC_WriteStrClient(Client, "CAP %s NAK :%s",
+       Set_CAP_Negotiation(Client);
+
+       new_cap = Parse_CAP(Client_Cap(Client), Arg);
+
+       if (new_cap < 0)
+               return IRC_WriteStrClient(Client, "CAP %s NAK :%s",
+                                         Client_ID(Client), Arg);
+
+       Client_CapSet(Client, new_cap);
+       return IRC_WriteStrClient(Client, "CAP %s ACK :%s",
                                  Client_ID(Client), Arg);
 }
 
 /**
- * Handler for the "CAP ACK" command.
+ * Handler for the IRCv3 sub-command "CAP ACK".
  *
  * @param Client The client from which this command has been received.
  * @param Arg Command argument.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
-Handle_CAP_ACK(CLIENT *Client, char *Arg)
+static bool
+Handle_CAP_ACK(UNUSED CLIENT *Client, UNUSED char *Arg)
 {
        assert(Client != NULL);
        assert(Arg != NULL);
@@ -152,26 +191,33 @@ Handle_CAP_ACK(CLIENT *Client, char *Arg)
 }
 
 /**
- * Handler for the "CAP CLEAR" command.
+ * Handler for the IRCv3 sub-command "CAP CLEAR".
  *
  * @param Client The client from which this command has been received.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_CLEAR(CLIENT *Client)
 {
+       int cap_old;
+
        assert(Client != NULL);
 
-       return IRC_WriteStrClient(Client, "CAP %s ACK :", Client_ID(Client));
+       cap_old = Client_Cap(Client);
+       if (cap_old & CLIENT_CAP_MULTI_PREFIX)
+               Client_CapDel(Client, CLIENT_CAP_MULTI_PREFIX);
+
+       return IRC_WriteStrClient(Client, "CAP %s ACK :%s", Client_ID(Client),
+                                 Get_CAP_String(cap_old));
 }
 
 /**
- * Handler for the "CAP END" command.
+ * Handler for the IRCv3 sub-command "CAP END".
  *
  * @param Client The client from which this command has been received.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_END(CLIENT *Client)
 {
        assert(Client != NULL);
@@ -180,7 +226,7 @@ Handle_CAP_END(CLIENT *Client)
                /* User is still logging in ... */
                Client_CapDel(Client, CLIENT_CAP_PENDING);
 
-               if (Client_Type(Client) == CLIENT_GOTUSER) {
+               if (Client_Type(Client) == CLIENT_WAITCAPEND) {
                        /* Only "CAP END" was missing: log in! */
                        return Login_User(Client);
                }
@@ -189,4 +235,45 @@ Handle_CAP_END(CLIENT *Client)
        return CONNECTED;
 }
 
+/* Global functions */
+
+/**
+ * Handler for the IRCv3 command "CAP".
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
+GLOBAL bool
+IRC_CAP(CLIENT *Client, REQUEST *Req)
+{
+       assert(Client != NULL);
+       assert(Req != NULL);
+
+       LogDebug("Got \"%s %s\" command from \"%s\" ...",
+                Req->command, Req->argv[0], Client_ID(Client));
+
+       if (Req->argc == 1) {
+               if (strcasecmp(Req->argv[0], "CLEAR") == 0)
+                       return Handle_CAP_CLEAR(Client);
+               if (strcasecmp(Req->argv[0], "END") == 0)
+                       return Handle_CAP_END(Client);
+       }
+       if (Req->argc >= 1 && Req->argc <= 2) {
+               if (strcasecmp(Req->argv[0], "LS") == 0)
+                       return Handle_CAP_LS(Client, Req->argv[1]);
+               if (strcasecmp(Req->argv[0], "LIST") == 0)
+                       return Handle_CAP_LIST(Client, Req->argv[1]);
+       }
+       if (Req->argc == 2) {
+               if (strcasecmp(Req->argv[0], "REQ") == 0)
+                       return Handle_CAP_REQ(Client, Req->argv[1]);
+               if (strcasecmp(Req->argv[0], "ACK") == 0)
+                       return Handle_CAP_ACK(Client, Req->argv[1]);
+       }
+
+       return IRC_WriteErrClient(Client, ERR_INVALIDCAP_MSG,
+                                 Client_ID(Client), Req->argv[0]);
+}
+
 /* -eof- */