numeric.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /*
  2. * ngIRCd -- The Next Generation IRC Daemon
  3. * Copyright (c)2001-2008 Alexander Barton (alex@barton.de)
  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. * Handlers for IRC numerics sent to the server
  12. */
  13. #include "portab.h"
  14. #include "imp.h"
  15. #include <assert.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include "defines.h"
  20. #include "conn.h"
  21. #include "conf.h"
  22. #include "conn.h"
  23. #include "conn-func.h"
  24. #include "channel.h"
  25. #include "irc-write.h"
  26. #include "lists.h"
  27. #include "log.h"
  28. #include "messages.h"
  29. #include "parse.h"
  30. #include "exp.h"
  31. #include "numeric.h"
  32. /**
  33. * Announce a channel and its users in the network.
  34. */
  35. static bool
  36. Announce_Channel(CLIENT *Client, CHANNEL *Chan)
  37. {
  38. CL2CHAN *cl2chan;
  39. CLIENT *cl;
  40. char str[LINE_LEN], *ptr;
  41. bool njoin;
  42. if (Conn_Options(Client_Conn(Client)) & CONN_RFC1459)
  43. njoin = false;
  44. else
  45. njoin = true;
  46. /* Get all the members of this channel */
  47. cl2chan = Channel_FirstMember(Chan);
  48. snprintf(str, sizeof(str), "NJOIN %s :", Channel_Name(Chan));
  49. while (cl2chan) {
  50. cl = Channel_GetClient(cl2chan);
  51. assert(cl != NULL);
  52. if (njoin) {
  53. /* RFC 2813: send NJOIN with nick names and modes
  54. * (if user is channel operator or has voice) */
  55. if (str[strlen(str) - 1] != ':')
  56. strlcat(str, ",", sizeof(str));
  57. if (strchr(Channel_UserModes(Chan, cl), 'v'))
  58. strlcat(str, "+", sizeof(str));
  59. if (strchr(Channel_UserModes(Chan, cl), 'o'))
  60. strlcat(str, "@", sizeof(str));
  61. strlcat(str, Client_ID(cl), sizeof(str));
  62. /* Send the data if the buffer is "full" */
  63. if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 8)) {
  64. if (!IRC_WriteStrClient(Client, "%s", str))
  65. return DISCONNECTED;
  66. snprintf(str, sizeof(str), "NJOIN %s :",
  67. Channel_Name(Chan));
  68. }
  69. } else {
  70. /* RFC 1459: no NJOIN, send JOIN and MODE */
  71. if (!IRC_WriteStrClientPrefix(Client, cl, "JOIN %s",
  72. Channel_Name(Chan)))
  73. return DISCONNECTED;
  74. ptr = Channel_UserModes(Chan, cl);
  75. while (*ptr) {
  76. if (!IRC_WriteStrClientPrefix(Client, cl,
  77. "MODE %s +%c %s",
  78. Channel_Name(Chan), ptr[0],
  79. Client_ID(cl)))
  80. return DISCONNECTED;
  81. ptr++;
  82. }
  83. }
  84. cl2chan = Channel_NextMember(Chan, cl2chan);
  85. }
  86. /* Data left in the buffer? */
  87. if (str[strlen(str) - 1] != ':') {
  88. /* Yes, send it ... */
  89. if (!IRC_WriteStrClient(Client, "%s", str))
  90. return DISCONNECTED;
  91. }
  92. return CONNECTED;
  93. } /* Announce_Channel */
  94. /**
  95. * Announce new server in the network
  96. * @param Client New server
  97. * @param Server Existing server in the network
  98. */
  99. static bool
  100. Announce_Server(CLIENT * Client, CLIENT * Server)
  101. {
  102. CLIENT *c;
  103. if (Client_Conn(Server) > NONE) {
  104. /* Announce the new server to the one already registered
  105. * which is directly connected to the local server */
  106. if (!IRC_WriteStrClient
  107. (Server, "SERVER %s %d %d :%s", Client_ID(Client),
  108. Client_Hops(Client) + 1, Client_MyToken(Client),
  109. Client_Info(Client)))
  110. return DISCONNECTED;
  111. }
  112. if (Client_Hops(Server) == 1)
  113. c = Client_ThisServer();
  114. else
  115. c = Client_TopServer(Server);
  116. /* Inform new server about the one already registered in the network */
  117. return IRC_WriteStrClientPrefix(Client, c, "SERVER %s %d %d :%s",
  118. Client_ID(Server), Client_Hops(Server) + 1,
  119. Client_MyToken(Server), Client_Info(Server));
  120. } /* Announce_Server */
  121. /**
  122. * Announce existing user to a new server
  123. * @param Client New server
  124. * @param User Existing user in the network
  125. */
  126. static bool
  127. Announce_User(CLIENT * Client, CLIENT * User)
  128. {
  129. CONN_ID conn;
  130. char *modes;
  131. conn = Client_Conn(Client);
  132. if (Conn_Options(conn) & CONN_RFC1459) {
  133. /* RFC 1459 mode: separate NICK and USER commands */
  134. if (! Conn_WriteStr(conn, "NICK %s :%d",
  135. Client_ID(User), Client_Hops(User) + 1))
  136. return DISCONNECTED;
  137. if (! Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
  138. Client_ID(User), Client_User(User),
  139. Client_Hostname(User),
  140. Client_ID(Client_Introducer(User)),
  141. Client_Info(User)))
  142. return DISCONNECTED;
  143. modes = Client_Modes(User);
  144. if (modes[0]) {
  145. return Conn_WriteStr(conn, ":%s MODE %s +%s",
  146. Client_ID(User), Client_ID(User),
  147. modes);
  148. }
  149. return CONNECTED;
  150. } else {
  151. /* RFC 2813 mode: one combined NICK or SERVICE command */
  152. if (Client_Type(User) == CLIENT_SERVICE
  153. && strchr(Client_Flags(Client), 'S'))
  154. return IRC_WriteStrClient(Client,
  155. "SERVICE %s %d * +%s %d :%s", Client_Mask(User),
  156. Client_MyToken(Client_Introducer(User)),
  157. Client_Modes(User), Client_Hops(User) + 1,
  158. Client_Info(User));
  159. else
  160. return IRC_WriteStrClient(Client,
  161. "NICK %s %d %s %s %d +%s :%s",
  162. Client_ID(User), Client_Hops(User) + 1,
  163. Client_User(User), Client_Hostname(User),
  164. Client_MyToken(Client_Introducer(User)),
  165. Client_Modes(User), Client_Info(User));
  166. }
  167. } /* Announce_User */
  168. #ifdef IRCPLUS
  169. /**
  170. * Synchronize invite and ban lists between servers
  171. * @param Client New server
  172. */
  173. static bool
  174. Synchronize_Lists(CLIENT * Client)
  175. {
  176. CHANNEL *c;
  177. struct list_head *head;
  178. struct list_elem *elem;
  179. assert(Client != NULL);
  180. c = Channel_First();
  181. while (c) {
  182. /* ban list */
  183. head = Channel_GetListBans(c);
  184. elem = Lists_GetFirst(head);
  185. while (elem) {
  186. if (!IRC_WriteStrClient(Client, "MODE %s +b %s",
  187. Channel_Name(c),
  188. Lists_GetMask(elem))) {
  189. return DISCONNECTED;
  190. }
  191. elem = Lists_GetNext(elem);
  192. }
  193. /* invite list */
  194. head = Channel_GetListInvites(c);
  195. elem = Lists_GetFirst(head);
  196. while (elem) {
  197. if (!IRC_WriteStrClient(Client, "MODE %s +I %s",
  198. Channel_Name(c),
  199. Lists_GetMask(elem))) {
  200. return DISCONNECTED;
  201. }
  202. elem = Lists_GetNext(elem);
  203. }
  204. c = Channel_Next(c);
  205. }
  206. return CONNECTED;
  207. }
  208. /**
  209. * Send CHANINFO commands to a new server (inform it about existing channels).
  210. * @param Client New server
  211. * @param Chan Channel
  212. */
  213. static bool
  214. Send_CHANINFO(CLIENT * Client, CHANNEL * Chan)
  215. {
  216. char *modes, *topic;
  217. bool has_k, has_l;
  218. #ifdef DEBUG
  219. Log(LOG_DEBUG, "Sending CHANINFO commands ...");
  220. #endif
  221. modes = Channel_Modes(Chan);
  222. topic = Channel_Topic(Chan);
  223. if (!*modes && !*topic)
  224. return CONNECTED;
  225. has_k = strchr(modes, 'k') != NULL;
  226. has_l = strchr(modes, 'l') != NULL;
  227. /* send CHANINFO */
  228. if (!has_k && !has_l) {
  229. if (!*topic) {
  230. /* "CHANINFO <chan> +<modes>" */
  231. return IRC_WriteStrClient(Client, "CHANINFO %s +%s",
  232. Channel_Name(Chan), modes);
  233. }
  234. /* "CHANINFO <chan> +<modes> :<topic>" */
  235. return IRC_WriteStrClient(Client, "CHANINFO %s +%s :%s",
  236. Channel_Name(Chan), modes, topic);
  237. }
  238. /* "CHANINFO <chan> +<modes> <key> <limit> :<topic>" */
  239. return IRC_WriteStrClient(Client, "CHANINFO %s +%s %s %lu :%s",
  240. Channel_Name(Chan), modes,
  241. has_k ? Channel_Key(Chan) : "*",
  242. has_l ? Channel_MaxUsers(Chan) : 0, topic);
  243. } /* Send_CHANINFO */
  244. #endif /* IRCPLUS */
  245. /**
  246. * Handle ENDOFMOTD (376) numeric and login remote server.
  247. * The peer is either an IRC server (no IRC+ protocol), or we got the
  248. * ENDOFMOTD numeric from an IRC+ server. We have to register the new server.
  249. */
  250. GLOBAL bool
  251. IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
  252. {
  253. int max_hops, i;
  254. CLIENT *c;
  255. CHANNEL *chan;
  256. Client_SetType(Client, CLIENT_SERVER);
  257. Log(LOG_NOTICE | LOG_snotice,
  258. "Server \"%s\" registered (connection %d, 1 hop - direct link).",
  259. Client_ID(Client), Client_Conn(Client));
  260. /* Get highest hop count */
  261. max_hops = 0;
  262. c = Client_First();
  263. while (c) {
  264. if (Client_Hops(c) > max_hops)
  265. max_hops = Client_Hops(c);
  266. c = Client_Next(c);
  267. }
  268. /* Inform the new server about all other servers, and announce the
  269. * new server to all the already registered ones. Important: we have
  270. * to do this "in order" and can't introduce servers of which the
  271. * "toplevel server" isn't known already. */
  272. for (i = 0; i < (max_hops + 1); i++) {
  273. for (c = Client_First(); c != NULL; c = Client_Next(c)) {
  274. if (Client_Type(c) != CLIENT_SERVER)
  275. continue; /* not a server */
  276. if (Client_Hops(c) != i)
  277. continue; /* not actual "nesting level" */
  278. if (c == Client || c == Client_ThisServer())
  279. continue; /* that's us or the peer! */
  280. if (!Announce_Server(Client, c))
  281. return DISCONNECTED;
  282. }
  283. }
  284. /* Announce all the users to the new server */
  285. c = Client_First();
  286. while (c) {
  287. if (Client_Type(c) == CLIENT_USER ||
  288. Client_Type(c) == CLIENT_SERVICE) {
  289. if (!Announce_User(Client, c))
  290. return DISCONNECTED;
  291. }
  292. c = Client_Next(c);
  293. }
  294. /* Announce all channels to the new server */
  295. chan = Channel_First();
  296. while (chan) {
  297. if (Channel_IsLocal(chan)) {
  298. chan = Channel_Next(chan);
  299. continue;
  300. }
  301. #ifdef IRCPLUS
  302. /* Send CHANINFO if the peer supports it */
  303. if (strchr(Client_Flags(Client), 'C')) {
  304. if (!Send_CHANINFO(Client, chan))
  305. return DISCONNECTED;
  306. }
  307. #endif
  308. if (!Announce_Channel(Client, chan))
  309. return DISCONNECTED;
  310. /* Get next channel ... */
  311. chan = Channel_Next(chan);
  312. }
  313. #ifdef IRCPLUS
  314. if (strchr(Client_Flags(Client), 'L')) {
  315. LogDebug("Synchronizing INVITE- and BAN-lists ...");
  316. if (!Synchronize_Lists(Client))
  317. return DISCONNECTED;
  318. }
  319. #endif
  320. return CONNECTED;
  321. } /* IRC_Num_ENDOFMOTD */
  322. /**
  323. * Handle ISUPPORT (005) numeric.
  324. */
  325. GLOBAL bool
  326. IRC_Num_ISUPPORT(CLIENT * Client, REQUEST * Req)
  327. {
  328. int i;
  329. char *key, *value;
  330. for (i = 1; i < Req->argc - 1; i++) {
  331. key = Req->argv[i];
  332. value = strchr(key, '=');
  333. if (value)
  334. *value++ = '\0';
  335. else
  336. value = "";
  337. if (strcmp("NICKLEN", key) == 0) {
  338. if ((unsigned int)atol(value) == Conf_MaxNickLength - 1)
  339. continue;
  340. /* Nick name length settings are different! */
  341. Log(LOG_ERR,
  342. "Peer uses incompatible nick name length (%d/%d)! Disconnecting ...",
  343. Conf_MaxNickLength - 1, atoi(value));
  344. Conn_Close(Client_Conn(Client),
  345. "Incompatible nick name length",
  346. NULL, false);
  347. return DISCONNECTED;
  348. }
  349. }
  350. return CONNECTED;
  351. } /* IRC_Num_ISUPPORT */
  352. /* -eof- */