irc-cap.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /*
  2. * ngIRCd -- The Next Generation IRC Daemon
  3. * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. * Please read the file COPYING, README and AUTHORS for more information.
  10. */
  11. #include "portab.h"
  12. /**
  13. * @file
  14. * Handler for IRC capability ("CAP") commands
  15. */
  16. #include "imp.h"
  17. #include <assert.h>
  18. #include <string.h>
  19. #include "defines.h"
  20. #include "conn.h"
  21. #include "channel.h"
  22. #include "client-cap.h"
  23. #include "irc-macros.h"
  24. #include "irc-write.h"
  25. #include "log.h"
  26. #include "login.h"
  27. #include "messages.h"
  28. #include "parse.h"
  29. #include "exp.h"
  30. #include "irc-cap.h"
  31. /* Local functions */
  32. /**
  33. * Set CAP negotiation status and mark client as "supports capabilities".
  34. *
  35. * @param Client The client to handle.
  36. */
  37. static void
  38. Set_CAP_Negotiation(CLIENT *Client)
  39. {
  40. assert(Client != NULL);
  41. if (Client_Type(Client) != CLIENT_USER)
  42. Client_CapAdd(Client, CLIENT_CAP_PENDING);
  43. Client_CapAdd(Client, CLIENT_CAP_SUPPORTED);
  44. }
  45. /**
  46. * Parse capability string and return numeric flag value.
  47. *
  48. * @param Args The string containing space-separated capability names.
  49. * @return Changed capability flags or 0 on error.
  50. */
  51. static int
  52. Parse_CAP(int Capabilities, char *Args)
  53. {
  54. static char tmp[COMMAND_LEN];
  55. char *ptr;
  56. assert(Args != NULL);
  57. strlcpy(tmp, Args, sizeof(tmp));
  58. ptr = strtok(tmp, " ");
  59. while (ptr) {
  60. if (*ptr == '-') {
  61. /* drop capabilities */
  62. ptr++;
  63. if (strcmp(ptr, "multi-prefix") == 0)
  64. Capabilities &= ~CLIENT_CAP_MULTI_PREFIX;
  65. else
  66. return -1;
  67. } else {
  68. /* request capabilities */
  69. if (strcmp(ptr, "multi-prefix") == 0)
  70. Capabilities |= CLIENT_CAP_MULTI_PREFIX;
  71. else
  72. return -1;
  73. }
  74. ptr = strtok(NULL, " ");
  75. }
  76. return Capabilities;
  77. }
  78. /**
  79. * Return textual representation of capability flags.
  80. *
  81. * Please note: this function returns a pointer to a global buffer and
  82. * therefore isn't thread safe!
  83. *
  84. * @param Capabilities Capability flags (bitmask).
  85. * @return Pointer to textual representation.
  86. */
  87. static char *
  88. Get_CAP_String(int Capabilities)
  89. {
  90. static char txt[COMMAND_LEN];
  91. txt[0] = '\0';
  92. if (Capabilities & CLIENT_CAP_MULTI_PREFIX)
  93. strlcat(txt, "multi-prefix ", sizeof(txt));
  94. return txt;
  95. }
  96. /**
  97. * Handler for the IRCv3 sub-command "CAP LS".
  98. *
  99. * @param Client The client from which this command has been received.
  100. * @param Arg Command argument or NULL.
  101. * @return CONNECTED or DISCONNECTED.
  102. */
  103. static bool
  104. Handle_CAP_LS(CLIENT *Client, UNUSED char *Arg)
  105. {
  106. assert(Client != NULL);
  107. Set_CAP_Negotiation(Client);
  108. return IRC_WriteStrClient(Client,
  109. "CAP %s LS :multi-prefix",
  110. Client_ID(Client));
  111. }
  112. /**
  113. * Handler for the IRCv3 sub-command "CAP LIST".
  114. *
  115. * @param Client The client from which this command has been received.
  116. * @param Arg Command argument or NULL.
  117. * @return CONNECTED or DISCONNECTED.
  118. */
  119. static bool
  120. Handle_CAP_LIST(CLIENT *Client, UNUSED char *Arg)
  121. {
  122. assert(Client != NULL);
  123. return IRC_WriteStrClient(Client, "CAP %s LIST :%s", Client_ID(Client),
  124. Get_CAP_String(Client_Cap(Client)));
  125. }
  126. /**
  127. * Handler for the IRCv3 sub-command "CAP REQ".
  128. *
  129. * @param Client The client from which this command has been received.
  130. * @param Arg Command argument.
  131. * @return CONNECTED or DISCONNECTED.
  132. */
  133. static bool
  134. Handle_CAP_REQ(CLIENT *Client, char *Arg)
  135. {
  136. int new_cap;
  137. assert(Client != NULL);
  138. assert(Arg != NULL);
  139. Set_CAP_Negotiation(Client);
  140. new_cap = Parse_CAP(Client_Cap(Client), Arg);
  141. if (new_cap < 0)
  142. return IRC_WriteStrClient(Client, "CAP %s NAK :%s",
  143. Client_ID(Client), Arg);
  144. Client_CapSet(Client, new_cap);
  145. return IRC_WriteStrClient(Client, "CAP %s ACK :%s",
  146. Client_ID(Client), Arg);
  147. }
  148. /**
  149. * Handler for the IRCv3 sub-command "CAP ACK".
  150. *
  151. * @param Client The client from which this command has been received.
  152. * @param Arg Command argument.
  153. * @return CONNECTED or DISCONNECTED.
  154. */
  155. static bool
  156. Handle_CAP_ACK(UNUSED CLIENT *Client, UNUSED char *Arg)
  157. {
  158. assert(Client != NULL);
  159. assert(Arg != NULL);
  160. return CONNECTED;
  161. }
  162. /**
  163. * Handler for the IRCv3 sub-command "CAP CLEAR".
  164. *
  165. * @param Client The client from which this command has been received.
  166. * @return CONNECTED or DISCONNECTED.
  167. */
  168. static bool
  169. Handle_CAP_CLEAR(CLIENT *Client)
  170. {
  171. int cap_old;
  172. assert(Client != NULL);
  173. cap_old = Client_Cap(Client);
  174. if (cap_old & CLIENT_CAP_MULTI_PREFIX)
  175. Client_CapDel(Client, CLIENT_CAP_MULTI_PREFIX);
  176. return IRC_WriteStrClient(Client, "CAP %s ACK :%s", Client_ID(Client),
  177. Get_CAP_String(cap_old));
  178. }
  179. /**
  180. * Handler for the IRCv3 sub-command "CAP END".
  181. *
  182. * @param Client The client from which this command has been received.
  183. * @return CONNECTED or DISCONNECTED.
  184. */
  185. static bool
  186. Handle_CAP_END(CLIENT *Client)
  187. {
  188. assert(Client != NULL);
  189. if (Client_Type(Client) != CLIENT_USER) {
  190. /* User is still logging in ... */
  191. Client_CapDel(Client, CLIENT_CAP_PENDING);
  192. if (Client_Type(Client) == CLIENT_WAITCAPEND) {
  193. /* Only "CAP END" was missing: log in! */
  194. return Login_User(Client);
  195. }
  196. }
  197. return CONNECTED;
  198. }
  199. /* Global functions */
  200. /**
  201. * Handler for the IRCv3 command "CAP".
  202. *
  203. * @param Client The client from which this command has been received.
  204. * @param Req Request structure with prefix and all parameters.
  205. * @return CONNECTED or DISCONNECTED.
  206. */
  207. GLOBAL bool
  208. IRC_CAP(CLIENT *Client, REQUEST *Req)
  209. {
  210. assert(Client != NULL);
  211. assert(Req != NULL);
  212. LogDebug("Got \"%s %s\" command from \"%s\" ...",
  213. Req->command, Req->argv[0], Client_ID(Client));
  214. if (Req->argc == 1) {
  215. if (strcasecmp(Req->argv[0], "CLEAR") == 0)
  216. return Handle_CAP_CLEAR(Client);
  217. if (strcasecmp(Req->argv[0], "END") == 0)
  218. return Handle_CAP_END(Client);
  219. }
  220. if (Req->argc >= 1 && Req->argc <= 2) {
  221. if (strcasecmp(Req->argv[0], "LS") == 0)
  222. return Handle_CAP_LS(Client, Req->argv[1]);
  223. if (strcasecmp(Req->argv[0], "LIST") == 0)
  224. return Handle_CAP_LIST(Client, Req->argv[1]);
  225. }
  226. if (Req->argc == 2) {
  227. if (strcasecmp(Req->argv[0], "REQ") == 0)
  228. return Handle_CAP_REQ(Client, Req->argv[1]);
  229. if (strcasecmp(Req->argv[0], "ACK") == 0)
  230. return Handle_CAP_ACK(Client, Req->argv[1]);
  231. }
  232. return IRC_WriteErrClient(Client, ERR_INVALIDCAP_MSG,
  233. Client_ID(Client), Req->argv[0]);
  234. }
  235. /* -eof- */