irc-cap.c 6.2 KB

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