X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Firc-cap.c;h=42b0e4da9aa112b464d9754bb2f2632a5f41fedf;hp=956b359930330347df3b601b8418ca762359f63f;hb=08f9d31d60220e8a389a2d24f42625be7749f090;hpb=da4c1ebe81bbd1335356ef40c91741b953c9f8d8 diff --git a/src/ngircd/irc-cap.c b/src/ngircd/irc-cap.c index 956b3599..42b0e4da 100644 --- a/src/ngircd/irc-cap.c +++ b/src/ngircd/irc-cap.c @@ -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" @@ -33,117 +34,155 @@ #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); @@ -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- */