irc-info.c 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632
  1. /*
  2. * ngIRCd -- The Next Generation IRC Daemon
  3. * Copyright (c)2001-2012 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. * IRC info commands
  15. */
  16. #include "imp.h"
  17. #include <assert.h>
  18. #include <errno.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <strings.h>
  23. #include "ngircd.h"
  24. #include "conn-func.h"
  25. #include "conn-zip.h"
  26. #include "channel.h"
  27. #include "class.h"
  28. #include "conf.h"
  29. #include "defines.h"
  30. #include "lists.h"
  31. #include "log.h"
  32. #include "messages.h"
  33. #include "match.h"
  34. #include "tool.h"
  35. #include "parse.h"
  36. #include "irc.h"
  37. #include "irc-write.h"
  38. #include "client-cap.h"
  39. #include "exp.h"
  40. #include "irc-info.h"
  41. GLOBAL bool
  42. IRC_ADMIN(CLIENT *Client, REQUEST *Req )
  43. {
  44. CLIENT *target, *prefix;
  45. assert( Client != NULL );
  46. assert( Req != NULL );
  47. if(( Req->argc > 1 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
  48. /* find target ... */
  49. if( Req->argc == 1 ) target = Client_Search( Req->argv[0] );
  50. else target = Client_ThisServer( );
  51. /* find Prefix */
  52. if( Client_Type( Client ) == CLIENT_SERVER ) prefix = Client_Search( Req->prefix );
  53. else prefix = Client;
  54. if( ! prefix ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
  55. /* forwad message to another server? */
  56. if( target != Client_ThisServer( ))
  57. {
  58. if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( prefix, ERR_NOSUCHSERVER_MSG, Client_ID( prefix ), Req->argv[0] );
  59. /* forward */
  60. IRC_WriteStrClientPrefix( target, prefix, "ADMIN %s", Req->argv[0] );
  61. return CONNECTED;
  62. }
  63. /* mit Versionsinfo antworten */
  64. if( ! IRC_WriteStrClient( Client, RPL_ADMINME_MSG, Client_ID( prefix ), Conf_ServerName )) return DISCONNECTED;
  65. if( ! IRC_WriteStrClient( Client, RPL_ADMINLOC1_MSG, Client_ID( prefix ), Conf_ServerAdmin1 )) return DISCONNECTED;
  66. if( ! IRC_WriteStrClient( Client, RPL_ADMINLOC2_MSG, Client_ID( prefix ), Conf_ServerAdmin2 )) return DISCONNECTED;
  67. if( ! IRC_WriteStrClient( Client, RPL_ADMINEMAIL_MSG, Client_ID( prefix ), Conf_ServerAdminMail )) return DISCONNECTED;
  68. IRC_SetPenalty( Client, 1 );
  69. return CONNECTED;
  70. } /* IRC_ADMIN */
  71. /**
  72. * Handler for the IRC command "INFO".
  73. * See RFC 2812 section 3.4.10.
  74. */
  75. GLOBAL bool
  76. IRC_INFO(CLIENT * Client, REQUEST * Req)
  77. {
  78. CLIENT *target, *prefix;
  79. char msg[510];
  80. assert(Client != NULL);
  81. assert(Req != NULL);
  82. /* Wrong number of parameters? */
  83. if (Req->argc > 1)
  84. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  85. Client_ID(Client), Req->command);
  86. /* Determine prefix */
  87. if (Client_Type(Client) == CLIENT_SERVER)
  88. prefix = Client_Search(Req->prefix);
  89. else
  90. prefix = Client;
  91. if (!prefix)
  92. return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
  93. Client_ID(Client), Req->prefix);
  94. /* Look for a target */
  95. if (Req->argc > 0)
  96. target = Client_Search(Req->argv[0]);
  97. else
  98. target = Client_ThisServer();
  99. /* Make sure that the target is a server */
  100. if (target && Client_Type(target) != CLIENT_SERVER)
  101. target = Client_Introducer(target);
  102. if (!target)
  103. return IRC_WriteStrClient(prefix, ERR_NOSUCHSERVER_MSG,
  104. Client_ID(prefix), Req->argv[0]);
  105. /* Pass on to another server? */
  106. if (target != Client_ThisServer()) {
  107. IRC_WriteStrClientPrefix(target, prefix, "INFO %s",
  108. Req->argv[0]);
  109. return CONNECTED;
  110. }
  111. if (!IRC_WriteStrClient(Client, RPL_INFO_MSG, Client_ID(prefix),
  112. NGIRCd_Version))
  113. return DISCONNECTED;
  114. #if defined(__DATE__) && defined(__TIME__)
  115. snprintf(msg, sizeof(msg), "Birth Date: %s at %s", __DATE__, __TIME__);
  116. if (!IRC_WriteStrClient(Client, RPL_INFO_MSG, Client_ID(prefix), msg))
  117. return DISCONNECTED;
  118. #endif
  119. strlcpy(msg, "On-line since ", sizeof(msg));
  120. strlcat(msg, NGIRCd_StartStr, sizeof(msg));
  121. if (!IRC_WriteStrClient(Client, RPL_INFO_MSG, Client_ID(prefix), msg))
  122. return DISCONNECTED;
  123. if (!IRC_WriteStrClient(Client, RPL_ENDOFINFO_MSG, Client_ID(prefix)))
  124. return DISCONNECTED;
  125. IRC_SetPenalty(Client, 2);
  126. return CONNECTED;
  127. } /* IRC_INFO */
  128. /**
  129. * Handler for the IRC "ISON" command.
  130. *
  131. * See RFC 2812, 4.9 "Ison message".
  132. *
  133. * @param Client The client from which this command has been received.
  134. * @param Req Request structure with prefix and all parameters.
  135. * @return CONNECTED or DISCONNECTED.
  136. */
  137. GLOBAL bool
  138. IRC_ISON( CLIENT *Client, REQUEST *Req )
  139. {
  140. char rpl[COMMAND_LEN];
  141. CLIENT *c;
  142. char *ptr;
  143. int i;
  144. assert(Client != NULL);
  145. assert(Req != NULL);
  146. /* Bad number of arguments? */
  147. if (Req->argc < 1)
  148. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  149. Client_ID(Client), Req->command);
  150. strlcpy(rpl, RPL_ISON_MSG, sizeof rpl);
  151. for (i = 0; i < Req->argc; i++) {
  152. /* "All" ircd even parse ":<x> <y> ..." arguments and split
  153. * them up; so we do the same ... */
  154. ptr = strtok(Req->argv[i], " ");
  155. while (ptr) {
  156. ngt_TrimStr(ptr);
  157. c = Client_Search(ptr);
  158. if (c && Client_Type(c) == CLIENT_USER) {
  159. strlcat(rpl, Client_ID(c), sizeof(rpl));
  160. strlcat(rpl, " ", sizeof(rpl));
  161. }
  162. ptr = strtok(NULL, " ");
  163. }
  164. }
  165. ngt_TrimLastChr(rpl, ' ');
  166. return IRC_WriteStrClient(Client, rpl, Client_ID(Client));
  167. } /* IRC_ISON */
  168. /**
  169. * Handler for the IRC "LINKS" command.
  170. *
  171. * See RFC 2812, 3.4.5 "Links message".
  172. *
  173. * @param Client The client from which this command has been received.
  174. * @param Req Request structure with prefix and all parameters.
  175. * @return CONNECTED or DISCONNECTED.
  176. */
  177. GLOBAL bool
  178. IRC_LINKS(CLIENT *Client, REQUEST *Req)
  179. {
  180. CLIENT *target, *from, *c;
  181. char *mask;
  182. assert(Client != NULL);
  183. assert(Req != NULL);
  184. IRC_SetPenalty(Client, 1);
  185. if (Req->argc > 2)
  186. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  187. Client_ID(Client), Req->command);
  188. /* Get pointer to server mask or "*", if none given */
  189. if (Req->argc > 0)
  190. mask = Req->argv[Req->argc - 1];
  191. else
  192. mask = "*";
  193. if (Client_Type(Client) == CLIENT_SERVER)
  194. from = Client_Search(Req->prefix);
  195. else
  196. from = Client;
  197. if (!from)
  198. return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
  199. Client_ID(Client), Req->prefix);
  200. /* Forward? */
  201. if (Req->argc == 2) {
  202. target = Client_Search(Req->argv[0]);
  203. if (! target || Client_Type(target) != CLIENT_SERVER)
  204. return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
  205. Client_ID(from),
  206. Req->argv[0] );
  207. else
  208. if (target != Client_ThisServer())
  209. return IRC_WriteStrClientPrefix(target, from,
  210. "LINKS %s %s", Req->argv[0],
  211. Req->argv[1]);
  212. }
  213. c = Client_First();
  214. while (c) {
  215. if (Client_Type(c) == CLIENT_SERVER
  216. && MatchCaseInsensitive(mask, Client_ID(c))) {
  217. if (!IRC_WriteStrClient(from, RPL_LINKS_MSG,
  218. Client_ID(from), Client_ID(c),
  219. Client_ID(Client_TopServer(c)
  220. ? Client_TopServer(c)
  221. : Client_ThisServer()),
  222. Client_Hops(c), Client_Info(c)))
  223. return DISCONNECTED;
  224. }
  225. c = Client_Next(c);
  226. }
  227. return IRC_WriteStrClient(from, RPL_ENDOFLINKS_MSG,
  228. Client_ID(from), mask);
  229. } /* IRC_LINKS */
  230. GLOBAL bool
  231. IRC_LUSERS( CLIENT *Client, REQUEST *Req )
  232. {
  233. CLIENT *target, *from;
  234. assert( Client != NULL );
  235. assert( Req != NULL );
  236. if(( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
  237. /* Absender ermitteln */
  238. if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
  239. else from = Client;
  240. if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
  241. /* An anderen Server forwarden? */
  242. if( Req->argc == 2 )
  243. {
  244. target = Client_Search( Req->argv[1] );
  245. if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[1] );
  246. else if( target != Client_ThisServer( )) return IRC_WriteStrClientPrefix( target, from, "LUSERS %s %s", Req->argv[0], Req->argv[1] );
  247. }
  248. /* Wer ist der Absender? */
  249. if( Client_Type( Client ) == CLIENT_SERVER ) target = Client_Search( Req->prefix );
  250. else target = Client;
  251. if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
  252. IRC_Send_LUSERS( target );
  253. IRC_SetPenalty( target, 1 );
  254. return CONNECTED;
  255. } /* IRC_LUSERS */
  256. /**
  257. * Handler for the IRC command "SERVLIST".
  258. * List registered services, see RFC 2811, section 3.5.1: the syntax is
  259. * "SERVLIST [<mask> [<type>]]".
  260. */
  261. GLOBAL bool
  262. IRC_SERVLIST(CLIENT *Client, REQUEST *Req)
  263. {
  264. CLIENT *c;
  265. assert(Client != NULL);
  266. assert(Req != NULL);
  267. if (Req->argc > 2)
  268. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  269. Client_ID(Client), Req->command);
  270. if (Req->argc < 2 || strcmp(Req->argv[1], "0") == 0) {
  271. for (c = Client_First(); c!= NULL; c = Client_Next(c)) {
  272. if (Client_Type(c) != CLIENT_SERVICE)
  273. continue;
  274. if (Req->argc > 0 && !MatchCaseInsensitive(Req->argv[0],
  275. Client_ID(c)))
  276. continue;
  277. if (!IRC_WriteStrClient(Client, RPL_SERVLIST_MSG,
  278. Client_ID(Client), Client_Mask(c),
  279. Client_Mask(Client_Introducer(c)), "*",
  280. 0, Client_Hops(c), Client_Info(c)))
  281. return DISCONNECTED;
  282. }
  283. }
  284. return IRC_WriteStrClient(Client, RPL_SERVLISTEND_MSG, Client_ID(Client),
  285. Req->argc > 0 ? Req->argv[0] : "*",
  286. Req->argc > 1 ? Req->argv[1] : "0");
  287. } /* IRC_SERVLIST */
  288. GLOBAL bool
  289. IRC_MOTD( CLIENT *Client, REQUEST *Req )
  290. {
  291. CLIENT *from, *target;
  292. assert( Client != NULL );
  293. assert( Req != NULL );
  294. if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
  295. /* From aus Prefix ermitteln */
  296. if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
  297. else from = Client;
  298. if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
  299. if( Req->argc == 1 )
  300. {
  301. /* forward? */
  302. target = Client_Search( Req->argv[0] );
  303. if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[0] );
  304. if( target != Client_ThisServer( ))
  305. {
  306. /* Ok, anderer Server ist das Ziel: forwarden */
  307. return IRC_WriteStrClientPrefix( target, from, "MOTD %s", Req->argv[0] );
  308. }
  309. }
  310. IRC_SetPenalty( from, 3 );
  311. return IRC_Show_MOTD( from );
  312. } /* IRC_MOTD */
  313. GLOBAL bool
  314. IRC_NAMES( CLIENT *Client, REQUEST *Req )
  315. {
  316. char rpl[COMMAND_LEN], *ptr;
  317. CLIENT *target, *from, *c;
  318. CHANNEL *chan;
  319. assert( Client != NULL );
  320. assert( Req != NULL );
  321. if( Req->argc > 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
  322. /* use prefix to determine "From" */
  323. if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
  324. else from = Client;
  325. if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
  326. if( Req->argc == 2 )
  327. {
  328. /* forward to another server? */
  329. target = Client_Search( Req->argv[1] );
  330. if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[1] );
  331. if( target != Client_ThisServer( )) {
  332. /* target is another server, forward */
  333. return IRC_WriteStrClientPrefix( target, from, "NAMES %s :%s", Req->argv[0], Req->argv[1] );
  334. }
  335. }
  336. if( Req->argc > 0 )
  337. {
  338. /* bestimmte Channels durchgehen */
  339. ptr = strtok( Req->argv[0], "," );
  340. while( ptr )
  341. {
  342. chan = Channel_Search( ptr );
  343. if( chan )
  344. {
  345. /* print name */
  346. if( ! IRC_Send_NAMES( from, chan )) return DISCONNECTED;
  347. }
  348. if( ! IRC_WriteStrClient( from, RPL_ENDOFNAMES_MSG, Client_ID( from ), ptr )) return DISCONNECTED;
  349. /* get next channel name */
  350. ptr = strtok( NULL, "," );
  351. }
  352. return CONNECTED;
  353. }
  354. chan = Channel_First( );
  355. while( chan )
  356. {
  357. if( ! IRC_Send_NAMES( from, chan )) return DISCONNECTED;
  358. chan = Channel_Next( chan );
  359. }
  360. /* Now print all clients which are not in any channel */
  361. c = Client_First( );
  362. snprintf( rpl, sizeof( rpl ), RPL_NAMREPLY_MSG, Client_ID( from ), "*", "*" );
  363. while( c )
  364. {
  365. if(( Client_Type( c ) == CLIENT_USER ) && ( Channel_FirstChannelOf( c ) == NULL ) && ( ! strchr( Client_Modes( c ), 'i' )))
  366. {
  367. /* its a user, concatenate ... */
  368. if( rpl[strlen( rpl ) - 1] != ':' ) strlcat( rpl, " ", sizeof( rpl ));
  369. strlcat( rpl, Client_ID( c ), sizeof( rpl ));
  370. if( strlen( rpl ) > ( LINE_LEN - CLIENT_NICK_LEN - 4 ))
  371. {
  372. /* Line is gwoing too long, send now */
  373. if( ! IRC_WriteStrClient( from, "%s", rpl )) return DISCONNECTED;
  374. snprintf( rpl, sizeof( rpl ), RPL_NAMREPLY_MSG, Client_ID( from ), "*", "*" );
  375. }
  376. }
  377. c = Client_Next( c );
  378. }
  379. if( rpl[strlen( rpl ) - 1] != ':')
  380. {
  381. if( ! IRC_WriteStrClient( from, "%s", rpl )) return DISCONNECTED;
  382. }
  383. IRC_SetPenalty( from, 1 );
  384. return IRC_WriteStrClient( from, RPL_ENDOFNAMES_MSG, Client_ID( from ), "*" );
  385. } /* IRC_NAMES */
  386. static unsigned int
  387. t_diff(time_t *t, const time_t d)
  388. {
  389. time_t diff, remain;
  390. diff = *t / d;
  391. remain = diff * d;
  392. *t -= remain;
  393. return (unsigned int)diff;
  394. }
  395. static unsigned int
  396. uptime_days(time_t *now)
  397. {
  398. return t_diff(now, 60 * 60 * 24);
  399. }
  400. static unsigned int
  401. uptime_hrs(time_t *now)
  402. {
  403. return t_diff(now, 60 * 60);
  404. }
  405. static unsigned int
  406. uptime_mins(time_t *now)
  407. {
  408. return t_diff(now, 60);
  409. }
  410. /**
  411. * Handler for the IRC command "STATS".
  412. * See RFC 2812 section 3.4.4.
  413. */
  414. GLOBAL bool
  415. IRC_STATS( CLIENT *Client, REQUEST *Req )
  416. {
  417. CLIENT *from, *target, *cl;
  418. CONN_ID con;
  419. char query;
  420. COMMAND *cmd;
  421. time_t time_now;
  422. unsigned int days, hrs, mins;
  423. struct list_head *list;
  424. struct list_elem *list_item;
  425. assert(Client != NULL);
  426. assert(Req != NULL);
  427. if (Req->argc > 2)
  428. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  429. Client_ID(Client), Req->command);
  430. /* use prefix to determine "From" */
  431. if (Client_Type(Client) == CLIENT_SERVER)
  432. from = Client_Search(Req->prefix);
  433. else
  434. from = Client;
  435. if (!from)
  436. return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
  437. Client_ID(Client), Req->prefix);
  438. if (Req->argc == 2) {
  439. /* forward to another server? */
  440. target = Client_Search(Req->argv[1]);
  441. if ((!target) || (Client_Type(target) != CLIENT_SERVER))
  442. return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
  443. Client_ID(from), Req->argv[1]);
  444. if (target != Client_ThisServer()) {
  445. /* forward to another server */
  446. return IRC_WriteStrClientPrefix(target, from,
  447. "STATS %s %s", Req->argv[0], Req->argv[1]);
  448. }
  449. }
  450. if (Req->argc > 0)
  451. query = Req->argv[0][0] ? Req->argv[0][0] : '*';
  452. else
  453. query = '*';
  454. switch (query) {
  455. case 'g': /* Network-wide bans ("G-Lines") */
  456. case 'G':
  457. case 'k': /* Server-local bans ("K-Lines") */
  458. case 'K':
  459. if (!Client_HasMode(from, 'o'))
  460. return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG,
  461. Client_ID(from));
  462. if (query == 'g' || query == 'G')
  463. list = Class_GetList(CLASS_GLINE);
  464. else
  465. list = Class_GetList(CLASS_KLINE);
  466. list_item = Lists_GetFirst(list);
  467. while (list_item) {
  468. if (!IRC_WriteStrClient(from, RPL_STATSXLINE_MSG,
  469. Client_ID(from), query,
  470. Lists_GetMask(list_item),
  471. Lists_GetValidity(list_item),
  472. Lists_GetReason(list_item)))
  473. return DISCONNECTED;
  474. list_item = Lists_GetNext(list_item);
  475. }
  476. break;
  477. case 'l': /* Link status (servers and own link) */
  478. case 'L':
  479. time_now = time(NULL);
  480. for (con = Conn_First(); con != NONE; con = Conn_Next(con)) {
  481. cl = Conn_GetClient(con);
  482. if (!cl)
  483. continue;
  484. if ((Client_Type(cl) == CLIENT_SERVER)
  485. || (cl == Client)) {
  486. /* Server link or our own connection */
  487. #ifdef ZLIB
  488. if (Conn_Options(con) & CONN_ZIP) {
  489. if (!IRC_WriteStrClient
  490. (from, RPL_STATSLINKINFOZIP_MSG,
  491. Client_ID(from), Client_Mask(cl),
  492. Conn_SendQ(con), Conn_SendMsg(con),
  493. Zip_SendBytes(con),
  494. Conn_SendBytes(con),
  495. Conn_RecvMsg(con),
  496. Zip_RecvBytes(con),
  497. Conn_RecvBytes(con),
  498. (long)(time_now - Conn_StartTime(con))))
  499. return DISCONNECTED;
  500. continue;
  501. }
  502. #endif
  503. if (!IRC_WriteStrClient
  504. (from, RPL_STATSLINKINFO_MSG,
  505. Client_ID(from), Client_Mask(cl),
  506. Conn_SendQ(con), Conn_SendMsg(con),
  507. Conn_SendBytes(con), Conn_RecvMsg(con),
  508. Conn_RecvBytes(con),
  509. (long)(time_now - Conn_StartTime(con))))
  510. return DISCONNECTED;
  511. }
  512. }
  513. break;
  514. case 'm': /* IRC command status (usage count) */
  515. case 'M':
  516. cmd = Parse_GetCommandStruct();
  517. for (; cmd->name; cmd++) {
  518. if (cmd->lcount == 0 && cmd->rcount == 0)
  519. continue;
  520. if (!IRC_WriteStrClient
  521. (from, RPL_STATSCOMMANDS_MSG, Client_ID(from),
  522. cmd->name, cmd->lcount, cmd->bytes, cmd->rcount))
  523. return DISCONNECTED;
  524. }
  525. break;
  526. case 'u': /* Server uptime */
  527. case 'U':
  528. time_now = time(NULL) - NGIRCd_Start;
  529. days = uptime_days(&time_now);
  530. hrs = uptime_hrs(&time_now);
  531. mins = uptime_mins(&time_now);
  532. if (!IRC_WriteStrClient(from, RPL_STATSUPTIME, Client_ID(from),
  533. days, hrs, mins, (unsigned int)time_now))
  534. return DISCONNECTED;
  535. break;
  536. }
  537. IRC_SetPenalty(from, 2);
  538. return IRC_WriteStrClient(from, RPL_ENDOFSTATS_MSG,
  539. Client_ID(from), query);
  540. } /* IRC_STATS */
  541. /**
  542. * Handler for the IRC command "SUMMON".
  543. * See RFC 2812 section 4.5. ngIRCd doesn't implement this functionality and
  544. * therefore answers with ERR_SUMMONDISABLED.
  545. */
  546. GLOBAL bool
  547. IRC_SUMMON(CLIENT * Client, UNUSED REQUEST * Req)
  548. {
  549. return IRC_WriteStrClient(Client, ERR_SUMMONDISABLED_MSG,
  550. Client_ID(Client));
  551. } /* IRC_SUMMON */
  552. GLOBAL bool
  553. IRC_TIME( CLIENT *Client, REQUEST *Req )
  554. {
  555. CLIENT *from, *target;
  556. char t_str[64];
  557. time_t t;
  558. assert( Client != NULL );
  559. assert( Req != NULL );
  560. if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
  561. if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
  562. else from = Client;
  563. if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
  564. if( Req->argc == 1 )
  565. {
  566. target = Client_Search( Req->argv[0] );
  567. if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->argv[0] );
  568. if( target != Client_ThisServer( ))
  569. {
  570. return IRC_WriteStrClientPrefix( target, from, "TIME %s", Req->argv[0] );
  571. }
  572. }
  573. t = time( NULL );
  574. (void)strftime( t_str, 60, "%A %B %d %Y -- %H:%M %Z", localtime( &t ));
  575. return IRC_WriteStrClient( from, RPL_TIME_MSG, Client_ID( from ), Client_ID( Client_ThisServer( )), t_str );
  576. } /* IRC_TIME */
  577. /**
  578. * Handler for the IRC command "USERHOST".
  579. * See RFC 2812 section 4.8.
  580. */
  581. GLOBAL bool
  582. IRC_USERHOST(CLIENT *Client, REQUEST *Req)
  583. {
  584. char rpl[COMMAND_LEN];
  585. CLIENT *c;
  586. int max, i;
  587. assert(Client != NULL);
  588. assert(Req != NULL);
  589. if ((Req->argc < 1))
  590. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  591. Client_ID(Client), Req->command);
  592. if (Req->argc > 5)
  593. max = 5;
  594. else
  595. max = Req->argc;
  596. strlcpy(rpl, RPL_USERHOST_MSG, sizeof rpl);
  597. for (i = 0; i < max; i++) {
  598. c = Client_Search(Req->argv[i]);
  599. if (c && (Client_Type(c) == CLIENT_USER)) {
  600. /* This Nick is "online" */
  601. strlcat(rpl, Client_ID(c), sizeof(rpl));
  602. if (Client_HasMode(c, 'o'))
  603. strlcat(rpl, "*", sizeof(rpl));
  604. strlcat(rpl, "=", sizeof(rpl));
  605. if (Client_HasMode(c, 'a'))
  606. strlcat(rpl, "-", sizeof(rpl));
  607. else
  608. strlcat(rpl, "+", sizeof(rpl));
  609. strlcat(rpl, Client_User(c), sizeof(rpl));
  610. strlcat(rpl, "@", sizeof(rpl));
  611. strlcat(rpl, Client_HostnameCloaked(c), sizeof(rpl));
  612. strlcat(rpl, " ", sizeof(rpl));
  613. }
  614. }
  615. ngt_TrimLastChr(rpl, ' ');
  616. return IRC_WriteStrClient(Client, rpl, Client_ID(Client));
  617. } /* IRC_USERHOST */
  618. /**
  619. * Handler for the IRC command "USERS".
  620. * See RFC 2812 section 4.6. As suggested there the command is disabled.
  621. */
  622. GLOBAL bool
  623. IRC_USERS(CLIENT * Client, UNUSED REQUEST * Req)
  624. {
  625. return IRC_WriteStrClient(Client, ERR_USERSDISABLED_MSG,
  626. Client_ID(Client));
  627. } /* IRC_USERS */
  628. GLOBAL bool
  629. IRC_VERSION( CLIENT *Client, REQUEST *Req )
  630. {
  631. CLIENT *target, *prefix;
  632. assert( Client != NULL );
  633. assert( Req != NULL );
  634. if(( Req->argc > 1 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
  635. /* Ziel suchen */
  636. if( Req->argc == 1 ) target = Client_Search( Req->argv[0] );
  637. else target = Client_ThisServer( );
  638. /* Prefix ermitteln */
  639. if( Client_Type( Client ) == CLIENT_SERVER ) prefix = Client_Search( Req->prefix );
  640. else prefix = Client;
  641. if( ! prefix ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
  642. /* An anderen Server weiterleiten? */
  643. if( target != Client_ThisServer( ))
  644. {
  645. if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( prefix, ERR_NOSUCHSERVER_MSG, Client_ID( prefix ), Req->argv[0] );
  646. /* forwarden */
  647. IRC_WriteStrClientPrefix( target, prefix, "VERSION %s", Req->argv[0] );
  648. return CONNECTED;
  649. }
  650. /* send version information */
  651. IRC_SetPenalty(Client, 1);
  652. return IRC_WriteStrClient(Client, RPL_VERSION_MSG, Client_ID(prefix),
  653. PACKAGE_NAME, PACKAGE_VERSION,
  654. NGIRCd_DebugLevel, Conf_ServerName,
  655. NGIRCd_VersionAddition);
  656. } /* IRC_VERSION */
  657. static bool
  658. write_whoreply(CLIENT *Client, CLIENT *c, const char *channelname, const char *flags)
  659. {
  660. return IRC_WriteStrClient(Client, RPL_WHOREPLY_MSG, Client_ID(Client),
  661. channelname, Client_User(c),
  662. Client_HostnameCloaked(c),
  663. Client_ID(Client_Introducer(c)), Client_ID(c),
  664. flags, Client_Hops(c), Client_Info(c));
  665. }
  666. static const char *
  667. who_flags_status(const char *client_modes)
  668. {
  669. if (strchr(client_modes, 'a'))
  670. return "G"; /* away */
  671. return "H";
  672. }
  673. static const char *
  674. who_flags_qualifier(CLIENT *Client, const char *chan_user_modes)
  675. {
  676. assert(Client != NULL);
  677. if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX) {
  678. if (strchr(chan_user_modes, 'o') &&
  679. strchr(chan_user_modes, 'v'))
  680. return "@+";
  681. }
  682. if (strchr(chan_user_modes, 'o'))
  683. return "@";
  684. else if (strchr(chan_user_modes, 'v'))
  685. return "+";
  686. return "";
  687. }
  688. /**
  689. * Send WHO reply for a "channel target" ("WHO #channel").
  690. *
  691. * @param Client Client requesting the information.
  692. * @param Chan Channel being requested.
  693. * @param OnlyOps Only display IRC operators.
  694. * @return CONNECTED or DISCONNECTED.
  695. */
  696. static bool
  697. IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
  698. {
  699. bool is_visible, is_member, is_ircop;
  700. CL2CHAN *cl2chan;
  701. const char *client_modes;
  702. const char *chan_user_modes;
  703. char flags[8];
  704. CLIENT *c;
  705. int count = 0;
  706. assert( Client != NULL );
  707. assert( Chan != NULL );
  708. is_member = Channel_IsMemberOf(Chan, Client);
  709. /* Secret channel? */
  710. if (!is_member && strchr(Channel_Modes(Chan), 's'))
  711. return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG,
  712. Client_ID(Client), Channel_Name(Chan));
  713. cl2chan = Channel_FirstMember(Chan);
  714. for (; cl2chan ; cl2chan = Channel_NextMember(Chan, cl2chan)) {
  715. c = Channel_GetClient(cl2chan);
  716. client_modes = Client_Modes(c);
  717. is_ircop = strchr(client_modes, 'o') != NULL;
  718. if (OnlyOps && !is_ircop)
  719. continue;
  720. is_visible = strchr(client_modes, 'i') == NULL;
  721. if (is_member || is_visible) {
  722. if (IRC_CheckListTooBig(Client, count, MAX_RPL_WHO, "WHO"))
  723. break;
  724. strcpy(flags, who_flags_status(client_modes));
  725. if (is_ircop)
  726. strlcat(flags, "*", sizeof(flags));
  727. chan_user_modes = Channel_UserModes(Chan, c);
  728. strlcat(flags, who_flags_qualifier(c, chan_user_modes),
  729. sizeof(flags));
  730. if (!write_whoreply(Client, c, Channel_Name(Chan),
  731. flags))
  732. return DISCONNECTED;
  733. count++;
  734. }
  735. }
  736. return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client),
  737. Channel_Name(Chan));
  738. }
  739. /**
  740. * Send WHO reply for a "mask target" ("WHO m*sk").
  741. *
  742. * @param Client Client requesting the information.
  743. * @param Mask Mask being requested or NULL for "all" clients.
  744. * @param OnlyOps Only display IRC operators.
  745. * @return CONNECTED or DISCONNECTED.
  746. */
  747. static bool
  748. IRC_WHO_Mask(CLIENT *Client, char *Mask, bool OnlyOps)
  749. {
  750. CLIENT *c;
  751. CL2CHAN *cl2chan;
  752. CHANNEL *chan;
  753. bool client_match, is_visible;
  754. char flags[4];
  755. int count = 0;
  756. assert (Client != NULL);
  757. if (Mask)
  758. ngt_LowerStr(Mask);
  759. for (c = Client_First(); c != NULL; c = Client_Next(c)) {
  760. if (Client_Type(c) != CLIENT_USER)
  761. continue;
  762. if (OnlyOps && !Client_HasMode(c, 'o'))
  763. continue;
  764. if (Mask) {
  765. /* Match pattern against user host/server/name/nick */
  766. client_match = MatchCaseInsensitive(Mask,
  767. Client_Hostname(c));
  768. if (!client_match)
  769. client_match = MatchCaseInsensitive(Mask,
  770. Client_ID(Client_Introducer(c)));
  771. if (!client_match)
  772. client_match = MatchCaseInsensitive(Mask,
  773. Client_Info(c));
  774. if (!client_match)
  775. client_match = MatchCaseInsensitive(Mask,
  776. Client_ID(c));
  777. if (!client_match)
  778. continue; /* no match: skip this client */
  779. }
  780. is_visible = !Client_HasMode(c, 'i');
  781. /* Target client is invisible, but mask matches exactly? */
  782. if (!is_visible && Mask && strcasecmp(Client_ID(c), Mask) == 0)
  783. is_visible = true;
  784. /* Target still invisible, but are both on the same channel? */
  785. if (!is_visible) {
  786. cl2chan = Channel_FirstChannelOf(Client);
  787. while (cl2chan && !is_visible) {
  788. chan = Channel_GetChannel(cl2chan);
  789. if (Channel_IsMemberOf(chan, c))
  790. is_visible = true;
  791. cl2chan = Channel_NextChannelOf(Client, cl2chan);
  792. }
  793. }
  794. if (!is_visible) /* target user is not visible */
  795. continue;
  796. if (IRC_CheckListTooBig(Client, count, MAX_RPL_WHO, "WHO"))
  797. break;
  798. strcpy(flags, who_flags_status(Client_Modes(c)));
  799. if (strchr(Client_Modes(c), 'o'))
  800. strlcat(flags, "*", sizeof(flags));
  801. if (!write_whoreply(Client, c, "*", flags))
  802. return DISCONNECTED;
  803. count++;
  804. }
  805. return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client),
  806. Mask ? Mask : "*");
  807. }
  808. /**
  809. * Handler for the IRC "WHO" command.
  810. *
  811. * See RFC 2812, 3.6.1 "Who query".
  812. *
  813. * @param Client The client from which this command has been received.
  814. * @param Req Request structure with prefix and all parameters.
  815. * @return CONNECTED or DISCONNECTED.
  816. */
  817. GLOBAL bool
  818. IRC_WHO(CLIENT *Client, REQUEST *Req)
  819. {
  820. bool only_ops;
  821. CHANNEL *chan;
  822. assert (Client != NULL);
  823. assert (Req != NULL);
  824. if (Req->argc > 2)
  825. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  826. Client_ID(Client), Req->command);
  827. only_ops = false;
  828. if (Req->argc == 2) {
  829. if (strcmp(Req->argv[1], "o") == 0)
  830. only_ops = true;
  831. #ifdef STRICT_RFC
  832. else
  833. return IRC_WriteStrClient(Client,
  834. ERR_NEEDMOREPARAMS_MSG,
  835. Client_ID(Client),
  836. Req->command);
  837. #endif
  838. }
  839. IRC_SetPenalty(Client, 1);
  840. if (Req->argc >= 1) {
  841. /* Channel or mask given */
  842. chan = Channel_Search(Req->argv[0]);
  843. if (chan) {
  844. /* Members of a channel have been requested */
  845. IRC_SetPenalty(Client, 1);
  846. return IRC_WHO_Channel(Client, chan, only_ops);
  847. }
  848. if (strcmp(Req->argv[0], "0") != 0) {
  849. /* A mask has been given. But please note this RFC
  850. * stupidity: "0" is same as no arguments ... */
  851. IRC_SetPenalty(Client, 3);
  852. return IRC_WHO_Mask(Client, Req->argv[0], only_ops);
  853. }
  854. }
  855. /* No channel or (valid) mask given */
  856. IRC_SetPenalty(Client, 2);
  857. return IRC_WHO_Mask(Client, NULL, only_ops);
  858. } /* IRC_WHO */
  859. /**
  860. * Generate WHOIS reply of one actual client.
  861. *
  862. * @param Client The client from which this command has been received.
  863. * @param from The client requesting the information ("originator").
  864. * @param c The client of which information should be returned.
  865. * @return CONNECTED or DISCONNECTED.
  866. */
  867. static bool
  868. IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
  869. {
  870. char str[LINE_LEN + 1];
  871. CL2CHAN *cl2chan;
  872. CHANNEL *chan;
  873. assert(Client != NULL);
  874. assert(from != NULL);
  875. assert(c != NULL);
  876. /* Nick, user, hostname and client info */
  877. if (!IRC_WriteStrClient(from, RPL_WHOISUSER_MSG, Client_ID(from),
  878. Client_ID(c), Client_User(c),
  879. Client_HostnameCloaked(c), Client_Info(c)))
  880. return DISCONNECTED;
  881. /* Server */
  882. if (!IRC_WriteStrClient(from, RPL_WHOISSERVER_MSG, Client_ID(from),
  883. Client_ID(c), Client_ID(Client_Introducer(c)),
  884. Client_Info(Client_Introducer(c))))
  885. return DISCONNECTED;
  886. /* Channels */
  887. snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG,
  888. Client_ID(from), Client_ID(c));
  889. cl2chan = Channel_FirstChannelOf(c);
  890. while (cl2chan) {
  891. chan = Channel_GetChannel(cl2chan);
  892. assert(chan != NULL);
  893. /* next */
  894. cl2chan = Channel_NextChannelOf(c, cl2chan);
  895. /* Secret channel? */
  896. if (strchr(Channel_Modes(chan), 's')
  897. && !Channel_IsMemberOf(chan, Client))
  898. continue;
  899. /* Local channel and request is not from a user? */
  900. if (Client_Type(Client) == CLIENT_SERVER
  901. && Channel_IsLocal(chan))
  902. continue;
  903. /* Concatenate channel names */
  904. if (str[strlen(str) - 1] != ':')
  905. strlcat(str, " ", sizeof(str));
  906. strlcat(str, who_flags_qualifier(c, Channel_UserModes(chan, c)),
  907. sizeof(str));
  908. strlcat(str, Channel_Name(chan), sizeof(str));
  909. if (strlen(str) > (LINE_LEN - CHANNEL_NAME_LEN - 4)) {
  910. /* Line becomes too long: send it! */
  911. if (!IRC_WriteStrClient(Client, "%s", str))
  912. return DISCONNECTED;
  913. snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG,
  914. Client_ID(from), Client_ID(c));
  915. }
  916. }
  917. if(str[strlen(str) - 1] != ':') {
  918. /* There is data left to send: */
  919. if (!IRC_WriteStrClient(Client, "%s", str))
  920. return DISCONNECTED;
  921. }
  922. /* IRC-Operator? */
  923. if (Client_HasMode(c, 'o') &&
  924. !IRC_WriteStrClient(from, RPL_WHOISOPERATOR_MSG,
  925. Client_ID(from), Client_ID(c)))
  926. return DISCONNECTED;
  927. /* Connected using SSL? */
  928. if (Conn_UsesSSL(Client_Conn(c)) &&
  929. !IRC_WriteStrClient(from, RPL_WHOISSSL_MSG, Client_ID(from),
  930. Client_ID(c)))
  931. return DISCONNECTED;
  932. /* Registered nick name? */
  933. if (Client_HasMode(c, 'R') &&
  934. !IRC_WriteStrClient(from, RPL_WHOISREGNICK_MSG,
  935. Client_ID(from), Client_ID(c)))
  936. return DISCONNECTED;
  937. if (Client_Conn(c) > NONE && (Client_OperByMe(from) || from == c) &&
  938. !IRC_WriteStrClient(from, RPL_WHOISHOST_MSG, Client_ID(from),
  939. Client_ID(c), Client_Hostname(c),
  940. Conn_GetIPAInfo(Client_Conn(c))))
  941. return DISCONNECTED;
  942. /* Idle and signon time (local clients only!) */
  943. if (!Conf_MorePrivacy && Client_Conn(c) > NONE &&
  944. !IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG,
  945. Client_ID(from), Client_ID(c),
  946. (unsigned long)Conn_GetIdle(Client_Conn(c)),
  947. (unsigned long)Conn_GetSignon(Client_Conn(c))))
  948. return DISCONNECTED;
  949. /* Away? */
  950. if (Client_HasMode(c, 'a') &&
  951. !IRC_WriteStrClient(from, RPL_AWAY_MSG,
  952. Client_ID(from), Client_ID(c), Client_Away(c)))
  953. return DISCONNECTED;
  954. return CONNECTED;
  955. } /* IRC_WHOIS_SendReply */
  956. /**
  957. * Handler for the IRC "WHOIS" command.
  958. *
  959. * See RFC 2812, 3.6.2 "Whois query".
  960. *
  961. * @param Client The client from which this command has been received.
  962. * @param Req Request structure with prefix and all parameters.
  963. * @return CONNECTED or DISCONNECTED.
  964. */
  965. GLOBAL bool
  966. IRC_WHOIS( CLIENT *Client, REQUEST *Req )
  967. {
  968. CLIENT *from, *target, *c;
  969. unsigned int match_count = 0, found = 0;
  970. bool has_wildcards, is_remote;
  971. bool got_wildcard = false;
  972. char mask[COMMAND_LEN], *query;
  973. assert( Client != NULL );
  974. assert( Req != NULL );
  975. /* Bad number of parameters? */
  976. if (Req->argc < 1 || Req->argc > 2)
  977. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  978. Client_ID(Client), Req->command);
  979. /* Search sender of the WHOIS */
  980. if (Client_Type(Client) == CLIENT_SERVER) {
  981. from = Client_Search(Req->prefix);
  982. } else {
  983. IRC_SetPenalty(Client, 1);
  984. from = Client;
  985. }
  986. if (!from)
  987. return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
  988. Client_ID(Client), Req->prefix);
  989. /* Get target server for this command */
  990. if (Req->argc > 1) {
  991. /* Search the target server, which can be specified as a
  992. * nick name on that server as well: */
  993. target = Client_Search(Req->argv[0]);
  994. if (!target)
  995. return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
  996. Client_ID(from), Req->argv[0]);
  997. } else
  998. target = Client_ThisServer();
  999. assert(target != NULL);
  1000. /* Forward to other server? */
  1001. if (Client_NextHop(target) != Client_ThisServer() &&
  1002. Client_Type(Client_NextHop(target)) == CLIENT_SERVER)
  1003. return IRC_WriteStrClientPrefix(target, from,
  1004. "WHOIS %s :%s",
  1005. Req->argv[0], Req->argv[1]);
  1006. is_remote = Client_Conn(from) < 0;
  1007. strlcpy(mask, Req->argv[Req->argc - 1], sizeof(mask));
  1008. for (query = strtok(ngt_LowerStr(mask), ",");
  1009. query && found < 3;
  1010. query = strtok(NULL, ","), found++)
  1011. {
  1012. has_wildcards = query[strcspn(query, "*?")] != 0;
  1013. /*
  1014. * follows ircd 2.10 implementation:
  1015. * - handle up to 3 targets
  1016. * - no wildcards for remote clients
  1017. * - only one wildcard target per local client
  1018. *
  1019. * Also, at most MAX_RPL_WHOIS matches are returned.
  1020. */
  1021. if (!has_wildcards || is_remote) {
  1022. c = Client_Search(query);
  1023. if (c && Client_Type(c) == CLIENT_USER) {
  1024. if (!IRC_WHOIS_SendReply(Client, from, c))
  1025. return DISCONNECTED;
  1026. } else {
  1027. if (!IRC_WriteStrClient(Client,
  1028. ERR_NOSUCHNICK_MSG,
  1029. Client_ID(Client),
  1030. query))
  1031. return DISCONNECTED;
  1032. }
  1033. continue;
  1034. }
  1035. if (got_wildcard) {
  1036. /* we already handled one wildcard query */
  1037. if (!IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
  1038. Client_ID(Client), query))
  1039. return DISCONNECTED;
  1040. continue;
  1041. }
  1042. got_wildcard = true;
  1043. IRC_SetPenalty(Client, 3);
  1044. for (c = Client_First(); c; c = Client_Next(c)) {
  1045. if (IRC_CheckListTooBig(Client, match_count,
  1046. MAX_RPL_WHOIS, "WHOIS"))
  1047. break;
  1048. if (Client_Type(c) != CLIENT_USER)
  1049. continue;
  1050. if (!MatchCaseInsensitive(query, Client_ID(c)))
  1051. continue;
  1052. if (!IRC_WHOIS_SendReply(Client, from, c))
  1053. return DISCONNECTED;
  1054. match_count++;
  1055. }
  1056. if (match_count == 0)
  1057. IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
  1058. Client_ID(Client),
  1059. Req->argv[Req->argc - 1]);
  1060. }
  1061. return IRC_WriteStrClient(from, RPL_ENDOFWHOIS_MSG,
  1062. Client_ID(from), Req->argv[Req->argc - 1]);
  1063. } /* IRC_WHOIS */
  1064. static bool
  1065. WHOWAS_EntryWrite(CLIENT *prefix, WHOWAS *entry)
  1066. {
  1067. char t_str[60];
  1068. (void)strftime(t_str, sizeof(t_str), "%a %b %d %H:%M:%S %Y",
  1069. localtime(&entry->time));
  1070. if (!IRC_WriteStrClient(prefix, RPL_WHOWASUSER_MSG, Client_ID(prefix),
  1071. entry->id, entry->user, entry->host, entry->info))
  1072. return DISCONNECTED;
  1073. return IRC_WriteStrClient(prefix, RPL_WHOISSERVER_MSG, Client_ID(prefix),
  1074. entry->id, entry->server, t_str);
  1075. }
  1076. /**
  1077. * IRC "WHOWAS" function.
  1078. * This function implements the IRC command "WHOWHAS". It handles local
  1079. * requests and request that should be forwarded to other servers.
  1080. */
  1081. GLOBAL bool
  1082. IRC_WHOWAS( CLIENT *Client, REQUEST *Req )
  1083. {
  1084. CLIENT *target, *prefix;
  1085. WHOWAS *whowas;
  1086. char tok_buf[COMMAND_LEN];
  1087. int max, last, count, i, nc;
  1088. const char *nick;
  1089. assert( Client != NULL );
  1090. assert( Req != NULL );
  1091. /* Do not reveal any info on disconnected users? */
  1092. if (Conf_MorePrivacy)
  1093. return CONNECTED;
  1094. /* Wrong number of parameters? */
  1095. if (Req->argc > 3)
  1096. return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
  1097. Client_ID(Client), Req->command);
  1098. if (Req->argc < 1)
  1099. return IRC_WriteStrClient(Client, ERR_NONICKNAMEGIVEN_MSG, Client_ID(Client));
  1100. /* Search target */
  1101. if (Req->argc == 3)
  1102. target = Client_Search(Req->argv[2]);
  1103. else
  1104. target = Client_ThisServer();
  1105. /* Get prefix */
  1106. if (Client_Type(Client) == CLIENT_SERVER)
  1107. prefix = Client_Search(Req->prefix);
  1108. else
  1109. prefix = Client;
  1110. if (!prefix)
  1111. return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
  1112. Client_ID(Client), Req->prefix);
  1113. /* Forward to other server? */
  1114. if (target != Client_ThisServer()) {
  1115. if (!target || (Client_Type(target) != CLIENT_SERVER))
  1116. return IRC_WriteStrClient(prefix, ERR_NOSUCHSERVER_MSG,
  1117. Client_ID(prefix), Req->argv[2]);
  1118. /* Forward */
  1119. IRC_WriteStrClientPrefix( target, prefix, "WHOWAS %s %s %s",
  1120. Req->argv[0], Req->argv[1],
  1121. Req->argv[2] );
  1122. return CONNECTED;
  1123. }
  1124. whowas = Client_GetWhowas( );
  1125. last = Client_GetLastWhowasIndex( );
  1126. if (last < 0)
  1127. last = 0;
  1128. max = DEF_RPL_WHOWAS;
  1129. if (Req->argc > 1) {
  1130. max = atoi(Req->argv[1]);
  1131. if (max < 1)
  1132. max = MAX_RPL_WHOWAS;
  1133. }
  1134. /*
  1135. * Break up the nick argument into a list of nicks, if applicable
  1136. * Can't modify Req->argv[0] because we need it for RPL_ENDOFWHOWAS_MSG.
  1137. */
  1138. strlcpy(tok_buf, Req->argv[0], sizeof(tok_buf));
  1139. nick = strtok(tok_buf, ",");
  1140. for (i=last, count=0; nick != NULL ; nick = strtok(NULL, ",")) {
  1141. nc = 0;
  1142. do {
  1143. /* Used entry? */
  1144. if (whowas[i].time > 0 && strcasecmp(nick, whowas[i].id) == 0) {
  1145. if (!WHOWAS_EntryWrite(prefix, &whowas[i]))
  1146. return DISCONNECTED;
  1147. nc++;
  1148. count++;
  1149. }
  1150. /* previous entry */
  1151. i--;
  1152. /* "underflow", wrap around */
  1153. if (i < 0)
  1154. i = MAX_WHOWAS - 1;
  1155. if (nc && count >= max)
  1156. break;
  1157. } while (i != last);
  1158. if (nc == 0 && !IRC_WriteStrClient(prefix, ERR_WASNOSUCHNICK_MSG,
  1159. Client_ID(prefix), nick))
  1160. return DISCONNECTED;
  1161. }
  1162. return IRC_WriteStrClient(prefix, RPL_ENDOFWHOWAS_MSG, Client_ID(prefix), Req->argv[0]);
  1163. } /* IRC_WHOWAS */
  1164. /**
  1165. * Send LUSERS reply to a client.
  1166. *
  1167. * @param Client The receipient of the information.
  1168. * @return CONNECTED or DISCONNECTED.
  1169. */
  1170. GLOBAL bool
  1171. IRC_Send_LUSERS(CLIENT *Client)
  1172. {
  1173. unsigned long cnt;
  1174. #ifndef STRICT_RFC
  1175. unsigned long max;
  1176. #endif
  1177. assert(Client != NULL);
  1178. /* Users, services and serevers in the network */
  1179. if (!IRC_WriteStrClient(Client, RPL_LUSERCLIENT_MSG, Client_ID(Client),
  1180. Client_UserCount(), Client_ServiceCount(),
  1181. Client_ServerCount()))
  1182. return DISCONNECTED;
  1183. /* Number of IRC operators */
  1184. cnt = Client_OperCount( );
  1185. if (cnt > 0) {
  1186. if (!IRC_WriteStrClient(Client, RPL_LUSEROP_MSG,
  1187. Client_ID(Client), cnt))
  1188. return DISCONNECTED;
  1189. }
  1190. /* Unknown connections */
  1191. cnt = Client_UnknownCount( );
  1192. if (cnt > 0) {
  1193. if (!IRC_WriteStrClient(Client, RPL_LUSERUNKNOWN_MSG,
  1194. Client_ID(Client), cnt))
  1195. return DISCONNECTED;
  1196. }
  1197. /* Number of created channels */
  1198. if (!IRC_WriteStrClient(Client, RPL_LUSERCHANNELS_MSG,
  1199. Client_ID(Client),
  1200. Channel_CountVisible(Client)))
  1201. return DISCONNECTED;
  1202. /* Number of local users, services and servers */
  1203. if (!IRC_WriteStrClient(Client, RPL_LUSERME_MSG, Client_ID(Client),
  1204. Client_MyUserCount(), Client_MyServiceCount(),
  1205. Client_MyServerCount()))
  1206. return DISCONNECTED;
  1207. #ifndef STRICT_RFC
  1208. /* Maximum number of local users */
  1209. cnt = Client_MyUserCount();
  1210. max = Client_MyMaxUserCount();
  1211. if (! IRC_WriteStrClient(Client, RPL_LOCALUSERS_MSG, Client_ID(Client),
  1212. cnt, max, cnt, max))
  1213. return DISCONNECTED;
  1214. /* Maximum number of users in the network */
  1215. cnt = Client_UserCount();
  1216. max = Client_MaxUserCount();
  1217. if(! IRC_WriteStrClient(Client, RPL_NETUSERS_MSG, Client_ID(Client),
  1218. cnt, max, cnt, max))
  1219. return DISCONNECTED;
  1220. /* Connection counters */
  1221. if (! IRC_WriteStrClient(Client, RPL_STATSCONN_MSG, Client_ID(Client),
  1222. Conn_CountMax(), Conn_CountAccepted()))
  1223. return DISCONNECTED;
  1224. #endif
  1225. return CONNECTED;
  1226. } /* IRC_Send_LUSERS */
  1227. static bool
  1228. Show_MOTD_Start(CLIENT *Client)
  1229. {
  1230. return IRC_WriteStrClient(Client, RPL_MOTDSTART_MSG,
  1231. Client_ID( Client ), Client_ID( Client_ThisServer( )));
  1232. }
  1233. static bool
  1234. Show_MOTD_Sendline(CLIENT *Client, const char *msg)
  1235. {
  1236. return IRC_WriteStrClient(Client, RPL_MOTD_MSG, Client_ID( Client ), msg);
  1237. }
  1238. static bool
  1239. Show_MOTD_End(CLIENT *Client)
  1240. {
  1241. return IRC_WriteStrClient( Client, RPL_ENDOFMOTD_MSG, Client_ID( Client ));
  1242. }
  1243. #ifdef SSL_SUPPORT
  1244. static bool Show_MOTD_SSLInfo(CLIENT *Client)
  1245. {
  1246. bool ret = true;
  1247. char buf[COMMAND_LEN] = "Connected using Cipher ";
  1248. if (!Conn_GetCipherInfo(Client_Conn(Client), buf + 23, sizeof buf - 23))
  1249. return true;
  1250. if (!Show_MOTD_Sendline(Client, buf))
  1251. ret = false;
  1252. return ret;
  1253. }
  1254. #else
  1255. static inline bool
  1256. Show_MOTD_SSLInfo(UNUSED CLIENT *c)
  1257. { return true; }
  1258. #endif
  1259. GLOBAL bool
  1260. IRC_Show_MOTD( CLIENT *Client )
  1261. {
  1262. const char *line;
  1263. size_t len_tot, len_str;
  1264. assert( Client != NULL );
  1265. len_tot = array_bytes(&Conf_Motd);
  1266. if (len_tot == 0 && !Conn_UsesSSL(Client_Conn(Client)))
  1267. return IRC_WriteStrClient(Client, ERR_NOMOTD_MSG, Client_ID(Client));
  1268. if (!Show_MOTD_Start(Client))
  1269. return DISCONNECTED;
  1270. line = array_start(&Conf_Motd);
  1271. while (len_tot > 0) {
  1272. len_str = strlen(line) + 1;
  1273. assert(len_tot >= len_str);
  1274. len_tot -= len_str;
  1275. if (!Show_MOTD_Sendline(Client, line))
  1276. return DISCONNECTED;
  1277. line += len_str;
  1278. }
  1279. if (!Show_MOTD_SSLInfo(Client))
  1280. return DISCONNECTED;
  1281. return Show_MOTD_End(Client);
  1282. } /* IRC_Show_MOTD */
  1283. /**
  1284. * Send NAMES reply for a specific client and channel.
  1285. *
  1286. * @param Client The client requesting the NAMES information.
  1287. * @param Chan The channel
  1288. * @return CONNECTED or DISCONNECTED.
  1289. */
  1290. GLOBAL bool
  1291. IRC_Send_NAMES(CLIENT * Client, CHANNEL * Chan)
  1292. {
  1293. bool is_visible, is_member;
  1294. char str[LINE_LEN + 1];
  1295. CL2CHAN *cl2chan;
  1296. CLIENT *cl;
  1297. assert(Client != NULL);
  1298. assert(Chan != NULL);
  1299. if (Channel_IsMemberOf(Chan, Client))
  1300. is_member = true;
  1301. else
  1302. is_member = false;
  1303. /* Do not print info on channel memberships to anyone that is not member? */
  1304. if (Conf_MorePrivacy && !is_member)
  1305. return CONNECTED;
  1306. /* Secret channel? */
  1307. if (!is_member && strchr(Channel_Modes(Chan), 's'))
  1308. return CONNECTED;
  1309. snprintf(str, sizeof(str), RPL_NAMREPLY_MSG, Client_ID(Client), "=",
  1310. Channel_Name(Chan));
  1311. cl2chan = Channel_FirstMember(Chan);
  1312. while (cl2chan) {
  1313. cl = Channel_GetClient(cl2chan);
  1314. if (strchr(Client_Modes(cl), 'i'))
  1315. is_visible = false;
  1316. else
  1317. is_visible = true;
  1318. if (is_member || is_visible) {
  1319. if (str[strlen(str) - 1] != ':')
  1320. strlcat(str, " ", sizeof(str));
  1321. if (Client_Cap(cl) & CLIENT_CAP_MULTI_PREFIX) {
  1322. if (strchr(Channel_UserModes(Chan, cl), 'o') &&
  1323. strchr(Channel_UserModes(Chan, cl), 'v'))
  1324. strlcat(str, "@+", sizeof(str));
  1325. } else {
  1326. if (strchr(Channel_UserModes(Chan, cl), 'o'))
  1327. strlcat(str, "@", sizeof(str));
  1328. else if (strchr(Channel_UserModes(Chan, cl), 'v'))
  1329. strlcat(str, "+", sizeof(str));
  1330. }
  1331. strlcat(str, Client_ID(cl), sizeof(str));
  1332. if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 4)) {
  1333. if (!IRC_WriteStrClient(Client, "%s", str))
  1334. return DISCONNECTED;
  1335. snprintf(str, sizeof(str), RPL_NAMREPLY_MSG,
  1336. Client_ID(Client), "=",
  1337. Channel_Name(Chan));
  1338. }
  1339. }
  1340. cl2chan = Channel_NextMember(Chan, cl2chan);
  1341. }
  1342. if (str[strlen(str) - 1] != ':') {
  1343. if (!IRC_WriteStrClient(Client, "%s", str))
  1344. return DISCONNECTED;
  1345. }
  1346. return CONNECTED;
  1347. } /* IRC_Send_NAMES */
  1348. /**
  1349. * Send the ISUPPORT numeric (005).
  1350. * This numeric indicates the features that are supported by this server.
  1351. * See <http://www.irc.org/tech_docs/005.html> for details.
  1352. */
  1353. GLOBAL bool
  1354. IRC_Send_ISUPPORT(CLIENT * Client)
  1355. {
  1356. if (!IRC_WriteStrClient(Client, RPL_ISUPPORT1_MSG, Client_ID(Client),
  1357. Conf_MaxJoins))
  1358. return DISCONNECTED;
  1359. return IRC_WriteStrClient(Client, RPL_ISUPPORT2_MSG, Client_ID(Client),
  1360. CHANNEL_NAME_LEN - 1, Conf_MaxNickLength - 1,
  1361. COMMAND_LEN - 23, CLIENT_AWAY_LEN - 1,
  1362. COMMAND_LEN - 113, MAX_HNDL_MODES_ARG,
  1363. MAX_HNDL_CHANNEL_LISTS);
  1364. } /* IRC_Send_ISUPPORT */
  1365. /* -eof- */