Experimental IRC client, daemon and bot
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

11149 lines
296KB

  1. /*
  2. * degesch.c: the experimental IRC client
  3. *
  4. * Copyright (c) 2015, Přemysl Janouch <p.janouch@gmail.com>
  5. *
  6. * Permission to use, copy, modify, and/or distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  13. * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  15. * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  16. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. *
  18. */
  19. // A table of all attributes we use for output
  20. // FIXME: awful naming, collides with ATTRIBUTE_*
  21. #define ATTR_TABLE(XX) \
  22. XX( PROMPT, "prompt", "Terminal attrs for the prompt" ) \
  23. XX( RESET, "reset", "String to reset terminal attributes" ) \
  24. XX( READ_MARKER, "read_marker", "Terminal attrs for the read marker" ) \
  25. XX( WARNING, "warning", "Terminal attrs for warnings" ) \
  26. XX( ERROR, "error", "Terminal attrs for errors" ) \
  27. XX( EXTERNAL, "external", "Terminal attrs for external lines" ) \
  28. XX( TIMESTAMP, "timestamp", "Terminal attrs for timestamps" ) \
  29. XX( HIGHLIGHT, "highlight", "Terminal attrs for highlights" ) \
  30. XX( ACTION, "action", "Terminal attrs for user actions" ) \
  31. XX( USERHOST, "userhost", "Terminal attrs for user@host" ) \
  32. XX( JOIN, "join", "Terminal attrs for joins" ) \
  33. XX( PART, "part", "Terminal attrs for parts" )
  34. enum
  35. {
  36. #define XX(x, y, z) ATTR_ ## x,
  37. ATTR_TABLE (XX)
  38. #undef XX
  39. ATTR_COUNT
  40. };
  41. // User data for logger functions to enable formatted logging
  42. #define print_fatal_data ((void *) ATTR_ERROR)
  43. #define print_error_data ((void *) ATTR_ERROR)
  44. #define print_warning_data ((void *) ATTR_WARNING)
  45. #include "config.h"
  46. #define PROGRAM_NAME "degesch"
  47. #include "common.c"
  48. #include "kike-replies.c"
  49. #include <langinfo.h>
  50. #include <locale.h>
  51. #include <pwd.h>
  52. #include <sys/utsname.h>
  53. #include <wchar.h>
  54. #include <termios.h>
  55. #ifdef TIOCGWINSZ
  56. #include <sys/ioctl.h>
  57. #endif // ! TIOCGWINSZ
  58. #include <curses.h>
  59. #include <term.h>
  60. // Literally cancer
  61. #undef lines
  62. #undef columns
  63. #ifdef HAVE_READLINE
  64. #include <readline/readline.h>
  65. #include <readline/history.h>
  66. #endif // HAVE_READLINE
  67. #ifdef HAVE_EDITLINE
  68. #include <histedit.h>
  69. #endif // HAVE_EDITLINE
  70. #ifdef HAVE_LUA
  71. #include <lua.h>
  72. #include <lualib.h>
  73. #include <lauxlib.h>
  74. #endif // HAVE_LUA
  75. /// Some arbitrary limit for the history file
  76. #define HISTORY_LIMIT 10000
  77. /// How many lines of backlog to store in memory
  78. #define BACKLOG_LIMIT 1000
  79. /// Characters that separate words
  80. #define WORD_BREAKING_CHARS " \f\n\r\t\v"
  81. // --- User interface ----------------------------------------------------------
  82. // I'm not sure which one of these backends is worse: whether it's GNU Readline
  83. // or BSD Editline. They both have their own annoying problems.
  84. struct input_buffer
  85. {
  86. #ifdef HAVE_READLINE
  87. HISTORY_STATE *history; ///< Saved history state
  88. char *saved_line; ///< Saved line content
  89. int saved_mark; ///< Saved mark
  90. #elif defined HAVE_EDITLINE
  91. HistoryW *history; ///< The history object
  92. wchar_t *saved_line; ///< Saved line content
  93. int saved_len; ///< Length of the saved line
  94. #endif // HAVE_EDITLINE
  95. int saved_point; ///< Saved cursor position
  96. };
  97. static struct input_buffer *
  98. input_buffer_new (void)
  99. {
  100. struct input_buffer *self = xcalloc (1, sizeof *self);
  101. #ifdef HAVE_EDITLINE
  102. self->history = history_winit ();
  103. HistEventW ev;
  104. history_w (self->history, &ev, H_SETSIZE, HISTORY_LIMIT);
  105. #endif // HAVE_EDITLINE
  106. return self;
  107. }
  108. static void
  109. input_buffer_destroy (struct input_buffer *self)
  110. {
  111. #ifdef HAVE_READLINE
  112. // Can't really free "history" contents from here
  113. free (self->history);
  114. #elif defined HAVE_EDITLINE
  115. history_wend (self->history);
  116. #endif // HAVE_EDITLINE
  117. free (self->saved_line);
  118. free (self);
  119. }
  120. struct input
  121. {
  122. bool active; ///< Are we a thing?
  123. #if defined HAVE_READLINE
  124. char *saved_line; ///< Saved line content
  125. int saved_point; ///< Saved cursor position
  126. int saved_mark; ///< Saved mark
  127. #elif defined HAVE_EDITLINE
  128. EditLine *editline; ///< The EditLine object
  129. char *(*saved_prompt) (EditLine *); ///< Saved prompt function
  130. char saved_char; ///< Saved char for the prompt
  131. #endif // HAVE_EDITLINE
  132. char *prompt; ///< The prompt we use
  133. int prompt_shown; ///< Whether the prompt is shown now
  134. struct input_buffer *current; ///< Current input buffer
  135. };
  136. static void
  137. input_init (struct input *self)
  138. {
  139. memset (self, 0, sizeof *self);
  140. }
  141. static void
  142. input_free (struct input *self)
  143. {
  144. #ifdef HAVE_READLINE
  145. free (self->saved_line);
  146. #endif // HAVE_READLINE
  147. free (self->prompt);
  148. }
  149. // --- GNU Readline ------------------------------------------------------------
  150. #ifdef HAVE_READLINE
  151. #define INPUT_START_IGNORE RL_PROMPT_START_IGNORE
  152. #define INPUT_END_IGNORE RL_PROMPT_END_IGNORE
  153. #define input_ding(self) rl_ding ()
  154. static void
  155. input_on_terminal_resized (struct input *self)
  156. {
  157. (void) self;
  158. // This fucks up big time on terminals with automatic wrapping such as
  159. // rxvt-unicode or newer VTE when the current line overflows, however we
  160. // can't do much about that
  161. rl_resize_terminal ();
  162. }
  163. static void
  164. input_on_readable (struct input *self)
  165. {
  166. (void) self;
  167. rl_callback_read_char ();
  168. }
  169. static void
  170. input_set_prompt (struct input *self, char *prompt)
  171. {
  172. free (self->prompt);
  173. self->prompt = prompt;
  174. if (!self->active)
  175. return;
  176. // First reset the prompt to work around a bug in readline
  177. rl_set_prompt ("");
  178. if (self->prompt_shown > 0)
  179. rl_redisplay ();
  180. rl_set_prompt (self->prompt);
  181. if (self->prompt_shown > 0)
  182. rl_redisplay ();
  183. }
  184. static void
  185. input_erase (struct input *self)
  186. {
  187. (void) self;
  188. rl_set_prompt ("");
  189. rl_replace_line ("", 0);
  190. rl_point = rl_mark = 0;
  191. rl_redisplay ();
  192. }
  193. static void
  194. input_bind (struct input *self, const char *seq, const char *function_name)
  195. {
  196. (void) self;
  197. rl_bind_keyseq (seq, rl_named_function (function_name));
  198. }
  199. static void
  200. input_bind_meta (struct input *self, char key, const char *function_name)
  201. {
  202. // This one seems to actually work
  203. char keyseq[] = { '\\', 'e', key, 0 };
  204. input_bind (self, keyseq, function_name);
  205. #if 0
  206. // While this one only fucks up UTF-8
  207. // Tested with urxvt and xterm, on Debian Jessie/Arch, default settings
  208. // \M-<key> behaves exactly the same
  209. rl_bind_key (META (key), rl_named_function (function_name));
  210. #endif
  211. }
  212. static void
  213. input_bind_control (struct input *self, char key, const char *function_name)
  214. {
  215. char keyseq[] = { '\\', 'C', '-', key, 0 };
  216. input_bind (self, keyseq, function_name);
  217. }
  218. static bool
  219. input_insert (struct input *self, const char *s)
  220. {
  221. rl_insert_text (s);
  222. if (self->prompt_shown > 0)
  223. rl_redisplay ();
  224. // GNU Readline, contrary to Editline, doesn't care about validity
  225. return true;
  226. }
  227. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  228. static int app_readline_init (void);
  229. static void on_readline_input (char *line);
  230. static char **app_readline_completion (const char *text, int start, int end);
  231. static void
  232. input_start (struct input *self, const char *program_name)
  233. {
  234. using_history ();
  235. // This can cause memory leaks, or maybe even a segfault. Funny, eh?
  236. stifle_history (HISTORY_LIMIT);
  237. const char *slash = strrchr (program_name, '/');
  238. rl_readline_name = slash ? ++slash : program_name;
  239. rl_startup_hook = app_readline_init;
  240. rl_catch_sigwinch = false;
  241. rl_basic_word_break_characters = WORD_BREAKING_CHARS;
  242. rl_completer_word_break_characters = NULL;
  243. rl_attempted_completion_function = app_readline_completion;
  244. hard_assert (self->prompt != NULL);
  245. rl_callback_handler_install (self->prompt, on_readline_input);
  246. self->prompt_shown = 1;
  247. self->active = true;
  248. }
  249. static void
  250. input_stop (struct input *self)
  251. {
  252. if (self->prompt_shown > 0)
  253. input_erase (self);
  254. // This is okay as long as we're not called from within readline
  255. rl_callback_handler_remove ();
  256. self->active = false;
  257. self->prompt_shown = false;
  258. }
  259. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  260. // The following part shows you why it's not a good idea to use
  261. // GNU Readline for this kind of software. Or for anything else, really.
  262. static void
  263. input_save_buffer (struct input *self, struct input_buffer *buffer)
  264. {
  265. (void) self;
  266. buffer->history = history_get_history_state ();
  267. buffer->saved_line = rl_copy_text (0, rl_end);
  268. buffer->saved_point = rl_point;
  269. buffer->saved_mark = rl_mark;
  270. }
  271. static void
  272. input_restore_buffer (struct input *self, struct input_buffer *buffer)
  273. {
  274. // Restore the target buffer's history
  275. if (buffer->history)
  276. {
  277. // history_get_history_state() just allocates a new HISTORY_STATE
  278. // and fills it with its current internal data. We don't need that
  279. // shell anymore after reviving it.
  280. history_set_history_state (buffer->history);
  281. free (buffer->history);
  282. buffer->history = NULL;
  283. }
  284. else
  285. {
  286. // This should get us a clean history while keeping the flags.
  287. // Note that we've either saved the previous history entries, or we've
  288. // cleared them altogether, so there should be nothing to leak.
  289. HISTORY_STATE *state = history_get_history_state ();
  290. state->offset = state->length = state->size = 0;
  291. state->entries = NULL;
  292. history_set_history_state (state);
  293. free (state);
  294. }
  295. // Try to restore the target buffer's readline state
  296. if (buffer->saved_line)
  297. {
  298. rl_replace_line (buffer->saved_line, 0);
  299. rl_point = buffer->saved_point;
  300. rl_mark = buffer->saved_mark;
  301. free (buffer->saved_line);
  302. buffer->saved_line = NULL;
  303. if (self->prompt_shown > 0)
  304. rl_redisplay ();
  305. }
  306. }
  307. static void
  308. input_switch_buffer (struct input *self, struct input_buffer *buffer)
  309. {
  310. // There could possibly be occurences of the current undo list in some
  311. // history entry. We either need to free the undo list, or move it
  312. // somewhere else to load back later, as the buffer we're switching to
  313. // has its own history state.
  314. rl_free_undo_list ();
  315. // Save this buffer's history so that it's independent for each buffer
  316. if (self->current)
  317. input_save_buffer (self, self->current);
  318. else
  319. // Just throw it away; there should always be an active buffer however
  320. #if RL_READLINE_VERSION >= 0x0603
  321. rl_clear_history ();
  322. #else // RL_READLINE_VERSION < 0x0603
  323. // At least something... this may leak undo entries
  324. clear_history ();
  325. #endif // RL_READLINE_VERSION < 0x0603
  326. input_restore_buffer (self, buffer);
  327. self->current = buffer;
  328. }
  329. static void
  330. input_destroy_buffer (struct input *self, struct input_buffer *buffer)
  331. {
  332. (void) self;
  333. // rl_clear_history, being the only way I know of to get rid of the complete
  334. // history including attached data, is a pretty recent addition. *sigh*
  335. #if RL_READLINE_VERSION >= 0x0603
  336. if (buffer->history)
  337. {
  338. // See input_switch_buffer() for why we need to do this BS
  339. rl_free_undo_list ();
  340. // This is probably the only way we can free the history fully
  341. HISTORY_STATE *state = history_get_history_state ();
  342. history_set_history_state (buffer->history);
  343. rl_clear_history ();
  344. // rl_clear_history just removes history entries,
  345. // we have to reclaim memory for their actual container ourselves
  346. free (buffer->history->entries);
  347. free (buffer->history);
  348. buffer->history = NULL;
  349. history_set_history_state (state);
  350. free (state);
  351. }
  352. #endif // RL_READLINE_VERSION
  353. input_buffer_destroy (buffer);
  354. }
  355. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  356. static void
  357. input_save (struct input *self)
  358. {
  359. hard_assert (!self->saved_line);
  360. self->saved_point = rl_point;
  361. self->saved_mark = rl_mark;
  362. self->saved_line = rl_copy_text (0, rl_end);
  363. }
  364. static void
  365. input_restore (struct input *self)
  366. {
  367. hard_assert (self->saved_line);
  368. rl_set_prompt (self->prompt);
  369. rl_replace_line (self->saved_line, 0);
  370. rl_point = self->saved_point;
  371. rl_mark = self->saved_mark;
  372. free (self->saved_line);
  373. self->saved_line = NULL;
  374. }
  375. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  376. static void
  377. input_hide (struct input *self)
  378. {
  379. if (!self->active || self->prompt_shown-- < 1)
  380. return;
  381. input_save (self);
  382. input_erase (self);
  383. }
  384. static void
  385. input_show (struct input *self)
  386. {
  387. if (!self->active || ++self->prompt_shown < 1)
  388. return;
  389. input_restore (self);
  390. rl_redisplay ();
  391. }
  392. #endif // HAVE_READLINE
  393. // --- BSD Editline ------------------------------------------------------------
  394. #ifdef HAVE_EDITLINE
  395. #define INPUT_START_IGNORE '\x01'
  396. #define INPUT_END_IGNORE '\x01'
  397. static void app_editline_init (struct input *self);
  398. static void
  399. input_ding (struct input *self)
  400. {
  401. (void) self;
  402. // XXX: this isn't probably very portable;
  403. // we could use "bell" from terminfo but that creates a dependency
  404. write (STDOUT_FILENO, "\a", 1);
  405. }
  406. static void
  407. input_on_terminal_resized (struct input *self)
  408. {
  409. el_resize (self->editline);
  410. }
  411. static void
  412. input_bind (struct input *self, const char *seq, const char *function_name)
  413. {
  414. el_set (self->editline, EL_BIND, seq, function_name, NULL);
  415. }
  416. static void
  417. input_bind_meta (struct input *self, char key, const char *function_name)
  418. {
  419. char keyseq[] = { 'M', '-', key, 0 };
  420. input_bind (self, keyseq, function_name);
  421. }
  422. static void
  423. input_bind_control (struct input *self, char key, const char *function_name)
  424. {
  425. char keyseq[] = { '^', key, 0 };
  426. input_bind (self, keyseq, function_name);
  427. }
  428. static void
  429. input_redisplay (struct input *self)
  430. {
  431. // See rl_redisplay()
  432. // The character is VREPRINT (usually C-r)
  433. // TODO: read it from terminal info
  434. // XXX: could we potentially break UTF-8 with this?
  435. char x[] = { ('R' - 'A' + 1), 0 };
  436. el_push (self->editline, x);
  437. // We have to do this or it gets stuck and nothing is done
  438. (void) el_gets (self->editline, NULL);
  439. }
  440. static void
  441. input_set_prompt (struct input *self, char *prompt)
  442. {
  443. free (self->prompt);
  444. self->prompt = prompt;
  445. if (self->prompt_shown > 0)
  446. input_redisplay (self);
  447. }
  448. static char *
  449. input_make_prompt (EditLine *editline)
  450. {
  451. struct input *self;
  452. el_get (editline, EL_CLIENTDATA, &self);
  453. if (!self->prompt)
  454. return "";
  455. return self->prompt;
  456. }
  457. static char *
  458. input_make_empty_prompt (EditLine *editline)
  459. {
  460. (void) editline;
  461. return "";
  462. }
  463. static void
  464. input_erase (struct input *self)
  465. {
  466. const LineInfoW *info = el_wline (self->editline);
  467. int len = info->lastchar - info->buffer;
  468. int point = info->cursor - info->buffer;
  469. el_cursor (self->editline, len - point);
  470. el_wdeletestr (self->editline, len);
  471. // XXX: this doesn't seem to save the escape character
  472. el_get (self->editline, EL_PROMPT, &self->saved_prompt, &self->saved_char);
  473. el_set (self->editline, EL_PROMPT, input_make_empty_prompt);
  474. input_redisplay (self);
  475. }
  476. static bool
  477. input_insert (struct input *self, const char *s)
  478. {
  479. bool success = !el_insertstr (self->editline, s);
  480. if (self->prompt_shown > 0)
  481. input_redisplay (self);
  482. return success;
  483. }
  484. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  485. static void
  486. input_start (struct input *self, const char *program_name)
  487. {
  488. self->editline = el_init (program_name, stdin, stdout, stderr);
  489. el_set (self->editline, EL_CLIENTDATA, self);
  490. el_set (self->editline, EL_PROMPT_ESC,
  491. input_make_prompt, INPUT_START_IGNORE);
  492. el_set (self->editline, EL_SIGNAL, false);
  493. el_set (self->editline, EL_UNBUFFERED, true);
  494. el_set (self->editline, EL_EDITOR, "emacs");
  495. app_editline_init (self);
  496. self->prompt_shown = 1;
  497. self->active = true;
  498. }
  499. static void
  500. input_stop (struct input *self)
  501. {
  502. if (self->prompt_shown > 0)
  503. input_erase (self);
  504. el_end (self->editline);
  505. self->editline = NULL;
  506. self->active = false;
  507. self->prompt_shown = false;
  508. }
  509. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  510. static void
  511. input_save_buffer (struct input *self, struct input_buffer *buffer)
  512. {
  513. const LineInfoW *info = el_wline (self->editline);
  514. int len = info->lastchar - info->buffer;
  515. int point = info->cursor - info->buffer;
  516. wchar_t *line = calloc (sizeof *info->buffer, len + 1);
  517. memcpy (line, info->buffer, sizeof *info->buffer * len);
  518. el_cursor (self->editline, len - point);
  519. el_wdeletestr (self->editline, len);
  520. buffer->saved_line = line;
  521. buffer->saved_point = point;
  522. buffer->saved_len = len;
  523. }
  524. static void
  525. input_restore_buffer (struct input *self, struct input_buffer *buffer)
  526. {
  527. if (buffer->saved_line)
  528. {
  529. el_winsertstr (self->editline, buffer->saved_line);
  530. el_cursor (self->editline,
  531. -(buffer->saved_len - buffer->saved_point));
  532. free (buffer->saved_line);
  533. buffer->saved_line = NULL;
  534. }
  535. }
  536. static void
  537. input_switch_buffer (struct input *self, struct input_buffer *buffer)
  538. {
  539. if (self->current)
  540. input_save_buffer (self, self->current);
  541. input_restore_buffer (self, buffer);
  542. el_wset (self->editline, EL_HIST, history, buffer->history);
  543. self->current = buffer;
  544. }
  545. static void
  546. input_destroy_buffer (struct input *self, struct input_buffer *buffer)
  547. {
  548. (void) self;
  549. input_buffer_destroy (buffer);
  550. }
  551. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  552. static void
  553. input_save (struct input *self)
  554. {
  555. if (self->current)
  556. input_save_buffer (self, self->current);
  557. }
  558. static void
  559. input_restore (struct input *self)
  560. {
  561. if (self->current)
  562. input_restore_buffer (self, self->current);
  563. }
  564. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  565. static void
  566. input_hide (struct input *self)
  567. {
  568. if (!self->active || self->prompt_shown-- < 1)
  569. return;
  570. input_save (self);
  571. input_erase (self);
  572. }
  573. static void
  574. input_show (struct input *self)
  575. {
  576. if (!self->active || ++self->prompt_shown < 1)
  577. return;
  578. input_restore (self);
  579. // Would have used "saved_char" but it doesn't seem to work.
  580. // And it doesn't even when it does anyway (it seems to just strip it).
  581. el_set (self->editline,
  582. EL_PROMPT_ESC, input_make_prompt, INPUT_START_IGNORE);
  583. input_redisplay (self);
  584. }
  585. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  586. static void
  587. input_on_readable (struct input *self)
  588. {
  589. // We bind the return key to process it how we need to
  590. // el_gets() with EL_UNBUFFERED doesn't work with UTF-8,
  591. // we must use the wide-character interface
  592. int count = 0;
  593. const wchar_t *buf = el_wgets (self->editline, &count);
  594. if (!buf || count-- <= 0)
  595. return;
  596. // The character is VEOF (usually C-d)
  597. // TODO: read it from terminal info
  598. if (count == 0 && buf[0] == ('D' - 'A' + 1))
  599. {
  600. el_deletestr (self->editline, 1);
  601. input_redisplay (self);
  602. input_ding (self);
  603. }
  604. }
  605. #endif // HAVE_EDITLINE
  606. // --- Application data --------------------------------------------------------
  607. // All text stored in our data structures is encoded in UTF-8.
  608. // Or at least should be. The exception is IRC identifiers.
  609. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  610. // We need a few reference countable objects with support for both strong
  611. // and weak references (mainly used for scripted plugins).
  612. //
  613. // Beware that if you don't own the object, you will most probably want
  614. // to keep the weak reference link so that you can get rid of it later.
  615. // Also note that you have to make sure the user_data don't leak resources.
  616. //
  617. // Having a callback is more versatile than just nulling out a pointer.
  618. /// Callback just before a reference counted object is destroyed
  619. typedef void (*destroy_cb_fn) (void *object, void *user_data);
  620. struct weak_ref_link
  621. {
  622. LIST_HEADER (struct weak_ref_link)
  623. destroy_cb_fn on_destroy; ///< Called when object is destroyed
  624. void *user_data; ///< User data
  625. };
  626. static struct weak_ref_link *
  627. weak_ref (struct weak_ref_link **list, destroy_cb_fn cb, void *user_data)
  628. {
  629. struct weak_ref_link *link = xcalloc (1, sizeof *link);
  630. link->on_destroy = cb;
  631. link->user_data = user_data;
  632. LIST_PREPEND (*list, link);
  633. return link;
  634. }
  635. static void
  636. weak_unref (struct weak_ref_link **list, struct weak_ref_link **link)
  637. {
  638. if (*link)
  639. LIST_UNLINK (*list, *link);
  640. free (*link);
  641. *link = NULL;
  642. }
  643. #define REF_COUNTABLE_HEADER \
  644. size_t ref_count; /**< Reference count */ \
  645. struct weak_ref_link *weak_refs; /**< To remove any weak references */
  646. #define REF_COUNTABLE_METHODS(name) \
  647. static struct name * \
  648. name ## _ref (struct name *self) \
  649. { \
  650. self->ref_count++; \
  651. return self; \
  652. } \
  653. \
  654. static void \
  655. name ## _unref (struct name *self) \
  656. { \
  657. if (--self->ref_count) \
  658. return; \
  659. LIST_FOR_EACH (struct weak_ref_link, iter, self->weak_refs) \
  660. { \
  661. iter->on_destroy (self, iter->user_data); \
  662. free (iter); \
  663. } \
  664. name ## _destroy (self); \
  665. } \
  666. \
  667. static struct weak_ref_link * \
  668. name ## _weak_ref (struct name *self, destroy_cb_fn cb, void *user_data) \
  669. { return weak_ref (&self->weak_refs, cb, user_data); } \
  670. \
  671. static void \
  672. name ## _weak_unref (struct name *self, struct weak_ref_link **link) \
  673. { weak_unref (&self->weak_refs, link); }
  674. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  675. struct user_channel
  676. {
  677. LIST_HEADER (struct user_channel)
  678. struct channel *channel; ///< Reference to channel
  679. };
  680. static struct user_channel *
  681. user_channel_new (void)
  682. {
  683. struct user_channel *self = xcalloc (1, sizeof *self);
  684. return self;
  685. }
  686. static void
  687. user_channel_destroy (struct user_channel *self)
  688. {
  689. // The "channel" reference is weak and this object should get
  690. // destroyed whenever the user stops being in the channel.
  691. free (self);
  692. }
  693. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  694. // We keep references to user information in channels and buffers,
  695. // and weak references in the name lookup table.
  696. struct user
  697. {
  698. REF_COUNTABLE_HEADER
  699. char *nickname; ///< Literal nickname
  700. // TODO: write code to poll for the away status
  701. bool away; ///< User is away
  702. struct user_channel *channels; ///< Channels the user is on
  703. };
  704. static struct user *
  705. user_new (void)
  706. {
  707. struct user *self = xcalloc (1, sizeof *self);
  708. self->ref_count = 1;
  709. return self;
  710. }
  711. static void
  712. user_destroy (struct user *self)
  713. {
  714. free (self->nickname);
  715. LIST_FOR_EACH (struct user_channel, iter, self->channels)
  716. user_channel_destroy (iter);
  717. free (self);
  718. }
  719. REF_COUNTABLE_METHODS (user)
  720. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  721. struct channel_user
  722. {
  723. LIST_HEADER (struct channel_user)
  724. struct user *user; ///< Reference to user
  725. struct str prefixes; ///< Ordered @+... characters
  726. };
  727. static struct channel_user *
  728. channel_user_new (void)
  729. {
  730. struct channel_user *self = xcalloc (1, sizeof *self);
  731. str_init (&self->prefixes);
  732. return self;
  733. }
  734. static void
  735. channel_user_destroy (struct channel_user *self)
  736. {
  737. user_unref (self->user);
  738. str_free (&self->prefixes);
  739. free (self);
  740. }
  741. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  742. // We keep references to channels in their buffers,
  743. // and weak references in their users and the name lookup table.
  744. // XXX: this doesn't really have to be reference countable
  745. struct channel
  746. {
  747. REF_COUNTABLE_HEADER
  748. char *name; ///< Channel name
  749. char *topic; ///< Channel topic
  750. // XXX: write something like an ordered set of characters object?
  751. struct str no_param_modes; ///< No parameter channel modes
  752. struct str_map param_modes; ///< Parametrized channel modes
  753. struct channel_user *users; ///< Channel users
  754. struct str_vector names_buf; ///< Buffer for RPL_NAMREPLY
  755. bool left_manually; ///< Don't rejoin on reconnect
  756. };
  757. static struct channel *
  758. channel_new (void)
  759. {
  760. struct channel *self = xcalloc (1, sizeof *self);
  761. self->ref_count = 1;
  762. str_init (&self->no_param_modes);
  763. str_map_init (&self->param_modes);
  764. self->param_modes.free = free;
  765. str_vector_init (&self->names_buf);
  766. return self;
  767. }
  768. static void
  769. channel_destroy (struct channel *self)
  770. {
  771. free (self->name);
  772. free (self->topic);
  773. str_free (&self->no_param_modes);
  774. str_map_free (&self->param_modes);
  775. // Owner has to make sure we have no users by now
  776. hard_assert (!self->users);
  777. str_vector_free (&self->names_buf);
  778. free (self);
  779. }
  780. REF_COUNTABLE_METHODS (channel)
  781. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  782. enum formatter_item_type
  783. {
  784. FORMATTER_ITEM_TEXT, ///< Text
  785. FORMATTER_ITEM_ATTR, ///< Formatting attributes
  786. FORMATTER_ITEM_FG_COLOR, ///< Foreground color
  787. FORMATTER_ITEM_BG_COLOR, ///< Background color
  788. FORMATTER_ITEM_SIMPLE, ///< Toggle mIRC formatting
  789. FORMATTER_ITEM_IGNORE_ATTR ///< Un/set attribute ignoration
  790. };
  791. struct formatter_item
  792. {
  793. LIST_HEADER (struct formatter_item)
  794. enum formatter_item_type type; ///< Type of this item
  795. int color; ///< Color
  796. int attribute; ///< Attribute ID
  797. char *text; ///< Either text or an attribute string
  798. };
  799. static struct formatter_item *
  800. formatter_item_new (void)
  801. {
  802. struct formatter_item *self = xcalloc (1, sizeof *self);
  803. return self;
  804. }
  805. static void
  806. formatter_item_destroy (struct formatter_item *self)
  807. {
  808. free (self->text);
  809. free (self);
  810. }
  811. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  812. struct formatter
  813. {
  814. struct app_context *ctx; ///< Application context
  815. struct server *s; ///< Server
  816. struct formatter_item *items; ///< Items
  817. struct formatter_item *items_tail; ///< Tail of items
  818. };
  819. static void
  820. formatter_init (struct formatter *self,
  821. struct app_context *ctx, struct server *s)
  822. {
  823. memset (self, 0, sizeof *self);
  824. self->ctx = ctx;
  825. self->s = s;
  826. }
  827. static void
  828. formatter_free (struct formatter *self)
  829. {
  830. LIST_FOR_EACH (struct formatter_item, iter, self->items)
  831. formatter_item_destroy (iter);
  832. }
  833. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  834. enum buffer_line_flags
  835. {
  836. BUFFER_LINE_STATUS = 1 << 0, ///< Status message
  837. BUFFER_LINE_ERROR = 1 << 1, ///< Error message
  838. BUFFER_LINE_HIGHLIGHT = 1 << 2, ///< The user was highlighted by this
  839. BUFFER_LINE_SKIP_FILE = 1 << 3, ///< Don't log this to file
  840. BUFFER_LINE_INDENT = 1 << 4, ///< Just indent the line
  841. BUFFER_LINE_UNIMPORTANT = 1 << 5 ///< Joins, parts, similar spam
  842. };
  843. struct buffer_line
  844. {
  845. LIST_HEADER (struct buffer_line)
  846. int flags; ///< Flags
  847. time_t when; ///< Time of the event
  848. struct formatter *formatter; ///< Line data
  849. };
  850. struct buffer_line *
  851. buffer_line_new (void)
  852. {
  853. struct buffer_line *self = xcalloc (1, sizeof *self);
  854. return self;
  855. }
  856. static void
  857. buffer_line_destroy (struct buffer_line *self)
  858. {
  859. if (self->formatter)
  860. formatter_free (self->formatter);
  861. free (self->formatter);
  862. free (self);
  863. }
  864. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  865. enum buffer_type
  866. {
  867. BUFFER_GLOBAL, ///< Global information
  868. BUFFER_SERVER, ///< Server-related messages
  869. BUFFER_CHANNEL, ///< Channels
  870. BUFFER_PM ///< Private messages (query)
  871. };
  872. struct buffer
  873. {
  874. LIST_HEADER (struct buffer)
  875. REF_COUNTABLE_HEADER
  876. enum buffer_type type; ///< Type of the buffer
  877. char *name; ///< The name of the buffer
  878. struct input_buffer *input_data; ///< User interface data
  879. // Buffer contents:
  880. struct buffer_line *lines; ///< All lines in this buffer
  881. struct buffer_line *lines_tail; ///< The tail of buffer lines
  882. unsigned lines_count; ///< How many lines we have
  883. unsigned unseen_messages_count; ///< # messages since last visited
  884. unsigned unseen_unimportant_count; ///< How much of that is unimportant
  885. bool highlighted; ///< We've been highlighted
  886. FILE *log_file; ///< Log file
  887. // Origin information:
  888. struct server *server; ///< Reference to server
  889. struct channel *channel; ///< Reference to channel
  890. struct user *user; ///< Reference to user
  891. };
  892. static struct buffer *
  893. buffer_new (void)
  894. {
  895. struct buffer *self = xcalloc (1, sizeof *self);
  896. self->ref_count = 1;
  897. self->input_data = input_buffer_new ();
  898. return self;
  899. }
  900. static void
  901. buffer_destroy (struct buffer *self)
  902. {
  903. free (self->name);
  904. if (self->input_data)
  905. input_buffer_destroy (self->input_data);
  906. LIST_FOR_EACH (struct buffer_line, iter, self->lines)
  907. buffer_line_destroy (iter);
  908. if (self->log_file)
  909. (void) fclose (self->log_file);
  910. if (self->user)
  911. user_unref (self->user);
  912. if (self->channel)
  913. channel_unref (self->channel);
  914. free (self);
  915. }
  916. REF_COUNTABLE_METHODS (buffer)
  917. #define buffer_ref do_not_use_dangerous
  918. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  919. enum transport_io_result
  920. {
  921. TRANSPORT_IO_OK = 0, ///< Completed successfully
  922. TRANSPORT_IO_EOF, ///< Connection shut down by peer
  923. TRANSPORT_IO_ERROR ///< Connection error
  924. };
  925. // The only real purpose of this is to abstract away TLS
  926. struct transport
  927. {
  928. /// Initialize the transport
  929. bool (*init) (struct server *s, struct error **e);
  930. /// Destroy the user data pointer
  931. void (*cleanup) (struct server *s);
  932. /// The underlying socket may have become readable, update `read_buffer'
  933. enum transport_io_result (*try_read) (struct server *s);
  934. /// The underlying socket may have become writeable, flush `write_buffer'
  935. enum transport_io_result (*try_write) (struct server *s);
  936. /// Return event mask to use in the poller
  937. int (*get_poll_events) (struct server *s);
  938. /// Called just before closing the connection from our side
  939. void (*in_before_shutdown) (struct server *s);
  940. };
  941. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  942. enum server_state
  943. {
  944. IRC_DISCONNECTED, ///< Not connected
  945. IRC_CONNECTING, ///< Connecting to the server
  946. IRC_CONNECTED, ///< Trying to register
  947. IRC_REGISTERED, ///< We can chat now
  948. IRC_CLOSING, ///< Flushing output before shutdown
  949. IRC_HALF_CLOSED ///< Connection shutdown from our side
  950. };
  951. /// Convert an IRC identifier character to lower-case
  952. typedef int (*irc_tolower_fn) (int);
  953. /// Key conversion function for hashmap lookups
  954. typedef size_t (*irc_strxfrm_fn) (char *, const char *, size_t);
  955. struct server
  956. {
  957. REF_COUNTABLE_HEADER
  958. struct app_context *ctx; ///< Application context
  959. char *name; ///< Server identifier
  960. struct buffer *buffer; ///< The buffer for this server
  961. struct config_item *config; ///< Configuration root
  962. // Connection:
  963. enum server_state state; ///< Connection state
  964. struct connector *connector; ///< Connection establisher
  965. struct socks_connector *socks_conn; ///< SOCKS connection establisher
  966. unsigned reconnect_attempt; ///< Number of reconnect attempt
  967. bool manual_disconnect; ///< Don't reconnect after disconnect
  968. int socket; ///< Socket FD of the server
  969. struct str read_buffer; ///< Input yet to be processed
  970. struct str write_buffer; ///< Outut yet to be be sent out
  971. struct poller_fd socket_event; ///< We can read from the socket
  972. struct transport *transport; ///< Transport method
  973. void *transport_data; ///< Transport data
  974. // Events:
  975. struct poller_timer ping_tmr; ///< We should send a ping
  976. struct poller_timer timeout_tmr; ///< Connection seems to be dead
  977. struct poller_timer reconnect_tmr; ///< We should reconnect now
  978. struct poller_timer autojoin_tmr; ///< Re/join channels as appropriate
  979. // IRC:
  980. // TODO: an output queue to prevent excess floods (this will be needed
  981. // especially for away status polling)
  982. bool rehashing; ///< Rehashing IRC identifiers
  983. struct str_map irc_users; ///< IRC user data
  984. struct str_map irc_channels; ///< IRC channel data
  985. struct str_map irc_buffer_map; ///< Maps IRC identifiers to buffers
  986. struct user *irc_user; ///< Our own user
  987. int nick_counter; ///< Iterates "nicks" when registering
  988. struct str irc_user_mode; ///< Our current user modes
  989. char *irc_user_host; ///< Our current user@host
  990. bool cap_echo_message; ///< Whether the server echos messages
  991. // Server-specific information (from RPL_ISUPPORT):
  992. irc_tolower_fn irc_tolower; ///< Server tolower()
  993. irc_strxfrm_fn irc_strxfrm; ///< Server strxfrm()
  994. char *irc_chantypes; ///< Channel types (name prefixes)
  995. char *irc_idchan_prefixes; ///< Prefixes for "safe channels"
  996. char *irc_statusmsg; ///< Prefixes for channel targets
  997. char *irc_chanmodes_list; ///< Channel modes for lists
  998. char *irc_chanmodes_param_always; ///< Channel modes with mandatory param
  999. char *irc_chanmodes_param_when_set; ///< Channel modes with param when set
  1000. char *irc_chanmodes_param_never; ///< Channel modes without param
  1001. char *irc_chanuser_prefixes; ///< Channel user prefixes
  1002. char *irc_chanuser_modes; ///< Channel user modes
  1003. unsigned irc_max_modes; ///< Max parametrized modes per command
  1004. };
  1005. static void on_irc_timeout (void *user_data);
  1006. static void on_irc_ping_timeout (void *user_data);
  1007. static void on_irc_autojoin_timeout (void *user_data);
  1008. static void irc_initiate_connect (struct server *s);
  1009. static void
  1010. server_init_specifics (struct server *self)
  1011. {
  1012. // Defaults as per the RPL_ISUPPORT drafts, or RFC 1459
  1013. self->irc_tolower = irc_tolower;
  1014. self->irc_strxfrm = irc_strxfrm;
  1015. self->irc_chantypes = xstrdup ("#&");
  1016. self->irc_idchan_prefixes = xstrdup ("");
  1017. self->irc_statusmsg = xstrdup ("");
  1018. self->irc_chanmodes_list = xstrdup ("b");
  1019. self->irc_chanmodes_param_always = xstrdup ("k");
  1020. self->irc_chanmodes_param_when_set = xstrdup ("l");
  1021. self->irc_chanmodes_param_never = xstrdup ("imnpst");
  1022. self->irc_chanuser_prefixes = xstrdup ("@+");
  1023. self->irc_chanuser_modes = xstrdup ("ov");
  1024. self->irc_max_modes = 3;
  1025. }
  1026. static void
  1027. server_free_specifics (struct server *self)
  1028. {
  1029. free (self->irc_chantypes);
  1030. free (self->irc_idchan_prefixes);
  1031. free (self->irc_statusmsg);
  1032. free (self->irc_chanmodes_list);
  1033. free (self->irc_chanmodes_param_always);
  1034. free (self->irc_chanmodes_param_when_set);
  1035. free (self->irc_chanmodes_param_never);
  1036. free (self->irc_chanuser_prefixes);
  1037. free (self->irc_chanuser_modes);
  1038. }
  1039. static struct server *
  1040. server_new (struct poller *poller)
  1041. {
  1042. struct server *self = xcalloc (1, sizeof *self);
  1043. self->ref_count = 1;
  1044. self->socket = -1;
  1045. str_init (&self->read_buffer);
  1046. str_init (&self->write_buffer);
  1047. self->state = IRC_DISCONNECTED;
  1048. poller_timer_init (&self->timeout_tmr, poller);
  1049. self->timeout_tmr.dispatcher = on_irc_timeout;
  1050. self->timeout_tmr.user_data = self;
  1051. poller_timer_init (&self->ping_tmr, poller);
  1052. self->ping_tmr.dispatcher = on_irc_ping_timeout;
  1053. self->ping_tmr.user_data = self;
  1054. poller_timer_init (&self->reconnect_tmr, poller);
  1055. self->reconnect_tmr.dispatcher = (poller_timer_fn) irc_initiate_connect;
  1056. self->reconnect_tmr.user_data = self;
  1057. poller_timer_init (&self->autojoin_tmr, poller);
  1058. self->autojoin_tmr.dispatcher = on_irc_autojoin_timeout;
  1059. self->autojoin_tmr.user_data = self;
  1060. str_map_init (&self->irc_users);
  1061. self->irc_users.key_xfrm = irc_strxfrm;
  1062. str_map_init (&self->irc_channels);
  1063. self->irc_channels.key_xfrm = irc_strxfrm;
  1064. str_map_init (&self->irc_buffer_map);
  1065. self->irc_buffer_map.key_xfrm = irc_strxfrm;
  1066. str_init (&self->irc_user_mode);
  1067. server_init_specifics (self);
  1068. return self;
  1069. }
  1070. static void
  1071. server_destroy (struct server *self)
  1072. {
  1073. free (self->name);
  1074. if (self->connector)
  1075. {
  1076. connector_free (self->connector);
  1077. free (self->connector);
  1078. }
  1079. if (self->socks_conn)
  1080. {
  1081. socks_connector_free (self->socks_conn);
  1082. free (self->socks_conn);
  1083. }
  1084. if (self->transport
  1085. && self->transport->cleanup)
  1086. self->transport->cleanup (self);
  1087. if (self->socket != -1)
  1088. {
  1089. xclose (self->socket);
  1090. self->socket_event.closed = true;
  1091. poller_fd_reset (&self->socket_event);
  1092. }
  1093. str_free (&self->read_buffer);
  1094. str_free (&self->write_buffer);
  1095. poller_timer_reset (&self->ping_tmr);
  1096. poller_timer_reset (&self->timeout_tmr);
  1097. poller_timer_reset (&self->reconnect_tmr);
  1098. poller_timer_reset (&self->autojoin_tmr);
  1099. str_map_free (&self->irc_users);
  1100. str_map_free (&self->irc_channels);
  1101. str_map_free (&self->irc_buffer_map);
  1102. if (self->irc_user)
  1103. user_unref (self->irc_user);
  1104. str_free (&self->irc_user_mode);
  1105. free (self->irc_user_host);
  1106. server_free_specifics (self);
  1107. free (self);
  1108. }
  1109. REF_COUNTABLE_METHODS (server)
  1110. #define server_ref do_not_use_dangerous
  1111. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1112. struct plugin
  1113. {
  1114. LIST_HEADER (struct plugin)
  1115. char *name; ///< Name of the plugin
  1116. struct plugin_vtable *vtable; ///< Methods
  1117. };
  1118. struct plugin_vtable
  1119. {
  1120. /// Unregister and free the plugin including all relevant resources
  1121. void (*free) (struct plugin *self);
  1122. };
  1123. static void
  1124. plugin_destroy (struct plugin *self)
  1125. {
  1126. self->vtable->free (self);
  1127. free (self->name);
  1128. free (self);
  1129. }
  1130. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1131. // This is a bit ugly since insertion is O(n) and the need to get rid of the
  1132. // specific type because of list macros, however I don't currently posses any
  1133. // strictly better, ordered data structure
  1134. struct hook
  1135. {
  1136. LIST_HEADER (struct hook)
  1137. int priority; ///< The lesser the sooner
  1138. };
  1139. static struct hook *
  1140. hook_insert (struct hook *list, struct hook *item)
  1141. {
  1142. // Corner cases: list is empty or we precede everything
  1143. if (!list || item->priority < list->priority)
  1144. {
  1145. LIST_PREPEND (list, item);
  1146. return list;
  1147. }
  1148. // Otherwise fast-forward to the last entry that precedes us
  1149. struct hook *before = list;
  1150. while (before->next && before->next->priority < item->next->priority)
  1151. before = before->next;
  1152. // And link ourselves in between it and its successor
  1153. if ((item->next = before->next))
  1154. item->next->prev = item;
  1155. before->next = item;
  1156. item->prev = before;
  1157. return list;
  1158. }
  1159. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1160. struct input_hook
  1161. {
  1162. struct hook super; ///< Common hook fields
  1163. struct input_hook_vtable *vtable; ///< Methods
  1164. };
  1165. struct input_hook_vtable
  1166. {
  1167. /// Takes over the ownership of "input", returns either NULL if input
  1168. /// was thrown away, or a possibly modified version of it
  1169. char *(*filter) (struct input_hook *self,
  1170. struct buffer *buffer, char *input);
  1171. };
  1172. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1173. struct irc_hook
  1174. {
  1175. struct hook super; ///< Common hook fields
  1176. struct irc_hook_vtable *vtable; ///< Methods
  1177. };
  1178. struct irc_hook_vtable
  1179. {
  1180. /// Takes over the ownership of "message", returns either NULL if message
  1181. /// was thrown away, or a possibly modified version of it
  1182. char *(*filter) (struct irc_hook *self,
  1183. struct server *server, char *message);
  1184. };
  1185. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1186. struct app_context
  1187. {
  1188. bool no_colors; ///< Disable attribute printing
  1189. char *attrs_defaults[ATTR_COUNT]; ///< Default terminal attributes
  1190. // Configuration:
  1191. struct config config; ///< Program configuration
  1192. char *attrs[ATTR_COUNT]; ///< Terminal attributes
  1193. bool isolate_buffers; ///< Isolate global/server buffers
  1194. bool beep_on_highlight; ///< Beep on highlight
  1195. bool logging; ///< Logging to file enabled
  1196. bool show_all_prefixes; ///< Show all prefixes before nicks
  1197. struct str_map servers; ///< Our servers
  1198. // Events:
  1199. struct poller_fd tty_event; ///< Terminal input event
  1200. struct poller_fd signal_event; ///< Signal FD event
  1201. struct poller_timer flush_timer; ///< Flush all open files (e.g. logs)
  1202. struct poller_timer date_chg_tmr; ///< Print a date change
  1203. struct poller poller; ///< Manages polled descriptors
  1204. bool quitting; ///< User requested quitting
  1205. bool polling; ///< The event loop is running
  1206. // Buffers:
  1207. struct buffer *buffers; ///< All our buffers in order
  1208. struct buffer *buffers_tail; ///< The tail of our buffers
  1209. struct buffer *global_buffer; ///< The global buffer
  1210. struct buffer *current_buffer; ///< The current buffer
  1211. struct buffer *last_buffer; ///< Last used buffer
  1212. // TODO: make buffer names fully unique like weechat does
  1213. struct str_map buffers_by_name; ///< Buffers by name
  1214. time_t last_displayed_msg_time; ///< Time of last displayed message
  1215. // Terminal:
  1216. iconv_t term_to_utf8; ///< Terminal encoding to UTF-8
  1217. iconv_t term_from_utf8; ///< UTF-8 to terminal encoding
  1218. iconv_t latin1_to_utf8; ///< ISO Latin 1 to UTF-8
  1219. struct input input; ///< User interface
  1220. int *nick_palette; ///< A 256-color palette for nicknames
  1221. size_t nick_palette_len; ///< Number of entries in nick_palette
  1222. bool awaiting_mirc_escape; ///< Awaiting a mIRC attribute escape
  1223. bool in_bracketed_paste; ///< User is pasting some content
  1224. struct str input_buffer; ///< Buffered pasted content
  1225. bool running_backlog_helper; ///< Running a backlog helper
  1226. int terminal_suspended; ///< Terminal suspension level
  1227. struct plugin *plugins; ///< Loaded plugins
  1228. struct hook *input_hooks; ///< Input hooks
  1229. struct hook *irc_hooks; ///< IRC hooks
  1230. }
  1231. *g_ctx;
  1232. static int *
  1233. filter_color_cube_for_acceptable_nick_colors (size_t *len)
  1234. {
  1235. // This is a pure function and we don't use threads, static storage is fine
  1236. static int table[6 * 6 * 6];
  1237. size_t len_counter = 0;
  1238. for (int x = 0; x < 6 * 6 * 6; x++)
  1239. {
  1240. int r = x / 36;
  1241. int g = (x / 6) % 6;
  1242. int b = (x % 6);
  1243. // Use the luma value of colours within the cube to filter colours that
  1244. // look okay-ish on terminals with both black and white backgrounds
  1245. double luma = 0.2126 * r / 6. + 0.7152 * g / 6. + 0.0722 * b / 6.;
  1246. if (luma >= .3 && luma <= .5)
  1247. table[len_counter++] = 16 + x;
  1248. }
  1249. *len = len_counter;
  1250. return table;
  1251. }
  1252. static void
  1253. app_context_init (struct app_context *self)
  1254. {
  1255. memset (self, 0, sizeof *self);
  1256. config_init (&self->config);
  1257. poller_init (&self->poller);
  1258. str_map_init (&self->servers);
  1259. self->servers.free = (str_map_free_fn) server_unref;
  1260. self->servers.key_xfrm = tolower_ascii_strxfrm;
  1261. str_map_init (&self->buffers_by_name);
  1262. self->buffers_by_name.key_xfrm = tolower_ascii_strxfrm;
  1263. self->last_displayed_msg_time = time (NULL);
  1264. char *encoding = nl_langinfo (CODESET);
  1265. // FIXME: put a check for "//TRANSLIT" in CMakeLists.txt
  1266. #ifdef __linux__
  1267. encoding = xstrdup_printf ("%s//TRANSLIT", encoding);
  1268. #else // ! __linux__
  1269. encoding = xstrdup (encoding);
  1270. #endif // ! __linux__
  1271. if ((self->term_from_utf8 =
  1272. iconv_open (encoding, "UTF-8")) == (iconv_t) -1
  1273. || (self->latin1_to_utf8 =
  1274. iconv_open ("UTF-8", "ISO-8859-1")) == (iconv_t) -1
  1275. || (self->term_to_utf8 =
  1276. iconv_open ("UTF-8", nl_langinfo (CODESET))) == (iconv_t) -1)
  1277. exit_fatal ("creating the UTF-8 conversion object failed: %s",
  1278. strerror (errno));
  1279. free (encoding);
  1280. input_init (&self->input);
  1281. str_init (&self->input_buffer);
  1282. self->nick_palette =
  1283. filter_color_cube_for_acceptable_nick_colors (&self->nick_palette_len);
  1284. }
  1285. static void
  1286. app_context_free (struct app_context *self)
  1287. {
  1288. // Plugins can try to use of the other fields when destroyed
  1289. LIST_FOR_EACH (struct plugin, iter, self->plugins)
  1290. plugin_destroy (iter);
  1291. config_free (&self->config);
  1292. for (size_t i = 0; i < ATTR_COUNT; i++)
  1293. {
  1294. free (self->attrs_defaults[i]);
  1295. free (self->attrs[i]);
  1296. }
  1297. LIST_FOR_EACH (struct buffer, iter, self->buffers)
  1298. {
  1299. #ifdef HAVE_READLINE
  1300. input_destroy_buffer (&self->input, iter->input_data);
  1301. iter->input_data = NULL;
  1302. #endif // HAVE_READLINE
  1303. buffer_unref (iter);
  1304. }
  1305. str_map_free (&self->buffers_by_name);
  1306. str_map_free (&self->servers);
  1307. poller_free (&self->poller);
  1308. iconv_close (self->latin1_to_utf8);
  1309. iconv_close (self->term_from_utf8);
  1310. iconv_close (self->term_to_utf8);
  1311. input_free (&self->input);
  1312. str_free (&self->input_buffer);
  1313. }
  1314. static void refresh_prompt (struct app_context *ctx);
  1315. // --- Configuration -----------------------------------------------------------
  1316. static void
  1317. on_config_debug_mode_change (struct config_item *item)
  1318. {
  1319. g_debug_mode = item->value.boolean;
  1320. }
  1321. static void
  1322. on_config_show_all_prefixes_change (struct config_item *item)
  1323. {
  1324. struct app_context *ctx = item->user_data;
  1325. ctx->show_all_prefixes = item->value.boolean;
  1326. refresh_prompt (ctx);
  1327. }
  1328. static void on_config_attribute_change (struct config_item *item);
  1329. static void on_config_logging_change (struct config_item *item);
  1330. #define TRIVIAL_BOOLEAN_ON_CHANGE(name) \
  1331. static void \
  1332. on_config_ ## name ## _change (struct config_item *item) \
  1333. { \
  1334. struct app_context *ctx = item->user_data; \
  1335. ctx->name = item->value.boolean; \
  1336. }
  1337. TRIVIAL_BOOLEAN_ON_CHANGE (isolate_buffers)
  1338. TRIVIAL_BOOLEAN_ON_CHANGE (beep_on_highlight)
  1339. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1340. static bool
  1341. config_validate_nonjunk_string
  1342. (const struct config_item *item, struct error **e)
  1343. {
  1344. if (item->type == CONFIG_ITEM_NULL)
  1345. return true;
  1346. hard_assert (config_item_type_is_string (item->type));
  1347. for (size_t i = 0; i < item->value.string.len; i++)
  1348. {
  1349. // Not even a tabulator
  1350. unsigned char c = item->value.string.str[i];
  1351. if (c < 32)
  1352. {
  1353. error_set (e, "control characters are not allowed");
  1354. return false;
  1355. }
  1356. }
  1357. return true;
  1358. }
  1359. static bool
  1360. config_validate_addresses
  1361. (const struct config_item *item, struct error **e)
  1362. {
  1363. if (item->type == CONFIG_ITEM_NULL)
  1364. return true;
  1365. if (!config_validate_nonjunk_string (item, e))
  1366. return false;
  1367. // Comma-separated list of "host[:port]" pairs
  1368. regex_t re;
  1369. int err = regcomp (&re, "^([^/:,]+(:[^/:,]+)?)?"
  1370. "(,([^/:,]+(:[^/:,]+)?)?)*$", REG_EXTENDED | REG_NOSUB);
  1371. hard_assert (!err);
  1372. bool result = !regexec (&re, item->value.string.str, 0, NULL, 0);
  1373. if (!result)
  1374. error_set (e, "invalid address list string");
  1375. regfree (&re);
  1376. return result;
  1377. }
  1378. static bool
  1379. config_validate_nonnegative
  1380. (const struct config_item *item, struct error **e)
  1381. {
  1382. if (item->type == CONFIG_ITEM_NULL)
  1383. return true;
  1384. hard_assert (item->type == CONFIG_ITEM_INTEGER);
  1385. if (item->value.integer >= 0)
  1386. return true;
  1387. error_set (e, "must be non-negative");
  1388. return false;
  1389. }
  1390. static struct config_schema g_config_server[] =
  1391. {
  1392. { .name = "nicks",
  1393. .comment = "IRC nickname",
  1394. .type = CONFIG_ITEM_STRING_ARRAY,
  1395. .validate = config_validate_nonjunk_string },
  1396. { .name = "username",
  1397. .comment = "IRC user name",
  1398. .type = CONFIG_ITEM_STRING,
  1399. .validate = config_validate_nonjunk_string },
  1400. { .name = "realname",
  1401. .comment = "IRC real name/e-mail",
  1402. .type = CONFIG_ITEM_STRING,
  1403. .validate = config_validate_nonjunk_string },
  1404. { .name = "addresses",
  1405. .comment = "Addresses of the IRC network (e.g. \"irc.net:6667\")",
  1406. .type = CONFIG_ITEM_STRING_ARRAY,
  1407. .validate = config_validate_addresses },
  1408. { .name = "password",
  1409. .comment = "Password to connect to the server, if any",
  1410. .type = CONFIG_ITEM_STRING,
  1411. .validate = config_validate_nonjunk_string },
  1412. { .name = "tls",
  1413. .comment = "Whether to use TLS",
  1414. .type = CONFIG_ITEM_BOOLEAN,
  1415. .default_ = "off" },
  1416. { .name = "tls_cert",
  1417. .comment = "Client TLS certificate (PEM)",
  1418. .type = CONFIG_ITEM_STRING },
  1419. { .name = "tls_verify",
  1420. .comment = "Whether to verify certificates",
  1421. .type = CONFIG_ITEM_BOOLEAN,
  1422. .default_ = "on" },
  1423. { .name = "tls_ca_file",
  1424. .comment = "OpenSSL CA bundle file",
  1425. .type = CONFIG_ITEM_STRING },
  1426. { .name = "tls_ca_path",
  1427. .comment = "OpenSSL CA bundle path",
  1428. .type = CONFIG_ITEM_STRING },
  1429. { .name = "tls_ciphers",
  1430. .comment = "OpenSSL cipher preference list",
  1431. .type = CONFIG_ITEM_STRING,
  1432. .default_ = "\"DEFAULT:!MEDIUM:!LOW\"" },
  1433. { .name = "autoconnect",
  1434. .comment = "Connect automatically on startup",
  1435. .type = CONFIG_ITEM_BOOLEAN,
  1436. .default_ = "on" },
  1437. { .name = "autojoin",
  1438. .comment = "Channels to join on start",
  1439. .type = CONFIG_ITEM_STRING_ARRAY,
  1440. .validate = config_validate_nonjunk_string },
  1441. { .name = "command",
  1442. .comment = "Command to execute after a successful connect",
  1443. .type = CONFIG_ITEM_STRING },
  1444. { .name = "command_delay",
  1445. .comment = "Delay between executing \"command\" and joining channels",
  1446. .type = CONFIG_ITEM_INTEGER,
  1447. .validate = config_validate_nonnegative,
  1448. .default_ = "0" },
  1449. { .name = "reconnect",
  1450. .comment = "Whether to reconnect on error",
  1451. .type = CONFIG_ITEM_BOOLEAN,
  1452. .default_ = "on" },
  1453. { .name = "reconnect_delay",
  1454. .comment = "Time between reconnecting",
  1455. .type = CONFIG_ITEM_INTEGER,
  1456. .validate = config_validate_nonnegative,
  1457. .default_ = "5" },
  1458. { .name = "socks_host",
  1459. .comment = "Address of a SOCKS 4a/5 proxy",
  1460. .type = CONFIG_ITEM_STRING,
  1461. .validate = config_validate_nonjunk_string },
  1462. { .name = "socks_port",
  1463. .comment = "SOCKS port number",
  1464. .type = CONFIG_ITEM_INTEGER,
  1465. .validate = config_validate_nonnegative,
  1466. .default_ = "1080" },
  1467. { .name = "socks_username",
  1468. .comment = "SOCKS auth. username",
  1469. .type = CONFIG_ITEM_STRING },
  1470. { .name = "socks_password",
  1471. .comment = "SOCKS auth. password",
  1472. .type = CONFIG_ITEM_STRING },
  1473. {}
  1474. };
  1475. static struct config_schema g_config_behaviour[] =
  1476. {
  1477. { .name = "isolate_buffers",
  1478. .comment = "Don't leak messages from the server and global buffers",
  1479. .type = CONFIG_ITEM_BOOLEAN,
  1480. .default_ = "off",
  1481. .on_change = on_config_isolate_buffers_change },
  1482. { .name = "beep_on_highlight",
  1483. .comment = "Beep when highlighted or on a new invisible PM",
  1484. .type = CONFIG_ITEM_BOOLEAN,
  1485. .default_ = "on",
  1486. .on_change = on_config_beep_on_highlight_change },
  1487. { .name = "show_all_prefixes",
  1488. .comment = "Show all prefixes in front of nicknames",
  1489. .type = CONFIG_ITEM_BOOLEAN,
  1490. .default_ = "off",
  1491. .on_change = on_config_show_all_prefixes_change },
  1492. { .name = "logging",
  1493. .comment = "Log buffer contents to file",
  1494. .type = CONFIG_ITEM_BOOLEAN,
  1495. .default_ = "off",
  1496. .on_change = on_config_logging_change },
  1497. { .name = "save_on_quit",
  1498. .comment = "Save configuration before quitting",
  1499. .type = CONFIG_ITEM_BOOLEAN,
  1500. .default_ = "on" },
  1501. { .name = "debug_mode",
  1502. .comment = "Produce some debugging output",
  1503. .type = CONFIG_ITEM_BOOLEAN,
  1504. .default_ = "off",
  1505. .on_change = on_config_debug_mode_change },
  1506. // GNU screen has an ^O in its formatting attributes reset string,
  1507. // therefore we can't just pipe raw formatting to `less -R`.
  1508. // You can use the -r switch, however that makes `less` very confused
  1509. // about line wrapping, and the result is suboptimal.
  1510. { .name = "backlog_helper",
  1511. .comment = "Shell command to display a buffer's history",
  1512. .type = CONFIG_ITEM_STRING,
  1513. .default_ = "\"LESSSECURE=1 less -M -R +G\"" },
  1514. { .name = "backlog_helper_strip_formatting",
  1515. .comment = "Strip formatting from backlog helper input",
  1516. .type = CONFIG_ITEM_BOOLEAN,
  1517. .default_ = "on" },
  1518. { .name = "reconnect_delay_growing",
  1519. .comment = "Growing factor for reconnect delay",
  1520. .type = CONFIG_ITEM_INTEGER,
  1521. .validate = config_validate_nonnegative,
  1522. .default_ = "2" },
  1523. { .name = "reconnect_delay_max",
  1524. .comment = "Maximum reconnect delay in seconds",
  1525. .type = CONFIG_ITEM_INTEGER,
  1526. .validate = config_validate_nonnegative,
  1527. .default_ = "600" },
  1528. { .name = "plugin_autoload",
  1529. .comment = "Plugins to automatically load on start",
  1530. .type = CONFIG_ITEM_STRING_ARRAY,
  1531. .validate = config_validate_nonjunk_string },
  1532. {}
  1533. };
  1534. static struct config_schema g_config_attributes[] =
  1535. {
  1536. #define XX(x, y, z) { .name = y, .comment = z, .type = CONFIG_ITEM_STRING, \
  1537. .on_change = on_config_attribute_change },
  1538. ATTR_TABLE (XX)
  1539. #undef XX
  1540. {}
  1541. };
  1542. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1543. static void
  1544. load_config_behaviour (struct config_item *subtree, void *user_data)
  1545. {
  1546. config_schema_apply_to_object (g_config_behaviour, subtree, user_data);
  1547. }
  1548. static void
  1549. load_config_attributes (struct config_item *subtree, void *user_data)
  1550. {
  1551. config_schema_apply_to_object (g_config_attributes, subtree, user_data);
  1552. }
  1553. static void
  1554. register_config_modules (struct app_context *ctx)
  1555. {
  1556. struct config *config = &ctx->config;
  1557. // The servers are loaded later when we can create buffers for them
  1558. config_register_module (config, "servers", NULL, NULL);
  1559. config_register_module (config, "aliases", NULL, NULL);
  1560. config_register_module (config, "behaviour", load_config_behaviour, ctx);
  1561. config_register_module (config, "attributes", load_config_attributes, ctx);
  1562. }
  1563. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1564. static const char *
  1565. get_config_string (struct config_item *root, const char *key)
  1566. {
  1567. struct config_item *item = config_item_get (root, key, NULL);
  1568. hard_assert (item);
  1569. if (item->type == CONFIG_ITEM_NULL)
  1570. return NULL;
  1571. hard_assert (config_item_type_is_string (item->type));
  1572. return item->value.string.str;
  1573. }
  1574. static bool
  1575. set_config_string
  1576. (struct config_item *root, const char *key, const char *value)
  1577. {
  1578. struct config_item *item = config_item_get (root, key, NULL);
  1579. hard_assert (item);
  1580. struct config_item *new_ = config_item_string_from_cstr (value);
  1581. struct error *e = NULL;
  1582. if (config_item_set_from (item, new_, &e))
  1583. return true;
  1584. config_item_destroy (new_);
  1585. print_error ("couldn't set `%s' in configuration: %s", key, e->message);
  1586. error_free (e);
  1587. return false;
  1588. }
  1589. static int64_t
  1590. get_config_integer (struct config_item *root, const char *key)
  1591. {
  1592. struct config_item *item = config_item_get (root, key, NULL);
  1593. hard_assert (item && item->type == CONFIG_ITEM_INTEGER);
  1594. return item->value.integer;
  1595. }
  1596. static bool
  1597. get_config_boolean (struct config_item *root, const char *key)
  1598. {
  1599. struct config_item *item = config_item_get (root, key, NULL);
  1600. hard_assert (item && item->type == CONFIG_ITEM_BOOLEAN);
  1601. return item->value.boolean;
  1602. }
  1603. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1604. static struct str_map *
  1605. get_servers_config (struct app_context *ctx)
  1606. {
  1607. return &config_item_get (ctx->config.root, "servers", NULL)->value.object;
  1608. }
  1609. static struct str_map *
  1610. get_aliases_config (struct app_context *ctx)
  1611. {
  1612. return &config_item_get (ctx->config.root, "aliases", NULL)->value.object;
  1613. }
  1614. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1615. static char *
  1616. write_configuration_file (const struct str *data, struct error **e)
  1617. {
  1618. struct str path;
  1619. str_init (&path);
  1620. get_xdg_home_dir (&path, "XDG_CONFIG_HOME", ".config");
  1621. str_append (&path, "/" PROGRAM_NAME);
  1622. if (!mkdir_with_parents (path.str, e))
  1623. goto error;
  1624. str_append (&path, "/" PROGRAM_NAME ".conf");
  1625. FILE *fp = fopen (path.str, "w");
  1626. if (!fp)
  1627. {
  1628. error_set (e, "could not open `%s' for writing: %s",
  1629. path.str, strerror (errno));
  1630. goto error;
  1631. }
  1632. errno = 0;
  1633. fwrite (data->str, data->len, 1, fp);
  1634. fclose (fp);
  1635. if (errno)
  1636. {
  1637. error_set (e, "writing to `%s' failed: %s", path.str, strerror (errno));
  1638. goto error;
  1639. }
  1640. return str_steal (&path);
  1641. error:
  1642. str_free (&path);
  1643. return NULL;
  1644. }
  1645. static void
  1646. serialize_configuration (struct app_context *ctx, struct str *output)
  1647. {
  1648. str_append (output,
  1649. "# " PROGRAM_NAME " " PROGRAM_VERSION " configuration file\n"
  1650. "#\n"
  1651. "# Relative paths are searched for in ${XDG_CONFIG_HOME:-~/.config}\n"
  1652. "# /" PROGRAM_NAME " as well as in $XDG_CONFIG_DIRS/" PROGRAM_NAME "\n"
  1653. "#\n"
  1654. "# Everything is in UTF-8. Any custom comments will be overwritten.\n"
  1655. "\n");
  1656. config_item_write (ctx->config.root, true, output);
  1657. }
  1658. // --- Terminal output ---------------------------------------------------------
  1659. /// Default color pair
  1660. #define COLOR_DEFAULT -1
  1661. /// Bright versions of the basic color set
  1662. #define COLOR_BRIGHT(x) (COLOR_ ## x + 8)
  1663. /// Builds a color pair for 256-color terminals with a 16-color backup value
  1664. #define COLOR_256(name, c256) \
  1665. (((COLOR_ ## name) & 0xFFFF) | (((c256) & 0xFFFF) << 16))
  1666. static struct
  1667. {
  1668. bool initialized; ///< Terminal is available
  1669. bool stdout_is_tty; ///< `stdout' is a terminal
  1670. bool stderr_is_tty; ///< `stderr' is a terminal
  1671. char *color_set_fg[256]; ///< Codes to set the foreground colour
  1672. char *color_set_bg[256]; ///< Codes to set the background colour
  1673. int lines; ///< Number of lines
  1674. int columns; ///< Number of columns
  1675. }
  1676. g_terminal;
  1677. static void
  1678. update_screen_size (void)
  1679. {
  1680. #ifdef TIOCGWINSZ
  1681. if (!g_terminal.stdout_is_tty)
  1682. return;
  1683. struct winsize size;
  1684. if (!ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &size))
  1685. {
  1686. char *row = getenv ("LINES");
  1687. char *col = getenv ("COLUMNS");
  1688. unsigned long tmp;
  1689. g_terminal.lines =
  1690. (row && xstrtoul (&tmp, row, 10)) ? tmp : size.ws_row;
  1691. g_terminal.columns =
  1692. (col && xstrtoul (&tmp, col, 10)) ? tmp : size.ws_col;
  1693. }
  1694. #endif // TIOCGWINSZ
  1695. }
  1696. static bool
  1697. init_terminal (void)
  1698. {
  1699. int tty_fd = -1;
  1700. if ((g_terminal.stderr_is_tty = isatty (STDERR_FILENO)))
  1701. tty_fd = STDERR_FILENO;
  1702. if ((g_terminal.stdout_is_tty = isatty (STDOUT_FILENO)))
  1703. tty_fd = STDOUT_FILENO;
  1704. int err;
  1705. if (tty_fd == -1 || setupterm (NULL, tty_fd, &err) == ERR)
  1706. return false;
  1707. // Make sure all terminal features used by us are supported
  1708. if (!set_a_foreground || !set_a_background
  1709. || !enter_bold_mode || !exit_attribute_mode)
  1710. {
  1711. del_curterm (cur_term);
  1712. return false;
  1713. }
  1714. g_terminal.lines = tigetnum ("lines");
  1715. g_terminal.columns = tigetnum ("cols");
  1716. update_screen_size ();
  1717. int max = MIN (256, max_colors);
  1718. for (int i = 0; i < max; i++)
  1719. {
  1720. g_terminal.color_set_fg[i] = xstrdup (tparm (set_a_foreground,
  1721. i, 0, 0, 0, 0, 0, 0, 0, 0));
  1722. g_terminal.color_set_bg[i] = xstrdup (tparm (set_a_background,
  1723. i, 0, 0, 0, 0, 0, 0, 0, 0));
  1724. }
  1725. return g_terminal.initialized = true;
  1726. }
  1727. static void
  1728. free_terminal (void)
  1729. {
  1730. if (!g_terminal.initialized)
  1731. return;
  1732. for (int i = 0; i < 256; i++)
  1733. {
  1734. free (g_terminal.color_set_fg[i]);
  1735. free (g_terminal.color_set_bg[i]);
  1736. }
  1737. del_curterm (cur_term);
  1738. }
  1739. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1740. typedef int (*terminal_printer_fn) (int);
  1741. static int
  1742. putchar_stderr (int c)
  1743. {
  1744. return fputc (c, stderr);
  1745. }
  1746. static terminal_printer_fn
  1747. get_attribute_printer (FILE *stream)
  1748. {
  1749. if (stream == stdout && g_terminal.stdout_is_tty)
  1750. return putchar;
  1751. if (stream == stderr && g_terminal.stderr_is_tty)
  1752. return putchar_stderr;
  1753. return NULL;
  1754. }
  1755. static void
  1756. vprint_attributed (struct app_context *ctx,
  1757. FILE *stream, intptr_t attribute, const char *fmt, va_list ap)
  1758. {
  1759. terminal_printer_fn printer = get_attribute_printer (stream);
  1760. if (!attribute)
  1761. printer = NULL;
  1762. if (printer)
  1763. tputs (ctx->attrs[attribute], 1, printer);
  1764. vfprintf (stream, fmt, ap);
  1765. if (printer)
  1766. tputs (ctx->attrs[ATTR_RESET], 1, printer);
  1767. }
  1768. static void
  1769. print_attributed (struct app_context *ctx,
  1770. FILE *stream, intptr_t attribute, const char *fmt, ...)
  1771. {
  1772. va_list ap;
  1773. va_start (ap, fmt);
  1774. vprint_attributed (ctx, stream, attribute, fmt, ap);
  1775. va_end (ap);
  1776. }
  1777. static void
  1778. log_message_attributed (void *user_data, const char *quote, const char *fmt,
  1779. va_list ap)
  1780. {
  1781. FILE *stream = stderr;
  1782. struct app_context *ctx = g_ctx;
  1783. input_hide (&ctx->input);
  1784. print_attributed (ctx, stream, (intptr_t) user_data, "%s", quote);
  1785. vprint_attributed (ctx, stream, (intptr_t) user_data, fmt, ap);
  1786. fputs ("\n", stream);
  1787. input_show (&ctx->input);
  1788. }
  1789. static void
  1790. apply_attribute_change (struct config_item *item, int id)
  1791. {
  1792. struct app_context *ctx = item->user_data;
  1793. free (ctx->attrs[id]);
  1794. ctx->attrs[id] = xstrdup (item->type == CONFIG_ITEM_NULL
  1795. ? ctx->attrs_defaults[id]
  1796. : item->value.string.str);
  1797. }
  1798. static void
  1799. on_config_attribute_change (struct config_item *item)
  1800. {
  1801. static const char *table[ATTR_COUNT] =
  1802. {
  1803. #define XX(x, y, z) [ATTR_ ## x] = y,
  1804. ATTR_TABLE (XX)
  1805. #undef XX
  1806. };
  1807. for (size_t i = 0; i < N_ELEMENTS (table); i++)
  1808. if (!strcmp (item->schema->name, table[i]))
  1809. {
  1810. apply_attribute_change (item, i);
  1811. return;
  1812. }
  1813. }
  1814. static void
  1815. init_colors (struct app_context *ctx)
  1816. {
  1817. bool have_ti = init_terminal ();
  1818. char **defaults = ctx->attrs_defaults;
  1819. #define INIT_ATTR(id, ti) defaults[ATTR_ ## id] = xstrdup (have_ti ? (ti) : "")
  1820. INIT_ATTR (PROMPT, enter_bold_mode);
  1821. INIT_ATTR (RESET, exit_attribute_mode);
  1822. INIT_ATTR (READ_MARKER, g_terminal.color_set_fg[COLOR_MAGENTA]);
  1823. INIT_ATTR (WARNING, g_terminal.color_set_fg[COLOR_YELLOW]);
  1824. INIT_ATTR (ERROR, g_terminal.color_set_fg[COLOR_RED]);
  1825. INIT_ATTR (EXTERNAL, g_terminal.color_set_fg[COLOR_WHITE]);
  1826. INIT_ATTR (TIMESTAMP, g_terminal.color_set_fg[COLOR_WHITE]);
  1827. INIT_ATTR (ACTION, g_terminal.color_set_fg[COLOR_RED]);
  1828. INIT_ATTR (USERHOST, g_terminal.color_set_fg[COLOR_CYAN]);
  1829. INIT_ATTR (JOIN, g_terminal.color_set_fg[COLOR_GREEN]);
  1830. INIT_ATTR (PART, g_terminal.color_set_fg[COLOR_RED]);
  1831. char *highlight = xstrdup_printf ("%s%s%s",
  1832. g_terminal.color_set_fg[COLOR_YELLOW],
  1833. g_terminal.color_set_bg[COLOR_MAGENTA],
  1834. enter_bold_mode);
  1835. INIT_ATTR (HIGHLIGHT, highlight);
  1836. free (highlight);
  1837. #undef INIT_ATTR
  1838. if (ctx->no_colors)
  1839. {
  1840. g_terminal.stdout_is_tty = false;
  1841. g_terminal.stderr_is_tty = false;
  1842. }
  1843. g_log_message_real = log_message_attributed;
  1844. // Apply the default values so that we start with any formatting at all
  1845. config_schema_call_changed
  1846. (config_item_get (ctx->config.root, "attributes", NULL));
  1847. }
  1848. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1849. // A little tool that tries to make the most of the terminal's capabilities
  1850. // to set up text attributes. It mostly targets just terminal emulators as that
  1851. // is what people are using these days. At least no stupid ncurses limits us
  1852. // with color pairs.
  1853. enum
  1854. {
  1855. ATTRIBUTE_BOLD = 1 << 0,
  1856. ATTRIBUTE_ITALIC = 1 << 1,
  1857. ATTRIBUTE_UNDERLINE = 1 << 2,
  1858. ATTRIBUTE_INVERSE = 1 << 3,
  1859. ATTRIBUTE_BLINK = 1 << 4
  1860. };
  1861. struct attribute_printer
  1862. {
  1863. struct app_context *ctx; ///< Application context
  1864. FILE *stream; ///< Output stream
  1865. bool dirty; ///< Attributes are set
  1866. int want; ///< Desired attributes
  1867. int want_foreground; ///< Desired foreground color
  1868. int want_background; ///< Desired background color
  1869. };
  1870. static void
  1871. attribute_printer_tputs (struct attribute_printer *self, const char *attr)
  1872. {
  1873. terminal_printer_fn printer = get_attribute_printer (self->stream);
  1874. if (printer)
  1875. tputs (attr, 1, printer);
  1876. else
  1877. // We shouldn't really do this but we need it to
  1878. // output formatting to the backlog
  1879. fputs (attr, self->stream);
  1880. }
  1881. static void
  1882. attribute_printer_reset (struct attribute_printer *self)
  1883. {
  1884. if (self->dirty)
  1885. attribute_printer_tputs (self, self->ctx->attrs[ATTR_RESET]);
  1886. self->dirty = false;
  1887. }
  1888. static void
  1889. attribute_printer_init (struct attribute_printer *self,
  1890. struct app_context *ctx, FILE *stream)
  1891. {
  1892. self->ctx = ctx;
  1893. self->stream = stream;
  1894. self->dirty = true;
  1895. self->want = 0;
  1896. self->want_foreground = -1;
  1897. self->want_background = -1;
  1898. }
  1899. static void
  1900. attribute_printer_apply (struct attribute_printer *self, int attribute)
  1901. {
  1902. attribute_printer_reset (self);
  1903. if (attribute != ATTR_RESET)
  1904. {
  1905. attribute_printer_tputs (self, self->ctx->attrs[attribute]);
  1906. self->dirty = true;
  1907. }
  1908. }
  1909. // NOTE: commonly terminals have:
  1910. // 8 colors (worst, bright fg with BOLD, bg sometimes with BLINK)
  1911. // 16 colors (okayish, we have the full basic range guaranteed)
  1912. // 88 colors (the same plus a 4^3 RGB cube and a few shades of gray)
  1913. // 256 colors (best, like above but with a larger cube and more gray)
  1914. /// Interpolate from the 256-color palette to the 88-color one
  1915. static int
  1916. attribute_printer_256_to_88 (int color)
  1917. {
  1918. // These colours are the same everywhere
  1919. if (color < 16)
  1920. return color;
  1921. // 24 -> 8 extra shades of gray
  1922. if (color >= 232)
  1923. return 80 + (color - 232) / 3;
  1924. // 6 * 6 * 6 cube -> 4 * 4 * 4 cube
  1925. int x[6] = { 0, 1, 1, 2, 2, 3 };
  1926. int index = color - 16;
  1927. return 16 +
  1928. ( x[ index / 36 ] << 8
  1929. | x[(index / 6) % 6 ] << 4
  1930. | x[(index % 6) ] );
  1931. }
  1932. static int
  1933. attribute_printer_decode_color (int color, bool *is_bright)
  1934. {
  1935. int16_t c16 = color; hard_assert (c16 < 16);
  1936. int16_t c256 = color >> 16; hard_assert (c256 < 256);
  1937. *is_bright = false;
  1938. switch (max_colors)
  1939. {
  1940. case 8:
  1941. if (c16 >= 8)
  1942. {
  1943. c16 -= 8;
  1944. *is_bright = true;
  1945. }
  1946. case 16:
  1947. return c16;
  1948. case 88:
  1949. return c256 <= 0 ? c16 : attribute_printer_256_to_88 (c256);
  1950. case 256:
  1951. return c256 <= 0 ? c16 : c256;
  1952. default:
  1953. // Unsupported palette
  1954. return -1;
  1955. }
  1956. }
  1957. static void
  1958. attribute_printer_update (struct attribute_printer *self)
  1959. {
  1960. bool fg_is_bright;
  1961. int fg = attribute_printer_decode_color
  1962. (self->want_foreground, &fg_is_bright);
  1963. bool bg_is_bright;
  1964. int bg = attribute_printer_decode_color
  1965. (self->want_background, &bg_is_bright);
  1966. int attributes = self->want;
  1967. bool have_inverse = !!(attributes & ATTRIBUTE_INVERSE);
  1968. if (have_inverse)
  1969. {
  1970. bool tmp = fg_is_bright;
  1971. fg_is_bright = bg_is_bright;
  1972. bg_is_bright = tmp;
  1973. }
  1974. // In 8 colour mode, some terminals don't support bright backgrounds.
  1975. // However, we can make use of the fact that the brightness change caused
  1976. // by the bold attribute is retained when inverting the colours.
  1977. // This has the downside of making the text bold when it's not supposed
  1978. // to be, and we still can't make both colours bright, so it's more of
  1979. // an interesting hack rather than anything else.
  1980. if (!fg_is_bright && bg_is_bright && have_inverse)
  1981. attributes |= ATTRIBUTE_BOLD;
  1982. else if (!fg_is_bright && bg_is_bright
  1983. && !have_inverse && fg >= 0 && bg >= 0)
  1984. {
  1985. // As long as none of the colours is the default, we can swap them
  1986. int tmp = fg; fg = bg; bg = tmp;
  1987. attributes |= ATTRIBUTE_BOLD | ATTRIBUTE_INVERSE;
  1988. }
  1989. else
  1990. {
  1991. // This is what works on normal, decent terminals
  1992. if (fg_is_bright) attributes |= ATTRIBUTE_BOLD;
  1993. if (bg_is_bright) attributes |= ATTRIBUTE_BLINK;
  1994. }
  1995. attribute_printer_reset (self);
  1996. if (attributes)
  1997. attribute_printer_tputs (self, tparm (set_attributes,
  1998. 0, // standout
  1999. attributes & ATTRIBUTE_UNDERLINE,
  2000. attributes & ATTRIBUTE_INVERSE,
  2001. attributes & ATTRIBUTE_BLINK,
  2002. 0, // dim
  2003. attributes & ATTRIBUTE_BOLD,
  2004. 0, // blank
  2005. 0, // protect
  2006. 0)); // acs
  2007. if (enter_italics_mode && (attributes & ATTRIBUTE_ITALIC))
  2008. attribute_printer_tputs (self, enter_italics_mode);
  2009. if (fg >= 0)
  2010. attribute_printer_tputs (self, g_terminal.color_set_fg[fg]);
  2011. if (bg >= 0)
  2012. attribute_printer_tputs (self, g_terminal.color_set_bg[bg]);
  2013. self->dirty = true;
  2014. }
  2015. // --- Helpers -----------------------------------------------------------------
  2016. static int
  2017. irc_server_strcmp (struct server *s, const char *a, const char *b)
  2018. {
  2019. int x;
  2020. while (*a || *b)
  2021. if ((x = s->irc_tolower (*a++) - s->irc_tolower (*b++)))
  2022. return x;
  2023. return 0;
  2024. }
  2025. static int
  2026. irc_server_strncmp (struct server *s, const char *a, const char *b, size_t n)
  2027. {
  2028. int x;
  2029. while (n-- && (*a || *b))
  2030. if ((x = s->irc_tolower (*a++) - s->irc_tolower (*b++)))
  2031. return x;
  2032. return 0;
  2033. }
  2034. static char *
  2035. irc_cut_nickname (const char *prefix)
  2036. {
  2037. return cstr_cut_until (prefix, "!@");
  2038. }
  2039. static const char *
  2040. irc_find_userhost (const char *prefix)
  2041. {
  2042. const char *p = strchr (prefix, '!');
  2043. return p ? p + 1 : NULL;
  2044. }
  2045. static bool
  2046. irc_is_this_us (struct server *s, const char *prefix)
  2047. {
  2048. // This shouldn't be called before successfully registering.
  2049. // Better safe than sorry, though.
  2050. if (!s->irc_user)
  2051. return false;
  2052. char *nick = irc_cut_nickname (prefix);
  2053. bool result = !irc_server_strcmp (s, nick, s->irc_user->nickname);
  2054. free (nick);
  2055. return result;
  2056. }
  2057. static bool
  2058. irc_is_channel (struct server *s, const char *ident)
  2059. {
  2060. return *ident
  2061. && (!!strchr (s->irc_chantypes, *ident) ||
  2062. !!strchr (s->irc_idchan_prefixes, *ident));
  2063. }
  2064. // Message targets can be prefixed by a character filtering their targets
  2065. static const char *
  2066. irc_skip_statusmsg (struct server *s, const char *target)
  2067. {
  2068. return target + (*target && strchr (s->irc_statusmsg, *target));
  2069. }
  2070. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2071. // As of 2015, everything should be in UTF-8. And if it's not, we'll decode it
  2072. // as ISO Latin 1. This function should not be called on the whole message.
  2073. static char *
  2074. irc_to_utf8 (struct app_context *ctx, const char *text)
  2075. {
  2076. if (!text)
  2077. return NULL;
  2078. size_t len = strlen (text) + 1;
  2079. if (utf8_validate (text, len))
  2080. return xstrdup (text);
  2081. return iconv_xstrdup (ctx->latin1_to_utf8, (char *) text, len, NULL);
  2082. }
  2083. // This function is used to output debugging IRC traffic to the terminal.
  2084. // It's far from ideal, as any non-UTF-8 text degrades the entire line to
  2085. // ISO Latin 1. But it should work good enough most of the time.
  2086. static char *
  2087. irc_to_term (struct app_context *ctx, const char *text)
  2088. {
  2089. char *utf8 = irc_to_utf8 (ctx, text);
  2090. char *term = iconv_xstrdup (ctx->term_from_utf8, utf8, -1, NULL);
  2091. free (utf8);
  2092. return term;
  2093. }
  2094. // --- Output formatter --------------------------------------------------------
  2095. // This complicated piece of code makes attributed text formatting simple.
  2096. // We use a printf-inspired syntax to push attributes and text to the object,
  2097. // then flush it either to a terminal, or a log file with formatting stripped.
  2098. //
  2099. // Format strings use a #-quoted notation, to differentiate from printf:
  2100. // #s inserts a string (expected to be in UTF-8)
  2101. // #d inserts a signed integer
  2102. //
  2103. // #S inserts a string from the server with unknown encoding
  2104. // #m inserts a mIRC-formatted string (auto-resets at boundaries)
  2105. // #n cuts the nickname from a string and automatically colours it
  2106. // #N is like #n but also appends userhost, if present
  2107. //
  2108. // #a inserts named attributes (auto-resets)
  2109. // #r resets terminal attributes
  2110. // #c sets foreground color
  2111. // #C sets background color
  2112. //
  2113. // Modifiers:
  2114. // & free() the string argument after using it
  2115. static void
  2116. formatter_add_item (struct formatter *self, struct formatter_item template_)
  2117. {
  2118. if (template_.text)
  2119. template_.text = xstrdup (template_.text);
  2120. struct formatter_item *item = formatter_item_new ();
  2121. *item = template_;
  2122. LIST_APPEND_WITH_TAIL (self->items, self->items_tail, item);
  2123. }
  2124. #define FORMATTER_ADD_ITEM(self, type_, ...) formatter_add_item ((self), \
  2125. (struct formatter_item) { .type = FORMATTER_ITEM_ ## type_, __VA_ARGS__ })
  2126. #define FORMATTER_ADD_RESET(self) \
  2127. FORMATTER_ADD_ITEM ((self), ATTR, .attribute = ATTR_RESET)
  2128. #define FORMATTER_ADD_TEXT(self, text_) \
  2129. FORMATTER_ADD_ITEM ((self), TEXT, .text = (text_))
  2130. #define FORMATTER_ADD_SIMPLE(self, attribute_) \
  2131. FORMATTER_ADD_ITEM ((self), SIMPLE, .attribute = ATTRIBUTE_ ## attribute_)
  2132. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2133. enum
  2134. {
  2135. MIRC_WHITE, MIRC_BLACK, MIRC_BLUE, MIRC_GREEN,
  2136. MIRC_L_RED, MIRC_RED, MIRC_PURPLE, MIRC_ORANGE,
  2137. MIRC_YELLOW, MIRC_L_GREEN, MIRC_CYAN, MIRC_L_CYAN,
  2138. MIRC_L_BLUE, MIRC_L_PURPLE, MIRC_GRAY, MIRC_L_GRAY,
  2139. };
  2140. // We use estimates from the 16 color terminal palette, or the 256 color cube,
  2141. // which is not always available. The mIRC orange colour is only in the cube.
  2142. static const int g_mirc_to_terminal[] =
  2143. {
  2144. [MIRC_WHITE] = COLOR_256 (BRIGHT (WHITE), 231),
  2145. [MIRC_BLACK] = COLOR_256 (BLACK, 16),
  2146. [MIRC_BLUE] = COLOR_256 (BLUE, 19),
  2147. [MIRC_GREEN] = COLOR_256 (GREEN, 34),
  2148. [MIRC_L_RED] = COLOR_256 (BRIGHT (RED), 196),
  2149. [MIRC_RED] = COLOR_256 (RED, 124),
  2150. [MIRC_PURPLE] = COLOR_256 (MAGENTA, 127),
  2151. [MIRC_ORANGE] = COLOR_256 (BRIGHT (YELLOW), 214),
  2152. [MIRC_YELLOW] = COLOR_256 (BRIGHT (YELLOW), 226),
  2153. [MIRC_L_GREEN] = COLOR_256 (BRIGHT (GREEN), 46),
  2154. [MIRC_CYAN] = COLOR_256 (CYAN, 37),
  2155. [MIRC_L_CYAN] = COLOR_256 (BRIGHT (CYAN), 51),
  2156. [MIRC_L_BLUE] = COLOR_256 (BRIGHT (BLUE), 21),
  2157. [MIRC_L_PURPLE] = COLOR_256 (BRIGHT (MAGENTA),201),
  2158. [MIRC_GRAY] = COLOR_256 (BRIGHT (BLACK), 244),
  2159. [MIRC_L_GRAY] = COLOR_256 (WHITE, 252),
  2160. };
  2161. static const char *
  2162. formatter_parse_mirc_color (struct formatter *self, const char *s)
  2163. {
  2164. if (!isdigit_ascii (*s))
  2165. return s;
  2166. int fg = *s++ - '0';
  2167. if (isdigit_ascii (*s))
  2168. fg = fg * 10 + (*s++ - '0');
  2169. if (fg >= 0 && fg < 16)
  2170. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = g_mirc_to_terminal[fg]);
  2171. if (*s != ',' || !isdigit_ascii (s[1]))
  2172. return s;
  2173. s++;
  2174. int bg = *s++ - '0';
  2175. if (isdigit_ascii (*s))
  2176. bg = bg * 10 + (*s++ - '0');
  2177. if (bg >= 0 && bg < 16)
  2178. FORMATTER_ADD_ITEM (self, BG_COLOR, .color = g_mirc_to_terminal[bg]);
  2179. return s;
  2180. }
  2181. static void
  2182. formatter_parse_mirc (struct formatter *self, const char *s)
  2183. {
  2184. struct str buf;
  2185. str_init (&buf);
  2186. FORMATTER_ADD_RESET (self);
  2187. unsigned char c;
  2188. while ((c = *s++))
  2189. {
  2190. if (buf.len && c < 0x20)
  2191. {
  2192. FORMATTER_ADD_TEXT (self, buf.str);
  2193. str_reset (&buf);
  2194. }
  2195. switch (c)
  2196. {
  2197. case '\x02': FORMATTER_ADD_SIMPLE (self, BOLD); break;
  2198. case '\x1d': FORMATTER_ADD_SIMPLE (self, ITALIC); break;
  2199. case '\x1f': FORMATTER_ADD_SIMPLE (self, UNDERLINE); break;
  2200. case '\x16': FORMATTER_ADD_SIMPLE (self, INVERSE); break;
  2201. case '\x03':
  2202. s = formatter_parse_mirc_color (self, s);
  2203. break;
  2204. case '\x0f':
  2205. FORMATTER_ADD_RESET (self);
  2206. break;
  2207. default:
  2208. str_append_c (&buf, c);
  2209. }
  2210. }
  2211. if (buf.len)
  2212. FORMATTER_ADD_TEXT (self, buf.str);
  2213. str_free (&buf);
  2214. FORMATTER_ADD_RESET (self);
  2215. }
  2216. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2217. static void
  2218. formatter_parse_nick (struct formatter *self, char *s)
  2219. {
  2220. // For outgoing messages; maybe we should add a special #t for them
  2221. // which would also make us not cut off the userhost part, ever
  2222. if (irc_is_channel (self->s, irc_skip_statusmsg (self->s, s)))
  2223. {
  2224. char *tmp = irc_to_utf8 (self->ctx, s);
  2225. FORMATTER_ADD_TEXT (self, tmp);
  2226. free (tmp);
  2227. return;
  2228. }
  2229. char *nick = irc_cut_nickname (s);
  2230. int color = siphash_wrapper (nick, strlen (nick)) % 7;
  2231. // Never use the black colour, could become transparent on black terminals;
  2232. // white is similarly excluded from the range
  2233. if (color == COLOR_BLACK)
  2234. color = (uint16_t) -1;
  2235. // Use a color from the 256-color cube if available
  2236. color |= self->ctx->nick_palette[siphash_wrapper (nick,
  2237. strlen (nick)) % self->ctx->nick_palette_len] << 16;
  2238. // We always use the default color for ourselves
  2239. if (self->s && irc_is_this_us (self->s, nick))
  2240. color = -1;
  2241. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = color);
  2242. char *x = irc_to_utf8 (self->ctx, nick);
  2243. free (nick);
  2244. FORMATTER_ADD_TEXT (self, x);
  2245. free (x);
  2246. // Need to reset the color afterwards
  2247. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = -1);
  2248. }
  2249. static void
  2250. formatter_parse_nick_full (struct formatter *self, char *s)
  2251. {
  2252. formatter_parse_nick (self, s);
  2253. const char *userhost;
  2254. if (!(userhost = irc_find_userhost (s)))
  2255. return;
  2256. FORMATTER_ADD_TEXT (self, " (");
  2257. FORMATTER_ADD_ITEM (self, ATTR, .attribute = ATTR_USERHOST);
  2258. char *x = irc_to_utf8 (self->ctx, userhost);
  2259. FORMATTER_ADD_TEXT (self, x);
  2260. free (x);
  2261. FORMATTER_ADD_RESET (self);
  2262. FORMATTER_ADD_TEXT (self, ")");
  2263. }
  2264. static const char *
  2265. formatter_parse_field (struct formatter *self,
  2266. const char *field, struct str *buf, va_list *ap)
  2267. {
  2268. bool free_string = false;
  2269. char *s = NULL;
  2270. char *tmp = NULL;
  2271. int c;
  2272. restart:
  2273. switch ((c = *field++))
  2274. {
  2275. // We can push boring text content to the caller's buffer
  2276. // and let it flush the buffer only when it's actually needed
  2277. case 'd':
  2278. tmp = xstrdup_printf ("%d", va_arg (*ap, int));
  2279. str_append (buf, tmp);
  2280. free (tmp);
  2281. break;
  2282. case 's':
  2283. str_append (buf, (s = va_arg (*ap, char *)));
  2284. break;
  2285. case 'S':
  2286. tmp = irc_to_utf8 (self->ctx, (s = va_arg (*ap, char *)));
  2287. str_append (buf, tmp);
  2288. free (tmp);
  2289. break;
  2290. case 'm':
  2291. tmp = irc_to_utf8 (self->ctx, (s = va_arg (*ap, char *)));
  2292. formatter_parse_mirc (self, tmp);
  2293. free (tmp);
  2294. break;
  2295. case 'n':
  2296. formatter_parse_nick (self, (s = va_arg (*ap, char *)));
  2297. break;
  2298. case 'N':
  2299. formatter_parse_nick_full (self, (s = va_arg (*ap, char *)));
  2300. break;
  2301. case 'a':
  2302. FORMATTER_ADD_ITEM (self, ATTR, .attribute = va_arg (*ap, int));
  2303. break;
  2304. case 'c':
  2305. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = va_arg (*ap, int));
  2306. break;
  2307. case 'C':
  2308. FORMATTER_ADD_ITEM (self, BG_COLOR, .color = va_arg (*ap, int));
  2309. break;
  2310. case 'r':
  2311. FORMATTER_ADD_RESET (self);
  2312. break;
  2313. default:
  2314. if (c == '&' && !free_string)
  2315. free_string = true;
  2316. else if (c)
  2317. hard_assert (!"unexpected format specifier");
  2318. else
  2319. hard_assert (!"unexpected end of format string");
  2320. goto restart;
  2321. }
  2322. if (free_string)
  2323. free (s);
  2324. return field;
  2325. }
  2326. // I was unable to take a pointer of a bare "va_list" when it was passed in
  2327. // as a function argument, so it has to be a pointer from the beginning
  2328. static void
  2329. formatter_addv (struct formatter *self, const char *format, va_list *ap)
  2330. {
  2331. struct str buf;
  2332. str_init (&buf);
  2333. while (*format)
  2334. {
  2335. if (*format != '#' || *++format == '#')
  2336. {
  2337. str_append_c (&buf, *format++);
  2338. continue;
  2339. }
  2340. if (buf.len)
  2341. {
  2342. FORMATTER_ADD_TEXT (self, buf.str);
  2343. str_reset (&buf);
  2344. }
  2345. format = formatter_parse_field (self, format, &buf, ap);
  2346. }
  2347. if (buf.len)
  2348. FORMATTER_ADD_TEXT (self, buf.str);
  2349. str_free (&buf);
  2350. }
  2351. static void
  2352. formatter_add (struct formatter *self, const char *format, ...)
  2353. {
  2354. va_list ap;
  2355. va_start (ap, format);
  2356. formatter_addv (self, format, &ap);
  2357. va_end (ap);
  2358. }
  2359. static void
  2360. formatter_add_from (struct formatter *self, struct formatter *other)
  2361. {
  2362. for (struct formatter_item *iter = other->items; iter; iter = iter->next)
  2363. formatter_add_item (self, *iter);
  2364. }
  2365. static bool
  2366. formatter_flush_attr
  2367. (struct attribute_printer *state, struct formatter_item *item)
  2368. {
  2369. switch (item->type)
  2370. {
  2371. case FORMATTER_ITEM_ATTR:
  2372. attribute_printer_apply (state, item->attribute);
  2373. state->want = 0;
  2374. state->want_foreground = -1;
  2375. state->want_background = -1;
  2376. return true;
  2377. case FORMATTER_ITEM_SIMPLE:
  2378. state->want ^= item->attribute;
  2379. attribute_printer_update (state);
  2380. return true;
  2381. case FORMATTER_ITEM_FG_COLOR:
  2382. state->want_foreground = item->color;
  2383. attribute_printer_update (state);
  2384. return true;
  2385. case FORMATTER_ITEM_BG_COLOR:
  2386. state->want_background = item->color;
  2387. attribute_printer_update (state);
  2388. return true;
  2389. default:
  2390. return false;
  2391. }
  2392. }
  2393. static void
  2394. formatter_flush_text (struct app_context *ctx, const char *text, FILE *stream)
  2395. {
  2396. struct str sanitized;
  2397. str_init (&sanitized);
  2398. // Throw away any potentially harmful control characters
  2399. char *term = iconv_xstrdup (ctx->term_from_utf8, (char *) text, -1, NULL);
  2400. for (char *p = term; *p; p++)
  2401. if (!strchr ("\a\b\x1b", *p))
  2402. str_append_c (&sanitized, *p);
  2403. free (term);
  2404. fputs (sanitized.str, stream);
  2405. str_free (&sanitized);
  2406. }
  2407. static void
  2408. formatter_flush (struct formatter *self, FILE *stream, bool raw_attributes)
  2409. {
  2410. if (!raw_attributes && !get_attribute_printer (stream))
  2411. {
  2412. LIST_FOR_EACH (struct formatter_item, iter, self->items)
  2413. if (iter->type == FORMATTER_ITEM_TEXT)
  2414. fputs (iter->text, stream);
  2415. return;
  2416. }
  2417. struct attribute_printer state;
  2418. attribute_printer_init (&state, self->ctx, stream);
  2419. attribute_printer_reset (&state);
  2420. int attribute_ignore = 0;
  2421. LIST_FOR_EACH (struct formatter_item, iter, self->items)
  2422. {
  2423. if (iter->type == FORMATTER_ITEM_TEXT)
  2424. formatter_flush_text (self->ctx, iter->text, stream);
  2425. else if (iter->type == FORMATTER_ITEM_IGNORE_ATTR)
  2426. attribute_ignore += iter->attribute;
  2427. else if (attribute_ignore <= 0
  2428. && !formatter_flush_attr (&state, iter))
  2429. hard_assert (!"unhandled formatter item type");
  2430. }
  2431. attribute_printer_reset (&state);
  2432. }
  2433. // --- Buffers -----------------------------------------------------------------
  2434. static void
  2435. buffer_update_time (struct app_context *ctx, time_t now)
  2436. {
  2437. struct tm last, current;
  2438. if (!localtime_r (&ctx->last_displayed_msg_time, &last)
  2439. || !localtime_r (&now, &current))
  2440. {
  2441. // Strange but nonfatal
  2442. print_error ("%s: %s", "localtime_r", strerror (errno));
  2443. return;
  2444. }
  2445. ctx->last_displayed_msg_time = now;
  2446. if (last.tm_year == current.tm_year
  2447. && last.tm_mon == current.tm_mon
  2448. && last.tm_mday == current.tm_mday)
  2449. return;
  2450. char buf[32] = "";
  2451. if (soft_assert (strftime (buf, sizeof buf, "%F", &current)))
  2452. print_status ("%s", buf);
  2453. // Else the buffer was too small, which is pretty weird
  2454. }
  2455. static void
  2456. buffer_line_flush (struct buffer_line *line, struct formatter *f, FILE *output,
  2457. bool raw_attributes)
  2458. {
  2459. int flags = line->flags;
  2460. if (flags & BUFFER_LINE_INDENT) formatter_add (f, " ");
  2461. if (flags & BUFFER_LINE_STATUS) formatter_add (f, " - ");
  2462. if (flags & BUFFER_LINE_ERROR) formatter_add (f, "#a=!=#r ", ATTR_ERROR);
  2463. formatter_add_from (f, line->formatter);
  2464. formatter_add (f, "\n");
  2465. formatter_flush (f, output, raw_attributes);
  2466. formatter_free (f);
  2467. }
  2468. static void
  2469. buffer_line_display (struct app_context *ctx,
  2470. struct buffer_line *line, bool is_external)
  2471. {
  2472. // Normal timestamps don't include the date, this way the user won't be
  2473. // confused as to when an event has happened
  2474. buffer_update_time (ctx, line->when);
  2475. struct formatter f;
  2476. formatter_init (&f, ctx, NULL);
  2477. struct tm current;
  2478. char buf[9];
  2479. if (!localtime_r (&line->when, &current))
  2480. print_error ("%s: %s", "localtime_r", strerror (errno));
  2481. else if (!strftime (buf, sizeof buf, "%T", &current))
  2482. print_error ("%s: %s", "strftime", "buffer too small");
  2483. else
  2484. formatter_add (&f, "#a#s#r ", ATTR_TIMESTAMP, buf);
  2485. // Ignore all formatting for messages coming from other buffers, that is
  2486. // either from the global or server buffer. Instead print them in grey.
  2487. if (is_external)
  2488. {
  2489. formatter_add (&f, "#a", ATTR_EXTERNAL);
  2490. FORMATTER_ADD_ITEM (&f, IGNORE_ATTR, .attribute = 1);
  2491. }
  2492. input_hide (&ctx->input);
  2493. buffer_line_flush (line, &f, stdout, false);
  2494. // Flush the trailing formatting reset item
  2495. fflush (stdout);
  2496. input_show (&ctx->input);
  2497. }
  2498. static void
  2499. buffer_line_write_to_backlog (struct app_context *ctx,
  2500. struct buffer_line *line, FILE *log_file)
  2501. {
  2502. struct formatter f;
  2503. formatter_init (&f, ctx, NULL);
  2504. struct tm current;
  2505. char buf[20];
  2506. if (!localtime_r (&line->when, &current))
  2507. print_error ("%s: %s", "localtime_r", strerror (errno));
  2508. else if (!strftime (buf, sizeof buf, "%F %T", &current))
  2509. print_error ("%s: %s", "strftime", "buffer too small");
  2510. else
  2511. formatter_add (&f, "#a#s#r ", ATTR_TIMESTAMP, buf);
  2512. buffer_line_flush (line, &f, log_file, !get_config_boolean
  2513. (ctx->config.root, "behaviour.backlog_helper_strip_formatting"));
  2514. }
  2515. static void
  2516. buffer_line_write_to_log (struct app_context *ctx,
  2517. struct buffer_line *line, FILE *log_file)
  2518. {
  2519. if (line->flags & BUFFER_LINE_SKIP_FILE)
  2520. return;
  2521. struct formatter f;
  2522. formatter_init (&f, ctx, NULL);
  2523. struct tm current;
  2524. char buf[20];
  2525. if (!gmtime_r (&line->when, &current))
  2526. print_error ("%s: %s", "gmtime_r", strerror (errno));
  2527. else if (!strftime (buf, sizeof buf, "%F %T", &current))
  2528. print_error ("%s: %s", "strftime", "buffer too small");
  2529. else
  2530. formatter_add (&f, "#s ", buf);
  2531. buffer_line_flush (line, &f, log_file, false);
  2532. }
  2533. static void
  2534. log_formatter (struct app_context *ctx,
  2535. struct buffer *buffer, int flags, struct formatter *f)
  2536. {
  2537. if (!buffer)
  2538. buffer = ctx->global_buffer;
  2539. if (buffer->lines_count >= BACKLOG_LIMIT)
  2540. {
  2541. struct buffer_line *popped = buffer->lines;
  2542. LIST_UNLINK_WITH_TAIL (buffer->lines, buffer->lines_tail, popped);
  2543. buffer_line_destroy (popped);
  2544. buffer->lines_count--;
  2545. }
  2546. struct buffer_line *line = buffer_line_new ();
  2547. line->flags = flags;
  2548. line->when = time (NULL);
  2549. // Move the formatter inside
  2550. line->formatter = xmalloc (sizeof *line->formatter);
  2551. *line->formatter = *f;
  2552. LIST_APPEND_WITH_TAIL (buffer->lines, buffer->lines_tail, line);
  2553. buffer->lines_count++;
  2554. if (buffer->log_file)
  2555. buffer_line_write_to_log (ctx, line, buffer->log_file);
  2556. bool unseen_pm = buffer->type == BUFFER_PM
  2557. && buffer != ctx->current_buffer
  2558. && !(flags & BUFFER_LINE_UNIMPORTANT);
  2559. bool important = (flags & BUFFER_LINE_HIGHLIGHT) || unseen_pm;
  2560. if (ctx->beep_on_highlight && important)
  2561. input_ding (&ctx->input);
  2562. bool can_leak = false;
  2563. if ((buffer == ctx->global_buffer)
  2564. || (ctx->current_buffer->type == BUFFER_GLOBAL
  2565. && buffer->type == BUFFER_SERVER)
  2566. || (ctx->current_buffer->type != BUFFER_GLOBAL
  2567. && buffer == ctx->current_buffer->server->buffer))
  2568. can_leak = true;
  2569. bool displayed = true;
  2570. if (ctx->running_backlog_helper)
  2571. // Another process is using the terminal
  2572. displayed = false;
  2573. else if (buffer == ctx->current_buffer)
  2574. buffer_line_display (ctx, line, false);
  2575. else if (!ctx->isolate_buffers && can_leak)
  2576. buffer_line_display (ctx, line, true);
  2577. else
  2578. displayed = false;
  2579. if (!displayed)
  2580. {
  2581. buffer->unseen_messages_count++;
  2582. if (flags & BUFFER_LINE_UNIMPORTANT)
  2583. buffer->unseen_unimportant_count++;
  2584. buffer->highlighted |= important;
  2585. refresh_prompt (ctx);
  2586. }
  2587. }
  2588. static void
  2589. log_full (struct app_context *ctx, struct server *s, struct buffer *buffer,
  2590. int flags, const char *format, ...)
  2591. {
  2592. va_list ap;
  2593. va_start (ap, format);
  2594. struct formatter f;
  2595. formatter_init (&f, ctx, s);
  2596. formatter_addv (&f, format, &ap);
  2597. log_formatter (ctx, buffer, flags, &f);
  2598. va_end (ap);
  2599. }
  2600. #define log_global(ctx, flags, ...) \
  2601. log_full ((ctx), NULL, (ctx)->global_buffer, flags, __VA_ARGS__)
  2602. #define log_server(s, buffer, flags, ...) \
  2603. log_full ((s)->ctx, s, (buffer), flags, __VA_ARGS__)
  2604. #define log_global_status(ctx, ...) \
  2605. log_global ((ctx), BUFFER_LINE_STATUS, __VA_ARGS__)
  2606. #define log_global_error(ctx, ...) \
  2607. log_global ((ctx), BUFFER_LINE_ERROR, __VA_ARGS__)
  2608. #define log_global_indent(ctx, ...) \
  2609. log_global ((ctx), BUFFER_LINE_INDENT, __VA_ARGS__)
  2610. #define log_server_status(s, buffer, ...) \
  2611. log_server ((s), (buffer), BUFFER_LINE_STATUS, __VA_ARGS__)
  2612. #define log_server_error(s, buffer, ...) \
  2613. log_server ((s), (buffer), BUFFER_LINE_ERROR, __VA_ARGS__)
  2614. #define log_global_debug(ctx, ...) \
  2615. BLOCK_START \
  2616. if (g_debug_mode) \
  2617. log_global ((ctx), 0, "(*) " __VA_ARGS__); \
  2618. BLOCK_END
  2619. #define log_server_debug(s, ...) \
  2620. BLOCK_START \
  2621. if (g_debug_mode) \
  2622. log_server ((s), (s)->buffer, 0, "(*) " __VA_ARGS__); \
  2623. BLOCK_END
  2624. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2625. // Lines that are used in more than one place
  2626. #define log_nick_self(s, buffer, new_) \
  2627. log_server ((s), (buffer), BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT, \
  2628. "You are now known as #n", (new_))
  2629. #define log_nick(s, buffer, old, new_) \
  2630. log_server ((s), (buffer), BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT, \
  2631. "#n is now known as #n", (old), (new_))
  2632. #define log_outcoming_notice(s, buffer, who, text) \
  2633. log_server_status ((s), (buffer), "#s(#n): #m", "Notice", (who), (text))
  2634. #define log_outcoming_privmsg(s, buffer, prefixes, who, text) \
  2635. log_server ((s), (buffer), 0, "<#s#n> #m", (prefixes), (who), (text))
  2636. #define log_outcoming_action(s, buffer, who, text) \
  2637. log_server ((s), (buffer), 0, " #a*#r #n #m", ATTR_ACTION, (who), (text))
  2638. #define log_outcoming_orphan_notice(s, target, text) \
  2639. log_server_status ((s), (s)->buffer, "Notice -> #n: #m", (target), (text))
  2640. #define log_outcoming_orphan_privmsg(s, target, text) \
  2641. log_server_status ((s), (s)->buffer, "MSG(#n): #m", (target), (text))
  2642. #define log_ctcp_query(s, target, tag) \
  2643. log_server_status ((s), (s)->buffer, "CTCP query to #S: #S", target, tag)
  2644. #define log_ctcp_reply(s, target, reply /* freed! */) \
  2645. log_server_status ((s), (s)->buffer, "CTCP reply to #S: #&S", target, reply)
  2646. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2647. static void
  2648. make_log_filename (const char *filename, struct str *output)
  2649. {
  2650. for (const char *p = filename; *p; p++)
  2651. // XXX: anything more to replace?
  2652. if (strchr ("/\\ ", *p))
  2653. str_append_c (output, '_');
  2654. else
  2655. str_append_c (output, tolower_ascii (*p));
  2656. }
  2657. static char *
  2658. buffer_get_log_path (struct buffer *buffer)
  2659. {
  2660. struct str path;
  2661. str_init (&path);
  2662. get_xdg_home_dir (&path, "XDG_DATA_HOME", ".local/share");
  2663. str_append_printf (&path, "/%s/%s", PROGRAM_NAME, "logs");
  2664. (void) mkdir_with_parents (path.str, NULL);
  2665. // TODO: make sure global and server buffers don't collide with filenames
  2666. str_append_c (&path, '/');
  2667. make_log_filename (buffer->name, &path);
  2668. str_append (&path, ".log");
  2669. return str_steal (&path);
  2670. }
  2671. static void
  2672. buffer_open_log_file (struct app_context *ctx, struct buffer *buffer)
  2673. {
  2674. if (!ctx->logging || buffer->log_file)
  2675. return;
  2676. char *path = buffer_get_log_path (buffer);
  2677. if (!(buffer->log_file = fopen (path, "ab")))
  2678. log_global_error (ctx, "Couldn't open log file `#s': #s",
  2679. path, strerror (errno));
  2680. else
  2681. set_cloexec (fileno (buffer->log_file));
  2682. free (path);
  2683. }
  2684. static void
  2685. buffer_close_log_file (struct buffer *buffer)
  2686. {
  2687. if (buffer->log_file)
  2688. (void) fclose (buffer->log_file);
  2689. buffer->log_file = NULL;
  2690. }
  2691. static void
  2692. on_config_logging_change (struct config_item *item)
  2693. {
  2694. struct app_context *ctx = item->user_data;
  2695. ctx->logging = item->value.boolean;
  2696. for (struct buffer *buffer = ctx->buffers; buffer; buffer = buffer->next)
  2697. if (ctx->logging)
  2698. buffer_open_log_file (ctx, buffer);
  2699. else
  2700. buffer_close_log_file (buffer);
  2701. }
  2702. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2703. static struct buffer *
  2704. buffer_by_name (struct app_context *ctx, const char *name)
  2705. {
  2706. return str_map_find (&ctx->buffers_by_name, name);
  2707. }
  2708. static void
  2709. buffer_add (struct app_context *ctx, struct buffer *buffer)
  2710. {
  2711. hard_assert (!buffer_by_name (ctx, buffer->name));
  2712. str_map_set (&ctx->buffers_by_name, buffer->name, buffer);
  2713. LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
  2714. buffer_open_log_file (ctx, buffer);
  2715. // In theory this can't cause changes in the prompt
  2716. refresh_prompt (ctx);
  2717. }
  2718. static void
  2719. buffer_remove (struct app_context *ctx, struct buffer *buffer)
  2720. {
  2721. hard_assert (buffer != ctx->current_buffer);
  2722. hard_assert (buffer != ctx->global_buffer);
  2723. input_destroy_buffer (&ctx->input, buffer->input_data);
  2724. buffer->input_data = NULL;
  2725. // And make sure to unlink the buffer from "irc_buffer_map"
  2726. struct server *s = buffer->server;
  2727. if (buffer->channel)
  2728. str_map_set (&s->irc_buffer_map, buffer->channel->name, NULL);
  2729. if (buffer->user)
  2730. str_map_set (&s->irc_buffer_map, buffer->user->nickname, NULL);
  2731. if (buffer == ctx->last_buffer)
  2732. ctx->last_buffer = NULL;
  2733. if (buffer->type == BUFFER_SERVER)
  2734. buffer->server->buffer = NULL;
  2735. str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
  2736. LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
  2737. buffer_unref (buffer);
  2738. refresh_prompt (ctx);
  2739. }
  2740. static void
  2741. buffer_print_read_marker (struct app_context *ctx)
  2742. {
  2743. struct formatter f;
  2744. formatter_init (&f, ctx, NULL);
  2745. formatter_add (&f, "#a-- -- -- ---\n", ATTR_READ_MARKER);
  2746. formatter_flush (&f, stdout, false);
  2747. // Flush the trailing formatting reset item
  2748. fflush (stdout);
  2749. formatter_free (&f);
  2750. }
  2751. static void
  2752. buffer_print_backlog (struct app_context *ctx, struct buffer *buffer)
  2753. {
  2754. // The prompt can take considerable time to redraw
  2755. input_hide (&ctx->input);
  2756. char *buffer_name_localized =
  2757. iconv_xstrdup (ctx->term_from_utf8, buffer->name, -1, NULL);
  2758. print_status ("%s", buffer_name_localized);
  2759. free (buffer_name_localized);
  2760. // That is, minus the buffer switch line and the readline prompt
  2761. int display_limit = MAX (10, g_terminal.lines - 2);
  2762. struct buffer_line *line = buffer->lines_tail;
  2763. int to_display = line != NULL;
  2764. for (; line && line->prev && --display_limit > 0; line = line->prev)
  2765. to_display++;
  2766. // Once we've found where we want to start with the backlog, print it
  2767. int until_marker = to_display - (int) buffer->unseen_messages_count;
  2768. for (; line; line = line->next)
  2769. {
  2770. if (until_marker-- == 0)
  2771. buffer_print_read_marker (ctx);
  2772. buffer_line_display (ctx, line, false);
  2773. }
  2774. buffer->unseen_messages_count = 0;
  2775. buffer->unseen_unimportant_count = 0;
  2776. buffer->highlighted = false;
  2777. // So that it is obvious if the last line in the buffer is not from today
  2778. buffer_update_time (ctx, time (NULL));
  2779. refresh_prompt (ctx);
  2780. input_show (&ctx->input);
  2781. }
  2782. static void
  2783. buffer_activate (struct app_context *ctx, struct buffer *buffer)
  2784. {
  2785. if (ctx->current_buffer == buffer)
  2786. return;
  2787. buffer_print_backlog (ctx, buffer);
  2788. input_switch_buffer (&ctx->input, buffer->input_data);
  2789. // Now at last we can switch the pointers
  2790. ctx->last_buffer = ctx->current_buffer;
  2791. ctx->current_buffer = buffer;
  2792. refresh_prompt (ctx);
  2793. }
  2794. static void
  2795. buffer_merge (struct app_context *ctx,
  2796. struct buffer *buffer, struct buffer *merged)
  2797. {
  2798. // XXX: anything better to do? This situation is arguably rare and I'm
  2799. // not entirely sure what action to take.
  2800. log_full (ctx, NULL, buffer, BUFFER_LINE_STATUS,
  2801. "Buffer #s was merged into this buffer", merged->name);
  2802. // Find all lines from "merged" newer than the newest line in "buffer"
  2803. struct buffer_line *start = merged->lines;
  2804. if (buffer->lines_tail)
  2805. while (start && start->when < buffer->lines_tail->when)
  2806. start = start->next;
  2807. if (!start)
  2808. return;
  2809. // Count how many of them we have
  2810. size_t n = 0;
  2811. for (struct buffer_line *iter = start; iter; iter = iter->next)
  2812. n++;
  2813. struct buffer_line *tail = merged->lines_tail;
  2814. // Cut them from the original buffer
  2815. if (start == merged->lines)
  2816. merged->lines = NULL;
  2817. else if (start->prev)
  2818. start->prev->next = NULL;
  2819. if (start == merged->lines_tail)
  2820. merged->lines_tail = start->prev;
  2821. merged->lines_count -= n;
  2822. // And append them to current lines in the buffer
  2823. buffer->lines_tail->next = start;
  2824. start->prev = buffer->lines_tail;
  2825. buffer->lines_tail = tail;
  2826. buffer->lines_count += n;
  2827. log_full (ctx, NULL, buffer, BUFFER_LINE_STATUS | BUFFER_LINE_SKIP_FILE,
  2828. "End of merged content");
  2829. }
  2830. static void
  2831. buffer_rename (struct app_context *ctx,
  2832. struct buffer *buffer, const char *new_name)
  2833. {
  2834. struct buffer *collision = str_map_find (&ctx->buffers_by_name, new_name);
  2835. if (collision == buffer)
  2836. return;
  2837. hard_assert (!collision);
  2838. str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
  2839. str_map_set (&ctx->buffers_by_name, new_name, buffer);
  2840. buffer_close_log_file (buffer);
  2841. buffer_open_log_file (ctx, buffer);
  2842. free (buffer->name);
  2843. buffer->name = xstrdup (new_name);
  2844. // We might have renamed the current buffer
  2845. refresh_prompt (ctx);
  2846. }
  2847. static void
  2848. buffer_clear (struct buffer *buffer)
  2849. {
  2850. LIST_FOR_EACH (struct buffer_line, iter, buffer->lines)
  2851. buffer_line_destroy (iter);
  2852. buffer->lines = buffer->lines_tail = NULL;
  2853. buffer->lines_count = 0;
  2854. }
  2855. static struct buffer *
  2856. buffer_at_index (struct app_context *ctx, int n)
  2857. {
  2858. int i = 0;
  2859. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  2860. if (++i == n)
  2861. return iter;
  2862. return NULL;
  2863. }
  2864. static struct buffer *
  2865. buffer_next (struct app_context *ctx, int count)
  2866. {
  2867. struct buffer *new_buffer = ctx->current_buffer;
  2868. while (count-- > 0)
  2869. if (!(new_buffer = new_buffer->next))
  2870. new_buffer = ctx->buffers;
  2871. return new_buffer;
  2872. }
  2873. static struct buffer *
  2874. buffer_previous (struct app_context *ctx, int count)
  2875. {
  2876. struct buffer *new_buffer = ctx->current_buffer;
  2877. while (count-- > 0)
  2878. if (!(new_buffer = new_buffer->prev))
  2879. new_buffer = ctx->buffers_tail;
  2880. return new_buffer;
  2881. }
  2882. static bool
  2883. buffer_goto (struct app_context *ctx, int n)
  2884. {
  2885. struct buffer *buffer = buffer_at_index (ctx, n);
  2886. if (!buffer)
  2887. return false;
  2888. buffer_activate (ctx, buffer);
  2889. return true;
  2890. }
  2891. static int
  2892. buffer_get_index (struct app_context *ctx, struct buffer *buffer)
  2893. {
  2894. int index = 1;
  2895. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  2896. {
  2897. if (iter == buffer)
  2898. return index;
  2899. index++;
  2900. }
  2901. return -1;
  2902. }
  2903. static void
  2904. buffer_remove_safe (struct app_context *ctx, struct buffer *buffer)
  2905. {
  2906. if (buffer == ctx->current_buffer)
  2907. buffer_activate (ctx, ctx->last_buffer
  2908. ? ctx->last_buffer
  2909. : buffer_next (ctx, 1));
  2910. buffer_remove (ctx, buffer);
  2911. }
  2912. static void
  2913. init_global_buffer (struct app_context *ctx)
  2914. {
  2915. struct buffer *global = ctx->global_buffer = buffer_new ();
  2916. global->type = BUFFER_GLOBAL;
  2917. global->name = xstrdup (PROGRAM_NAME);
  2918. buffer_add (ctx, global);
  2919. buffer_activate (ctx, global);
  2920. }
  2921. // --- Users, channels ---------------------------------------------------------
  2922. static void
  2923. irc_user_on_destroy (void *object, void *user_data)
  2924. {
  2925. struct user *user = object;
  2926. struct server *s = user_data;
  2927. if (!s->rehashing)
  2928. str_map_set (&s->irc_users, user->nickname, NULL);
  2929. }
  2930. static struct user *
  2931. irc_make_user (struct server *s, char *nickname)
  2932. {
  2933. hard_assert (!str_map_find (&s->irc_users, nickname));
  2934. struct user *user = user_new ();
  2935. (void) user_weak_ref (user, irc_user_on_destroy, s);
  2936. user->nickname = nickname;
  2937. str_map_set (&s->irc_users, user->nickname, user);
  2938. return user;
  2939. }
  2940. struct user *
  2941. irc_get_or_make_user (struct server *s, const char *nickname)
  2942. {
  2943. struct user *user = str_map_find (&s->irc_users, nickname);
  2944. if (user)
  2945. return user_ref (user);
  2946. return irc_make_user (s, xstrdup (nickname));
  2947. }
  2948. static struct buffer *
  2949. irc_get_or_make_user_buffer (struct server *s, const char *nickname)
  2950. {
  2951. struct buffer *buffer = str_map_find (&s->irc_buffer_map, nickname);
  2952. if (buffer)
  2953. return buffer;
  2954. struct user *user = irc_get_or_make_user (s, nickname);
  2955. // Open a new buffer for the user
  2956. buffer = buffer_new ();
  2957. buffer->type = BUFFER_PM;
  2958. buffer->name = xstrdup_printf ("%s.%s", s->name, nickname);
  2959. buffer->server = s;
  2960. buffer->user = user;
  2961. str_map_set (&s->irc_buffer_map, user->nickname, buffer);
  2962. buffer_add (s->ctx, buffer);
  2963. return buffer;
  2964. }
  2965. static void
  2966. irc_get_channel_user_prefix (struct server *s,
  2967. struct channel_user *channel_user, struct str *output)
  2968. {
  2969. if (s->ctx->show_all_prefixes)
  2970. str_append (output, channel_user->prefixes.str);
  2971. else if (channel_user->prefixes.len)
  2972. str_append_c (output, channel_user->prefixes.str[0]);
  2973. }
  2974. static bool
  2975. irc_channel_is_joined (struct channel *channel)
  2976. {
  2977. // TODO: find a better way of checking if we're on a channel
  2978. return !!channel->users;
  2979. }
  2980. // Note that this eats the user reference
  2981. static void
  2982. irc_channel_link_user (struct channel *channel, struct user *user,
  2983. const char *prefixes)
  2984. {
  2985. struct user_channel *user_channel = user_channel_new ();
  2986. user_channel->channel = channel;
  2987. LIST_PREPEND (user->channels, user_channel);
  2988. struct channel_user *channel_user = channel_user_new ();
  2989. channel_user->user = user;
  2990. str_append (&channel_user->prefixes, prefixes);
  2991. LIST_PREPEND (channel->users, channel_user);
  2992. }
  2993. static void
  2994. irc_channel_unlink_user
  2995. (struct channel *channel, struct channel_user *channel_user)
  2996. {
  2997. // First destroy the user's weak references to the channel
  2998. struct user *user = channel_user->user;
  2999. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  3000. if (iter->channel == channel)
  3001. {
  3002. LIST_UNLINK (user->channels, iter);
  3003. user_channel_destroy (iter);
  3004. }
  3005. // Then just unlink the user from the channel
  3006. LIST_UNLINK (channel->users, channel_user);
  3007. channel_user_destroy (channel_user);
  3008. }
  3009. static void
  3010. irc_channel_on_destroy (void *object, void *user_data)
  3011. {
  3012. struct channel *channel = object;
  3013. struct server *s = user_data;
  3014. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3015. irc_channel_unlink_user (channel, iter);
  3016. if (!s->rehashing)
  3017. str_map_set (&s->irc_channels, channel->name, NULL);
  3018. }
  3019. static struct channel *
  3020. irc_make_channel (struct server *s, char *name)
  3021. {
  3022. hard_assert (!str_map_find (&s->irc_channels, name));
  3023. struct channel *channel = channel_new ();
  3024. (void) channel_weak_ref (channel, irc_channel_on_destroy, s);
  3025. channel->name = name;
  3026. channel->topic = NULL;
  3027. str_map_set (&s->irc_channels, channel->name, channel);
  3028. return channel;
  3029. }
  3030. static struct channel_user *
  3031. irc_channel_get_user (struct channel *channel, struct user *user)
  3032. {
  3033. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3034. if (iter->user == user)
  3035. return iter;
  3036. return NULL;
  3037. }
  3038. static void
  3039. irc_remove_user_from_channel (struct user *user, struct channel *channel)
  3040. {
  3041. struct channel_user *channel_user = irc_channel_get_user (channel, user);
  3042. if (channel_user)
  3043. irc_channel_unlink_user (channel, channel_user);
  3044. }
  3045. static void
  3046. irc_left_channel (struct channel *channel)
  3047. {
  3048. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3049. irc_channel_unlink_user (channel, iter);
  3050. }
  3051. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3052. static void
  3053. remove_conflicting_buffer (struct server *s, struct buffer *buffer)
  3054. {
  3055. log_server_status (s, s->buffer,
  3056. "Removed buffer #s because of casemapping conflict", buffer->name);
  3057. if (s->ctx->current_buffer == buffer)
  3058. buffer_activate (s->ctx, s->buffer);
  3059. buffer_remove (s->ctx, buffer);
  3060. }
  3061. static void
  3062. irc_try_readd_user (struct server *s,
  3063. struct user *user, struct buffer *buffer)
  3064. {
  3065. if (str_map_find (&s->irc_users, user->nickname))
  3066. {
  3067. // Remove user from all channels and destroy any PM buffer
  3068. user_ref (user);
  3069. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  3070. irc_remove_user_from_channel (user, iter->channel);
  3071. if (buffer)
  3072. remove_conflicting_buffer (s, buffer);
  3073. user_unref (user);
  3074. }
  3075. else
  3076. {
  3077. str_map_set (&s->irc_users, user->nickname, user);
  3078. str_map_set (&s->irc_buffer_map, user->nickname, buffer);
  3079. }
  3080. }
  3081. static void
  3082. irc_try_readd_channel (struct server *s,
  3083. struct channel *channel, struct buffer *buffer)
  3084. {
  3085. if (str_map_find (&s->irc_channels, channel->name))
  3086. {
  3087. // Remove all users from channel and destroy any channel buffer
  3088. channel_ref (channel);
  3089. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3090. irc_channel_unlink_user (channel, iter);
  3091. if (buffer)
  3092. remove_conflicting_buffer (s, buffer);
  3093. channel_unref (channel);
  3094. }
  3095. else
  3096. {
  3097. str_map_set (&s->irc_channels, channel->name, channel);
  3098. str_map_set (&s->irc_buffer_map, channel->name, buffer);
  3099. }
  3100. }
  3101. static void
  3102. irc_rehash_and_fix_conflicts (struct server *s)
  3103. {
  3104. // Save the old maps and initialize new ones
  3105. struct str_map old_users = s->irc_users;
  3106. struct str_map old_channels = s->irc_channels;
  3107. struct str_map old_buffer_map = s->irc_buffer_map;
  3108. str_map_init (&s->irc_users);
  3109. str_map_init (&s->irc_channels);
  3110. str_map_init (&s->irc_buffer_map);
  3111. s->irc_users .key_xfrm = s->irc_strxfrm;
  3112. s->irc_channels .key_xfrm = s->irc_strxfrm;
  3113. s->irc_buffer_map.key_xfrm = s->irc_strxfrm;
  3114. // Prevent channels and users from unsetting themselves
  3115. // from server maps upon removing the last reference to them
  3116. s->rehashing = true;
  3117. // XXX: to be perfectly sure, we should also check
  3118. // whether any users collide with channels and vice versa
  3119. // Our own user always takes priority, add him first
  3120. if (s->irc_user)
  3121. irc_try_readd_user (s, s->irc_user,
  3122. str_map_find (&old_buffer_map, s->irc_user->nickname));
  3123. struct str_map_iter iter;
  3124. struct user *user;
  3125. struct channel *channel;
  3126. str_map_iter_init (&iter, &old_users);
  3127. while ((user = str_map_iter_next (&iter)))
  3128. irc_try_readd_user (s, user,
  3129. str_map_find (&old_buffer_map, user->nickname));
  3130. str_map_iter_init (&iter, &old_channels);
  3131. while ((channel = str_map_iter_next (&iter)))
  3132. irc_try_readd_channel (s, channel,
  3133. str_map_find (&old_buffer_map, channel->name));
  3134. // Hopefully we've either moved or destroyed all the old content
  3135. s->rehashing = false;
  3136. str_map_free (&old_users);
  3137. str_map_free (&old_channels);
  3138. str_map_free (&old_buffer_map);
  3139. }
  3140. static void
  3141. irc_set_casemapping (struct server *s,
  3142. irc_tolower_fn tolower, irc_strxfrm_fn strxfrm)
  3143. {
  3144. if (tolower == s->irc_tolower
  3145. && strxfrm == s->irc_strxfrm)
  3146. return;
  3147. s->irc_tolower = tolower;
  3148. s->irc_strxfrm = strxfrm;
  3149. // Ideally we would never have to do this but I can't think of a workaround
  3150. irc_rehash_and_fix_conflicts (s);
  3151. }
  3152. // --- Core functionality ------------------------------------------------------
  3153. static bool
  3154. irc_is_connected (struct server *s)
  3155. {
  3156. return s->state != IRC_DISCONNECTED && s->state != IRC_CONNECTING;
  3157. }
  3158. static void
  3159. irc_update_poller (struct server *s, const struct pollfd *pfd)
  3160. {
  3161. int new_events = s->transport->get_poll_events (s);
  3162. hard_assert (new_events != 0);
  3163. if (!pfd || pfd->events != new_events)
  3164. poller_fd_set (&s->socket_event, new_events);
  3165. }
  3166. static void
  3167. irc_cancel_timers (struct server *s)
  3168. {
  3169. poller_timer_reset (&s->timeout_tmr);
  3170. poller_timer_reset (&s->ping_tmr);
  3171. poller_timer_reset (&s->reconnect_tmr);
  3172. poller_timer_reset (&s->autojoin_tmr);
  3173. }
  3174. static void
  3175. irc_reset_connection_timeouts (struct server *s)
  3176. {
  3177. poller_timer_set (&s->timeout_tmr, 3 * 60 * 1000);
  3178. poller_timer_set (&s->ping_tmr, (3 * 60 + 30) * 1000);
  3179. poller_timer_reset (&s->reconnect_tmr);
  3180. }
  3181. static int64_t
  3182. irc_get_reconnect_delay (struct server *s)
  3183. {
  3184. int64_t delay = get_config_integer (s->config, "reconnect_delay");
  3185. int64_t delay_factor = get_config_integer (s->ctx->config.root,
  3186. "behaviour.reconnect_delay_growing");
  3187. for (unsigned i = 0; i < s->reconnect_attempt; i++)
  3188. {
  3189. if (delay_factor && delay > INT64_MAX / delay_factor)
  3190. break;
  3191. delay *= delay_factor;
  3192. }
  3193. int64_t delay_max = get_config_integer (s->ctx->config.root,
  3194. "behaviour.reconnect_delay_max");
  3195. return MIN (delay, delay_max);
  3196. }
  3197. static void
  3198. irc_queue_reconnect (struct server *s)
  3199. {
  3200. // As long as the user wants us to, that is
  3201. if (!get_config_boolean (s->config, "reconnect"))
  3202. return;
  3203. // XXX: maybe add a state for when a connect is queued?
  3204. hard_assert (s->state == IRC_DISCONNECTED);
  3205. int64_t delay = irc_get_reconnect_delay (s);
  3206. s->reconnect_attempt++;
  3207. log_server_status (s, s->buffer,
  3208. "Trying to reconnect in #&s seconds...",
  3209. xstrdup_printf ("%" PRId64, delay));
  3210. poller_timer_set (&s->reconnect_tmr, delay * 1000);
  3211. }
  3212. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3213. static void irc_send (struct server *s,
  3214. const char *format, ...) ATTRIBUTE_PRINTF (2, 3);
  3215. static void
  3216. irc_send (struct server *s, const char *format, ...)
  3217. {
  3218. if (!soft_assert (irc_is_connected (s)))
  3219. {
  3220. log_server_debug (s, "sending a message to a dead server connection");
  3221. return;
  3222. }
  3223. if (s->state == IRC_CLOSING
  3224. || s->state == IRC_HALF_CLOSED)
  3225. return;
  3226. va_list ap;
  3227. va_start (ap, format);
  3228. struct str str;
  3229. str_init (&str);
  3230. str_append_vprintf (&str, format, ap);
  3231. va_end (ap);
  3232. log_server_debug (s, "#a<< \"#S\"#r", ATTR_PART, str.str);
  3233. str_append_str (&s->write_buffer, &str);
  3234. str_free (&str);
  3235. str_append (&s->write_buffer, "\r\n");
  3236. irc_update_poller (s, NULL);
  3237. }
  3238. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3239. static void
  3240. irc_real_shutdown (struct server *s)
  3241. {
  3242. hard_assert (irc_is_connected (s) && s->state != IRC_HALF_CLOSED);
  3243. if (s->transport
  3244. && s->transport->in_before_shutdown)
  3245. s->transport->in_before_shutdown (s);
  3246. while (shutdown (s->socket, SHUT_WR) == -1)
  3247. if (!soft_assert (errno == EINTR))
  3248. break;
  3249. s->state = IRC_HALF_CLOSED;
  3250. }
  3251. static void
  3252. irc_shutdown (struct server *s)
  3253. {
  3254. if (s->state == IRC_CLOSING
  3255. || s->state == IRC_HALF_CLOSED)
  3256. return;
  3257. // TODO: set a timer to cut the connection if we don't receive an EOF
  3258. s->state = IRC_CLOSING;
  3259. // Either there's still some data in the write buffer and we wait
  3260. // until they're sent, or we send an EOF to the server right away
  3261. if (!s->write_buffer.len)
  3262. irc_real_shutdown (s);
  3263. }
  3264. static void
  3265. irc_destroy_connector (struct server *s)
  3266. {
  3267. if (s->connector)
  3268. connector_free (s->connector);
  3269. free (s->connector);
  3270. s->connector = NULL;
  3271. if (s->socks_conn)
  3272. socks_connector_free (s->socks_conn);
  3273. free (s->socks_conn);
  3274. s->socks_conn = NULL;
  3275. // Not connecting anymore
  3276. s->state = IRC_DISCONNECTED;
  3277. }
  3278. static void
  3279. try_finish_quit (struct app_context *ctx)
  3280. {
  3281. if (!ctx->quitting)
  3282. return;
  3283. struct str_map_iter iter;
  3284. str_map_iter_init (&iter, &ctx->servers);
  3285. bool disconnected_all = true;
  3286. struct server *s;
  3287. while ((s = str_map_iter_next (&iter)))
  3288. if (irc_is_connected (s))
  3289. disconnected_all = false;
  3290. if (disconnected_all)
  3291. ctx->polling = false;
  3292. }
  3293. static void
  3294. initiate_quit (struct app_context *ctx)
  3295. {
  3296. log_global_status (ctx, "Shutting down");
  3297. // Destroy the user interface
  3298. input_stop (&ctx->input);
  3299. // Initiate a connection close
  3300. struct str_map_iter iter;
  3301. str_map_iter_init (&iter, &ctx->servers);
  3302. struct server *s;
  3303. while ((s = str_map_iter_next (&iter)))
  3304. {
  3305. // There may be a timer set to reconnect to the server
  3306. poller_timer_reset (&s->reconnect_tmr);
  3307. if (irc_is_connected (s))
  3308. {
  3309. irc_shutdown (s);
  3310. s->manual_disconnect = true;
  3311. }
  3312. else if (s->state == IRC_CONNECTING)
  3313. irc_destroy_connector (s);
  3314. }
  3315. ctx->quitting = true;
  3316. try_finish_quit (ctx);
  3317. }
  3318. static void
  3319. irc_destroy_transport (struct server *s)
  3320. {
  3321. if (s->transport
  3322. && s->transport->cleanup)
  3323. s->transport->cleanup (s);
  3324. s->transport = NULL;
  3325. xclose (s->socket);
  3326. s->socket = -1;
  3327. s->state = IRC_DISCONNECTED;
  3328. s->socket_event.closed = true;
  3329. poller_fd_reset (&s->socket_event);
  3330. str_reset (&s->read_buffer);
  3331. str_reset (&s->write_buffer);
  3332. }
  3333. static void
  3334. irc_destroy_state (struct server *s)
  3335. {
  3336. struct str_map_iter iter;
  3337. str_map_iter_init (&iter, &s->irc_channels);
  3338. struct channel *channel;
  3339. while ((channel = str_map_iter_next (&iter)))
  3340. irc_left_channel (channel);
  3341. if (s->irc_user)
  3342. {
  3343. user_unref (s->irc_user);
  3344. s->irc_user = NULL;
  3345. }
  3346. str_reset (&s->irc_user_mode);
  3347. free (s->irc_user_host);
  3348. s->irc_user_host = NULL;
  3349. s->cap_echo_message = false;
  3350. // Need to call this before server_init_specifics()
  3351. irc_set_casemapping (s, irc_tolower, irc_strxfrm);
  3352. server_free_specifics (s);
  3353. server_init_specifics (s);
  3354. }
  3355. static void
  3356. irc_disconnect (struct server *s)
  3357. {
  3358. hard_assert (irc_is_connected (s));
  3359. struct str_map_iter iter;
  3360. str_map_iter_init (&iter, &s->irc_buffer_map);
  3361. struct buffer *buffer;
  3362. while ((buffer = str_map_iter_next (&iter)))
  3363. log_server (s, buffer, BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT,
  3364. "Disconnected from server");
  3365. irc_cancel_timers (s);
  3366. irc_destroy_transport (s);
  3367. irc_destroy_state (s);
  3368. // Take any relevant actions
  3369. if (s->ctx->quitting)
  3370. try_finish_quit (s->ctx);
  3371. else if (s->manual_disconnect)
  3372. s->manual_disconnect = false;
  3373. else
  3374. {
  3375. s->reconnect_attempt = 0;
  3376. irc_queue_reconnect (s);
  3377. }
  3378. refresh_prompt (s->ctx);
  3379. }
  3380. static void
  3381. irc_initiate_disconnect (struct server *s, const char *reason)
  3382. {
  3383. hard_assert (irc_is_connected (s));
  3384. s->manual_disconnect = true;
  3385. if (reason)
  3386. irc_send (s, "QUIT :%s", reason);
  3387. else
  3388. irc_send (s, "QUIT :%s", PROGRAM_NAME " " PROGRAM_VERSION);
  3389. }
  3390. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3391. static void
  3392. on_irc_ping_timeout (void *user_data)
  3393. {
  3394. struct server *s = user_data;
  3395. log_server_error (s, s->buffer, "Connection timeout");
  3396. irc_disconnect (s);
  3397. }
  3398. static void
  3399. on_irc_timeout (void *user_data)
  3400. {
  3401. // Provoke a response from the server
  3402. struct server *s = user_data;
  3403. irc_send (s, "PING :%" PRIi64, (int64_t) time (NULL));
  3404. }
  3405. static void
  3406. on_irc_autojoin_timeout (void *user_data)
  3407. {
  3408. struct server *s = user_data;
  3409. // Since we may not have information from RPL_ISUPPORT yet,
  3410. // it's our safest bet to send the channels one at a time
  3411. struct str_map joins_sent;
  3412. str_map_init (&joins_sent);
  3413. // We don't know the casemapping yet either, however ASCII should do
  3414. joins_sent.key_xfrm = tolower_ascii_strxfrm;
  3415. // First join autojoin channels in their given order
  3416. const char *autojoin = get_config_string (s->config, "autojoin");
  3417. if (autojoin)
  3418. {
  3419. struct str_vector v;
  3420. str_vector_init (&v);
  3421. split_str (autojoin, ",", &v);
  3422. for (size_t i = 0; i < v.len; i++)
  3423. {
  3424. irc_send (s, "JOIN :%s", v.vector[i]);
  3425. str_map_set (&joins_sent, v.vector[i], (void *) 1);
  3426. }
  3427. str_vector_free (&v);
  3428. }
  3429. // Then also rejoin any channels from the last disconnect
  3430. struct str_map_iter iter;
  3431. str_map_iter_init (&iter, &s->irc_channels);
  3432. struct channel *channel;
  3433. while ((channel = str_map_iter_next (&iter)))
  3434. if (!channel->left_manually
  3435. && !str_map_find (&joins_sent, channel->name))
  3436. irc_send (s, "JOIN :%s", channel->name);
  3437. str_map_free (&joins_sent);
  3438. }
  3439. // --- Server I/O --------------------------------------------------------------
  3440. static char *
  3441. irc_process_hooks (struct server *s, char *input)
  3442. {
  3443. log_server_debug (s, "#a>> \"#S\"#r", ATTR_JOIN, input);
  3444. LIST_FOR_EACH (struct hook, iter, s->ctx->irc_hooks)
  3445. {
  3446. struct irc_hook *hook = (struct irc_hook *) iter;
  3447. char *processed = hook->vtable->filter (hook, s, input);
  3448. if (input == processed)
  3449. continue;
  3450. if (processed == NULL)
  3451. {
  3452. log_server_debug (s, "#a>= #s#r", ATTR_JOIN, "thrown away by hook");
  3453. return NULL;
  3454. }
  3455. log_server_debug (s, "#a>= \"#S\"#r", ATTR_JOIN, (input = processed));
  3456. }
  3457. return input;
  3458. }
  3459. static void irc_process_message
  3460. (const struct irc_message *msg, struct server *s);
  3461. static void
  3462. irc_process_buffer_custom (struct server *s, struct str *buf)
  3463. {
  3464. const char *start = buf->str, *end = start + buf->len;
  3465. for (const char *p = start; p + 1 < end; p++)
  3466. {
  3467. // Split the input on newlines
  3468. if (p[0] != '\r' || p[1] != '\n')
  3469. continue;
  3470. char *processed = irc_process_hooks (s, xstrndup (start, p - start));
  3471. start = p + 2;
  3472. if (!processed)
  3473. continue;
  3474. struct irc_message msg;
  3475. irc_parse_message (&msg, processed);
  3476. irc_process_message (&msg, s);
  3477. irc_free_message (&msg);
  3478. free (processed);
  3479. }
  3480. str_remove_slice (buf, 0, start - buf->str);
  3481. }
  3482. static enum transport_io_result
  3483. irc_try_read (struct server *s)
  3484. {
  3485. enum transport_io_result result = s->transport->try_read (s);
  3486. if (s->read_buffer.len >= (1 << 20))
  3487. {
  3488. // XXX: this is stupid; if anything, count it in dependence of time
  3489. log_server_error (s, s->buffer,
  3490. "The IRC server seems to spew out data frantically");
  3491. return TRANSPORT_IO_ERROR;
  3492. }
  3493. if (s->read_buffer.len)
  3494. irc_process_buffer_custom (s, &s->read_buffer);
  3495. return result;
  3496. }
  3497. static enum transport_io_result
  3498. irc_try_write (struct server *s)
  3499. {
  3500. enum transport_io_result result = s->transport->try_write (s);
  3501. if (result == TRANSPORT_IO_OK)
  3502. {
  3503. // If we're flushing the write buffer and our job is complete, we send
  3504. // an EOF to the server, changing the state to IRC_HALF_CLOSED
  3505. if (s->state == IRC_CLOSING && !s->write_buffer.len)
  3506. irc_real_shutdown (s);
  3507. }
  3508. return result;
  3509. }
  3510. static bool
  3511. irc_try_read_write (struct server *s)
  3512. {
  3513. enum transport_io_result read_result;
  3514. enum transport_io_result write_result;
  3515. if ((read_result = irc_try_read (s)) == TRANSPORT_IO_ERROR
  3516. || (write_result = irc_try_write (s)) == TRANSPORT_IO_ERROR)
  3517. {
  3518. log_server_error (s, s->buffer, "Server connection failed");
  3519. return false;
  3520. }
  3521. // FIXME: this may probably fire multiple times when we're flushing,
  3522. // we should probably store a flag next to the state
  3523. if (read_result == TRANSPORT_IO_EOF
  3524. || write_result == TRANSPORT_IO_EOF)
  3525. log_server_error (s, s->buffer, "Server closed the connection");
  3526. // If the write needs to read and we receive an EOF, we can't flush
  3527. if (write_result == TRANSPORT_IO_EOF)
  3528. return false;
  3529. if (read_result == TRANSPORT_IO_EOF)
  3530. {
  3531. // Eventually initiate shutdown to flush the write buffer
  3532. irc_shutdown (s);
  3533. // If there's nothing to write, we can disconnect now
  3534. if (s->state == IRC_HALF_CLOSED)
  3535. return false;
  3536. }
  3537. return true;
  3538. }
  3539. static void
  3540. on_irc_ready (const struct pollfd *pfd, struct server *s)
  3541. {
  3542. if (irc_try_read_write (s))
  3543. {
  3544. // XXX: shouldn't we rather wait for PONG messages?
  3545. irc_reset_connection_timeouts (s);
  3546. irc_update_poller (s, pfd);
  3547. }
  3548. else
  3549. // We don't want to keep the socket anymore
  3550. irc_disconnect (s);
  3551. }
  3552. // --- Plain transport ---------------------------------------------------------
  3553. static enum transport_io_result
  3554. transport_plain_try_read (struct server *s)
  3555. {
  3556. struct str *buf = &s->read_buffer;
  3557. ssize_t n_read;
  3558. while (true)
  3559. {
  3560. str_ensure_space (buf, 512);
  3561. n_read = recv (s->socket, buf->str + buf->len,
  3562. buf->alloc - buf->len - 1 /* null byte */, 0);
  3563. if (n_read > 0)
  3564. {
  3565. buf->str[buf->len += n_read] = '\0';
  3566. continue;
  3567. }
  3568. if (n_read == 0)
  3569. return TRANSPORT_IO_EOF;
  3570. if (errno == EAGAIN)
  3571. return TRANSPORT_IO_OK;
  3572. if (errno == EINTR)
  3573. continue;
  3574. LOG_LIBC_FAILURE ("recv");
  3575. return TRANSPORT_IO_ERROR;
  3576. }
  3577. }
  3578. static enum transport_io_result
  3579. transport_plain_try_write (struct server *s)
  3580. {
  3581. struct str *buf = &s->write_buffer;
  3582. ssize_t n_written;
  3583. while (buf->len)
  3584. {
  3585. n_written = send (s->socket, buf->str, buf->len, 0);
  3586. if (n_written >= 0)
  3587. {
  3588. str_remove_slice (buf, 0, n_written);
  3589. continue;
  3590. }
  3591. if (errno == EAGAIN)
  3592. return TRANSPORT_IO_OK;
  3593. if (errno == EINTR)
  3594. continue;
  3595. LOG_LIBC_FAILURE ("send");
  3596. return TRANSPORT_IO_ERROR;
  3597. }
  3598. return TRANSPORT_IO_OK;
  3599. }
  3600. static int
  3601. transport_plain_get_poll_events (struct server *s)
  3602. {
  3603. int events = POLLIN;
  3604. if (s->write_buffer.len)
  3605. events |= POLLOUT;
  3606. return events;
  3607. }
  3608. static struct transport g_transport_plain =
  3609. {
  3610. .try_read = transport_plain_try_read,
  3611. .try_write = transport_plain_try_write,
  3612. .get_poll_events = transport_plain_get_poll_events,
  3613. };
  3614. // --- TLS transport -----------------------------------------------------------
  3615. struct transport_tls_data
  3616. {
  3617. SSL_CTX *ssl_ctx; ///< SSL context
  3618. SSL *ssl; ///< SSL connection
  3619. bool ssl_rx_want_tx; ///< SSL_read() wants to write
  3620. bool ssl_tx_want_rx; ///< SSL_write() wants to read
  3621. };
  3622. /// The index in SSL_CTX user data for a reference to the server
  3623. static int g_transport_tls_data_index = -1;
  3624. static int
  3625. transport_tls_verify_callback (int preverify_ok, X509_STORE_CTX *ctx)
  3626. {
  3627. SSL *ssl = X509_STORE_CTX_get_ex_data
  3628. (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ());
  3629. struct server *s = SSL_CTX_get_ex_data
  3630. (SSL_get_SSL_CTX (ssl), g_transport_tls_data_index);
  3631. X509 *cert = X509_STORE_CTX_get_current_cert (ctx);
  3632. char *subject = X509_NAME_oneline (X509_get_subject_name (cert), NULL, 0);
  3633. char *issuer = X509_NAME_oneline (X509_get_issuer_name (cert), NULL, 0);
  3634. log_server_status (s, s->buffer, "Certificate subject: #s", subject);
  3635. log_server_status (s, s->buffer, "Certificate issuer: #s", issuer);
  3636. if (!preverify_ok)
  3637. {
  3638. log_server_error (s, s->buffer,
  3639. "Certificate verification failed: #s",
  3640. X509_verify_cert_error_string (X509_STORE_CTX_get_error (ctx)));
  3641. }
  3642. free (subject);
  3643. free (issuer);
  3644. return preverify_ok;
  3645. }
  3646. static bool
  3647. transport_tls_init_ctx (struct server *s, SSL_CTX *ssl_ctx, struct error **e)
  3648. {
  3649. bool verify = get_config_boolean (s->config, "tls_verify");
  3650. SSL_CTX_set_verify (ssl_ctx, verify ? SSL_VERIFY_PEER : SSL_VERIFY_NONE,
  3651. transport_tls_verify_callback);
  3652. if (g_transport_tls_data_index == -1)
  3653. g_transport_tls_data_index =
  3654. SSL_CTX_get_ex_new_index (0, "server", NULL, NULL, NULL);
  3655. SSL_CTX_set_ex_data (ssl_ctx, g_transport_tls_data_index, s);
  3656. const char *ciphers = get_config_string (s->config, "tls_ciphers");
  3657. if (ciphers && !SSL_CTX_set_cipher_list (ssl_ctx, ciphers))
  3658. log_server_error (s, s->buffer,
  3659. "Failed to select any cipher from the cipher list");
  3660. SSL_CTX_set_mode (ssl_ctx,
  3661. SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
  3662. // Disable deprecated protocols (see RFC 7568)
  3663. SSL_CTX_set_options (ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
  3664. const char *ca_file = get_config_string (s->config, "tls_ca_file");
  3665. const char *ca_path = get_config_string (s->config, "tls_ca_path");
  3666. ERR_clear_error ();
  3667. struct error *error = NULL;
  3668. if (ca_file || ca_path)
  3669. {
  3670. if (SSL_CTX_load_verify_locations (ssl_ctx, ca_file, ca_path))
  3671. return true;
  3672. error_set (&error, "%s: %s",
  3673. "Failed to set locations for the CA certificate bundle",
  3674. ERR_reason_error_string (ERR_get_error ()));
  3675. goto ca_error;
  3676. }
  3677. if (!SSL_CTX_set_default_verify_paths (ssl_ctx))
  3678. {
  3679. error_set (&error, "%s: %s",
  3680. "Couldn't load the default CA certificate bundle",
  3681. ERR_reason_error_string (ERR_get_error ()));
  3682. goto ca_error;
  3683. }
  3684. return true;
  3685. ca_error:
  3686. if (verify)
  3687. {
  3688. error_propagate (e, error);
  3689. return false;
  3690. }
  3691. // Only inform the user if we're not actually verifying
  3692. log_server_error (s, s->buffer, "#s", error->message);
  3693. error_free (error);
  3694. return true;
  3695. }
  3696. static bool
  3697. transport_tls_init_cert (struct server *s, SSL *ssl, struct error **e)
  3698. {
  3699. const char *tls_cert = get_config_string (s->config, "tls_cert");
  3700. if (!tls_cert)
  3701. return true;
  3702. ERR_clear_error ();
  3703. bool result = false;
  3704. char *path = resolve_filename (tls_cert, resolve_relative_config_filename);
  3705. if (!path)
  3706. error_set (e, "%s: %s", "Cannot open file", tls_cert);
  3707. // XXX: perhaps we should read the file ourselves for better messages
  3708. else if (!SSL_use_certificate_file (ssl, path, SSL_FILETYPE_PEM)
  3709. || !SSL_use_PrivateKey_file (ssl, path, SSL_FILETYPE_PEM))
  3710. error_set (e, "%s: %s", "Setting the TLS client certificate failed",
  3711. ERR_reason_error_string (ERR_get_error ()));
  3712. else
  3713. result = true;
  3714. free (path);
  3715. return result;
  3716. }
  3717. static bool
  3718. transport_tls_init (struct server *s, struct error **e)
  3719. {
  3720. ERR_clear_error ();
  3721. struct error *error = NULL;
  3722. SSL_CTX *ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
  3723. if (!ssl_ctx)
  3724. goto error_ssl_1;
  3725. if (!transport_tls_init_ctx (s, ssl_ctx, &error))
  3726. goto error_ssl_2;
  3727. SSL *ssl = SSL_new (ssl_ctx);
  3728. if (!ssl)
  3729. goto error_ssl_2;
  3730. if (!transport_tls_init_cert (s, ssl, &error))
  3731. {
  3732. // XXX: is this a reason to abort the connection?
  3733. log_server_error (s, s->buffer, "#s", error->message);
  3734. error_free (error);
  3735. error = NULL;
  3736. }
  3737. SSL_set_connect_state (ssl);
  3738. if (!SSL_set_fd (ssl, s->socket))
  3739. goto error_ssl_3;
  3740. struct transport_tls_data *data = xcalloc (1, sizeof *data);
  3741. data->ssl_ctx = ssl_ctx;
  3742. data->ssl = ssl;
  3743. // Forces a handshake even if neither side wants to transmit data
  3744. data->ssl_rx_want_tx = true;
  3745. s->transport_data = data;
  3746. return true;
  3747. error_ssl_3:
  3748. SSL_free (ssl);
  3749. error_ssl_2:
  3750. SSL_CTX_free (ssl_ctx);
  3751. error_ssl_1:
  3752. if (!error)
  3753. error_set (&error, "%s: %s", "Could not initialize TLS",
  3754. ERR_reason_error_string (ERR_get_error ()));
  3755. error_propagate (e, error);
  3756. return false;
  3757. }
  3758. static void
  3759. transport_tls_cleanup (struct server *s)
  3760. {
  3761. struct transport_tls_data *data = s->transport_data;
  3762. if (data->ssl)
  3763. SSL_free (data->ssl);
  3764. if (data->ssl_ctx)
  3765. SSL_CTX_free (data->ssl_ctx);
  3766. free (data);
  3767. }
  3768. static enum transport_io_result
  3769. transport_tls_try_read (struct server *s)
  3770. {
  3771. struct transport_tls_data *data = s->transport_data;
  3772. if (data->ssl_tx_want_rx)
  3773. return TRANSPORT_IO_OK;
  3774. struct str *buf = &s->read_buffer;
  3775. data->ssl_rx_want_tx = false;
  3776. while (true)
  3777. {
  3778. ERR_clear_error ();
  3779. str_ensure_space (buf, 512);
  3780. int n_read = SSL_read (data->ssl, buf->str + buf->len,
  3781. buf->alloc - buf->len - 1 /* null byte */);
  3782. const char *error_info = NULL;
  3783. switch (xssl_get_error (data->ssl, n_read, &error_info))
  3784. {
  3785. case SSL_ERROR_NONE:
  3786. buf->str[buf->len += n_read] = '\0';
  3787. continue;
  3788. case SSL_ERROR_ZERO_RETURN:
  3789. return TRANSPORT_IO_EOF;
  3790. case SSL_ERROR_WANT_READ:
  3791. return TRANSPORT_IO_OK;
  3792. case SSL_ERROR_WANT_WRITE:
  3793. data->ssl_rx_want_tx = true;
  3794. return TRANSPORT_IO_OK;
  3795. case XSSL_ERROR_TRY_AGAIN:
  3796. continue;
  3797. default:
  3798. LOG_FUNC_FAILURE ("SSL_read", error_info);
  3799. return TRANSPORT_IO_ERROR;
  3800. }
  3801. }
  3802. }
  3803. static enum transport_io_result
  3804. transport_tls_try_write (struct server *s)
  3805. {
  3806. struct transport_tls_data *data = s->transport_data;
  3807. if (data->ssl_rx_want_tx)
  3808. return TRANSPORT_IO_OK;
  3809. struct str *buf = &s->write_buffer;
  3810. data->ssl_tx_want_rx = false;
  3811. while (buf->len)
  3812. {
  3813. ERR_clear_error ();
  3814. int n_written = SSL_write (data->ssl, buf->str, buf->len);
  3815. const char *error_info = NULL;
  3816. switch (xssl_get_error (data->ssl, n_written, &error_info))
  3817. {
  3818. case SSL_ERROR_NONE:
  3819. str_remove_slice (buf, 0, n_written);
  3820. continue;
  3821. case SSL_ERROR_ZERO_RETURN:
  3822. return TRANSPORT_IO_EOF;
  3823. case SSL_ERROR_WANT_WRITE:
  3824. return TRANSPORT_IO_OK;
  3825. case SSL_ERROR_WANT_READ:
  3826. data->ssl_tx_want_rx = true;
  3827. return TRANSPORT_IO_OK;
  3828. case XSSL_ERROR_TRY_AGAIN:
  3829. continue;
  3830. default:
  3831. LOG_FUNC_FAILURE ("SSL_write", error_info);
  3832. return TRANSPORT_IO_ERROR;
  3833. }
  3834. }
  3835. return TRANSPORT_IO_OK;
  3836. }
  3837. static int
  3838. transport_tls_get_poll_events (struct server *s)
  3839. {
  3840. struct transport_tls_data *data = s->transport_data;
  3841. int events = POLLIN;
  3842. if (s->write_buffer.len || data->ssl_rx_want_tx)
  3843. events |= POLLOUT;
  3844. // While we're waiting for an opposite event, we ignore the original
  3845. if (data->ssl_rx_want_tx) events &= ~POLLIN;
  3846. if (data->ssl_tx_want_rx) events &= ~POLLOUT;
  3847. return events;
  3848. }
  3849. static void
  3850. transport_tls_in_before_shutdown (struct server *s)
  3851. {
  3852. struct transport_tls_data *data = s->transport_data;
  3853. (void) SSL_shutdown (data->ssl);
  3854. }
  3855. static struct transport g_transport_tls =
  3856. {
  3857. .init = transport_tls_init,
  3858. .cleanup = transport_tls_cleanup,
  3859. .try_read = transport_tls_try_read,
  3860. .try_write = transport_tls_try_write,
  3861. .get_poll_events = transport_tls_get_poll_events,
  3862. .in_before_shutdown = transport_tls_in_before_shutdown,
  3863. };
  3864. // --- Connection establishment ------------------------------------------------
  3865. static bool
  3866. irc_autofill_user_info (struct server *s, struct error **e)
  3867. {
  3868. const char *nicks = get_config_string (s->config, "nicks");
  3869. const char *username = get_config_string (s->config, "username");
  3870. const char *realname = get_config_string (s->config, "realname");
  3871. if (nicks && *nicks && username && *username && realname)
  3872. return true;
  3873. // Read POSIX user info and fill the configuration if needed
  3874. struct passwd *pwd = getpwuid (geteuid ());
  3875. if (!pwd)
  3876. FAIL ("cannot retrieve user information: %s", strerror (errno));
  3877. // FIXME: set_config_strings() writes errors on its own
  3878. if (!nicks || !*nicks)
  3879. set_config_string (s->config, "nicks", pwd->pw_name);
  3880. if (!username || !*username)
  3881. set_config_string (s->config, "username", pwd->pw_name);
  3882. // Not all systems have the GECOS field but the vast majority does
  3883. if (!realname)
  3884. {
  3885. char *gecos = pwd->pw_gecos;
  3886. // The first comma, if any, ends the user's real name
  3887. char *comma = strchr (gecos, ',');
  3888. if (comma)
  3889. *comma = '\0';
  3890. set_config_string (s->config, "realname", gecos);
  3891. }
  3892. return true;
  3893. }
  3894. static char *
  3895. irc_fetch_next_nickname (struct server *s)
  3896. {
  3897. struct str_vector v;
  3898. str_vector_init (&v);
  3899. cstr_split_ignore_empty (get_config_string (s->config, "nicks"), ',', &v);
  3900. char *result = NULL;
  3901. if (s->nick_counter >= 0 && (size_t) s->nick_counter < v.len)
  3902. result = xstrdup (v.vector[s->nick_counter++]);
  3903. if ((size_t) s->nick_counter >= v.len)
  3904. // Exhausted all nicknames
  3905. s->nick_counter = -1;
  3906. str_vector_free (&v);
  3907. return result;
  3908. }
  3909. static void
  3910. irc_register (struct server *s)
  3911. {
  3912. // Fill in user information automatically if needed
  3913. irc_autofill_user_info (s, NULL);
  3914. const char *username = get_config_string (s->config, "username");
  3915. const char *realname = get_config_string (s->config, "realname");
  3916. hard_assert (username && realname);
  3917. // Start IRCv3.1 capability negotiation;
  3918. // at worst the server will ignore this or send a harmless error message
  3919. irc_send (s, "CAP LS");
  3920. const char *password = get_config_string (s->config, "password");
  3921. if (password)
  3922. irc_send (s, "PASS :%s", password);
  3923. s->nick_counter = 0;
  3924. char *nickname = irc_fetch_next_nickname (s);
  3925. if (nickname)
  3926. irc_send (s, "NICK :%s", nickname);
  3927. else
  3928. log_server_error (s, s->buffer, "No nicks present in configuration");
  3929. free (nickname);
  3930. // IRC servers may ignore the last argument if it's empty
  3931. irc_send (s, "USER %s 8 * :%s", username, *realname ? realname : " ");
  3932. }
  3933. static void
  3934. irc_finish_connection (struct server *s, int socket)
  3935. {
  3936. struct app_context *ctx = s->ctx;
  3937. set_blocking (socket, false);
  3938. s->socket = socket;
  3939. s->transport = get_config_boolean (s->config, "tls")
  3940. ? &g_transport_tls
  3941. : &g_transport_plain;
  3942. struct error *e = NULL;
  3943. if (s->transport->init && !s->transport->init (s, &e))
  3944. {
  3945. log_server_error (s, s->buffer, "Connection failed: #s", e->message);
  3946. error_free (e);
  3947. xclose (s->socket);
  3948. s->socket = -1;
  3949. s->transport = NULL;
  3950. return;
  3951. }
  3952. log_server_status (s, s->buffer, "Connection established");
  3953. s->state = IRC_CONNECTED;
  3954. poller_fd_init (&s->socket_event, &ctx->poller, s->socket);
  3955. s->socket_event.dispatcher = (poller_fd_fn) on_irc_ready;
  3956. s->socket_event.user_data = s;
  3957. irc_update_poller (s, NULL);
  3958. irc_reset_connection_timeouts (s);
  3959. irc_register (s);
  3960. refresh_prompt (s->ctx);
  3961. }
  3962. static void
  3963. irc_split_host_port (char *s, char **host, char **port)
  3964. {
  3965. char *colon = strrchr (s, ':');
  3966. if (colon)
  3967. {
  3968. *colon = '\0';
  3969. *port = ++colon;
  3970. }
  3971. else
  3972. *port = "6667";
  3973. // Unwrap IPv6 addresses in format_host_port_pair() format
  3974. size_t host_end = strlen (s) - 1;
  3975. if (*s == '[' && s[host_end] == ']')
  3976. s++[host_end] = '\0';
  3977. *host = s;
  3978. }
  3979. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3980. static void
  3981. irc_on_connector_connecting (void *user_data, const char *address)
  3982. {
  3983. struct server *s = user_data;
  3984. log_server_status (s, s->buffer, "Connecting to #s...", address);
  3985. }
  3986. static void
  3987. irc_on_connector_error (void *user_data, const char *error)
  3988. {
  3989. struct server *s = user_data;
  3990. log_server_error (s, s->buffer, "Connection failed: #s", error);
  3991. }
  3992. static void
  3993. irc_on_connector_failure (void *user_data)
  3994. {
  3995. struct server *s = user_data;
  3996. irc_destroy_connector (s);
  3997. irc_queue_reconnect (s);
  3998. }
  3999. static void
  4000. irc_on_connector_connected (void *user_data, int socket)
  4001. {
  4002. struct server *s = user_data;
  4003. irc_destroy_connector (s);
  4004. irc_finish_connection (s, socket);
  4005. }
  4006. static bool
  4007. irc_setup_connector (struct server *s,
  4008. const struct str_vector *addresses, struct error **e)
  4009. {
  4010. struct connector *connector = xmalloc (sizeof *connector);
  4011. connector_init (connector, &s->ctx->poller);
  4012. s->connector = connector;
  4013. connector->user_data = s;
  4014. connector->on_connecting = irc_on_connector_connecting;
  4015. connector->on_error = irc_on_connector_error;
  4016. connector->on_connected = irc_on_connector_connected;
  4017. connector->on_failure = irc_on_connector_failure;
  4018. bool at_least_one_address_succeeded = false;
  4019. for (size_t i = 0; i < addresses->len; i++)
  4020. {
  4021. char *host, *port;
  4022. irc_split_host_port (addresses->vector[i], &host, &port);
  4023. struct error *error = NULL;
  4024. if (connector_add_target (connector, host, port, &error))
  4025. at_least_one_address_succeeded = true;
  4026. else
  4027. {
  4028. log_server_error (s, s->buffer,
  4029. "Address resolution failed for #&s: #s",
  4030. format_host_port_pair (host, port), error->message);
  4031. error_free (error);
  4032. }
  4033. }
  4034. if (!at_least_one_address_succeeded)
  4035. {
  4036. error_set (e, "No address to connect to");
  4037. return false;
  4038. }
  4039. connector_step (connector);
  4040. return true;
  4041. }
  4042. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4043. // TODO: see if we can further merge code for the two connectors, for example
  4044. // by making SOCKS 4A and 5 mere plugins for the connector, or by using
  4045. // a virtual interface common to them both (seems more likely)
  4046. static void
  4047. irc_on_socks_connecting (void *user_data,
  4048. const char *address, const char *via, const char *version)
  4049. {
  4050. struct server *s = user_data;
  4051. log_server_status (s, s->buffer,
  4052. "Connecting to #s via #s (#s)...", address, via, version);
  4053. }
  4054. static bool
  4055. irc_setup_connector_socks (struct server *s,
  4056. const struct str_vector *addresses, struct error **e)
  4057. {
  4058. const char *socks_host = get_config_string (s->config, "socks_host");
  4059. int64_t socks_port_int = get_config_integer (s->config, "socks_port");
  4060. if (!socks_host)
  4061. return false;
  4062. struct socks_connector *connector = xmalloc (sizeof *connector);
  4063. socks_connector_init (connector, &s->ctx->poller);
  4064. s->socks_conn = connector;
  4065. connector->user_data = s;
  4066. connector->on_connecting = irc_on_socks_connecting;
  4067. connector->on_error = irc_on_connector_error;
  4068. connector->on_connected = irc_on_connector_connected;
  4069. connector->on_failure = irc_on_connector_failure;
  4070. for (size_t i = 0; i < addresses->len; i++)
  4071. {
  4072. char *host, *port;
  4073. irc_split_host_port (addresses->vector[i], &host, &port);
  4074. if (!socks_connector_add_target (connector, host, port, e))
  4075. return false;
  4076. }
  4077. char *service = xstrdup_printf ("%" PRIi64, socks_port_int);
  4078. socks_connector_run (connector, socks_host, service,
  4079. get_config_string (s->config, "socks_username"),
  4080. get_config_string (s->config, "socks_password"));
  4081. free (service);
  4082. // The SOCKS connector can have already failed; we mustn't return true then
  4083. if (!s->socks_conn)
  4084. FAIL ("SOCKS connection failed");
  4085. return true;
  4086. }
  4087. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4088. static void
  4089. irc_initiate_connect (struct server *s)
  4090. {
  4091. hard_assert (s->state == IRC_DISCONNECTED);
  4092. const char *addresses = get_config_string (s->config, "addresses");
  4093. if (!addresses || !addresses[strspn (addresses, ",")])
  4094. {
  4095. // No sense in trying to reconnect
  4096. log_server_error (s, s->buffer,
  4097. "No addresses specified in configuration");
  4098. return;
  4099. }
  4100. struct str_vector servers;
  4101. str_vector_init (&servers);
  4102. cstr_split_ignore_empty (addresses, ',', &servers);
  4103. struct error *e = NULL;
  4104. if (!irc_setup_connector_socks (s, &servers, &e) && !e)
  4105. irc_setup_connector (s, &servers, &e);
  4106. str_vector_free (&servers);
  4107. if (e)
  4108. {
  4109. irc_destroy_connector (s);
  4110. log_server_error (s, s->buffer, "#s", e->message);
  4111. error_free (e);
  4112. irc_queue_reconnect (s);
  4113. }
  4114. else if (s->state != IRC_CONNECTED)
  4115. s->state = IRC_CONNECTING;
  4116. }
  4117. // --- Input prompt ------------------------------------------------------------
  4118. static void
  4119. make_unseen_prefix (struct app_context *ctx, struct str *active_buffers)
  4120. {
  4121. size_t buffer_no = 0;
  4122. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  4123. {
  4124. buffer_no++;
  4125. if (!(iter->unseen_messages_count - iter->unseen_unimportant_count))
  4126. continue;
  4127. if (active_buffers->len)
  4128. str_append_c (active_buffers, ',');
  4129. if (iter->highlighted)
  4130. str_append_c (active_buffers, '!');
  4131. str_append_printf (active_buffers, "%zu", buffer_no);
  4132. }
  4133. }
  4134. static void
  4135. make_chanmode_postfix (struct channel *channel, struct str *modes)
  4136. {
  4137. if (channel->no_param_modes.len)
  4138. str_append (modes, channel->no_param_modes.str);
  4139. struct str_map_iter iter;
  4140. str_map_iter_init (&iter, &channel->param_modes);
  4141. char *param;
  4142. while ((param = str_map_iter_next (&iter)))
  4143. str_append_c (modes, iter.link->key[0]);
  4144. }
  4145. static void
  4146. make_server_postfix_registered (struct buffer *buffer, struct str *output)
  4147. {
  4148. struct server *s = buffer->server;
  4149. if (buffer->type == BUFFER_CHANNEL)
  4150. {
  4151. struct server *s = buffer->server;
  4152. struct channel_user *channel_user =
  4153. irc_channel_get_user (buffer->channel, s->irc_user);
  4154. if (channel_user)
  4155. irc_get_channel_user_prefix (s, channel_user, output);
  4156. }
  4157. str_append (output, s->irc_user->nickname);
  4158. if (s->irc_user_mode.len)
  4159. str_append_printf (output, "(%s)", s->irc_user_mode.str);
  4160. }
  4161. static void
  4162. make_server_postfix (struct buffer *buffer, struct str *output)
  4163. {
  4164. struct server *s = buffer->server;
  4165. str_append_c (output, ' ');
  4166. if (!irc_is_connected (s))
  4167. str_append (output, "(disconnected)");
  4168. else if (s->state != IRC_REGISTERED)
  4169. str_append (output, "(unregistered)");
  4170. else
  4171. make_server_postfix_registered (buffer, output);
  4172. }
  4173. static void
  4174. make_prompt (struct app_context *ctx, struct str *output)
  4175. {
  4176. struct buffer *buffer = ctx->current_buffer;
  4177. if (!buffer)
  4178. return;
  4179. str_append_c (output, '[');
  4180. struct str active_buffers;
  4181. str_init (&active_buffers);
  4182. make_unseen_prefix (ctx, &active_buffers);
  4183. if (active_buffers.len)
  4184. str_append_printf (output, "(%s) ", active_buffers.str);
  4185. str_free (&active_buffers);
  4186. str_append_printf (output, "%d:%s",
  4187. buffer_get_index (ctx, buffer), buffer->name);
  4188. if (buffer->type == BUFFER_CHANNEL)
  4189. {
  4190. struct str modes;
  4191. str_init (&modes);
  4192. make_chanmode_postfix (buffer->channel, &modes);
  4193. if (modes.len)
  4194. str_append_printf (output, "(+%s)", modes.str);
  4195. str_free (&modes);
  4196. }
  4197. if (buffer != ctx->global_buffer)
  4198. make_server_postfix (buffer, output);
  4199. str_append_c (output, ']');
  4200. }
  4201. static void
  4202. refresh_prompt (struct app_context *ctx)
  4203. {
  4204. bool have_attributes = !!get_attribute_printer (stdout);
  4205. struct str prompt;
  4206. str_init (&prompt);
  4207. make_prompt (ctx, &prompt);
  4208. str_append_c (&prompt, ' ');
  4209. char *localized = iconv_xstrdup (ctx->term_from_utf8, prompt.str, -1, NULL);
  4210. str_free (&prompt);
  4211. if (have_attributes)
  4212. {
  4213. // XXX: to be completely correct, we should use tputs, but we cannot
  4214. input_set_prompt (&ctx->input, xstrdup_printf ("%c%s%c%s%c%s%c",
  4215. INPUT_START_IGNORE, ctx->attrs[ATTR_PROMPT],
  4216. INPUT_END_IGNORE,
  4217. localized,
  4218. INPUT_START_IGNORE, ctx->attrs[ATTR_RESET],
  4219. INPUT_END_IGNORE));
  4220. free (localized);
  4221. }
  4222. else
  4223. input_set_prompt (&ctx->input, localized);
  4224. }
  4225. // --- Helpers -----------------------------------------------------------------
  4226. static struct buffer *
  4227. irc_get_buffer_for_message (struct server *s,
  4228. const struct irc_message *msg, const char *target)
  4229. {
  4230. // TODO: display such messages differently
  4231. target = irc_skip_statusmsg (s, target);
  4232. struct buffer *buffer = str_map_find (&s->irc_buffer_map, target);
  4233. if (irc_is_channel (s, target))
  4234. {
  4235. struct channel *channel = str_map_find (&s->irc_channels, target);
  4236. hard_assert ((channel && buffer) ||
  4237. (channel && !buffer) || (!channel && !buffer));
  4238. // This is weird
  4239. if (!channel)
  4240. return NULL;
  4241. }
  4242. else if (!buffer)
  4243. {
  4244. // Don't make user buffers for servers (they can send NOTICEs)
  4245. if (!irc_find_userhost (msg->prefix))
  4246. return s->buffer;
  4247. char *nickname = irc_cut_nickname (msg->prefix);
  4248. if (irc_is_this_us (s, target))
  4249. buffer = irc_get_or_make_user_buffer (s, nickname);
  4250. free (nickname);
  4251. // With the IRCv3.2 echo-message capability, we can receive messages
  4252. // as they are delivered to the target; in that case we return NULL
  4253. // and the caller should check the origin
  4254. }
  4255. return buffer;
  4256. }
  4257. static bool
  4258. irc_is_highlight (struct server *s, const char *message)
  4259. {
  4260. // This may be called by notices before even successfully registering
  4261. if (!s->irc_user)
  4262. return false;
  4263. // Well, this is rather crude but it should make most users happy.
  4264. // Ideally we could do this at least in proper Unicode.
  4265. char *copy = xstrdup (message);
  4266. cstr_transform (copy, s->irc_tolower);
  4267. char *nick = xstrdup (s->irc_user->nickname);
  4268. cstr_transform (nick, s->irc_tolower);
  4269. // Special characters allowed in nicknames by RFC 2812: []\`_^{|} and -
  4270. // Also excluded from the ASCII: common user channel prefixes: +%@&~
  4271. const char *delimiters = ",.;:!?()<>/=#$* \t\r\n\v\f\"'";
  4272. bool result = false;
  4273. char *save = NULL;
  4274. for (char *token = strtok_r (copy, delimiters, &save);
  4275. token; token = strtok_r (NULL, delimiters, &save))
  4276. if (!strcmp (token, nick))
  4277. {
  4278. result = true;
  4279. break;
  4280. }
  4281. free (copy);
  4282. free (nick);
  4283. return result;
  4284. }
  4285. static char *
  4286. irc_get_privmsg_prefix (struct server *s, struct user *user, const char *target)
  4287. {
  4288. struct str prefix;
  4289. str_init (&prefix);
  4290. target = irc_skip_statusmsg (s, target);
  4291. if (user && irc_is_channel (s, target))
  4292. {
  4293. struct channel *channel;
  4294. struct channel_user *channel_user;
  4295. if ((channel = str_map_find (&s->irc_channels, target))
  4296. && (channel_user = irc_channel_get_user (channel, user)))
  4297. irc_get_channel_user_prefix (s, channel_user, &prefix);
  4298. }
  4299. return str_steal (&prefix);
  4300. }
  4301. // --- Mode processor ----------------------------------------------------------
  4302. struct mode_processor
  4303. {
  4304. char **params; ///< Mode string parameters
  4305. bool adding; ///< Currently adding modes
  4306. char mode_char; ///< Currently processed mode char
  4307. // User data:
  4308. struct server *s; ///< Server
  4309. struct channel *channel; ///< The channel being modified
  4310. };
  4311. /// Process a single mode character
  4312. typedef bool (*mode_processor_apply_fn) (struct mode_processor *);
  4313. static const char *
  4314. mode_processor_next_param (struct mode_processor *self)
  4315. {
  4316. if (!*self->params)
  4317. return NULL;
  4318. return *self->params++;
  4319. }
  4320. static void
  4321. mode_processor_run (struct mode_processor *self,
  4322. char **params, mode_processor_apply_fn apply_cb)
  4323. {
  4324. self->params = params;
  4325. const char *mode_string;
  4326. while ((mode_string = mode_processor_next_param (self)))
  4327. {
  4328. self->adding = true;
  4329. while ((self->mode_char = *mode_string++))
  4330. {
  4331. if (self->mode_char == '+') self->adding = true;
  4332. else if (self->mode_char == '-') self->adding = false;
  4333. else if (!apply_cb (self))
  4334. break;
  4335. }
  4336. }
  4337. }
  4338. static int
  4339. mode_char_cmp (const void *a, const void *b)
  4340. {
  4341. return *(const char *) a - *(const char *) b;
  4342. }
  4343. /// Add/remove the current mode character to/from the given ordered list
  4344. static void
  4345. mode_processor_toggle (struct mode_processor *self, struct str *modes)
  4346. {
  4347. const char *pos = strchr (modes->str, self->mode_char);
  4348. if (self->adding == !!pos)
  4349. return;
  4350. if (self->adding)
  4351. {
  4352. str_append_c (modes, self->mode_char);
  4353. qsort (modes->str, modes->len, 1, mode_char_cmp);
  4354. }
  4355. else
  4356. str_remove_slice (modes, pos - modes->str, 1);
  4357. }
  4358. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4359. static void
  4360. mode_processor_do_user (struct mode_processor *self)
  4361. {
  4362. const char *nickname;
  4363. struct user *user;
  4364. struct channel_user *channel_user;
  4365. if (!(nickname = mode_processor_next_param (self))
  4366. || !(user = str_map_find (&self->s->irc_users, nickname))
  4367. || !(channel_user = irc_channel_get_user (self->channel, user)))
  4368. return;
  4369. const char *all_prefixes = self->s->irc_chanuser_prefixes;
  4370. const char *all_modes = self->s->irc_chanuser_modes;
  4371. char prefix = all_prefixes[strchr (all_modes, self->mode_char) - all_modes];
  4372. struct str *prefixes = &channel_user->prefixes;
  4373. const char *pos = strchr (prefixes->str, prefix);
  4374. if (self->adding == !!pos)
  4375. return;
  4376. if (self->adding)
  4377. {
  4378. // Add the new mode prefix while retaining the right order
  4379. char *old_prefixes = str_steal (prefixes);
  4380. str_init (prefixes);
  4381. for (const char *p = all_prefixes; *p; p++)
  4382. if (*p == prefix || strchr (old_prefixes, *p))
  4383. str_append_c (prefixes, *p);
  4384. free (old_prefixes);
  4385. }
  4386. else
  4387. str_remove_slice (prefixes, pos - prefixes->str, 1);
  4388. }
  4389. static void
  4390. mode_processor_do_param_always (struct mode_processor *self)
  4391. {
  4392. const char *param = NULL;
  4393. if (!(param = mode_processor_next_param (self)))
  4394. return;
  4395. char key[2] = { self->mode_char, 0 };
  4396. str_map_set (&self->channel->param_modes, key,
  4397. self->adding ? xstrdup (param) : NULL);
  4398. }
  4399. static void
  4400. mode_processor_do_param_when_set (struct mode_processor *self)
  4401. {
  4402. const char *param = NULL;
  4403. if (self->adding && !(param = mode_processor_next_param (self)))
  4404. return;
  4405. char key[2] = { self->mode_char, 0 };
  4406. str_map_set (&self->channel->param_modes, key,
  4407. self->adding ? xstrdup (param) : NULL);
  4408. }
  4409. static bool
  4410. mode_processor_apply_channel (struct mode_processor *self)
  4411. {
  4412. if (strchr (self->s->irc_chanuser_modes, self->mode_char))
  4413. mode_processor_do_user (self);
  4414. else if (strchr (self->s->irc_chanmodes_list, self->mode_char))
  4415. // Nothing to do here, just skip the next argument if there's any
  4416. (void) mode_processor_next_param (self);
  4417. else if (strchr (self->s->irc_chanmodes_param_always, self->mode_char))
  4418. mode_processor_do_param_always (self);
  4419. else if (strchr (self->s->irc_chanmodes_param_when_set, self->mode_char))
  4420. mode_processor_do_param_when_set (self);
  4421. else if (strchr (self->s->irc_chanmodes_param_never, self->mode_char))
  4422. mode_processor_toggle (self, &self->channel->no_param_modes);
  4423. else
  4424. // It's not safe to continue, results could be undesired
  4425. return false;
  4426. return true;
  4427. }
  4428. static void
  4429. irc_handle_mode_channel
  4430. (struct server *s, struct channel *channel, char **params)
  4431. {
  4432. struct mode_processor p = { .s = s, .channel = channel };
  4433. mode_processor_run (&p, params, mode_processor_apply_channel);
  4434. }
  4435. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4436. static bool
  4437. mode_processor_apply_user (struct mode_processor *self)
  4438. {
  4439. mode_processor_toggle (self, &self->s->irc_user_mode);
  4440. return true;
  4441. }
  4442. static void
  4443. irc_handle_mode_user (struct server *s, char **params)
  4444. {
  4445. struct mode_processor p = { .s = s };
  4446. mode_processor_run (&p, params, mode_processor_apply_user);
  4447. }
  4448. // --- Input handling ----------------------------------------------------------
  4449. static void
  4450. irc_handle_cap (struct server *s, const struct irc_message *msg)
  4451. {
  4452. if (msg->params.len < 2)
  4453. return;
  4454. struct str_vector v;
  4455. str_vector_init (&v);
  4456. const char *args = "";
  4457. if (msg->params.len > 2)
  4458. cstr_split_ignore_empty ((args = msg->params.vector[2]), ' ', &v);
  4459. const char *subcommand = msg->params.vector[1];
  4460. if (!strcasecmp_ascii (subcommand, "ACK"))
  4461. {
  4462. log_server_status (s, s->buffer,
  4463. "#s: #S", "Capabilities acknowledged", args);
  4464. for (size_t i = 0; i < v.len; i++)
  4465. {
  4466. const char *cap = v.vector[i];
  4467. bool active = true;
  4468. if (*cap == '-')
  4469. {
  4470. active = false;
  4471. cap++;
  4472. }
  4473. if (!strcasecmp_ascii (cap, "echo-message"))
  4474. s->cap_echo_message = active;
  4475. }
  4476. irc_send (s, "CAP END");
  4477. }
  4478. else if (!strcasecmp_ascii (subcommand, "NAK"))
  4479. {
  4480. log_server_error (s, s->buffer,
  4481. "#s: #S", "Capabilities not acknowledged", args);
  4482. irc_send (s, "CAP END");
  4483. }
  4484. else if (!strcasecmp_ascii (subcommand, "LS"))
  4485. {
  4486. log_server_status (s, s->buffer,
  4487. "#s: #S", "Capabilities supported", args);
  4488. struct str_vector chosen;
  4489. str_vector_init (&chosen);
  4490. // Filter server capabilities for ones we can make use of
  4491. for (size_t i = 0; i < v.len; i++)
  4492. {
  4493. const char *cap = v.vector[i];
  4494. if (!strcasecmp_ascii (cap, "multi-prefix")
  4495. || !strcasecmp_ascii (cap, "invite-notify")
  4496. || !strcasecmp_ascii (cap, "echo-message"))
  4497. str_vector_add (&chosen, cap);
  4498. }
  4499. char *chosen_str = join_str_vector (&chosen, ' ');
  4500. str_vector_free (&chosen);
  4501. irc_send (s, "CAP REQ :%s", chosen_str);
  4502. log_server_status (s, s->buffer,
  4503. "#s: #S", "Capabilities requested", chosen_str);
  4504. free (chosen_str);
  4505. }
  4506. str_vector_free (&v);
  4507. }
  4508. static void
  4509. irc_handle_error (struct server *s, const struct irc_message *msg)
  4510. {
  4511. if (msg->params.len < 1)
  4512. return;
  4513. log_server_error (s, s->buffer, "#m", msg->params.vector[0]);
  4514. }
  4515. static void
  4516. irc_handle_invite (struct server *s, const struct irc_message *msg)
  4517. {
  4518. if (!msg->prefix || msg->params.len < 2)
  4519. return;
  4520. const char *target = msg->params.vector[0];
  4521. const char *channel_name = msg->params.vector[1];
  4522. struct buffer *buffer;
  4523. if (!(buffer = str_map_find (&s->irc_buffer_map, channel_name)))
  4524. buffer = s->buffer;
  4525. // IRCv3.2 invite-notify extension allows the target to be someone else
  4526. if (irc_is_this_us (s, target))
  4527. log_server_status (s, buffer,
  4528. "#n has invited you to #S", msg->prefix, channel_name);
  4529. else
  4530. log_server_status (s, buffer,
  4531. "#n has invited #n to #S", msg->prefix, target, channel_name);
  4532. }
  4533. static void
  4534. irc_handle_join (struct server *s, const struct irc_message *msg)
  4535. {
  4536. if (!msg->prefix || msg->params.len < 1)
  4537. return;
  4538. const char *channel_name = msg->params.vector[0];
  4539. if (!irc_is_channel (s, channel_name))
  4540. return;
  4541. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  4542. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  4543. hard_assert ((channel && buffer) ||
  4544. (channel && !buffer) || (!channel && !buffer));
  4545. // We've joined a new channel
  4546. if (!channel && irc_is_this_us (s, msg->prefix))
  4547. {
  4548. buffer = buffer_new ();
  4549. buffer->type = BUFFER_CHANNEL;
  4550. buffer->name = xstrdup_printf ("%s.%s", s->name, channel_name);
  4551. buffer->server = s;
  4552. buffer->channel = channel =
  4553. irc_make_channel (s, xstrdup (channel_name));
  4554. str_map_set (&s->irc_buffer_map, channel->name, buffer);
  4555. buffer_add (s->ctx, buffer);
  4556. buffer_activate (s->ctx, buffer);
  4557. // Request the channel mode as we don't get it automatically
  4558. irc_send (s, "MODE %s", channel_name);
  4559. }
  4560. // This is weird, ignoring
  4561. if (!channel)
  4562. return;
  4563. // Reset the field so that we rejoin the channel after reconnecting
  4564. channel->left_manually = false;
  4565. // Add the user to the channel
  4566. char *nickname = irc_cut_nickname (msg->prefix);
  4567. irc_channel_link_user (channel, irc_get_or_make_user (s, nickname), "");
  4568. free (nickname);
  4569. // Finally log the message
  4570. if (buffer)
  4571. {
  4572. log_server (s, buffer, BUFFER_LINE_UNIMPORTANT, "#a-->#r #N #a#s#r #S",
  4573. ATTR_JOIN, msg->prefix, ATTR_JOIN, "has joined", channel_name);
  4574. }
  4575. }
  4576. static void
  4577. irc_handle_kick (struct server *s, const struct irc_message *msg)
  4578. {
  4579. if (!msg->prefix || msg->params.len < 2)
  4580. return;
  4581. const char *channel_name = msg->params.vector[0];
  4582. const char *target = msg->params.vector[1];
  4583. if (!irc_is_channel (s, channel_name)
  4584. || irc_is_channel (s, target))
  4585. return;
  4586. const char *message = NULL;
  4587. if (msg->params.len > 2)
  4588. message = msg->params.vector[2];
  4589. struct user *user = str_map_find (&s->irc_users, target);
  4590. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  4591. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  4592. hard_assert ((channel && buffer) ||
  4593. (channel && !buffer) || (!channel && !buffer));
  4594. // It would be is weird for this to be false
  4595. if (user && channel)
  4596. {
  4597. if (irc_is_this_us (s, target))
  4598. irc_left_channel (channel);
  4599. else
  4600. irc_remove_user_from_channel (user, channel);
  4601. }
  4602. if (buffer)
  4603. {
  4604. struct formatter f;
  4605. formatter_init (&f, s->ctx, s);
  4606. formatter_add (&f, "#a<--#r #N #a#s#r #n",
  4607. ATTR_PART, msg->prefix, ATTR_PART, "has kicked", target);
  4608. if (message)
  4609. formatter_add (&f, " (#m)", message);
  4610. log_formatter (s->ctx, buffer, 0, &f);
  4611. }
  4612. }
  4613. static void
  4614. irc_handle_mode (struct server *s, const struct irc_message *msg)
  4615. {
  4616. if (!msg->prefix || msg->params.len < 1)
  4617. return;
  4618. const char *context = msg->params.vector[0];
  4619. // Join the modes back to a single string
  4620. struct str_vector copy;
  4621. str_vector_init (&copy);
  4622. str_vector_add_vector (&copy, msg->params.vector + 1);
  4623. char *modes = join_str_vector (&copy, ' ');
  4624. str_vector_free (&copy);
  4625. if (irc_is_channel (s, context))
  4626. {
  4627. struct channel *channel = str_map_find (&s->irc_channels, context);
  4628. struct buffer *buffer = str_map_find (&s->irc_buffer_map, context);
  4629. hard_assert ((channel && buffer) ||
  4630. (channel && !buffer) || (!channel && !buffer));
  4631. if (channel)
  4632. irc_handle_mode_channel (s, channel, msg->params.vector + 1);
  4633. if (buffer)
  4634. {
  4635. log_server_status (s, buffer,
  4636. "Mode #S [#S] by #n", context, modes, msg->prefix);
  4637. }
  4638. }
  4639. else if (irc_is_this_us (s, context))
  4640. {
  4641. irc_handle_mode_user (s, msg->params.vector + 1);
  4642. log_server_status (s, s->buffer,
  4643. "User mode [#S] by #n", modes, msg->prefix);
  4644. }
  4645. free (modes);
  4646. // Our own modes might have changed
  4647. refresh_prompt (s->ctx);
  4648. }
  4649. static void
  4650. irc_handle_nick (struct server *s, const struct irc_message *msg)
  4651. {
  4652. if (!msg->prefix || msg->params.len < 1)
  4653. return;
  4654. const char *new_nickname = msg->params.vector[0];
  4655. char *nickname = irc_cut_nickname (msg->prefix);
  4656. struct user *user = str_map_find (&s->irc_users, nickname);
  4657. free (nickname);
  4658. if (!user)
  4659. return;
  4660. bool lexicographically_different =
  4661. !!irc_server_strcmp (s, user->nickname, new_nickname);
  4662. // What the fuck, someone renamed themselves to ourselves
  4663. // TODO: probably log a message and force a reconnect
  4664. if (lexicographically_different
  4665. && !irc_server_strcmp (s, new_nickname, s->irc_user->nickname))
  4666. return;
  4667. // Log a message in any PM buffer (we may even have one for ourselves)
  4668. struct buffer *pm_buffer =
  4669. str_map_find (&s->irc_buffer_map, user->nickname);
  4670. if (pm_buffer)
  4671. {
  4672. if (irc_is_this_us (s, msg->prefix))
  4673. log_nick_self (s, pm_buffer, new_nickname);
  4674. else
  4675. log_nick (s, pm_buffer, msg->prefix, new_nickname);
  4676. }
  4677. // The new nickname may collide with a user referenced by a PM buffer,
  4678. // or in case of data inconsistency with the server, channels.
  4679. // In the latter case we need the colliding user to leave all of them.
  4680. struct user *user_collision = NULL;
  4681. if (lexicographically_different
  4682. && (user_collision = str_map_find (&s->irc_users, new_nickname)))
  4683. LIST_FOR_EACH (struct user_channel, iter, user_collision->channels)
  4684. irc_remove_user_from_channel (user_collision, iter->channel);
  4685. struct buffer *buffer_collision = NULL;
  4686. if (lexicographically_different
  4687. && (buffer_collision = str_map_find (&s->irc_buffer_map, new_nickname)))
  4688. {
  4689. hard_assert (buffer_collision->type == BUFFER_PM);
  4690. hard_assert (buffer_collision->user == user_collision);
  4691. user_unref (buffer_collision->user);
  4692. buffer_collision->user = user_ref (user);
  4693. }
  4694. if (pm_buffer && buffer_collision)
  4695. {
  4696. // There's not much else we can do other than somehow try to merge
  4697. // one buffer into the other. In our case, the original buffer wins.
  4698. buffer_merge (s->ctx, buffer_collision, pm_buffer);
  4699. if (s->ctx->current_buffer == pm_buffer)
  4700. buffer_activate (s->ctx, buffer_collision);
  4701. buffer_remove (s->ctx, pm_buffer);
  4702. pm_buffer = buffer_collision;
  4703. }
  4704. // The colliding user should be completely gone by now
  4705. if (lexicographically_different)
  4706. hard_assert (!str_map_find (&s->irc_users, new_nickname));
  4707. // Now we can rename the PM buffer to reflect the new nickname
  4708. if (pm_buffer)
  4709. {
  4710. str_map_set (&s->irc_buffer_map, user->nickname, NULL);
  4711. str_map_set (&s->irc_buffer_map, new_nickname, pm_buffer);
  4712. char *x = xstrdup_printf ("%s.%s", s->name, new_nickname);
  4713. buffer_rename (s->ctx, pm_buffer, x);
  4714. free (x);
  4715. }
  4716. if (irc_is_this_us (s, msg->prefix))
  4717. {
  4718. log_nick_self (s, s->buffer, new_nickname);
  4719. // Log a message in all open buffers on this server
  4720. struct str_map_iter iter;
  4721. str_map_iter_init (&iter, &s->irc_buffer_map);
  4722. struct buffer *buffer;
  4723. while ((buffer = str_map_iter_next (&iter)))
  4724. {
  4725. // We've already done that
  4726. if (buffer != pm_buffer)
  4727. log_nick_self (s, buffer, new_nickname);
  4728. }
  4729. }
  4730. else
  4731. {
  4732. // Log a message in all channels the user is in
  4733. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  4734. {
  4735. struct buffer *buffer =
  4736. str_map_find (&s->irc_buffer_map, iter->channel->name);
  4737. hard_assert (buffer != NULL);
  4738. log_nick (s, buffer, msg->prefix, new_nickname);
  4739. }
  4740. }
  4741. // Finally rename the user as it should be safe now
  4742. str_map_set (&s->irc_users, user->nickname, NULL);
  4743. str_map_set (&s->irc_users, new_nickname, user);
  4744. free (user->nickname);
  4745. user->nickname = xstrdup (new_nickname);
  4746. // We might have renamed ourselves
  4747. refresh_prompt (s->ctx);
  4748. }
  4749. static void
  4750. irc_handle_ctcp_reply (struct server *s,
  4751. const struct irc_message *msg, struct ctcp_chunk *chunk)
  4752. {
  4753. const char *target = msg->params.vector[0];
  4754. if (irc_is_this_us (s, msg->prefix))
  4755. log_ctcp_reply (s, target,
  4756. xstrdup_printf ("%s %s", chunk->tag.str, chunk->text.str));
  4757. else
  4758. log_server_status (s, s->buffer, "CTCP reply from #n: #S #S",
  4759. msg->prefix, chunk->tag.str, chunk->text.str);
  4760. }
  4761. static void
  4762. irc_handle_notice_text (struct server *s,
  4763. const struct irc_message *msg, struct str *text)
  4764. {
  4765. const char *target = msg->params.vector[0];
  4766. struct buffer *buffer = irc_get_buffer_for_message (s, msg, target);
  4767. if (!buffer)
  4768. {
  4769. if (irc_is_this_us (s, msg->prefix))
  4770. log_outcoming_orphan_notice (s, target, text->str);
  4771. return;
  4772. }
  4773. char *nick = irc_cut_nickname (msg->prefix);
  4774. // IRCv3.2 echo-message could otherwise cause us to highlight ourselves
  4775. if (!irc_is_this_us (s, msg->prefix) && irc_is_highlight (s, text->str))
  4776. log_server (s, buffer, BUFFER_LINE_STATUS | BUFFER_LINE_HIGHLIGHT,
  4777. "#a#s(#S)#r: #m", ATTR_HIGHLIGHT, "Notice", nick, text->str);
  4778. else
  4779. log_outcoming_notice (s, buffer, msg->prefix, text->str);
  4780. free (nick);
  4781. }
  4782. static void
  4783. irc_handle_notice (struct server *s, const struct irc_message *msg)
  4784. {
  4785. if (!msg->prefix || msg->params.len < 2)
  4786. return;
  4787. // This ignores empty messages which we should never receive anyway
  4788. struct ctcp_chunk *chunks = ctcp_parse (msg->params.vector[1]);
  4789. LIST_FOR_EACH (struct ctcp_chunk, iter, chunks)
  4790. if (!iter->is_extended)
  4791. irc_handle_notice_text (s, msg, &iter->text);
  4792. else
  4793. irc_handle_ctcp_reply (s, msg, iter);
  4794. ctcp_destroy (chunks);
  4795. }
  4796. static void
  4797. irc_handle_part (struct server *s, const struct irc_message *msg)
  4798. {
  4799. if (!msg->prefix || msg->params.len < 1)
  4800. return;
  4801. const char *channel_name = msg->params.vector[0];
  4802. if (!irc_is_channel (s, channel_name))
  4803. return;
  4804. const char *message = NULL;
  4805. if (msg->params.len > 1)
  4806. message = msg->params.vector[1];
  4807. char *nickname = irc_cut_nickname (msg->prefix);
  4808. struct user *user = str_map_find (&s->irc_users, nickname);
  4809. free (nickname);
  4810. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  4811. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  4812. hard_assert ((channel && buffer) ||
  4813. (channel && !buffer) || (!channel && !buffer));
  4814. // It would be is weird for this to be false
  4815. if (user && channel)
  4816. {
  4817. if (irc_is_this_us (s, msg->prefix))
  4818. irc_left_channel (channel);
  4819. else
  4820. irc_remove_user_from_channel (user, channel);
  4821. }
  4822. if (buffer)
  4823. {
  4824. struct formatter f;
  4825. formatter_init (&f, s->ctx, s);
  4826. formatter_add (&f, "#a<--#r #N #a#s#r #S",
  4827. ATTR_PART, msg->prefix, ATTR_PART, "has left", channel_name);
  4828. if (message)
  4829. formatter_add (&f, " (#m)", message);
  4830. log_formatter (s->ctx, buffer, BUFFER_LINE_UNIMPORTANT, &f);
  4831. }
  4832. }
  4833. static void
  4834. irc_handle_ping (struct server *s, const struct irc_message *msg)
  4835. {
  4836. if (msg->params.len)
  4837. irc_send (s, "PONG :%s", msg->params.vector[0]);
  4838. else
  4839. irc_send (s, "PONG");
  4840. }
  4841. static char *
  4842. ctime_now (char buf[26])
  4843. {
  4844. struct tm tm_;
  4845. time_t now = time (NULL);
  4846. if (!asctime_r (localtime_r (&now, &tm_), buf))
  4847. return NULL;
  4848. // Annoying thing
  4849. *strchr (buf, '\n') = '\0';
  4850. return buf;
  4851. }
  4852. static void irc_send_ctcp_reply (struct server *s, const char *recipient,
  4853. const char *format, ...) ATTRIBUTE_PRINTF (3, 4);
  4854. static void
  4855. irc_send_ctcp_reply (struct server *s,
  4856. const char *recipient, const char *format, ...)
  4857. {
  4858. struct str m;
  4859. str_init (&m);
  4860. va_list ap;
  4861. va_start (ap, format);
  4862. str_append_vprintf (&m, format, ap);
  4863. va_end (ap);
  4864. irc_send (s, "NOTICE %s :\x01%s\x01", recipient, m.str);
  4865. if (!s->cap_echo_message)
  4866. log_ctcp_reply (s, recipient, str_steal (&m));
  4867. else
  4868. str_free (&m);
  4869. }
  4870. static void
  4871. irc_handle_ctcp_request (struct server *s,
  4872. const struct irc_message *msg, struct ctcp_chunk *chunk)
  4873. {
  4874. const char *target = msg->params.vector[0];
  4875. if (irc_is_this_us (s, msg->prefix))
  4876. {
  4877. if (s->cap_echo_message)
  4878. log_ctcp_query (s, target, chunk->tag.str);
  4879. if (!irc_is_this_us (s, target))
  4880. return;
  4881. }
  4882. struct formatter f;
  4883. formatter_init (&f, s->ctx, s);
  4884. formatter_add (&f, "CTCP requested by #n", msg->prefix);
  4885. if (irc_is_channel (s, irc_skip_statusmsg (s, target)))
  4886. formatter_add (&f, " (to #S)", target);
  4887. formatter_add (&f, ": #S", chunk->tag.str);
  4888. log_formatter (s->ctx, s->buffer, BUFFER_LINE_STATUS, &f);
  4889. char *nickname = irc_cut_nickname (msg->prefix);
  4890. if (!strcmp (chunk->tag.str, "CLIENTINFO"))
  4891. irc_send_ctcp_reply (s, nickname, "CLIENTINFO %s %s %s %s",
  4892. "PING", "VERSION", "TIME", "CLIENTINFO");
  4893. else if (!strcmp (chunk->tag.str, "PING"))
  4894. irc_send_ctcp_reply (s, nickname, "PING %s", chunk->text.str);
  4895. else if (!strcmp (chunk->tag.str, "VERSION"))
  4896. {
  4897. struct utsname info;
  4898. if (uname (&info))
  4899. LOG_LIBC_FAILURE ("uname");
  4900. else
  4901. irc_send_ctcp_reply (s, nickname, "VERSION %s %s on %s %s",
  4902. PROGRAM_NAME, PROGRAM_VERSION, info.sysname, info.machine);
  4903. }
  4904. else if (!strcmp (chunk->tag.str, "TIME"))
  4905. {
  4906. char buf[26];
  4907. if (!ctime_now (buf))
  4908. LOG_LIBC_FAILURE ("asctime_r");
  4909. else
  4910. irc_send_ctcp_reply (s, nickname, "TIME %s", buf);
  4911. }
  4912. free (nickname);
  4913. }
  4914. static void
  4915. irc_handle_privmsg_text (struct server *s,
  4916. const struct irc_message *msg, struct str *text, bool is_action)
  4917. {
  4918. const char *target = msg->params.vector[0];
  4919. struct buffer *buffer = irc_get_buffer_for_message (s, msg, target);
  4920. if (!buffer)
  4921. {
  4922. if (irc_is_this_us (s, msg->prefix))
  4923. log_outcoming_orphan_privmsg (s, target, text->str);
  4924. return;
  4925. }
  4926. char *nickname = irc_cut_nickname (msg->prefix);
  4927. char *prefixes = irc_get_privmsg_prefix
  4928. (s, str_map_find (&s->irc_users, nickname), target);
  4929. // IRCv3.2 echo-message could otherwise cause us to highlight ourselves
  4930. if (irc_is_this_us (s, msg->prefix) || !irc_is_highlight (s, text->str))
  4931. {
  4932. if (is_action)
  4933. log_outcoming_action (s, buffer, nickname, text->str);
  4934. else
  4935. log_outcoming_privmsg (s, buffer, prefixes, nickname, text->str);
  4936. }
  4937. else if (is_action)
  4938. log_server (s, buffer, BUFFER_LINE_HIGHLIGHT,
  4939. " #a*#r #n #m", ATTR_HIGHLIGHT, msg->prefix, text->str);
  4940. else
  4941. log_server (s, buffer, BUFFER_LINE_HIGHLIGHT,
  4942. "#a<#S#S>#r #m", ATTR_HIGHLIGHT, prefixes, nickname, text->str);
  4943. free (nickname);
  4944. free (prefixes);
  4945. }
  4946. static void
  4947. irc_handle_privmsg (struct server *s, const struct irc_message *msg)
  4948. {
  4949. if (!msg->prefix || msg->params.len < 2)
  4950. return;
  4951. // This ignores empty messages which we should never receive anyway
  4952. struct ctcp_chunk *chunks = ctcp_parse (msg->params.vector[1]);
  4953. LIST_FOR_EACH (struct ctcp_chunk, iter, chunks)
  4954. if (!iter->is_extended)
  4955. irc_handle_privmsg_text (s, msg, &iter->text, false);
  4956. else if (!strcmp (iter->tag.str, "ACTION"))
  4957. irc_handle_privmsg_text (s, msg, &iter->text, true);
  4958. else
  4959. irc_handle_ctcp_request (s, msg, iter);
  4960. ctcp_destroy (chunks);
  4961. }
  4962. static void
  4963. log_quit (struct server *s,
  4964. struct buffer *buffer, const char *prefix, const char *reason)
  4965. {
  4966. struct formatter f;
  4967. formatter_init (&f, s->ctx, s);
  4968. formatter_add (&f, "#a<--#r #N #a#s#r",
  4969. ATTR_PART, prefix, ATTR_PART, "has quit");
  4970. if (reason)
  4971. formatter_add (&f, " (#m)", reason);
  4972. log_formatter (s->ctx, buffer, BUFFER_LINE_UNIMPORTANT, &f);
  4973. }
  4974. static void
  4975. irc_handle_quit (struct server *s, const struct irc_message *msg)
  4976. {
  4977. if (!msg->prefix)
  4978. return;
  4979. // What the fuck, the server never sends this back
  4980. if (irc_is_this_us (s, msg->prefix))
  4981. return;
  4982. char *nickname = irc_cut_nickname (msg->prefix);
  4983. struct user *user = str_map_find (&s->irc_users, nickname);
  4984. free (nickname);
  4985. if (!user)
  4986. return;
  4987. const char *message = NULL;
  4988. if (msg->params.len > 0)
  4989. message = msg->params.vector[0];
  4990. // Log a message in any PM buffer
  4991. struct buffer *buffer =
  4992. str_map_find (&s->irc_buffer_map, user->nickname);
  4993. if (buffer)
  4994. {
  4995. log_quit (s, buffer, msg->prefix, message);
  4996. // TODO: set some kind of a flag in the buffer and when the user
  4997. // reappears on a channel (JOIN), log a "is back online" message.
  4998. // Also set this flag when we receive a "no such nick" numeric
  4999. // and reset it when we send something to the buffer.
  5000. }
  5001. // Log a message in all channels the user is in
  5002. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  5003. {
  5004. if ((buffer = str_map_find (&s->irc_buffer_map, iter->channel->name)))
  5005. log_quit (s, buffer, msg->prefix, message);
  5006. // This destroys "iter" which doesn't matter to us
  5007. irc_remove_user_from_channel (user, iter->channel);
  5008. }
  5009. }
  5010. static void
  5011. irc_handle_topic (struct server *s, const struct irc_message *msg)
  5012. {
  5013. if (!msg->prefix || msg->params.len < 2)
  5014. return;
  5015. const char *channel_name = msg->params.vector[0];
  5016. const char *topic = msg->params.vector[1];
  5017. if (!irc_is_channel (s, channel_name))
  5018. return;
  5019. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  5020. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  5021. hard_assert ((channel && buffer) ||
  5022. (channel && !buffer) || (!channel && !buffer));
  5023. // It would be is weird for this to be false
  5024. if (channel)
  5025. {
  5026. free (channel->topic);
  5027. channel->topic = xstrdup (topic);
  5028. }
  5029. if (buffer)
  5030. {
  5031. log_server (s, buffer, BUFFER_LINE_STATUS, "#n #s \"#m\"",
  5032. msg->prefix, "has changed the topic to", topic);
  5033. }
  5034. }
  5035. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  5036. static struct irc_handler
  5037. {
  5038. const char *name;
  5039. void (*handler) (struct server *s, const struct irc_message *msg);
  5040. }
  5041. g_irc_handlers[] =
  5042. {
  5043. // This list needs to stay sorted
  5044. { "CAP", irc_handle_cap },
  5045. { "ERROR", irc_handle_error },
  5046. { "INVITE", irc_handle_invite },
  5047. { "JOIN", irc_handle_join },
  5048. { "KICK", irc_handle_kick },
  5049. { "MODE", irc_handle_mode },
  5050. { "NICK", irc_handle_nick },
  5051. { "NOTICE", irc_handle_notice },
  5052. { "PART", irc_handle_part },
  5053. { "PING", irc_handle_ping },
  5054. { "PRIVMSG", irc_handle_privmsg },
  5055. { "QUIT", irc_handle_quit },
  5056. { "TOPIC", irc_handle_topic },
  5057. };
  5058. static int
  5059. irc_handler_cmp_by_name (const void *a, const void *b)
  5060. {
  5061. const struct irc_handler *first = a;
  5062. const struct irc_handler *second = b;
  5063. return strcasecmp_ascii (first->name, second->name);
  5064. }
  5065. static bool
  5066. irc_try_parse_word_for_userhost (struct server *s, const char *word)
  5067. {
  5068. regex_t re;
  5069. int err = regcomp (&re, "^[^!@]+!([^!@]+@[^!@]+)$", REG_EXTENDED);
  5070. if (!soft_assert (!err))
  5071. return false;
  5072. regmatch_t matches[2];
  5073. bool result = false;
  5074. if (!regexec (&re, word, 2, matches, 0))
  5075. {
  5076. free (s->irc_user_host);
  5077. s->irc_user_host = xstrndup (word + matches[1].rm_so,
  5078. matches[1].rm_eo - matches[1].rm_so);
  5079. result = true;
  5080. }
  5081. regfree (&re);
  5082. return result;
  5083. }
  5084. static void
  5085. irc_try_parse_welcome_for_userhost (struct server *s, const char *m)
  5086. {
  5087. struct str_vector v;
  5088. str_vector_init (&v);
  5089. cstr_split_ignore_empty (m, ' ', &v);
  5090. for (size_t i = 0; i < v.len; i++)
  5091. if (irc_try_parse_word_for_userhost (s, v.vector[i]))
  5092. break;
  5093. str_vector_free (&v);
  5094. }
  5095. static bool process_input_utf8
  5096. (struct app_context *, struct buffer *, const char *, int);
  5097. static void
  5098. irc_on_registered (struct server *s, const char *nickname)
  5099. {
  5100. s->irc_user = irc_get_or_make_user (s, nickname);
  5101. str_reset (&s->irc_user_mode);
  5102. s->irc_user_host = NULL;
  5103. s->state = IRC_REGISTERED;
  5104. refresh_prompt (s->ctx);
  5105. // XXX: we can also use WHOIS if it's not supported (optional by RFC 2812)
  5106. irc_send (s, "USERHOST %s", s->irc_user->nickname);
  5107. const char *command = get_config_string (s->config, "command");
  5108. if (command)
  5109. {
  5110. log_server_debug (s, "Executing \"#s\"", command);
  5111. process_input_utf8 (s->ctx, s->buffer, command, 0);
  5112. }
  5113. int64_t command_delay = get_config_integer (s->config, "command_delay");
  5114. log_server_debug (s, "Autojoining channels in #&s seconds...",
  5115. xstrdup_printf ("%" PRId64, command_delay));
  5116. poller_timer_set (&s->autojoin_tmr, command_delay * 1000);
  5117. }
  5118. static void
  5119. irc_handle_rpl_userhost (struct server *s, const struct irc_message *msg)
  5120. {
  5121. if (msg->params.len < 2)
  5122. return;
  5123. const char *response = msg->params.vector[1];
  5124. struct str_vector v;
  5125. str_vector_init (&v);
  5126. cstr_split_ignore_empty (response, ' ', &v);
  5127. for (size_t i = 0; i < v.len; i++)
  5128. {
  5129. char *nick = v.vector[i];
  5130. char *equals = strchr (nick, '=');
  5131. if (!equals || equals == nick)
  5132. continue;
  5133. // User is an IRC operator
  5134. if (equals[-1] == '*')
  5135. equals[-1] = '\0';
  5136. else
  5137. equals[ 0] = '\0';
  5138. // TODO: make use of this (away status polling?)
  5139. char away_status = equals[1];
  5140. if (!strchr ("+-", away_status))
  5141. continue;
  5142. char *userhost = equals + 2;
  5143. if (irc_is_this_us (s, nick))
  5144. {
  5145. free (s->irc_user_host);
  5146. s->irc_user_host = xstrdup (userhost);
  5147. }
  5148. }
  5149. str_vector_free (&v);
  5150. }
  5151. static void
  5152. irc_handle_rpl_umodeis (struct server *s, const struct irc_message *msg)
  5153. {
  5154. if (msg->params.len < 2)
  5155. return;
  5156. str_reset (&s->irc_user_mode);
  5157. irc_handle_mode_user (s, msg->params.vector + 1);
  5158. // XXX: do we want to log a message?
  5159. refresh_prompt (s->ctx);
  5160. }
  5161. static void
  5162. irc_handle_rpl_namreply (struct server *s, const struct irc_message *msg)
  5163. {
  5164. if (msg->params.len < 4)
  5165. return;
  5166. const char *channel_name = msg->params.vector[2];
  5167. const char *nicks = msg->params.vector[3];
  5168. // Just push the nicknames to a string vector to process later
  5169. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  5170. if (channel)
  5171. cstr_split_ignore_empty (nicks, ' ', &channel->names_buf);
  5172. }
  5173. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  5174. struct channel_user_sort_entry
  5175. {
  5176. struct server *s; ///< Server
  5177. struct channel_user *channel_user; ///< Channel user
  5178. };
  5179. static int
  5180. channel_user_sort_entry_cmp (const void *entry_a, const void *entry_b)
  5181. {
  5182. const struct channel_user_sort_entry *a = entry_a;
  5183. const struct channel_user_sort_entry *b = entry_b;
  5184. struct server *s = a->s;
  5185. // First order by the most significant channel user prefix
  5186. const char *prio_a = strchr (s->irc_chanuser_prefixes,
  5187. *a->channel_user->prefixes.str);
  5188. const char *prio_b = strchr (s->irc_chanuser_prefixes,
  5189. *b->channel_user->prefixes.str);
  5190. // Put unrecognized prefixes at the end of the list
  5191. if (prio_a || prio_b)
  5192. {
  5193. if (!prio_a) return 1;
  5194. if (!prio_b) return -1;
  5195. if (prio_a != prio_b)
  5196. return prio_a - prio_b;
  5197. }
  5198. return irc_server_strcmp (s,
  5199. a->channel_user->user->nickname,
  5200. b->channel_user->user->nickname);
  5201. }
  5202. static char *
  5203. make_channel_users_list (struct server *s, struct channel *channel)
  5204. {
  5205. size_t n_users = 0;
  5206. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  5207. n_users++;
  5208. struct channel_user_sort_entry entries[n_users];
  5209. size_t i = 0;
  5210. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  5211. {
  5212. entries[i].s = s;
  5213. entries[i].channel_user = iter;
  5214. i++;
  5215. }
  5216. qsort (entries, n_users, sizeof *entries, channel_user_sort_entry_cmp);
  5217. struct str list;
  5218. str_init (&list);
  5219. for (i = 0; i < n_users; i++)
  5220. {
  5221. irc_get_channel_user_prefix (s, entries[i].channel_user, &list);
  5222. str_append (&list, entries[i].channel_user->user->nickname);
  5223. str_append_c (&list, ' ');
  5224. }
  5225. if (list.len)
  5226. list.str[--list.len] = '\0';
  5227. return str_steal (&list);
  5228. }
  5229. static void
  5230. irc_sync_channel_user (struct server *s, struct channel *channel,
  5231. const char *nickname, const char *prefixes)
  5232. {
  5233. struct user *user = irc_get_or_make_user (s, nickname);
  5234. struct channel_user *channel_user =
  5235. irc_channel_get_user (channel, user);
  5236. if (!channel_user)
  5237. {
  5238. irc_channel_link_user (channel, user, prefixes);
  5239. return;
  5240. }
  5241. user_unref (user);
  5242. // If our idea of the user's modes disagrees with what the server's
  5243. // sent us (the most powerful modes differ), use the latter one
  5244. if (channel_user->prefixes.str[0] != prefixes[0])
  5245. {
  5246. str_reset (&channel_user->prefixes);
  5247. str_append (&channel_user->prefixes, prefixes);
  5248. }
  5249. }
  5250. static void
  5251. irc_process_names (struct server *s, struct channel *channel)
  5252. {
  5253. struct str_map present;
  5254. str_map_init (&present);
  5255. present.key_xfrm = s->irc_strxfrm;
  5256. struct str_vector *updates = &channel->names_buf;
  5257. for (size_t i = 0; i < updates->len; i++)
  5258. {
  5259. const char *item = updates->vector[i];
  5260. size_t n_prefixes = strspn (item, s->irc_chanuser_prefixes);
  5261. const char *nickname = item + n_prefixes;
  5262. // Store the nickname in a hashset
  5263. str_map_set (&present, nickname, (void *) 1);
  5264. char *prefixes = xstrndup (item, n_prefixes);
  5265. irc_sync_channel_user (s, channel, nickname, prefixes);
  5266. free (prefixes);
  5267. }
  5268. // Get rid of channel users missing from "updates"
  5269. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  5270. if (!str_map_find (&present, iter->user->nickname))
  5271. irc_channel_unlink_user (channel, iter);
  5272. str_map_free (&present);
  5273. str_vector_reset (&channel->names_buf);
  5274. char *all_users = make_channel_users_list (s, channel);
  5275. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel->name);
  5276. if (buffer)
  5277. {
  5278. log_server_status (s, buffer, "Users on #S: #S",
  5279. channel->name, all_users);
  5280. }
  5281. free (all_users);
  5282. }
  5283. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  5284. static void
  5285. irc_handle_rpl_endofnames (struct server *s, const struct irc_message *msg)
  5286. {
  5287. if (msg->params.len < 2)
  5288. return;
  5289. const char *channel_name = msg->params.vector[1];
  5290. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  5291. if (!strcmp (channel_name, "*"))
  5292. {
  5293. struct str_map_iter iter;
  5294. str_map_iter_init (&iter, &s->irc_channels);
  5295. struct channel *channel;
  5296. while ((channel = str_map_iter_next (&iter)))
  5297. irc_process_names (s, channel);
  5298. }
  5299. else if (channel)
  5300. irc_process_names (s, channel);
  5301. }
  5302. static void
  5303. irc_handle_rpl_topic (struct server *s, const struct irc_message *msg)
  5304. {
  5305. if (msg->params.len < 3)
  5306. return;
  5307. const char *channel_name = msg->params.vector[1];
  5308. const char *topic = msg->params.vector[2];
  5309. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  5310. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  5311. hard_assert ((channel && buffer) ||
  5312. (channel && !buffer) || (!channel && !buffer));
  5313. if (channel)
  5314. {
  5315. free (channel->topic);
  5316. channel->topic = xstrdup (topic);
  5317. }
  5318. if (buffer)
  5319. log_server_status (s, buffer, "The topic is: #m", topic);
  5320. }
  5321. static void
  5322. irc_handle_rpl_channelmodeis (struct server *s, const struct irc_message *msg)
  5323. {
  5324. if (msg->params.len < 2)
  5325. return;
  5326. const char *channel_name = msg->params.vector[1];
  5327. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  5328. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  5329. hard_assert ((channel && buffer) ||
  5330. (channel && !buffer) || (!channel && !buffer));
  5331. if (channel)
  5332. {
  5333. str_reset (&channel->no_param_modes);
  5334. str_map_clear (&channel->param_modes);
  5335. irc_handle_mode_channel (s, channel, msg->params.vector + 1);
  5336. }
  5337. // XXX: do we want to log a message?
  5338. refresh_prompt (s->ctx);
  5339. }
  5340. static char *
  5341. make_time_string (time_t time)
  5342. {
  5343. char buf[32];
  5344. struct tm tm;
  5345. strftime (buf, sizeof buf, "%a %b %d %Y %T", localtime_r (&time, &tm));
  5346. return xstrdup (buf);
  5347. }
  5348. static void
  5349. irc_handle_rpl_creationtime (struct server *s, const struct irc_message *msg)
  5350. {
  5351. if (msg->params.len < 3)
  5352. return;
  5353. const char *channel_name = msg->params.vector[1];
  5354. const char *creation_time = msg->params.vector[2];
  5355. unsigned long created;
  5356. if (!xstrtoul (&created, creation_time, 10))
  5357. return;
  5358. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  5359. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  5360. hard_assert ((channel && buffer) ||
  5361. (channel && !buffer) || (!channel && !buffer));
  5362. if (buffer)
  5363. {
  5364. log_server_status (s, buffer, "Channel created on #&s",
  5365. make_time_string (created));
  5366. }
  5367. }
  5368. static void
  5369. irc_handle_rpl_topicwhotime (struct server *s, const struct irc_message *msg)
  5370. {
  5371. if (msg->params.len < 4)
  5372. return;
  5373. const char *channel_name = msg->params.vector[1];
  5374. const char *who = msg->params.vector[2];
  5375. const char *change_time = msg->params.vector[3];
  5376. unsigned long changed;
  5377. if (!xstrtoul (&changed, change_time, 10))
  5378. return;
  5379. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  5380. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  5381. hard_assert ((channel && buffer) ||
  5382. (channel && !buffer) || (!channel && !buffer));
  5383. if (buffer)
  5384. {
  5385. log_server_status (s, buffer, "Topic set by #N on #&s",
  5386. who, make_time_string (changed));
  5387. }
  5388. }
  5389. static void
  5390. irc_handle_rpl_inviting (struct server *s, const struct irc_message *msg)
  5391. {
  5392. if (msg->params.len < 3)
  5393. return;
  5394. const char *nickname = msg->params.vector[1];
  5395. const char *channel_name = msg->params.vector[2];
  5396. struct buffer *buffer;
  5397. if (!(buffer = str_map_find (&s->irc_buffer_map, channel_name)))
  5398. buffer = s->buffer;
  5399. log_server_status (s, buffer,
  5400. "You have invited #n to #S", nickname, channel_name);
  5401. }
  5402. static void
  5403. irc_handle_err_nicknameinuse (struct server *s, const struct irc_message *msg)
  5404. {
  5405. if (msg->params.len < 2)
  5406. return;
  5407. log_server_error (s, s->buffer,
  5408. "Nickname is already in use: #S", msg->params.vector[1]);
  5409. // Only do this while we haven't successfully registered yet
  5410. if (s->state != IRC_CONNECTED)
  5411. return;
  5412. char *nickname = irc_fetch_next_nickname (s);
  5413. if (nickname)
  5414. {
  5415. log_server_status (s, s->buffer, "Retrying with #s...", nickname);
  5416. irc_send (s, "NICK :%s", nickname);
  5417. free (nickname);
  5418. }
  5419. }
  5420. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  5421. static void
  5422. irc_handle_isupport_prefix (struct server *s, char *value)
  5423. {
  5424. char *modes = value;
  5425. char *prefixes = strchr (value, ')');
  5426. size_t n_prefixes = prefixes - modes;
  5427. if (*modes++ != '(' || !prefixes++ || strlen (value) != 2 * n_prefixes--)
  5428. return;
  5429. free (s->irc_chanuser_modes);
  5430. free (s->irc_chanuser_prefixes);
  5431. s->irc_chanuser_modes = xstrndup (modes, n_prefixes);
  5432. s->irc_chanuser_prefixes = xstrndup (prefixes, n_prefixes);
  5433. }
  5434. static void
  5435. irc_handle_isupport_casemapping (struct server *s, char *value)
  5436. {
  5437. if (!strcmp (value, "ascii"))
  5438. irc_set_casemapping (s, tolower_ascii, tolower_ascii_strxfrm);
  5439. else if (!strcmp (value, "rfc1459"))
  5440. irc_set_casemapping (s, irc_tolower, irc_strxfrm);
  5441. else if (!strcmp (value, "rfc1459-strict"))
  5442. irc_set_casemapping (s, irc_tolower_strict, irc_strxfrm_strict);
  5443. }
  5444. static void
  5445. irc_handle_isupport_chantypes (struct server *s, char *value)
  5446. {
  5447. free (s->irc_chantypes);
  5448. s->irc_chantypes = xstrdup (value);
  5449. }
  5450. static void
  5451. irc_handle_isupport_idchan (struct server *s, char *value)
  5452. {
  5453. struct str prefixes;
  5454. str_init (&prefixes);
  5455. struct str_vector v;
  5456. str_vector_init (&v);
  5457. cstr_split_ignore_empty (value, ',', &v);
  5458. for (size_t i = 0; i < v.len; i++)
  5459. {
  5460. // Not using or validating the numeric part
  5461. const char *pair = v.vector[i];
  5462. const char *colon = strchr (pair, ':');
  5463. if (colon)
  5464. str_append_data (&prefixes, pair, colon - pair);
  5465. }
  5466. str_vector_free (&v);
  5467. free (s->irc_idchan_prefixes);
  5468. s->irc_idchan_prefixes = str_steal (&prefixes);
  5469. }
  5470. static void
  5471. irc_handle_isupport_statusmsg (struct server *s, char *value)
  5472. {
  5473. free (s->irc_statusmsg);
  5474. s->irc_statusmsg = xstrdup (value);
  5475. }
  5476. static void
  5477. irc_handle_isupport_chanmodes (struct server *s, char *value)
  5478. {
  5479. struct str_vector v;
  5480. str_vector_init (&v);
  5481. cstr_split_ignore_empty (value, ',', &v);
  5482. if (v.len >= 4)
  5483. {
  5484. free (s->irc_chanmodes_list);
  5485. s->irc_chanmodes_list = xstrdup (v.vector[0]);
  5486. free (s->irc_chanmodes_param_always);
  5487. s->irc_chanmodes_param_always = xstrdup (v.vector[1]);
  5488. free (s->irc_chanmodes_param_when_set);
  5489. s->irc_chanmodes_param_when_set = xstrdup (v.vector[2]);
  5490. free (s->irc_chanmodes_param_never);
  5491. s->irc_chanmodes_param_never = xstrdup (v.vector[3]);
  5492. }
  5493. str_vector_free (&v);
  5494. }
  5495. static void
  5496. irc_handle_isupport_modes (struct server *s, char *value)
  5497. {
  5498. unsigned long modes;
  5499. if (!*value)
  5500. s->irc_max_modes = UINT_MAX;
  5501. else if (xstrtoul (&modes, value, 10) && modes && modes <= UINT_MAX)
  5502. s->irc_max_modes = modes;
  5503. }
  5504. static void
  5505. unescape_isupport_value (const char *value, struct str *output)
  5506. {
  5507. const char *alphabet = "0123456789abcdef", *a, *b;
  5508. for (const char *p = value; *p; p++)
  5509. {
  5510. if (p[0] == '\\'
  5511. && p[1] == 'x'
  5512. && p[2] && (a = strchr (alphabet, tolower_ascii (p[2])))
  5513. && p[3] && (b = strchr (alphabet, tolower_ascii (p[3]))))
  5514. {
  5515. str_append_c (output, (a - alphabet) << 4 | (b - alphabet));
  5516. p += 3;
  5517. }
  5518. else
  5519. str_append_c (output, *p);
  5520. }
  5521. }
  5522. static void
  5523. dispatch_isupport (struct server *s, const char *name, char *value)
  5524. {
  5525. #define MATCH(from, to) if (!strcmp (name, (from))) { (to) (s, value); return; }
  5526. // TODO: also make use of TARGMAX to split client commands as necessary
  5527. MATCH ("PREFIX", irc_handle_isupport_prefix);
  5528. MATCH ("CASEMAPPING", irc_handle_isupport_casemapping);
  5529. MATCH ("CHANTYPES", irc_handle_isupport_chantypes);
  5530. MATCH ("IDCHAN", irc_handle_isupport_idchan);
  5531. MATCH ("STATUSMSG", irc_handle_isupport_statusmsg);
  5532. MATCH ("CHANMODES", irc_handle_isupport_chanmodes);
  5533. MATCH ("MODES", irc_handle_isupport_modes);
  5534. #undef MATCH
  5535. }
  5536. static void
  5537. irc_handle_rpl_isupport (struct server *s, const struct irc_message *msg)
  5538. {
  5539. if (msg->params.len < 2)
  5540. return;
  5541. for (size_t i = 1; i < msg->params.len - 1; i++)
  5542. {
  5543. // TODO: if the parameter starts with "-", it resets to default
  5544. char *param = msg->params.vector[i];
  5545. char *value = param + strcspn (param, "=");
  5546. if (*value) *value++ = '\0';
  5547. struct str value_unescaped;
  5548. str_init (&value_unescaped);
  5549. unescape_isupport_value (value, &value_unescaped);
  5550. dispatch_isupport (s, param, value_unescaped.str);
  5551. str_free (&value_unescaped);
  5552. }
  5553. }
  5554. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  5555. static void
  5556. irc_process_numeric (struct server *s,
  5557. const struct irc_message *msg, unsigned long numeric)
  5558. {
  5559. // Numerics typically have human-readable information
  5560. // Get rid of the first parameter, if there's any at all,
  5561. // as it contains our nickname and is of no practical use to the user
  5562. struct str_vector copy;
  5563. str_vector_init (&copy);
  5564. str_vector_add_vector (&copy, msg->params.vector + !!msg->params.len);
  5565. struct buffer *buffer = s->buffer;
  5566. switch (numeric)
  5567. {
  5568. case IRC_RPL_WELCOME:
  5569. irc_on_registered (s, msg->params.vector[0]);
  5570. // We still issue a USERHOST anyway as this is in general unreliable
  5571. if (msg->params.len == 2)
  5572. irc_try_parse_welcome_for_userhost (s, msg->params.vector[1]);
  5573. break;
  5574. case IRC_RPL_ISUPPORT:
  5575. irc_handle_rpl_isupport (s, msg); break;
  5576. case IRC_RPL_USERHOST:
  5577. irc_handle_rpl_userhost (s, msg); break;
  5578. case IRC_RPL_UMODEIS:
  5579. irc_handle_rpl_umodeis (s, msg); buffer = NULL; break;
  5580. case IRC_RPL_NAMREPLY:
  5581. irc_handle_rpl_namreply (s, msg); buffer = NULL; break;
  5582. case IRC_RPL_ENDOFNAMES:
  5583. irc_handle_rpl_endofnames (s, msg); buffer = NULL; break;
  5584. case IRC_RPL_TOPIC:
  5585. irc_handle_rpl_topic (s, msg); buffer = NULL; break;
  5586. case IRC_RPL_CHANNELMODEIS:
  5587. irc_handle_rpl_channelmodeis (s, msg); buffer = NULL; break;
  5588. case IRC_RPL_CREATIONTIME:
  5589. irc_handle_rpl_creationtime (s, msg); buffer = NULL; break;
  5590. case IRC_RPL_TOPICWHOTIME:
  5591. irc_handle_rpl_topicwhotime (s, msg); buffer = NULL; break;
  5592. case IRC_RPL_INVITING:
  5593. irc_handle_rpl_inviting (s, msg); buffer = NULL; break;
  5594. case IRC_ERR_NICKNAMEINUSE:
  5595. irc_handle_err_nicknameinuse (s, msg); buffer = NULL; break;
  5596. case IRC_RPL_LIST:
  5597. case IRC_RPL_WHOREPLY:
  5598. case IRC_RPL_ENDOFWHO:
  5599. case IRC_ERR_UNKNOWNCOMMAND:
  5600. case IRC_ERR_NEEDMOREPARAMS:
  5601. // Just preventing these commands from getting printed in a more
  5602. // specific buffer as that would be unwanted
  5603. break;
  5604. default:
  5605. // If the second parameter is something we have a buffer for
  5606. // (a channel, a PM buffer), log it in that buffer. This is very basic.
  5607. // TODO: whitelist/blacklist a lot more replies in here.
  5608. // TODO: we should either strip the first parameter from the resulting
  5609. // buffer line, or at least put it in brackets
  5610. if (msg->params.len > 1)
  5611. {
  5612. struct buffer *x;
  5613. if ((x = str_map_find (&s->irc_buffer_map, msg->params.vector[1])))
  5614. buffer = x;
  5615. }
  5616. }
  5617. if (buffer)
  5618. {
  5619. // Join the parameter vector back and send it to the server buffer
  5620. log_server (s, buffer, BUFFER_LINE_STATUS,
  5621. "#&m", join_str_vector (&copy, ' '));
  5622. }
  5623. str_vector_free (&copy);
  5624. }
  5625. static void
  5626. irc_process_message (const struct irc_message *msg, struct server *s)
  5627. {
  5628. struct irc_handler key = { .name = msg->command };
  5629. struct irc_handler *handler = bsearch (&key, g_irc_handlers,
  5630. N_ELEMENTS (g_irc_handlers), sizeof key, irc_handler_cmp_by_name);
  5631. if (handler)
  5632. handler->handler (s, msg);
  5633. unsigned long numeric;
  5634. if (xstrtoul (&numeric, msg->command, 10))
  5635. irc_process_numeric (s, msg, numeric);
  5636. }
  5637. // --- Message autosplitting magic ---------------------------------------------
  5638. // This is the most basic acceptable algorithm; something like ICU with proper
  5639. // locale specification would be needed to make it work better.
  5640. static size_t
  5641. wrap_text_for_single_line (const char *text, size_t text_len,
  5642. size_t line_len, struct str *output)
  5643. {
  5644. int eaten = 0;
  5645. // First try going word by word
  5646. const char *word_start;
  5647. const char *word_end = text + strcspn (text, " ");
  5648. size_t word_len = word_end - text;
  5649. while (line_len && word_len <= line_len)
  5650. {
  5651. if (word_len)
  5652. {
  5653. str_append_data (output, text, word_len);
  5654. text += word_len;
  5655. eaten += word_len;
  5656. line_len -= word_len;
  5657. }
  5658. // Find the next word's end
  5659. word_start = text + strspn (text, " ");
  5660. word_end = word_start + strcspn (word_start, " ");
  5661. word_len = word_end - text;
  5662. }
  5663. if (eaten)
  5664. // Discard whitespace between words if split
  5665. return eaten + (word_start - text);
  5666. // And if that doesn't help, cut the longest valid block of characters
  5667. while (true)
  5668. {
  5669. const char *next = utf8_next (text, text_len - eaten, NULL);
  5670. hard_assert (next);
  5671. size_t char_len = next - text;
  5672. if (char_len > line_len)
  5673. break;
  5674. str_append_data (output, text, char_len);
  5675. text += char_len;
  5676. eaten += char_len;
  5677. line_len -= char_len;
  5678. }
  5679. return eaten;
  5680. }
  5681. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  5682. static bool
  5683. wrap_message (const char *message,
  5684. int line_max, struct str_vector *output, struct error **e)
  5685. {
  5686. if (line_max <= 0)
  5687. goto error;
  5688. for (size_t message_left = strlen (message); message_left; )
  5689. {
  5690. struct str m;
  5691. str_init (&m);
  5692. size_t eaten = wrap_text_for_single_line (message,
  5693. MIN ((size_t) line_max, message_left), message_left, &m);
  5694. if (!eaten)
  5695. {
  5696. str_free (&m);
  5697. goto error;
  5698. }
  5699. str_vector_add_owned (output, str_steal (&m));
  5700. message += eaten;
  5701. message_left -= eaten;
  5702. }
  5703. return true;
  5704. error:
  5705. // Well, that's just weird
  5706. error_set (e,
  5707. "Message splitting was unsuccessful as there was "
  5708. "too little room for UTF-8 characters");
  5709. return false;
  5710. }
  5711. /// Automatically splits messages that arrive at other clients with our prefix
  5712. /// so that they don't arrive cut off by the server
  5713. static bool
  5714. irc_autosplit_message (struct server *s, const char *message,
  5715. int fixed_part, struct str_vector *output, struct error **e)
  5716. {
  5717. // :<nick>!<user>@<host> <fixed-part><message>
  5718. int space_in_one_message = 0;
  5719. if (s->irc_user && s->irc_user_host)
  5720. space_in_one_message = 510
  5721. - 1 - (int) strlen (s->irc_user->nickname)
  5722. - 1 - (int) strlen (s->irc_user_host)
  5723. - 1 - fixed_part;
  5724. // However we don't always have the full info for message splitting
  5725. if (!space_in_one_message)
  5726. str_vector_add (output, message);
  5727. else if (!wrap_message (message, space_in_one_message, output, e))
  5728. return false;
  5729. return true;
  5730. }
  5731. struct send_autosplit_args;
  5732. typedef void (*send_autosplit_logger_fn) (struct server *s,
  5733. struct send_autosplit_args *args, struct buffer *buffer, const char *line);
  5734. struct send_autosplit_args
  5735. {
  5736. const char *command; ///< E.g. PRIVMSG or NOTICE
  5737. const char *target; ///< User or channel
  5738. const char *message; ///< A message to be autosplit
  5739. send_autosplit_logger_fn logger; ///< Logger for all resulting lines
  5740. const char *prefix; ///< E.g. "\x01ACTION"
  5741. const char *suffix; ///< E.g. "\x01"
  5742. };
  5743. static void
  5744. send_autosplit_message (struct server *s, struct send_autosplit_args a)
  5745. {
  5746. struct buffer *buffer = str_map_find (&s->irc_buffer_map, a.target);
  5747. int fixed_part = strlen (a.command) + 1 + strlen (a.target) + 1 + 1
  5748. + strlen (a.prefix) + strlen (a.suffix);
  5749. // We might also want to preserve attributes across splits but
  5750. // that would make this code a lot more complicated
  5751. struct str_vector lines;
  5752. str_vector_init (&lines);
  5753. struct error *e = NULL;
  5754. if (!irc_autosplit_message (s, a.message, fixed_part, &lines, &e))
  5755. {
  5756. log_server_error (s, buffer ? buffer : s->buffer, "#s", e->message);
  5757. error_free (e);
  5758. goto end;
  5759. }
  5760. for (size_t i = 0; i < lines.len; i++)
  5761. {
  5762. irc_send (s, "%s %s :%s%s%s", a.command, a.target,
  5763. a.prefix, lines.vector[i], a.suffix);
  5764. if (!s->cap_echo_message)
  5765. a.logger (s, &a, buffer, lines.vector[i]);
  5766. }
  5767. end:
  5768. str_vector_free (&lines);
  5769. }
  5770. static void
  5771. log_autosplit_action (struct server *s,
  5772. struct send_autosplit_args *a, struct buffer *buffer, const char *line)
  5773. {
  5774. (void) a;
  5775. if (buffer && soft_assert (s->irc_user))
  5776. log_outcoming_action (s, buffer, s->irc_user->nickname, line);
  5777. // This can only be sent from a user or channel buffer
  5778. }
  5779. #define SEND_AUTOSPLIT_ACTION(s, target, message) \
  5780. send_autosplit_message ((s), (struct send_autosplit_args) \
  5781. { "PRIVMSG", (target), (message), log_autosplit_action, \
  5782. "\x01" "ACTION ", "\x01" })
  5783. static void
  5784. log_autosplit_privmsg (struct server *s,
  5785. struct send_autosplit_args *a, struct buffer *buffer, const char *line)
  5786. {
  5787. char *prefixes = irc_get_privmsg_prefix (s, s->irc_user, a->target);
  5788. if (buffer && soft_assert (s->irc_user))
  5789. log_outcoming_privmsg (s, buffer,
  5790. prefixes, s->irc_user->nickname, line);
  5791. else
  5792. log_outcoming_orphan_privmsg (s, a->target, line);
  5793. free (prefixes);
  5794. }
  5795. #define SEND_AUTOSPLIT_PRIVMSG(s, target, message) \
  5796. send_autosplit_message ((s), (struct send_autosplit_args) \
  5797. { "PRIVMSG", (target), (message), log_autosplit_privmsg, "", "" })
  5798. static void
  5799. log_autosplit_notice (struct server *s,
  5800. struct send_autosplit_args *a, struct buffer *buffer, const char *line)
  5801. {
  5802. if (buffer && soft_assert (s->irc_user))
  5803. log_outcoming_notice (s, buffer, s->irc_user->nickname, line);
  5804. else
  5805. log_outcoming_orphan_notice (s, a->target, line);
  5806. }
  5807. #define SEND_AUTOSPLIT_NOTICE(s, target, message) \
  5808. send_autosplit_message ((s), (struct send_autosplit_args) \
  5809. { "NOTICE", (target), (message), log_autosplit_notice, "", "" })
  5810. // --- Configuration dumper ----------------------------------------------------
  5811. struct config_dump_level
  5812. {
  5813. struct config_dump_level *next; ///< Next print level
  5814. const char *name; ///< Name of the object
  5815. };
  5816. struct config_dump_data
  5817. {
  5818. struct config_dump_level *head; ///< The first level
  5819. struct config_dump_level **tail; ///< Where to place further levels
  5820. struct str_vector *output; ///< Where to place new entries
  5821. };
  5822. static void config_dump_item
  5823. (struct config_item *item, struct config_dump_data *data);
  5824. static void
  5825. config_dump_children
  5826. (struct config_item *object, struct config_dump_data *data)
  5827. {
  5828. hard_assert (object->type == CONFIG_ITEM_OBJECT);
  5829. struct config_dump_level level;
  5830. level.next = NULL;
  5831. struct config_dump_level **prev_tail = data->tail;
  5832. *data->tail = &level;
  5833. data->tail = &level.next;
  5834. struct str_map_iter iter;
  5835. str_map_iter_init (&iter, &object->value.object);
  5836. struct config_item *child;
  5837. while ((child = str_map_iter_next (&iter)))
  5838. {
  5839. level.name = iter.link->key;
  5840. config_dump_item (child, data);
  5841. }
  5842. data->tail = prev_tail;
  5843. *data->tail = NULL;
  5844. }
  5845. static void
  5846. config_dump_item (struct config_item *item, struct config_dump_data *data)
  5847. {
  5848. // Empty objects will show as such
  5849. if (item->type == CONFIG_ITEM_OBJECT
  5850. && item->value.object.len)
  5851. {
  5852. config_dump_children (item, data);
  5853. return;
  5854. }
  5855. // Currently there's no reason for us to dump unknown items
  5856. struct config_schema *schema = item->schema;
  5857. if (!schema)
  5858. return;
  5859. struct str line;
  5860. str_init (&line);
  5861. struct config_dump_level *iter = data->head;
  5862. if (iter)
  5863. {
  5864. str_append (&line, iter->name);
  5865. iter = iter->next;
  5866. }
  5867. for (; iter; iter = iter->next)
  5868. str_append_printf (&line, ".%s", iter->name);
  5869. struct str value;
  5870. str_init (&value);
  5871. config_item_write (item, false, &value);
  5872. // Don't bother writing out null values everywhere
  5873. bool has_default = schema && schema->default_;
  5874. if (item->type != CONFIG_ITEM_NULL || has_default)
  5875. {
  5876. str_append (&line, " = ");
  5877. str_append_str (&line, &value);
  5878. }
  5879. if (!schema)
  5880. str_append (&line, " (unrecognized)");
  5881. else if (has_default && strcmp (schema->default_, value.str))
  5882. str_append_printf (&line, " (default: %s)", schema->default_);
  5883. else if (!has_default && item->type != CONFIG_ITEM_NULL)
  5884. str_append_printf (&line, " (default: %s)", "null");
  5885. str_free (&value);
  5886. str_vector_add_owned (data->output, str_steal (&line));
  5887. }
  5888. static void
  5889. config_dump (struct config_item *root, struct str_vector *output)
  5890. {
  5891. struct config_dump_data data;
  5892. data.head = NULL;
  5893. data.tail = &data.head;
  5894. data.output = output;
  5895. config_dump_item (root, &data);
  5896. }
  5897. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  5898. static int
  5899. str_vector_sort_cb (const void *a, const void *b)
  5900. {
  5901. return strcmp (*(const char **) a, *(const char **) b);
  5902. }
  5903. static void
  5904. str_vector_sort (struct str_vector *self)
  5905. {
  5906. qsort (self->vector, self->len, sizeof *self->vector, str_vector_sort_cb);
  5907. }
  5908. static void
  5909. dump_matching_options
  5910. (struct app_context *ctx, const char *mask, struct str_vector *output)
  5911. {
  5912. config_dump (ctx->config.root, output);
  5913. str_vector_sort (output);
  5914. // Filter out results by wildcard matching
  5915. for (size_t i = 0; i < output->len; i++)
  5916. {
  5917. // Yeah, I know
  5918. char *key = cstr_cut_until (output->vector[i], " ");
  5919. if (fnmatch (mask, key, 0))
  5920. str_vector_remove (output, i--);
  5921. free (key);
  5922. }
  5923. }
  5924. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  5925. static void
  5926. save_configuration (struct app_context *ctx)
  5927. {
  5928. struct str data;
  5929. str_init (&data);
  5930. serialize_configuration (ctx, &data);
  5931. struct error *e = NULL;
  5932. char *filename = write_configuration_file (&data, &e);
  5933. str_free (&data);
  5934. if (!filename)
  5935. {
  5936. log_global_error (ctx,
  5937. "#s: #s", "Saving configuration failed", e->message);
  5938. error_free (e);
  5939. }
  5940. else
  5941. log_global_status (ctx, "Configuration written to `#s'", filename);
  5942. free (filename);
  5943. }
  5944. // --- Server management -------------------------------------------------------
  5945. static bool
  5946. validate_server_name (const char *name)
  5947. {
  5948. for (const unsigned char *p = (const unsigned char *) name; *p; p++)
  5949. if (*p < 32 || *p == '.')
  5950. return false;
  5951. return true;
  5952. }
  5953. static const char *
  5954. check_server_name_for_addition (struct app_context *ctx, const char *name)
  5955. {
  5956. if (!strcasecmp_ascii (name, ctx->global_buffer->name))
  5957. return "name collides with the global buffer";
  5958. if (str_map_find (&ctx->servers, name))
  5959. return "server already exists";
  5960. if (!validate_server_name (name))
  5961. return "invalid server name";
  5962. return NULL;
  5963. }
  5964. static struct server *
  5965. server_add (struct app_context *ctx,
  5966. const char *name, struct config_item *subtree)
  5967. {
  5968. hard_assert (!str_map_find (&ctx->servers, name));
  5969. struct server *s = server_new (&ctx->poller);
  5970. s->ctx = ctx;
  5971. s->name = xstrdup (name);
  5972. str_map_set (&ctx->servers, s->name, s);
  5973. s->config = subtree;
  5974. // Add a buffer and activate it
  5975. struct buffer *buffer = s->buffer = buffer_new ();
  5976. buffer->type = BUFFER_SERVER;
  5977. buffer->name = xstrdup (s->name);
  5978. buffer->server = s;
  5979. buffer_add (ctx, buffer);
  5980. buffer_activate (ctx, buffer);
  5981. config_schema_apply_to_object (g_config_server, subtree, s);
  5982. config_schema_call_changed (subtree);
  5983. if (get_config_boolean (s->config, "autoconnect"))
  5984. // Connect to the server ASAP
  5985. poller_timer_set (&s->reconnect_tmr, 0);
  5986. return s;
  5987. }
  5988. static void
  5989. server_add_new (struct app_context *ctx, const char *name)
  5990. {
  5991. // Note that there may already be something in the configuration under
  5992. // that key that we've ignored earlier, and there may also be
  5993. // a case-insensitive conflict. Those things may only happen as a result
  5994. // of manual edits to the configuration, though, and they're not really
  5995. // going to break anything. They only cause surprises when loading.
  5996. struct str_map *servers = get_servers_config (ctx);
  5997. struct config_item *subtree = config_item_object ();
  5998. str_map_set (servers, name, subtree);
  5999. struct server *s = server_add (ctx, name, subtree);
  6000. struct error *e = NULL;
  6001. if (!irc_autofill_user_info (s, &e))
  6002. {
  6003. log_server_error (s, s->buffer,
  6004. "#s: #s", "Failed to fill in user details", e->message);
  6005. error_free (e);
  6006. }
  6007. }
  6008. static void
  6009. server_remove (struct app_context *ctx, struct server *s)
  6010. {
  6011. hard_assert (!irc_is_connected (s));
  6012. if (s->buffer)
  6013. buffer_remove_safe (ctx, s->buffer);
  6014. struct str_map_unset_iter iter;
  6015. str_map_unset_iter_init (&iter, &s->irc_buffer_map);
  6016. struct buffer *buffer;
  6017. while ((buffer = str_map_unset_iter_next (&iter)))
  6018. buffer_remove_safe (ctx, buffer);
  6019. str_map_unset_iter_free (&iter);
  6020. hard_assert (!s->buffer);
  6021. hard_assert (!s->irc_buffer_map.len);
  6022. hard_assert (!s->irc_channels.len);
  6023. soft_assert (!s->irc_users.len);
  6024. str_map_set (get_servers_config (ctx), s->name, NULL);
  6025. s->config = NULL;
  6026. // This actually destroys the server as it's owned by the map
  6027. str_map_set (&ctx->servers, s->name, NULL);
  6028. }
  6029. static void
  6030. server_rename (struct app_context *ctx, struct server *s, const char *new_name)
  6031. {
  6032. str_map_set (&ctx->servers, new_name,
  6033. str_map_steal (&ctx->servers, s->name));
  6034. struct str_map *servers = get_servers_config (ctx);
  6035. str_map_set (servers, new_name, str_map_steal (servers, s->name));
  6036. free (s->name);
  6037. s->name = xstrdup (new_name);
  6038. buffer_rename (ctx, s->buffer, new_name);
  6039. struct str_map_iter iter;
  6040. str_map_iter_init (&iter, &s->irc_buffer_map);
  6041. struct buffer *buffer;
  6042. while ((buffer = str_map_iter_next (&iter)))
  6043. {
  6044. // FIXME: creation of buffer names should be centralized
  6045. char *x = NULL;
  6046. switch (buffer->type)
  6047. {
  6048. case BUFFER_PM:
  6049. x = xstrdup_printf ("%s.%s", s->name, buffer->user->nickname);
  6050. break;
  6051. case BUFFER_CHANNEL:
  6052. x = xstrdup_printf ("%s.%s", s->name, buffer->channel->name);
  6053. break;
  6054. default:
  6055. hard_assert (!"unexpected type of server-related buffer");
  6056. }
  6057. buffer_rename (ctx, buffer, x);
  6058. free (x);
  6059. }
  6060. }
  6061. // --- Lua ---------------------------------------------------------------------
  6062. // Each plugin has its own Lua state object, so that a/ they don't disturb each
  6063. // other and b/ unloading a plugin releases all resources.
  6064. //
  6065. // References to internal objects (buffers, servers) are all weak.
  6066. #ifdef HAVE_LUA
  6067. struct lua_plugin
  6068. {
  6069. struct plugin super; ///< The structure we're deriving
  6070. struct app_context *ctx; ///< Application context
  6071. lua_State *L; ///< Lua state
  6072. };
  6073. static void
  6074. lua_plugin_free (struct plugin *self_)
  6075. {
  6076. struct lua_plugin *self = (struct lua_plugin *) self_;
  6077. lua_close (self->L);
  6078. }
  6079. struct plugin_vtable lua_plugin_vtable =
  6080. {
  6081. .free = lua_plugin_free,
  6082. };
  6083. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6084. // The registry can be used as a cache for weakly referenced objects
  6085. static bool
  6086. lua_cache_get (lua_State *L, void *object)
  6087. {
  6088. lua_rawgetp (L, LUA_REGISTRYINDEX, object);
  6089. if (lua_isnil (L, -1))
  6090. {
  6091. lua_pop (L, 1);
  6092. return false;
  6093. }
  6094. return true;
  6095. }
  6096. static void
  6097. lua_cache_store (lua_State *L, void *object, int index)
  6098. {
  6099. lua_pushvalue (L, index);
  6100. lua_rawsetp (L, LUA_REGISTRYINDEX, object);
  6101. }
  6102. static void
  6103. lua_cache_invalidate (lua_State *L, void *object)
  6104. {
  6105. lua_pushnil (L);
  6106. lua_rawsetp (L, LUA_REGISTRYINDEX, object);
  6107. }
  6108. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6109. #define XLUA_BUFFER_METATABLE "buffer" ///< Identifier for the Lua metatable
  6110. struct lua_buffer
  6111. {
  6112. struct lua_plugin *plugin; ///< The plugin we belong to
  6113. struct buffer *buffer; ///< The buffer
  6114. struct weak_ref_link *weak_ref; ///< A weak reference link
  6115. };
  6116. static int
  6117. lua_buffer_gc (lua_State *L)
  6118. {
  6119. struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE);
  6120. if (wrapper->buffer)
  6121. {
  6122. lua_cache_invalidate (L, wrapper->buffer);
  6123. buffer_weak_unref (wrapper->buffer, &wrapper->weak_ref);
  6124. wrapper->buffer = NULL;
  6125. }
  6126. return 0;
  6127. }
  6128. static int
  6129. lua_buffer_log (lua_State *L)
  6130. {
  6131. struct lua_buffer *wrapper = luaL_checkudata (L, 1, XLUA_BUFFER_METATABLE);
  6132. luaL_argcheck (L, wrapper->buffer, 1, "dead reference used");
  6133. const char *message = luaL_checkstring (L, 2);
  6134. struct buffer *buffer = wrapper->buffer;
  6135. log_full (wrapper->plugin->ctx, buffer->server, buffer,
  6136. BUFFER_LINE_STATUS, "#s", message);
  6137. return 0;
  6138. }
  6139. static luaL_Reg lua_buffer_table[] =
  6140. {
  6141. // TODO: some useful methods or values
  6142. { "__gc", lua_buffer_gc },
  6143. { "log", lua_buffer_log },
  6144. { NULL, NULL }
  6145. };
  6146. static void
  6147. lua_buffer_invalidate (void *object, void *user_data)
  6148. {
  6149. struct lua_buffer *wrapper = user_data;
  6150. wrapper->buffer = NULL;
  6151. wrapper->weak_ref = NULL;
  6152. // This can in theory call the GC, order isn't arbitrary here
  6153. lua_cache_invalidate (wrapper->plugin->L, object);
  6154. }
  6155. static void
  6156. lua_plugin_push_buffer (struct lua_plugin *plugin, struct buffer *buffer)
  6157. {
  6158. lua_State *L = plugin->L;
  6159. if (lua_cache_get (L, buffer))
  6160. return;
  6161. struct lua_buffer *wrapper = lua_newuserdata (L, sizeof *wrapper);
  6162. luaL_setmetatable (L, XLUA_BUFFER_METATABLE);
  6163. wrapper->plugin = plugin;
  6164. wrapper->buffer = buffer;
  6165. wrapper->weak_ref = buffer_weak_ref
  6166. (buffer, lua_buffer_invalidate, wrapper);
  6167. lua_cache_store (L, buffer, -1);
  6168. }
  6169. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6170. #define XLUA_SERVER_METATABLE "server" ///< Identifier for the Lua metatable
  6171. struct lua_server
  6172. {
  6173. struct lua_plugin *plugin; ///< The plugin we belong to
  6174. struct server *server; ///< The server
  6175. struct weak_ref_link *weak_ref; ///< A weak reference link
  6176. };
  6177. static int
  6178. lua_server_gc (lua_State *L)
  6179. {
  6180. struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE);
  6181. if (wrapper->server)
  6182. {
  6183. lua_cache_invalidate (L, wrapper->server);
  6184. server_weak_unref (wrapper->server, &wrapper->weak_ref);
  6185. wrapper->server = NULL;
  6186. }
  6187. return 0;
  6188. }
  6189. static int
  6190. lua_server_send (lua_State *L)
  6191. {
  6192. struct lua_server *wrapper = luaL_checkudata (L, 1, XLUA_SERVER_METATABLE);
  6193. luaL_argcheck (L, wrapper->server, 1, "dead reference used");
  6194. const char *line = luaL_checkstring (L, 2);
  6195. irc_send (wrapper->server, "%s", line);
  6196. return 0;
  6197. }
  6198. static luaL_Reg lua_server_table[] =
  6199. {
  6200. // TODO: some useful methods or values
  6201. { "__gc", lua_server_gc },
  6202. { "send", lua_server_send },
  6203. { NULL, NULL }
  6204. };
  6205. static void
  6206. lua_server_invalidate (void *object, void *user_data)
  6207. {
  6208. struct lua_server *wrapper = user_data;
  6209. wrapper->server = NULL;
  6210. wrapper->weak_ref = NULL;
  6211. // This can in theory call the GC, order isn't arbitrary here
  6212. lua_cache_invalidate (wrapper->plugin->L, object);
  6213. }
  6214. static void
  6215. lua_plugin_push_server (struct lua_plugin *plugin, struct server *server)
  6216. {
  6217. lua_State *L = plugin->L;
  6218. if (lua_cache_get (L, server))
  6219. return;
  6220. struct lua_server *wrapper = lua_newuserdata (L, sizeof *wrapper);
  6221. luaL_setmetatable (L, XLUA_SERVER_METATABLE);
  6222. wrapper->plugin = plugin;
  6223. wrapper->server = server;
  6224. wrapper->weak_ref = server_weak_ref
  6225. (server, lua_server_invalidate, wrapper);
  6226. lua_cache_store (L, server, -1);
  6227. }
  6228. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6229. #define XLUA_HOOK_METATABLE "hook" ///< Identifier for the Lua metatable
  6230. enum lua_hook_type
  6231. {
  6232. XLUA_HOOK_DEFUNCT, ///< No longer functional
  6233. XLUA_HOOK_INPUT, ///< Input hook
  6234. XLUA_HOOK_IRC, ///< IRC hook
  6235. XLUA_HOOK_TIMER, ///< One-shot timer
  6236. };
  6237. struct lua_hook
  6238. {
  6239. struct lua_plugin *plugin; ///< The plugin we belong to
  6240. enum lua_hook_type type; ///< Type of the hook
  6241. union
  6242. {
  6243. struct hook hook; ///< Hook base structure
  6244. struct input_hook input_hook; ///< Input hook
  6245. struct irc_hook irc_hook; ///< IRC hook
  6246. struct poller_timer timer; ///< Timer
  6247. }
  6248. data; ///< Hook data
  6249. };
  6250. static int
  6251. lua_hook_unhook (lua_State *L)
  6252. {
  6253. struct lua_hook *hook = luaL_checkudata (L, 1, XLUA_HOOK_METATABLE);
  6254. switch (hook->type)
  6255. {
  6256. case XLUA_HOOK_INPUT:
  6257. LIST_UNLINK (hook->plugin->ctx->input_hooks, &hook->data.hook);
  6258. break;
  6259. case XLUA_HOOK_IRC:
  6260. LIST_UNLINK (hook->plugin->ctx->irc_hooks, &hook->data.hook);
  6261. break;
  6262. case XLUA_HOOK_TIMER:
  6263. poller_timer_reset (&hook->data.timer);
  6264. break;
  6265. default:
  6266. hard_assert (!"invalid hook type");
  6267. case XLUA_HOOK_DEFUNCT:
  6268. break;
  6269. }
  6270. // The hook no longer has to stay alive
  6271. hook->type = XLUA_HOOK_DEFUNCT;
  6272. lua_cache_invalidate (L, hook);
  6273. return 0;
  6274. }
  6275. // The hook dies either when the plugin requests it or at plugin unload
  6276. static luaL_Reg lua_hook_table[] =
  6277. {
  6278. { "unhook", lua_hook_unhook },
  6279. { "__gc", lua_hook_unhook },
  6280. { NULL, NULL }
  6281. };
  6282. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6283. // Append a traceback to all errors so that we can later extract it
  6284. static int
  6285. lua_plugin_error_handler (lua_State *L)
  6286. {
  6287. luaL_traceback (L, L, luaL_checkstring (L, 1), 1);
  6288. return 1;
  6289. }
  6290. static bool
  6291. lua_plugin_process_error (struct lua_plugin *self, const char *message,
  6292. struct error **e)
  6293. {
  6294. struct str_vector v;
  6295. str_vector_init (&v);
  6296. cstr_split_ignore_empty (message, '\n', &v);
  6297. if (v.len < 2)
  6298. error_set (e, "%s", message);
  6299. else
  6300. {
  6301. error_set (e, "%s", v.vector[0]);
  6302. log_global_debug (self->ctx, "Lua: plugin \"#s\": #s",
  6303. self->super.name, v.vector[1]);
  6304. for (size_t i = 2; i < v.len; i++)
  6305. log_global_debug (self->ctx, " #s", v.vector[i]);
  6306. }
  6307. str_vector_free (&v);
  6308. return false;
  6309. }
  6310. // Convenience function; replaces the "original" string or produces an error
  6311. static bool
  6312. lua_plugin_handle_string_filter_result (struct lua_plugin *self,
  6313. int result, char **original, bool utf8, struct error **e)
  6314. {
  6315. lua_State *L = self->L;
  6316. if (result)
  6317. return lua_plugin_process_error (self, lua_tostring (L, -1), e);
  6318. if (lua_isnil (L, -1))
  6319. return NULL;
  6320. if (!lua_isstring (L, -1))
  6321. FAIL ("must return either a string or nil");
  6322. size_t len;
  6323. const char *processed = lua_tolstring (L, -1, &len);
  6324. if (utf8 && !utf8_validate (processed, len))
  6325. FAIL ("must return valid UTF-8");
  6326. // Only replace the string if it's different
  6327. if (strcmp (processed, *original))
  6328. {
  6329. free (*original);
  6330. *original = xstrdup (processed);
  6331. }
  6332. return true;
  6333. }
  6334. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6335. static char *
  6336. lua_input_hook_filter (struct input_hook *self, struct buffer *buffer,
  6337. char *input)
  6338. {
  6339. struct lua_hook *hook =
  6340. CONTAINER_OF (self, struct lua_hook, data.input_hook);
  6341. struct lua_plugin *plugin = hook->plugin;
  6342. lua_State *L = plugin->L;
  6343. lua_pushcfunction (L, lua_plugin_error_handler);
  6344. lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook
  6345. lua_getuservalue (L, -1); // Retrieve function
  6346. lua_insert (L, -2); // Swap with the hook
  6347. lua_plugin_push_buffer (plugin, buffer); // 2: buffer
  6348. lua_pushstring (L, input); // 3: input
  6349. struct error *e = NULL;
  6350. bool failed = !lua_plugin_handle_string_filter_result
  6351. (plugin, lua_pcall (L, 3, 1, -5), &input, true, &e);
  6352. lua_pop (L, 1);
  6353. if (failed)
  6354. {
  6355. log_global_error (plugin->ctx, "Lua: plugin \"#s\": #s: #s",
  6356. plugin->super.name, "input hook", e->message);
  6357. error_free (e);
  6358. }
  6359. return input;
  6360. }
  6361. struct input_hook_vtable lua_input_hook_vtable =
  6362. {
  6363. .filter = lua_input_hook_filter,
  6364. };
  6365. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6366. static char *
  6367. lua_irc_hook_filter (struct irc_hook *self, struct server *s, char *message)
  6368. {
  6369. struct lua_hook *hook =
  6370. CONTAINER_OF (self, struct lua_hook, data.irc_hook);
  6371. struct lua_plugin *plugin = hook->plugin;
  6372. lua_State *L = plugin->L;
  6373. lua_pushcfunction (L, lua_plugin_error_handler);
  6374. lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook
  6375. lua_getuservalue (L, -1); // Retrieve function
  6376. lua_insert (L, -2); // Swap with the hook
  6377. lua_plugin_push_server (plugin, s); // 2: server
  6378. lua_pushstring (L, message); // 3: message
  6379. struct error *e = NULL;
  6380. bool failed = !lua_plugin_handle_string_filter_result
  6381. (plugin, lua_pcall (L, 3, 1, -5), &message, false, &e);
  6382. lua_pop (L, 1);
  6383. if (failed)
  6384. {
  6385. log_global_error (plugin->ctx, "Lua: plugin \"#s\": #s: #s",
  6386. plugin->super.name, "IRC hook", e->message);
  6387. error_free (e);
  6388. }
  6389. return message;
  6390. }
  6391. struct irc_hook_vtable lua_irc_hook_vtable =
  6392. {
  6393. .filter = lua_irc_hook_filter,
  6394. };
  6395. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6396. static void
  6397. lua_timer_hook_dispatch (void *user_data)
  6398. {
  6399. struct lua_hook *hook = user_data;
  6400. struct lua_plugin *plugin = hook->plugin;
  6401. lua_State *L = plugin->L;
  6402. lua_pushcfunction (L, lua_plugin_error_handler);
  6403. lua_rawgetp (L, LUA_REGISTRYINDEX, hook); // 1: hook
  6404. lua_getuservalue (L, -1); // Retrieve function
  6405. lua_insert (L, -2); // Swap with the hook
  6406. if (lua_pcall (L, 1, 0, -3))
  6407. {
  6408. struct error *e = NULL;
  6409. (void) lua_plugin_process_error (plugin, lua_tostring (L, -1), &e);
  6410. log_global_error (plugin->ctx, "Lua: plugin \"#s\": #s: #s",
  6411. plugin->super.name, "timer hook", e->message);
  6412. error_free (e);
  6413. }
  6414. }
  6415. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6416. static struct lua_hook *
  6417. lua_plugin_push_hook (struct lua_plugin *plugin, int callback_index,
  6418. enum lua_hook_type type, int priority)
  6419. {
  6420. lua_State *L = plugin->L;
  6421. luaL_checktype (L, callback_index, LUA_TFUNCTION);
  6422. struct lua_hook *hook = lua_newuserdata (L, sizeof *hook);
  6423. luaL_setmetatable (L, XLUA_HOOK_METATABLE);
  6424. memset (hook, 0, sizeof *hook);
  6425. hook->data.hook.priority = priority;
  6426. hook->type = type;
  6427. hook->plugin = plugin;
  6428. // Associate the callback with the hook
  6429. lua_pushvalue (L, callback_index);
  6430. lua_setuservalue (L, -2);
  6431. // Make sure the hook doesn't get garbage collected and return it
  6432. lua_cache_store (L, hook, -1);
  6433. return hook;
  6434. }
  6435. static int
  6436. lua_plugin_hook_input (lua_State *L)
  6437. {
  6438. struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
  6439. struct lua_hook *hook = lua_plugin_push_hook
  6440. (plugin, 1, XLUA_HOOK_INPUT, luaL_optinteger (L, 2, 0));
  6441. hook->data.input_hook.vtable = &lua_input_hook_vtable;
  6442. plugin->ctx->input_hooks =
  6443. hook_insert (plugin->ctx->input_hooks, &hook->data.hook);
  6444. return 1;
  6445. }
  6446. static int
  6447. lua_plugin_hook_irc (lua_State *L)
  6448. {
  6449. struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
  6450. struct lua_hook *hook = lua_plugin_push_hook
  6451. (plugin, 1, XLUA_HOOK_IRC, luaL_optinteger (L, 2, 0));
  6452. hook->data.irc_hook.vtable = &lua_irc_hook_vtable;
  6453. plugin->ctx->irc_hooks =
  6454. hook_insert (plugin->ctx->irc_hooks, &hook->data.hook);
  6455. return 1;
  6456. }
  6457. static int
  6458. lua_plugin_hook_timer (lua_State *L)
  6459. {
  6460. struct lua_plugin *plugin = lua_touserdata (L, lua_upvalueindex (1));
  6461. lua_Integer timeout = luaL_checkinteger (L, 2);
  6462. if (timeout < 0)
  6463. luaL_argerror (L, 2, "timeout mustn't be negative");
  6464. // This doesn't really hook anything but we can reuse the code
  6465. struct lua_hook *hook = lua_plugin_push_hook
  6466. (plugin, 1, XLUA_HOOK_TIMER, 0 /* priority doesn't apply */);
  6467. struct poller_timer *timer = &hook->data.timer;
  6468. poller_timer_init (timer, &plugin->ctx->poller);
  6469. timer->dispatcher = lua_timer_hook_dispatch;
  6470. timer->user_data = hook;
  6471. poller_timer_set (timer, timeout);
  6472. return 1;
  6473. }
  6474. static luaL_Reg lua_plugin_library[] =
  6475. {
  6476. { "hook_input", lua_plugin_hook_input },
  6477. { "hook_irc", lua_plugin_hook_irc },
  6478. { "hook_timer", lua_plugin_hook_timer },
  6479. { NULL, NULL },
  6480. };
  6481. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6482. static void *
  6483. lua_plugin_alloc (void *ud, void *ptr, size_t o_size, size_t n_size)
  6484. {
  6485. (void) ud;
  6486. (void) o_size;
  6487. if (n_size)
  6488. return realloc (ptr, n_size);
  6489. free (ptr);
  6490. return NULL;
  6491. }