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.

12598 lines
339KB

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