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.

13961 lines
381KB

  1. /*
  2. * degesch.c: the experimental IRC client
  3. *
  4. * Copyright (c) 2015 - 2017, 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. #define ATTR_TABLE(XX) \
  21. XX( PROMPT, "prompt", "Terminal attrs for the prompt" ) \
  22. XX( RESET, "reset", "String to reset terminal attributes" ) \
  23. XX( DATE_CHANGE, "date_change", "Terminal attrs for date change" ) \
  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_LUA
  63. #include <lua.h>
  64. #include <lualib.h>
  65. #include <lauxlib.h>
  66. #endif // HAVE_LUA
  67. // --- Terminal information ----------------------------------------------------
  68. static struct
  69. {
  70. bool initialized; ///< Terminal is available
  71. bool stdout_is_tty; ///< `stdout' is a terminal
  72. bool stderr_is_tty; ///< `stderr' is a terminal
  73. struct termios termios; ///< Terminal attributes
  74. char *color_set_fg[256]; ///< Codes to set the foreground colour
  75. char *color_set_bg[256]; ///< Codes to set the background colour
  76. int lines; ///< Number of lines
  77. int columns; ///< Number of columns
  78. }
  79. g_terminal;
  80. static void
  81. update_screen_size (void)
  82. {
  83. #ifdef TIOCGWINSZ
  84. if (!g_terminal.stdout_is_tty)
  85. return;
  86. struct winsize size;
  87. if (!ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &size))
  88. {
  89. char *row = getenv ("LINES");
  90. char *col = getenv ("COLUMNS");
  91. unsigned long tmp;
  92. g_terminal.lines =
  93. (row && xstrtoul (&tmp, row, 10)) ? tmp : size.ws_row;
  94. g_terminal.columns =
  95. (col && xstrtoul (&tmp, col, 10)) ? tmp : size.ws_col;
  96. }
  97. #endif // TIOCGWINSZ
  98. }
  99. static bool
  100. init_terminal (void)
  101. {
  102. int tty_fd = -1;
  103. if ((g_terminal.stderr_is_tty = isatty (STDERR_FILENO)))
  104. tty_fd = STDERR_FILENO;
  105. if ((g_terminal.stdout_is_tty = isatty (STDOUT_FILENO)))
  106. tty_fd = STDOUT_FILENO;
  107. int err;
  108. if (tty_fd == -1 || setupterm (NULL, tty_fd, &err) == ERR)
  109. return false;
  110. // Make sure all terminal features used by us are supported
  111. if (!set_a_foreground || !set_a_background
  112. || !enter_bold_mode || !exit_attribute_mode
  113. || tcgetattr (tty_fd, &g_terminal.termios))
  114. {
  115. del_curterm (cur_term);
  116. return false;
  117. }
  118. // Make sure newlines are output correctly
  119. g_terminal.termios.c_oflag |= ONLCR;
  120. (void) tcsetattr (tty_fd, TCSADRAIN, &g_terminal.termios);
  121. g_terminal.lines = tigetnum ("lines");
  122. g_terminal.columns = tigetnum ("cols");
  123. update_screen_size ();
  124. int max = MIN (256, max_colors);
  125. for (int i = 0; i < max; i++)
  126. {
  127. g_terminal.color_set_fg[i] = xstrdup (tparm (set_a_foreground,
  128. i, 0, 0, 0, 0, 0, 0, 0, 0));
  129. g_terminal.color_set_bg[i] = xstrdup (tparm (set_a_background,
  130. i, 0, 0, 0, 0, 0, 0, 0, 0));
  131. }
  132. return g_terminal.initialized = true;
  133. }
  134. static void
  135. free_terminal (void)
  136. {
  137. if (!g_terminal.initialized)
  138. return;
  139. for (int i = 0; i < 256; i++)
  140. {
  141. free (g_terminal.color_set_fg[i]);
  142. free (g_terminal.color_set_bg[i]);
  143. }
  144. del_curterm (cur_term);
  145. }
  146. // --- User interface ----------------------------------------------------------
  147. // I'm not sure which one of these backends is worse: whether it's GNU Readline
  148. // or BSD Editline. They both have their own annoying problems. We use lots
  149. // of hacks to get the results we want and need.
  150. //
  151. // The abstraction is a necessary evil. It's still not 100%, though.
  152. /// Some arbitrary limit for the history
  153. #define HISTORY_LIMIT 10000
  154. /// Characters that separate words
  155. #define WORD_BREAKING_CHARS " \f\n\r\t\v"
  156. struct input
  157. {
  158. struct input_vtable *vtable; ///< Virtual methods
  159. void (*add_functions) (void *); ///< Define functions for binding
  160. void *user_data; ///< User data for callbacks
  161. };
  162. typedef void *input_buffer_t; ///< Pointer alias for input buffers
  163. /// Named function that can be bound to a sequence of characters
  164. typedef bool (*input_fn) (int count, int key, void *user_data);
  165. // A little bit better than tons of forwarder functions in our case
  166. #define CALL(self, name) ((self)->vtable->name ((self)))
  167. #define CALL_(self, name, ...) ((self)->vtable->name ((self), __VA_ARGS__))
  168. struct input_vtable
  169. {
  170. /// Start the interface under the given program name
  171. void (*start) (void *input, const char *program_name);
  172. /// Stop the interface
  173. void (*stop) (void *input);
  174. /// Prepare or unprepare terminal for our needs
  175. void (*prepare) (void *input, bool enabled);
  176. /// Destroy the object
  177. void (*destroy) (void *input);
  178. /// Hide the prompt if shown
  179. void (*hide) (void *input);
  180. /// Show the prompt if hidden
  181. void (*show) (void *input);
  182. /// Retrieve current prompt string
  183. const char *(*get_prompt) (void *input);
  184. /// Change the prompt string; takes ownership
  185. void (*set_prompt) (void *input, char *prompt);
  186. /// Ring the terminal bell
  187. void (*ding) (void *input);
  188. /// Create a new input buffer
  189. input_buffer_t (*buffer_new) (void *input);
  190. /// Destroy an input buffer
  191. void (*buffer_destroy) (void *input, input_buffer_t buffer);
  192. /// Switch to a different input buffer
  193. void (*buffer_switch) (void *input, input_buffer_t buffer);
  194. /// Register a function that can be bound to character sequences
  195. void (*register_fn) (void *input,
  196. const char *name, const char *help, input_fn fn, void *user_data);
  197. /// Bind an arbitrary sequence of characters to the given named function
  198. void (*bind) (void *input, const char *seq, const char *fn);
  199. /// Bind Ctrl+key to the given named function
  200. void (*bind_control) (void *input, char key, const char *fn);
  201. /// Bind Alt+key to the given named function
  202. void (*bind_meta) (void *input, char key, const char *fn);
  203. /// Get the current line input
  204. char *(*get_line) (void *input);
  205. /// Clear the current line input
  206. void (*clear_line) (void *input);
  207. /// Insert text at current position
  208. bool (*insert) (void *input, const char *text);
  209. /// Handle terminal resize
  210. void (*on_tty_resized) (void *input);
  211. /// Handle terminal input
  212. void (*on_tty_readable) (void *input);
  213. };
  214. #define INPUT_VTABLE(XX) \
  215. XX (start) XX (stop) XX (prepare) XX (destroy) \
  216. XX (hide) XX (show) XX (get_prompt) XX (set_prompt) XX (ding) \
  217. XX (buffer_new) XX (buffer_destroy) XX (buffer_switch) \
  218. XX (register_fn) XX (bind) XX (bind_control) XX (bind_meta) \
  219. XX (get_line) XX (clear_line) XX (insert) \
  220. XX (on_tty_resized) XX (on_tty_readable)
  221. // --- GNU Readline ------------------------------------------------------------
  222. #ifdef HAVE_READLINE
  223. #include <readline/readline.h>
  224. #include <readline/history.h>
  225. #define INPUT_START_IGNORE RL_PROMPT_START_IGNORE
  226. #define INPUT_END_IGNORE RL_PROMPT_END_IGNORE
  227. struct input_rl_fn
  228. {
  229. ffi_closure closure; ///< Closure
  230. LIST_HEADER (struct input_rl_fn)
  231. input_fn callback; ///< Real callback
  232. void *user_data; ///< Real callback user data
  233. };
  234. struct input_rl_buffer
  235. {
  236. HISTORY_STATE *history; ///< Saved history state
  237. char *saved_line; ///< Saved line content
  238. int saved_point; ///< Saved cursor position
  239. int saved_mark; ///< Saved mark
  240. };
  241. struct input_rl
  242. {
  243. struct input super; ///< Parent class
  244. bool active; ///< Interface has been started
  245. char *prompt; ///< The prompt we use
  246. int prompt_shown; ///< Whether the prompt is shown now
  247. char *saved_line; ///< Saved line content
  248. int saved_point; ///< Saved cursor position
  249. int saved_mark; ///< Saved mark
  250. struct input_rl_fn *fns; ///< Named functions
  251. struct input_rl_buffer *current; ///< Current input buffer
  252. };
  253. static void
  254. input_rl_ding (void *input)
  255. {
  256. (void) input;
  257. rl_ding ();
  258. }
  259. static const char *
  260. input_rl_get_prompt (void *input)
  261. {
  262. struct input_rl *self = input;
  263. return self->prompt;
  264. }
  265. static void
  266. input_rl_set_prompt (void *input, char *prompt)
  267. {
  268. struct input_rl *self = input;
  269. free (self->prompt);
  270. self->prompt = prompt;
  271. if (!self->active)
  272. return;
  273. // First reset the prompt to work around a bug in readline
  274. rl_set_prompt ("");
  275. if (self->prompt_shown > 0)
  276. rl_redisplay ();
  277. rl_set_prompt (self->prompt);
  278. if (self->prompt_shown > 0)
  279. rl_redisplay ();
  280. }
  281. static void
  282. input_rl_clear_line (void *input)
  283. {
  284. (void) input;
  285. rl_replace_line ("", false);
  286. rl_redisplay ();
  287. }
  288. static void
  289. input_rl__erase (struct input_rl *self)
  290. {
  291. rl_set_prompt ("");
  292. input_rl_clear_line (self);
  293. }
  294. static bool
  295. input_rl_insert (void *input, const char *s)
  296. {
  297. struct input_rl *self = input;
  298. rl_insert_text (s);
  299. if (self->prompt_shown > 0)
  300. rl_redisplay ();
  301. // GNU Readline, contrary to Editline, doesn't care about validity
  302. return true;
  303. }
  304. static char *
  305. input_rl_get_line (void *input)
  306. {
  307. (void) input;
  308. return rl_copy_text (0, rl_end);
  309. }
  310. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  311. static void
  312. input_rl_bind (void *input, const char *seq, const char *function_name)
  313. {
  314. (void) input;
  315. rl_bind_keyseq (seq, rl_named_function (function_name));
  316. }
  317. static void
  318. input_rl_bind_meta (void *input, char key, const char *function_name)
  319. {
  320. // This one seems to actually work
  321. char keyseq[] = { '\\', 'e', key, 0 };
  322. input_rl_bind (input, keyseq, function_name);
  323. #if 0
  324. // While this one only fucks up UTF-8
  325. // Tested with urxvt and xterm, on Debian Jessie/Arch, default settings
  326. // \M-<key> behaves exactly the same
  327. rl_bind_key (META (key), rl_named_function (function_name));
  328. #endif
  329. }
  330. static void
  331. input_rl_bind_control (void *input, char key, const char *function_name)
  332. {
  333. char keyseq[] = { '\\', 'C', '-', key, 0 };
  334. input_rl_bind (input, keyseq, function_name);
  335. }
  336. static void
  337. input_rl__forward (ffi_cif *cif, void *ret, void **args, void *user_data)
  338. {
  339. (void) cif;
  340. struct input_rl_fn *data = user_data;
  341. if (!data->callback
  342. (*(int *) args[0], UNMETA (*(int *) args[1]), data->user_data))
  343. rl_ding ();
  344. *(int *) ret = 0;
  345. }
  346. static void
  347. input_rl_register_fn (void *input,
  348. const char *name, const char *help, input_fn callback, void *user_data)
  349. {
  350. struct input_rl *self = input;
  351. (void) help;
  352. void *bound_fn = NULL;
  353. struct input_rl_fn *data = ffi_closure_alloc (sizeof *data, &bound_fn);
  354. hard_assert (data);
  355. static ffi_cif cif;
  356. static ffi_type *args[2] = { &ffi_type_sint, &ffi_type_sint };
  357. hard_assert (ffi_prep_cif
  358. (&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint, args) == FFI_OK);
  359. data->prev = data->next = NULL;
  360. data->callback = callback;
  361. data->user_data = user_data;
  362. hard_assert (ffi_prep_closure_loc (&data->closure,
  363. &cif, input_rl__forward, data, bound_fn) == FFI_OK);
  364. rl_add_defun (name, (rl_command_func_t *) bound_fn, -1);
  365. LIST_PREPEND (self->fns, data);
  366. }
  367. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  368. static int app_readline_init (void);
  369. static void on_readline_input (char *line);
  370. static char **app_readline_completion (const char *text, int start, int end);
  371. static void
  372. input_rl_start (void *input, const char *program_name)
  373. {
  374. struct input_rl *self = input;
  375. using_history ();
  376. // This can cause memory leaks, or maybe even a segfault. Funny, eh?
  377. stifle_history (HISTORY_LIMIT);
  378. const char *slash = strrchr (program_name, '/');
  379. rl_readline_name = slash ? ++slash : program_name;
  380. rl_startup_hook = app_readline_init;
  381. rl_catch_sigwinch = false;
  382. rl_basic_word_break_characters = WORD_BREAKING_CHARS;
  383. rl_completer_word_break_characters = NULL;
  384. rl_attempted_completion_function = app_readline_completion;
  385. hard_assert (self->prompt != NULL);
  386. // The inputrc is read before any callbacks are called, so we need to
  387. // register all functions that our user may want to map up front
  388. self->super.add_functions (self->super.user_data);
  389. rl_callback_handler_install (self->prompt, on_readline_input);
  390. self->prompt_shown = 1;
  391. self->active = true;
  392. }
  393. static void
  394. input_rl_stop (void *input)
  395. {
  396. struct input_rl *self = input;
  397. if (self->prompt_shown > 0)
  398. input_rl__erase (self);
  399. // This is okay as long as we're not called from within readline
  400. rl_callback_handler_remove ();
  401. self->active = false;
  402. self->prompt_shown = false;
  403. }
  404. static void
  405. input_rl_prepare (void *input, bool enabled)
  406. {
  407. (void) input;
  408. if (enabled)
  409. rl_prep_terminal (true);
  410. else
  411. rl_deprep_terminal ();
  412. }
  413. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  414. // The following part shows you why it's not a good idea to use
  415. // GNU Readline for this kind of software. Or for anything else, really.
  416. static void
  417. input_rl__save_buffer (struct input_rl *self, struct input_rl_buffer *buffer)
  418. {
  419. (void) self;
  420. buffer->history = history_get_history_state ();
  421. buffer->saved_line = rl_copy_text (0, rl_end);
  422. buffer->saved_point = rl_point;
  423. buffer->saved_mark = rl_mark;
  424. rl_replace_line ("", true);
  425. if (self->prompt_shown > 0)
  426. rl_redisplay ();
  427. }
  428. static void
  429. input_rl__restore_buffer (struct input_rl *self, struct input_rl_buffer *buffer)
  430. {
  431. if (buffer->history)
  432. {
  433. // history_get_history_state() just allocates a new HISTORY_STATE
  434. // and fills it with its current internal data. We don't need that
  435. // shell anymore after reviving it.
  436. history_set_history_state (buffer->history);
  437. free (buffer->history);
  438. buffer->history = NULL;
  439. }
  440. else
  441. {
  442. // This should get us a clean history while keeping the flags.
  443. // Note that we've either saved the previous history entries, or we've
  444. // cleared them altogether, so there should be nothing to leak.
  445. HISTORY_STATE *state = history_get_history_state ();
  446. state->offset = state->length = state->size = 0;
  447. state->entries = NULL;
  448. history_set_history_state (state);
  449. free (state);
  450. }
  451. if (buffer->saved_line)
  452. {
  453. rl_replace_line (buffer->saved_line, true);
  454. rl_point = buffer->saved_point;
  455. rl_mark = buffer->saved_mark;
  456. free (buffer->saved_line);
  457. buffer->saved_line = NULL;
  458. if (self->prompt_shown > 0)
  459. rl_redisplay ();
  460. }
  461. }
  462. static void
  463. input_rl_buffer_switch (void *input, input_buffer_t input_buffer)
  464. {
  465. struct input_rl *self = input;
  466. struct input_rl_buffer *buffer = input_buffer;
  467. // There could possibly be occurences of the current undo list in some
  468. // history entry. We either need to free the undo list, or move it
  469. // somewhere else to load back later, as the buffer we're switching to
  470. // has its own history state.
  471. rl_free_undo_list ();
  472. // Save this buffer's history so that it's independent for each buffer
  473. if (self->current)
  474. input_rl__save_buffer (self, self->current);
  475. else
  476. // Just throw it away; there should always be an active buffer however
  477. #if RL_READLINE_VERSION >= 0x0603
  478. rl_clear_history ();
  479. #else // RL_READLINE_VERSION < 0x0603
  480. // At least something... this may leak undo entries
  481. clear_history ();
  482. #endif // RL_READLINE_VERSION < 0x0603
  483. input_rl__restore_buffer (self, buffer);
  484. self->current = buffer;
  485. }
  486. static void
  487. input_rl__buffer_destroy_wo_history (struct input_rl_buffer *self)
  488. {
  489. free (self->history);
  490. free (self->saved_line);
  491. free (self);
  492. }
  493. static void
  494. input_rl_buffer_destroy (void *input, input_buffer_t input_buffer)
  495. {
  496. (void) input;
  497. struct input_rl_buffer *buffer = input_buffer;
  498. // rl_clear_history, being the only way I know of to get rid of the complete
  499. // history including attached data, is a pretty recent addition. *sigh*
  500. #if RL_READLINE_VERSION >= 0x0603
  501. if (buffer->history)
  502. {
  503. // See input_rl_buffer_switch() for why we need to do this BS
  504. rl_free_undo_list ();
  505. // This is probably the only way we can free the history fully
  506. HISTORY_STATE *state = history_get_history_state ();
  507. history_set_history_state (buffer->history);
  508. rl_clear_history ();
  509. // rl_clear_history just removes history entries,
  510. // we have to reclaim memory for their actual container ourselves
  511. free (buffer->history->entries);
  512. free (buffer->history);
  513. buffer->history = NULL;
  514. history_set_history_state (state);
  515. free (state);
  516. }
  517. #endif // RL_READLINE_VERSION
  518. input_rl__buffer_destroy_wo_history (buffer);
  519. }
  520. static input_buffer_t
  521. input_rl_buffer_new (void *input)
  522. {
  523. (void) input;
  524. struct input_rl_buffer *self = xcalloc (1, sizeof *self);
  525. return self;
  526. }
  527. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  528. // Since {save,restore}_buffer() store history, we can't use them here like we
  529. // do with libedit, because then buffer_destroy() can free memory that's still
  530. // being used by readline. This situation is bound to happen on quit.
  531. static void
  532. input_rl__save (struct input_rl *self)
  533. {
  534. hard_assert (!self->saved_line);
  535. self->saved_point = rl_point;
  536. self->saved_mark = rl_mark;
  537. self->saved_line = rl_copy_text (0, rl_end);
  538. }
  539. static void
  540. input_rl__restore (struct input_rl *self)
  541. {
  542. hard_assert (self->saved_line);
  543. rl_set_prompt (self->prompt);
  544. rl_replace_line (self->saved_line, false);
  545. rl_point = self->saved_point;
  546. rl_mark = self->saved_mark;
  547. free (self->saved_line);
  548. self->saved_line = NULL;
  549. }
  550. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  551. static void
  552. input_rl_hide (void *input)
  553. {
  554. struct input_rl *self = input;
  555. if (!self->active || self->prompt_shown-- < 1)
  556. return;
  557. input_rl__save (self);
  558. input_rl__erase (self);
  559. }
  560. static void
  561. input_rl_show (void *input)
  562. {
  563. struct input_rl *self = input;
  564. if (!self->active || ++self->prompt_shown < 1)
  565. return;
  566. input_rl__restore (self);
  567. rl_redisplay ();
  568. }
  569. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  570. static void
  571. input_rl_on_tty_resized (void *input)
  572. {
  573. (void) input;
  574. // This fucks up big time on terminals with automatic wrapping such as
  575. // rxvt-unicode or newer VTE when the current line overflows, however we
  576. // can't do much about that
  577. rl_resize_terminal ();
  578. }
  579. static void
  580. input_rl_on_tty_readable (void *input)
  581. {
  582. (void) input;
  583. rl_callback_read_char ();
  584. }
  585. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  586. static void
  587. input_rl_destroy (void *input)
  588. {
  589. struct input_rl *self = input;
  590. free (self->saved_line);
  591. LIST_FOR_EACH (struct input_rl_fn, iter, self->fns)
  592. ffi_closure_free (iter);
  593. free (self->prompt);
  594. free (self);
  595. }
  596. #define XX(a) .a = input_rl_ ## a,
  597. static struct input_vtable input_rl_vtable = { INPUT_VTABLE (XX) };
  598. #undef XX
  599. static struct input *
  600. input_rl_new (void)
  601. {
  602. struct input_rl *self = xcalloc (1, sizeof *self);
  603. self->super.vtable = &input_rl_vtable;
  604. return &self->super;
  605. }
  606. #define input_new input_rl_new
  607. #endif // HAVE_READLINE
  608. // --- BSD Editline ------------------------------------------------------------
  609. #ifdef HAVE_EDITLINE
  610. #include <histedit.h>
  611. #define INPUT_START_IGNORE '\x01'
  612. #define INPUT_END_IGNORE '\x01'
  613. struct input_el_fn
  614. {
  615. ffi_closure closure; ///< Closure
  616. LIST_HEADER (struct input_el_fn)
  617. input_fn callback; ///< Real callback
  618. void *user_data; ///< Real callback user data
  619. wchar_t *name; ///< Function name
  620. wchar_t *help; ///< Function help
  621. };
  622. struct input_el_buffer
  623. {
  624. HistoryW *history; ///< The history object
  625. wchar_t *saved_line; ///< Saved line content
  626. int saved_len; ///< Length of the saved line
  627. int saved_point; ///< Saved cursor position
  628. };
  629. struct input_el
  630. {
  631. struct input super; ///< Parent class
  632. EditLine *editline; ///< The EditLine object
  633. bool active; ///< Are we a thing?
  634. char *prompt; ///< The prompt we use
  635. int prompt_shown; ///< Whether the prompt is shown now
  636. struct input_el_fn *fns; ///< Named functions
  637. struct input_el_buffer *current; ///< Current input buffer
  638. };
  639. static void app_editline_init (struct input_el *self);
  640. static int
  641. input_el__get_termios (int character, int fallback)
  642. {
  643. if (!g_terminal.initialized)
  644. return fallback;
  645. cc_t value = g_terminal.termios.c_cc[character];
  646. if (value == _POSIX_VDISABLE)
  647. return fallback;
  648. return value;
  649. }
  650. static void
  651. input_el__redisplay (void *input)
  652. {
  653. // See rl_redisplay()
  654. struct input_el *self = input;
  655. char x[] = { input_el__get_termios (VREPRINT, 'R' - 0x40), 0 };
  656. el_push (self->editline, x);
  657. // We have to do this or it gets stuck and nothing is done
  658. (void) el_gets (self->editline, NULL);
  659. }
  660. static char *
  661. input_el__make_prompt (EditLine *editline)
  662. {
  663. struct input_el *self;
  664. el_get (editline, EL_CLIENTDATA, &self);
  665. if (!self->prompt)
  666. return "";
  667. return self->prompt;
  668. }
  669. static char *
  670. input_el__make_empty_prompt (EditLine *editline)
  671. {
  672. (void) editline;
  673. return "";
  674. }
  675. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  676. static void
  677. input_el_ding (void *input)
  678. {
  679. // XXX: this isn't probably very portable;
  680. // we could use "bell" from terminfo but that creates a dependency
  681. (void) input;
  682. write (STDOUT_FILENO, "\a", 1);
  683. }
  684. static const char *
  685. input_el_get_prompt (void *input)
  686. {
  687. struct input_el *self = input;
  688. return self->prompt;
  689. }
  690. static void
  691. input_el_set_prompt (void *input, char *prompt)
  692. {
  693. struct input_el *self = input;
  694. free (self->prompt);
  695. self->prompt = prompt;
  696. if (self->prompt_shown > 0)
  697. input_el__redisplay (self);
  698. }
  699. static void
  700. input_el_clear_line (void *input)
  701. {
  702. struct input_el *self = input;
  703. const LineInfoW *info = el_wline (self->editline);
  704. int len = info->lastchar - info->buffer;
  705. int point = info->cursor - info->buffer;
  706. el_cursor (self->editline, len - point);
  707. el_wdeletestr (self->editline, len);
  708. input_el__redisplay (self);
  709. }
  710. static void
  711. input_el__erase (struct input_el *self)
  712. {
  713. el_set (self->editline, EL_PROMPT, input_el__make_empty_prompt);
  714. input_el_clear_line (self);
  715. }
  716. static bool
  717. input_el_insert (void *input, const char *s)
  718. {
  719. struct input_el *self = input;
  720. bool success = !*s || !el_insertstr (self->editline, s);
  721. if (self->prompt_shown > 0)
  722. input_el__redisplay (self);
  723. return success;
  724. }
  725. static char *
  726. input_el_get_line (void *input)
  727. {
  728. struct input_el *self = input;
  729. const LineInfo *info = el_line (self->editline);
  730. return xstrndup (info->buffer, info->lastchar - info->buffer);
  731. }
  732. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  733. static void
  734. input_el_bind (void *input, const char *seq, const char *function_name)
  735. {
  736. struct input_el *self = input;
  737. el_set (self->editline, EL_BIND, seq, function_name, NULL);
  738. }
  739. static void
  740. input_el_bind_meta (void *input, char key, const char *function_name)
  741. {
  742. char keyseq[] = { 'M', '-', key, 0 };
  743. input_el_bind (input, keyseq, function_name);
  744. }
  745. static void
  746. input_el_bind_control (void *input, char key, const char *function_name)
  747. {
  748. char keyseq[] = { '^', key, 0 };
  749. input_el_bind (input, keyseq, function_name);
  750. }
  751. static void
  752. input_el__forward (ffi_cif *cif, void *ret, void **args, void *user_data)
  753. {
  754. (void) cif;
  755. struct input_el_fn *data = user_data;
  756. *(unsigned char *) ret = data->callback
  757. (1, *(int *) args[1], data->user_data) ? CC_NORM : CC_ERROR;
  758. }
  759. static wchar_t *
  760. ascii_to_wide (const char *ascii)
  761. {
  762. size_t len = strlen (ascii) + 1;
  763. wchar_t *wide = xcalloc (sizeof *wide, len);
  764. while (len--)
  765. hard_assert ((wide[len] = (unsigned char) ascii[len]) < 0x80);
  766. return wide;
  767. }
  768. static void
  769. input_el_register_fn (void *input,
  770. const char *name, const char *help, input_fn callback, void *user_data)
  771. {
  772. void *bound_fn = NULL;
  773. struct input_el_fn *data = ffi_closure_alloc (sizeof *data, &bound_fn);
  774. hard_assert (data);
  775. static ffi_cif cif;
  776. static ffi_type *args[2] = { &ffi_type_pointer, &ffi_type_sint };
  777. hard_assert (ffi_prep_cif
  778. (&cif, FFI_DEFAULT_ABI, 2, &ffi_type_uchar, args) == FFI_OK);
  779. data->user_data = user_data;
  780. data->callback = callback;
  781. data->name = ascii_to_wide (name);
  782. data->help = ascii_to_wide (help);
  783. hard_assert (ffi_prep_closure_loc (&data->closure,
  784. &cif, input_el__forward, data, bound_fn) == FFI_OK);
  785. struct input_el *self = input;
  786. el_wset (self->editline, EL_ADDFN, data->name, data->help, bound_fn);
  787. LIST_PREPEND (self->fns, data);
  788. }
  789. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  790. static void
  791. input_el_start (void *input, const char *program_name)
  792. {
  793. struct input_el *self = input;
  794. self->editline = el_init (program_name, stdin, stdout, stderr);
  795. el_set (self->editline, EL_CLIENTDATA, self);
  796. el_set (self->editline, EL_PROMPT_ESC,
  797. input_el__make_prompt, INPUT_START_IGNORE);
  798. el_set (self->editline, EL_SIGNAL, false);
  799. el_set (self->editline, EL_UNBUFFERED, true);
  800. el_set (self->editline, EL_EDITOR, "emacs");
  801. app_editline_init (self);
  802. self->prompt_shown = 1;
  803. self->active = true;
  804. }
  805. static void
  806. input_el_stop (void *input)
  807. {
  808. struct input_el *self = input;
  809. if (self->prompt_shown > 0)
  810. input_el__erase (self);
  811. el_end (self->editline);
  812. self->editline = NULL;
  813. self->active = false;
  814. self->prompt_shown = false;
  815. }
  816. static void
  817. input_el_prepare (void *input, bool enabled)
  818. {
  819. struct input_el *self = input;
  820. el_set (self->editline, EL_PREP_TERM, enabled);
  821. }
  822. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  823. static void
  824. input_el__save_buffer (struct input_el *self, struct input_el_buffer *buffer)
  825. {
  826. const LineInfoW *info = el_wline (self->editline);
  827. int len = info->lastchar - info->buffer;
  828. int point = info->cursor - info->buffer;
  829. wchar_t *line = calloc (sizeof *info->buffer, len + 1);
  830. memcpy (line, info->buffer, sizeof *info->buffer * len);
  831. el_cursor (self->editline, len - point);
  832. el_wdeletestr (self->editline, len);
  833. buffer->saved_line = line;
  834. buffer->saved_point = point;
  835. buffer->saved_len = len;
  836. }
  837. static void
  838. input_el__save (struct input_el *self)
  839. {
  840. if (self->current)
  841. input_el__save_buffer (self, self->current);
  842. }
  843. static void
  844. input_el__restore_buffer (struct input_el *self, struct input_el_buffer *buffer)
  845. {
  846. if (buffer->saved_line)
  847. {
  848. el_winsertstr (self->editline, buffer->saved_line);
  849. el_cursor (self->editline,
  850. -(buffer->saved_len - buffer->saved_point));
  851. free (buffer->saved_line);
  852. buffer->saved_line = NULL;
  853. }
  854. }
  855. static void
  856. input_el__restore (struct input_el *self)
  857. {
  858. if (self->current)
  859. input_el__restore_buffer (self, self->current);
  860. }
  861. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  862. static void
  863. input_el_buffer_switch (void *input, input_buffer_t input_buffer)
  864. {
  865. struct input_el *self = input;
  866. struct input_el_buffer *buffer = input_buffer;
  867. if (self->current)
  868. input_el__save_buffer (self, self->current);
  869. input_el__restore_buffer (self, buffer);
  870. el_wset (self->editline, EL_HIST, history, buffer->history);
  871. self->current = buffer;
  872. }
  873. static void
  874. input_el_buffer_destroy (void *input, input_buffer_t input_buffer)
  875. {
  876. (void) input;
  877. struct input_el_buffer *buffer = input_buffer;
  878. history_wend (buffer->history);
  879. free (buffer->saved_line);
  880. free (buffer);
  881. }
  882. static input_buffer_t
  883. input_el_buffer_new (void *input)
  884. {
  885. (void) input;
  886. struct input_el_buffer *self = xcalloc (1, sizeof *self);
  887. self->history = history_winit ();
  888. HistEventW ev;
  889. history_w (self->history, &ev, H_SETSIZE, HISTORY_LIMIT);
  890. return self;
  891. }
  892. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  893. static void
  894. input_el_hide (void *input)
  895. {
  896. struct input_el *self = input;
  897. if (!self->active || self->prompt_shown-- < 1)
  898. return;
  899. input_el__save (self);
  900. input_el__erase (self);
  901. }
  902. static void
  903. input_el_show (void *input)
  904. {
  905. struct input_el *self = input;
  906. if (!self->active || ++self->prompt_shown < 1)
  907. return;
  908. input_el__restore (self);
  909. // XXX: the ignore doesn't quite work, see https://gnats.netbsd.org/47539
  910. el_set (self->editline,
  911. EL_PROMPT_ESC, input_el__make_prompt, INPUT_START_IGNORE);
  912. input_el__redisplay (self);
  913. }
  914. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  915. static void
  916. input_el_on_tty_resized (void *input)
  917. {
  918. struct input_el *self = input;
  919. el_resize (self->editline);
  920. }
  921. static void
  922. input_el_on_tty_readable (void *input)
  923. {
  924. // We bind the return key to process it how we need to
  925. struct input_el *self = input;
  926. // el_gets() with EL_UNBUFFERED doesn't work with UTF-8,
  927. // we must use the wide-character interface
  928. int count = 0;
  929. const wchar_t *buf = el_wgets (self->editline, &count);
  930. if (!buf || count-- <= 0)
  931. return;
  932. if (count == 0 && buf[0] == ('D' - 0x40) /* hardcoded VEOF in editline */)
  933. {
  934. el_deletestr (self->editline, 1);
  935. input_el__redisplay (self);
  936. input_el_ding (self);
  937. }
  938. }
  939. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  940. static void
  941. input_el_destroy (void *input)
  942. {
  943. struct input_el *self = input;
  944. LIST_FOR_EACH (struct input_el_fn, iter, self->fns)
  945. {
  946. free (iter->name);
  947. free (iter->help);
  948. ffi_closure_free (iter);
  949. }
  950. free (self->prompt);
  951. free (self);
  952. }
  953. #define XX(a) .a = input_el_ ## a,
  954. static struct input_vtable input_el_vtable = { INPUT_VTABLE (XX) };
  955. #undef XX
  956. static struct input *
  957. input_el_new (void)
  958. {
  959. struct input_el *self = xcalloc (1, sizeof *self);
  960. self->super.vtable = &input_el_vtable;
  961. return &self->super;
  962. }
  963. #define input_new input_el_new
  964. #endif // HAVE_EDITLINE
  965. // --- Application data --------------------------------------------------------
  966. // All text stored in our data structures is encoded in UTF-8.
  967. // Or at least should be. The exception is IRC identifiers.
  968. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  969. // We need a few reference countable objects with support for both strong
  970. // and weak references (mainly used for scripted plugins).
  971. //
  972. // Beware that if you don't own the object, you will most probably want
  973. // to keep the weak reference link so that you can get rid of it later.
  974. // Also note that you have to make sure the user_data don't leak resources.
  975. //
  976. // Having a callback is more versatile than just nulling out a pointer.
  977. /// Callback just before a reference counted object is destroyed
  978. typedef void (*destroy_cb_fn) (void *object, void *user_data);
  979. struct weak_ref_link
  980. {
  981. LIST_HEADER (struct weak_ref_link)
  982. destroy_cb_fn on_destroy; ///< Called when object is destroyed
  983. void *user_data; ///< User data
  984. };
  985. static struct weak_ref_link *
  986. weak_ref (struct weak_ref_link **list, destroy_cb_fn cb, void *user_data)
  987. {
  988. struct weak_ref_link *link = xcalloc (1, sizeof *link);
  989. link->on_destroy = cb;
  990. link->user_data = user_data;
  991. LIST_PREPEND (*list, link);
  992. return link;
  993. }
  994. static void
  995. weak_unref (struct weak_ref_link **list, struct weak_ref_link **link)
  996. {
  997. if (*link)
  998. LIST_UNLINK (*list, *link);
  999. free (*link);
  1000. *link = NULL;
  1001. }
  1002. #define REF_COUNTABLE_HEADER \
  1003. size_t ref_count; /**< Reference count */ \
  1004. struct weak_ref_link *weak_refs; /**< To remove any weak references */
  1005. #define REF_COUNTABLE_METHODS(name) \
  1006. static struct name * \
  1007. name ## _ref (struct name *self) \
  1008. { \
  1009. self->ref_count++; \
  1010. return self; \
  1011. } \
  1012. \
  1013. static void \
  1014. name ## _unref (struct name *self) \
  1015. { \
  1016. if (--self->ref_count) \
  1017. return; \
  1018. LIST_FOR_EACH (struct weak_ref_link, iter, self->weak_refs) \
  1019. { \
  1020. iter->on_destroy (self, iter->user_data); \
  1021. free (iter); \
  1022. } \
  1023. name ## _destroy (self); \
  1024. } \
  1025. \
  1026. static struct weak_ref_link * \
  1027. name ## _weak_ref (struct name *self, destroy_cb_fn cb, void *user_data) \
  1028. { return weak_ref (&self->weak_refs, cb, user_data); } \
  1029. \
  1030. static void \
  1031. name ## _weak_unref (struct name *self, struct weak_ref_link **link) \
  1032. { weak_unref (&self->weak_refs, link); }
  1033. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1034. // Simple introspection framework to simplify exporting stuff to Lua, since
  1035. // there is a lot of it. While not fully automated, at least we have control
  1036. // over which fields are exported.
  1037. enum ispect_type
  1038. {
  1039. ISPECT_BOOL, ISPECT_INT, ISPECT_UINT, ISPECT_SIZE, ISPECT_STRING,
  1040. ISPECT_STR, ///< "struct str"
  1041. ISPECT_STR_MAP, ///< "struct str_map"
  1042. ISPECT_REF ///< Weakly referenced object
  1043. };
  1044. struct ispect_field
  1045. {
  1046. const char *name; ///< Name of the field
  1047. size_t offset; ///< Offset in the structure
  1048. enum ispect_type type; ///< Type of the field
  1049. enum ispect_type subtype; ///< STR_MAP subtype
  1050. struct ispect_field *fields; ///< REF target fields
  1051. bool is_list; ///< REF target is a list
  1052. };
  1053. #define ISPECT(object, field, type) \
  1054. { #field, offsetof (struct object, field), ISPECT_##type, 0, NULL, false },
  1055. #define ISPECT_REF(object, field, is_list, ref_type) \
  1056. { #field, offsetof (struct object, field), ISPECT_REF, 0, \
  1057. g_##ref_type##_ispect, is_list },
  1058. #define ISPECT_MAP(object, field, subtype) \
  1059. { #field, offsetof (struct object, field), ISPECT_STR_MAP, \
  1060. ISPECT_##subtype, NULL, false },
  1061. #define ISPECT_MAP_REF(object, field, is_list, ref_type) \
  1062. { #field, offsetof (struct object, field), ISPECT_STR_MAP, \
  1063. ISPECT_REF, g_##ref_type##_ispect, is_list },
  1064. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1065. struct user_channel
  1066. {
  1067. LIST_HEADER (struct user_channel)
  1068. struct channel *channel; ///< Reference to channel
  1069. };
  1070. static struct user_channel *
  1071. user_channel_new (void)
  1072. {
  1073. struct user_channel *self = xcalloc (1, sizeof *self);
  1074. return self;
  1075. }
  1076. static void
  1077. user_channel_destroy (struct user_channel *self)
  1078. {
  1079. // The "channel" reference is weak and this object should get
  1080. // destroyed whenever the user stops being in the channel.
  1081. free (self);
  1082. }
  1083. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1084. // We keep references to user information in channels and buffers,
  1085. // and weak references in the name lookup table.
  1086. struct user
  1087. {
  1088. REF_COUNTABLE_HEADER
  1089. char *nickname; ///< Literal nickname
  1090. // TODO: write code to poll for the away status
  1091. bool away; ///< User is away
  1092. struct user_channel *channels; ///< Channels the user is on
  1093. };
  1094. static struct ispect_field g_user_ispect[] =
  1095. {
  1096. ISPECT( user, nickname, STRING )
  1097. ISPECT( user, away, BOOL )
  1098. {}
  1099. };
  1100. static struct user *
  1101. user_new (void)
  1102. {
  1103. struct user *self = xcalloc (1, sizeof *self);
  1104. self->ref_count = 1;
  1105. return self;
  1106. }
  1107. static void
  1108. user_destroy (struct user *self)
  1109. {
  1110. free (self->nickname);
  1111. LIST_FOR_EACH (struct user_channel, iter, self->channels)
  1112. user_channel_destroy (iter);
  1113. free (self);
  1114. }
  1115. REF_COUNTABLE_METHODS (user)
  1116. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1117. struct channel_user
  1118. {
  1119. LIST_HEADER (struct channel_user)
  1120. struct user *user; ///< Reference to user
  1121. struct str prefixes; ///< Ordered @+... characters
  1122. };
  1123. static struct channel_user *
  1124. channel_user_new (void)
  1125. {
  1126. struct channel_user *self = xcalloc (1, sizeof *self);
  1127. self->prefixes = str_make ();
  1128. return self;
  1129. }
  1130. static void
  1131. channel_user_destroy (struct channel_user *self)
  1132. {
  1133. user_unref (self->user);
  1134. str_free (&self->prefixes);
  1135. free (self);
  1136. }
  1137. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1138. // We keep references to channels in their buffers,
  1139. // and weak references in their users and the name lookup table.
  1140. struct channel
  1141. {
  1142. REF_COUNTABLE_HEADER
  1143. char *name; ///< Channel name
  1144. char *topic; ///< Channel topic
  1145. // XXX: write something like an ordered set of characters object?
  1146. struct str no_param_modes; ///< No parameter channel modes
  1147. struct str_map param_modes; ///< Parametrized channel modes
  1148. struct channel_user *users; ///< Channel users
  1149. struct strv names_buf; ///< Buffer for RPL_NAMREPLY
  1150. size_t users_len; ///< User count
  1151. bool left_manually; ///< Don't rejoin on reconnect
  1152. };
  1153. static struct ispect_field g_channel_ispect[] =
  1154. {
  1155. ISPECT( channel, name, STRING )
  1156. ISPECT( channel, topic, STRING )
  1157. ISPECT( channel, no_param_modes, STR )
  1158. ISPECT_MAP( channel, param_modes, STRING )
  1159. ISPECT( channel, users_len, SIZE )
  1160. ISPECT( channel, left_manually, BOOL )
  1161. {}
  1162. };
  1163. static struct channel *
  1164. channel_new (void)
  1165. {
  1166. struct channel *self = xcalloc (1, sizeof *self);
  1167. self->ref_count = 1;
  1168. self->no_param_modes = str_make ();
  1169. self->param_modes = str_map_make (free);
  1170. self->names_buf = strv_make ();
  1171. return self;
  1172. }
  1173. static void
  1174. channel_destroy (struct channel *self)
  1175. {
  1176. free (self->name);
  1177. free (self->topic);
  1178. str_free (&self->no_param_modes);
  1179. str_map_free (&self->param_modes);
  1180. // Owner has to make sure we have no users by now
  1181. hard_assert (!self->users && !self->users_len);
  1182. strv_free (&self->names_buf);
  1183. free (self);
  1184. }
  1185. REF_COUNTABLE_METHODS (channel)
  1186. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1187. enum formatter_item_type
  1188. {
  1189. FORMATTER_ITEM_END, ///< Sentinel value for arrays
  1190. FORMATTER_ITEM_TEXT, ///< Text
  1191. FORMATTER_ITEM_ATTR, ///< Formatting attributes
  1192. FORMATTER_ITEM_FG_COLOR, ///< Foreground color
  1193. FORMATTER_ITEM_BG_COLOR, ///< Background color
  1194. FORMATTER_ITEM_SIMPLE, ///< Toggle mIRC formatting
  1195. FORMATTER_ITEM_IGNORE_ATTR ///< Un/set attribute ignoration
  1196. };
  1197. struct formatter_item
  1198. {
  1199. enum formatter_item_type type : 16; ///< Type of this item
  1200. int attribute : 16; ///< Attribute ID
  1201. int color; ///< Color
  1202. char *text; ///< String
  1203. };
  1204. static void
  1205. formatter_item_free (struct formatter_item *self)
  1206. {
  1207. free (self->text);
  1208. }
  1209. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1210. struct formatter
  1211. {
  1212. struct app_context *ctx; ///< Application context
  1213. struct server *s; ///< Server
  1214. struct formatter_item *items; ///< Items
  1215. size_t items_len; ///< Items used
  1216. size_t items_alloc; ///< Items allocated
  1217. };
  1218. static struct formatter
  1219. formatter_make (struct app_context *ctx, struct server *s)
  1220. {
  1221. struct formatter self = { .ctx = ctx, .s = s };
  1222. self.items = xcalloc (sizeof *self.items, (self.items_alloc = 16));
  1223. return self;
  1224. }
  1225. static void
  1226. formatter_free (struct formatter *self)
  1227. {
  1228. for (size_t i = 0; i < self->items_len; i++)
  1229. formatter_item_free (&self->items[i]);
  1230. free (self->items);
  1231. }
  1232. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1233. enum buffer_line_flags
  1234. {
  1235. BUFFER_LINE_STATUS = 1 << 0, ///< Status message
  1236. BUFFER_LINE_ERROR = 1 << 1, ///< Error message
  1237. BUFFER_LINE_HIGHLIGHT = 1 << 2, ///< The user was highlighted by this
  1238. BUFFER_LINE_SKIP_FILE = 1 << 3, ///< Don't log this to file
  1239. BUFFER_LINE_INDENT = 1 << 4, ///< Just indent the line
  1240. BUFFER_LINE_UNIMPORTANT = 1 << 5 ///< Joins, parts, similar spam
  1241. };
  1242. struct buffer_line
  1243. {
  1244. LIST_HEADER (struct buffer_line)
  1245. int flags; ///< Flags
  1246. time_t when; ///< Time of the event
  1247. struct formatter_item items[]; ///< Line data
  1248. };
  1249. /// Create a new buffer line stealing all data from the provided formatter
  1250. struct buffer_line *
  1251. buffer_line_new (struct formatter *f)
  1252. {
  1253. // We make space for one more item that gets initialized to all zeros,
  1254. // meaning FORMATTER_ITEM_END (because it's the first value in the enum)
  1255. size_t items_size = f->items_len * sizeof *f->items;
  1256. struct buffer_line *self =
  1257. xcalloc (1, sizeof *self + items_size + sizeof *self->items);
  1258. memcpy (self->items, f->items, items_size);
  1259. // We've stolen pointers from the formatter, let's destroy it altogether
  1260. free (f->items);
  1261. memset (f, 0, sizeof *f);
  1262. return self;
  1263. }
  1264. static void
  1265. buffer_line_destroy (struct buffer_line *self)
  1266. {
  1267. for (struct formatter_item *iter = self->items; iter->type; iter++)
  1268. formatter_item_free (iter);
  1269. free (self);
  1270. }
  1271. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1272. enum buffer_type
  1273. {
  1274. BUFFER_GLOBAL, ///< Global information
  1275. BUFFER_SERVER, ///< Server-related messages
  1276. BUFFER_CHANNEL, ///< Channels
  1277. BUFFER_PM ///< Private messages (query)
  1278. };
  1279. struct buffer
  1280. {
  1281. LIST_HEADER (struct buffer)
  1282. REF_COUNTABLE_HEADER
  1283. enum buffer_type type; ///< Type of the buffer
  1284. char *name; ///< The name of the buffer
  1285. struct input *input; ///< API for "input_data"
  1286. input_buffer_t input_data; ///< User interface data
  1287. // Buffer contents:
  1288. struct buffer_line *lines; ///< All lines in this buffer
  1289. struct buffer_line *lines_tail; ///< The tail of buffer lines
  1290. unsigned lines_count; ///< How many lines we have
  1291. unsigned new_messages_count; ///< # messages since last left
  1292. unsigned new_unimportant_count; ///< How much of that is unimportant
  1293. bool highlighted; ///< We've been highlighted
  1294. bool hide_unimportant; ///< Hide unimportant messages
  1295. FILE *log_file; ///< Log file
  1296. // Origin information:
  1297. struct server *server; ///< Reference to server
  1298. struct channel *channel; ///< Reference to channel
  1299. struct user *user; ///< Reference to user
  1300. };
  1301. static struct ispect_field g_server_ispect[];
  1302. static struct ispect_field g_buffer_ispect[] =
  1303. {
  1304. ISPECT( buffer, name, STRING )
  1305. ISPECT( buffer, new_messages_count, UINT )
  1306. ISPECT( buffer, new_unimportant_count, UINT )
  1307. ISPECT( buffer, highlighted, BOOL )
  1308. ISPECT( buffer, hide_unimportant, BOOL )
  1309. ISPECT_REF( buffer, server, false, server )
  1310. ISPECT_REF( buffer, channel, false, channel )
  1311. ISPECT_REF( buffer, user, false, user )
  1312. {}
  1313. };
  1314. static struct buffer *
  1315. buffer_new (struct input *input)
  1316. {
  1317. struct buffer *self = xcalloc (1, sizeof *self);
  1318. self->ref_count = 1;
  1319. self->input = input;
  1320. self->input_data = CALL (input, buffer_new);
  1321. return self;
  1322. }
  1323. static void
  1324. buffer_destroy (struct buffer *self)
  1325. {
  1326. free (self->name);
  1327. if (self->input_data)
  1328. {
  1329. #ifdef HAVE_READLINE
  1330. // FIXME: can't really free "history" contents from here, as we cannot
  1331. // be sure that the user interface pointer is valid and usable
  1332. input_rl__buffer_destroy_wo_history (self->input_data);
  1333. #else // ! HAVE_READLINE
  1334. CALL_ (self->input, buffer_destroy, self->input_data);
  1335. #endif // ! HAVE_READLINE
  1336. }
  1337. LIST_FOR_EACH (struct buffer_line, iter, self->lines)
  1338. buffer_line_destroy (iter);
  1339. if (self->log_file)
  1340. (void) fclose (self->log_file);
  1341. if (self->user)
  1342. user_unref (self->user);
  1343. if (self->channel)
  1344. channel_unref (self->channel);
  1345. free (self);
  1346. }
  1347. REF_COUNTABLE_METHODS (buffer)
  1348. #define buffer_ref do_not_use_dangerous
  1349. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1350. // The only real purpose of this is to abstract away TLS
  1351. struct transport
  1352. {
  1353. /// Initialize the transport
  1354. bool (*init) (struct server *s, const char *hostname, struct error **e);
  1355. /// Destroy the user data pointer
  1356. void (*cleanup) (struct server *s);
  1357. /// The underlying socket may have become readable, update `read_buffer'
  1358. enum socket_io_result (*try_read) (struct server *s);
  1359. /// The underlying socket may have become writeable, flush `write_buffer'
  1360. enum socket_io_result (*try_write) (struct server *s);
  1361. /// Return event mask to use in the poller
  1362. int (*get_poll_events) (struct server *s);
  1363. /// Called just before closing the connection from our side
  1364. void (*in_before_shutdown) (struct server *s);
  1365. };
  1366. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1367. enum server_state
  1368. {
  1369. IRC_DISCONNECTED, ///< Not connected
  1370. IRC_CONNECTING, ///< Connecting to the server
  1371. IRC_CONNECTED, ///< Trying to register
  1372. IRC_REGISTERED, ///< We can chat now
  1373. IRC_CLOSING, ///< Flushing output before shutdown
  1374. IRC_HALF_CLOSED ///< Connection shutdown from our side
  1375. };
  1376. /// Convert an IRC identifier character to lower-case
  1377. typedef int (*irc_tolower_fn) (int);
  1378. /// Key conversion function for hashmap lookups
  1379. typedef size_t (*irc_strxfrm_fn) (char *, const char *, size_t);
  1380. struct server
  1381. {
  1382. REF_COUNTABLE_HEADER
  1383. struct app_context *ctx; ///< Application context
  1384. char *name; ///< Server identifier
  1385. struct buffer *buffer; ///< The buffer for this server
  1386. struct config_item *config; ///< Configuration root
  1387. // Connection:
  1388. enum server_state state; ///< Connection state
  1389. struct connector *connector; ///< Connection establisher
  1390. struct socks_connector *socks_conn; ///< SOCKS connection establisher
  1391. unsigned reconnect_attempt; ///< Number of reconnect attempt
  1392. bool manual_disconnect; ///< Don't reconnect after disconnect
  1393. int socket; ///< Socket FD of the server
  1394. struct str read_buffer; ///< Input yet to be processed
  1395. struct str write_buffer; ///< Outut yet to be be sent out
  1396. struct poller_fd socket_event; ///< We can read from the socket
  1397. struct transport *transport; ///< Transport method
  1398. void *transport_data; ///< Transport data
  1399. // Events:
  1400. struct poller_timer ping_tmr; ///< We should send a ping
  1401. struct poller_timer timeout_tmr; ///< Connection seems to be dead
  1402. struct poller_timer reconnect_tmr; ///< We should reconnect now
  1403. struct poller_timer autojoin_tmr; ///< Re/join channels as appropriate
  1404. // IRC:
  1405. // TODO: an output queue to prevent excess floods (this will be needed
  1406. // especially for away status polling)
  1407. bool rehashing; ///< Rehashing IRC identifiers
  1408. struct str_map irc_users; ///< IRC user data
  1409. struct str_map irc_channels; ///< IRC channel data
  1410. struct str_map irc_buffer_map; ///< Maps IRC identifiers to buffers
  1411. struct user *irc_user; ///< Our own user
  1412. int nick_counter; ///< Iterates "nicks" when registering
  1413. struct str irc_user_mode; ///< Our current user modes
  1414. char *irc_user_host; ///< Our current user@host
  1415. bool autoaway_active; ///< Autoaway is currently active
  1416. bool cap_echo_message; ///< Whether the server echos messages
  1417. // Server-specific information (from RPL_ISUPPORT):
  1418. irc_tolower_fn irc_tolower; ///< Server tolower()
  1419. irc_strxfrm_fn irc_strxfrm; ///< Server strxfrm()
  1420. char *irc_chantypes; ///< Channel types (name prefixes)
  1421. char *irc_idchan_prefixes; ///< Prefixes for "safe channels"
  1422. char *irc_statusmsg; ///< Prefixes for channel targets
  1423. char *irc_chanmodes_list; ///< Channel modes for lists
  1424. char *irc_chanmodes_param_always; ///< Channel modes with mandatory param
  1425. char *irc_chanmodes_param_when_set; ///< Channel modes with param when set
  1426. char *irc_chanmodes_param_never; ///< Channel modes without param
  1427. char *irc_chanuser_prefixes; ///< Channel user prefixes
  1428. char *irc_chanuser_modes; ///< Channel user modes
  1429. unsigned irc_max_modes; ///< Max parametrized modes per command
  1430. };
  1431. static struct ispect_field g_server_ispect[] =
  1432. {
  1433. ISPECT( server, name, STRING )
  1434. ISPECT( server, state, INT )
  1435. ISPECT( server, reconnect_attempt, UINT )
  1436. ISPECT( server, manual_disconnect, BOOL )
  1437. ISPECT( server, irc_user_host, STRING )
  1438. ISPECT( server, autoaway_active, BOOL )
  1439. ISPECT( server, cap_echo_message, BOOL )
  1440. ISPECT_REF( server, buffer, false, buffer )
  1441. // TODO: either rename the underlying field or fix the plugins
  1442. { "user", offsetof (struct server, irc_user),
  1443. ISPECT_REF, 0, g_user_ispect, false },
  1444. { "user_mode", offsetof (struct server, irc_user_mode),
  1445. ISPECT_STR, 0, NULL, false },
  1446. {}
  1447. };
  1448. static void on_irc_timeout (void *user_data);
  1449. static void on_irc_ping_timeout (void *user_data);
  1450. static void on_irc_autojoin_timeout (void *user_data);
  1451. static void irc_initiate_connect (struct server *s);
  1452. static void
  1453. server_init_specifics (struct server *self)
  1454. {
  1455. // Defaults as per the RPL_ISUPPORT drafts, or RFC 1459
  1456. self->irc_tolower = irc_tolower;
  1457. self->irc_strxfrm = irc_strxfrm;
  1458. self->irc_chantypes = xstrdup ("#&");
  1459. self->irc_idchan_prefixes = xstrdup ("");
  1460. self->irc_statusmsg = xstrdup ("");
  1461. self->irc_chanmodes_list = xstrdup ("b");
  1462. self->irc_chanmodes_param_always = xstrdup ("k");
  1463. self->irc_chanmodes_param_when_set = xstrdup ("l");
  1464. self->irc_chanmodes_param_never = xstrdup ("imnpst");
  1465. self->irc_chanuser_prefixes = xstrdup ("@+");
  1466. self->irc_chanuser_modes = xstrdup ("ov");
  1467. self->irc_max_modes = 3;
  1468. }
  1469. static void
  1470. server_free_specifics (struct server *self)
  1471. {
  1472. free (self->irc_chantypes);
  1473. free (self->irc_idchan_prefixes);
  1474. free (self->irc_statusmsg);
  1475. free (self->irc_chanmodes_list);
  1476. free (self->irc_chanmodes_param_always);
  1477. free (self->irc_chanmodes_param_when_set);
  1478. free (self->irc_chanmodes_param_never);
  1479. free (self->irc_chanuser_prefixes);
  1480. free (self->irc_chanuser_modes);
  1481. }
  1482. static struct server *
  1483. server_new (struct poller *poller)
  1484. {
  1485. struct server *self = xcalloc (1, sizeof *self);
  1486. self->ref_count = 1;
  1487. self->socket = -1;
  1488. self->read_buffer = str_make ();
  1489. self->write_buffer = str_make ();
  1490. self->state = IRC_DISCONNECTED;
  1491. self->timeout_tmr = poller_timer_make (poller);
  1492. self->timeout_tmr.dispatcher = on_irc_timeout;
  1493. self->timeout_tmr.user_data = self;
  1494. self->ping_tmr = poller_timer_make (poller);
  1495. self->ping_tmr.dispatcher = on_irc_ping_timeout;
  1496. self->ping_tmr.user_data = self;
  1497. self->reconnect_tmr = poller_timer_make (poller);
  1498. self->reconnect_tmr.dispatcher = (poller_timer_fn) irc_initiate_connect;
  1499. self->reconnect_tmr.user_data = self;
  1500. self->autojoin_tmr = poller_timer_make (poller);
  1501. self->autojoin_tmr.dispatcher = on_irc_autojoin_timeout;
  1502. self->autojoin_tmr.user_data = self;
  1503. self->irc_users = str_map_make (NULL);
  1504. self->irc_users.key_xfrm = irc_strxfrm;
  1505. self->irc_channels = str_map_make (NULL);
  1506. self->irc_channels.key_xfrm = irc_strxfrm;
  1507. self->irc_buffer_map = str_map_make (NULL);
  1508. self->irc_buffer_map.key_xfrm = irc_strxfrm;
  1509. self->irc_user_mode = str_make ();
  1510. server_init_specifics (self);
  1511. return self;
  1512. }
  1513. static void
  1514. server_destroy (struct server *self)
  1515. {
  1516. free (self->name);
  1517. if (self->connector)
  1518. {
  1519. connector_free (self->connector);
  1520. free (self->connector);
  1521. }
  1522. if (self->socks_conn)
  1523. {
  1524. socks_connector_free (self->socks_conn);
  1525. free (self->socks_conn);
  1526. }
  1527. if (self->transport
  1528. && self->transport->cleanup)
  1529. self->transport->cleanup (self);
  1530. if (self->socket != -1)
  1531. {
  1532. poller_fd_reset (&self->socket_event);
  1533. xclose (self->socket);
  1534. }
  1535. str_free (&self->read_buffer);
  1536. str_free (&self->write_buffer);
  1537. poller_timer_reset (&self->ping_tmr);
  1538. poller_timer_reset (&self->timeout_tmr);
  1539. poller_timer_reset (&self->reconnect_tmr);
  1540. poller_timer_reset (&self->autojoin_tmr);
  1541. str_map_free (&self->irc_users);
  1542. str_map_free (&self->irc_channels);
  1543. str_map_free (&self->irc_buffer_map);
  1544. if (self->irc_user)
  1545. user_unref (self->irc_user);
  1546. str_free (&self->irc_user_mode);
  1547. free (self->irc_user_host);
  1548. server_free_specifics (self);
  1549. free (self);
  1550. }
  1551. REF_COUNTABLE_METHODS (server)
  1552. #define server_ref do_not_use_dangerous
  1553. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1554. struct plugin
  1555. {
  1556. LIST_HEADER (struct plugin)
  1557. char *name; ///< Name of the plugin
  1558. struct plugin_vtable *vtable; ///< Methods
  1559. };
  1560. struct plugin_vtable
  1561. {
  1562. /// Unregister and free the plugin including all relevant resources
  1563. void (*free) (struct plugin *self);
  1564. };
  1565. static void
  1566. plugin_destroy (struct plugin *self)
  1567. {
  1568. self->vtable->free (self);
  1569. free (self->name);
  1570. free (self);
  1571. }
  1572. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1573. // This is a bit ugly since insertion is O(n) and the need to get rid of the
  1574. // specific type because of list macros, however I don't currently posses any
  1575. // strictly better, ordered data structure
  1576. struct hook
  1577. {
  1578. LIST_HEADER (struct hook)
  1579. int priority; ///< The lesser the sooner
  1580. };
  1581. static struct hook *
  1582. hook_insert (struct hook *list, struct hook *item)
  1583. {
  1584. // Corner cases: list is empty or we precede everything
  1585. if (!list || item->priority < list->priority)
  1586. {
  1587. LIST_PREPEND (list, item);
  1588. return list;
  1589. }
  1590. // Otherwise fast-forward to the last entry that precedes us
  1591. struct hook *before = list;
  1592. while (before->next && before->next->priority < item->priority)
  1593. before = before->next;
  1594. // And link ourselves in between it and its successor
  1595. if ((item->next = before->next))
  1596. item->next->prev = item;
  1597. before->next = item;
  1598. item->prev = before;
  1599. return list;
  1600. }
  1601. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1602. struct input_hook
  1603. {
  1604. struct hook super; ///< Common hook fields
  1605. /// Takes over the ownership of "input", returns either NULL if input
  1606. /// was thrown away, or a possibly modified version of it
  1607. char *(*filter) (struct input_hook *self,
  1608. struct buffer *buffer, char *input);
  1609. };
  1610. struct irc_hook
  1611. {
  1612. struct hook super; ///< Common hook fields
  1613. /// Takes over the ownership of "message", returns either NULL if message
  1614. /// was thrown away, or a possibly modified version of it
  1615. char *(*filter) (struct irc_hook *self,
  1616. struct server *server, char *message);
  1617. };
  1618. struct prompt_hook
  1619. {
  1620. struct hook super; ///< Common hook fields
  1621. /// Returns what the prompt should look like right now based on other state
  1622. char *(*make) (struct prompt_hook *self);
  1623. };
  1624. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1625. struct completion_word
  1626. {
  1627. size_t start; ///< Offset to start of word
  1628. size_t end; ///< Offset to end of word
  1629. };
  1630. struct completion
  1631. {
  1632. char *line; ///< The line which is being completed
  1633. struct completion_word *words; ///< Word locations
  1634. size_t words_len; ///< Number of words
  1635. size_t words_alloc; ///< Number of words allocated
  1636. size_t location; ///< Which word is being completed
  1637. };
  1638. struct completion_hook
  1639. {
  1640. struct hook super; ///< Common hook fields
  1641. /// Tries to add possible completions of "word" to "output"
  1642. void (*complete) (struct completion_hook *self,
  1643. struct completion *data, const char *word, struct strv *output);
  1644. };
  1645. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1646. struct app_context
  1647. {
  1648. char *attrs_defaults[ATTR_COUNT]; ///< Default terminal attributes
  1649. // Configuration:
  1650. struct config config; ///< Program configuration
  1651. char *attrs[ATTR_COUNT]; ///< Terminal attributes
  1652. bool isolate_buffers; ///< Isolate global/server buffers
  1653. bool beep_on_highlight; ///< Beep on highlight
  1654. bool logging; ///< Logging to file enabled
  1655. bool show_all_prefixes; ///< Show all prefixes before nicks
  1656. bool word_wrapping; ///< Enable simple word wrapping
  1657. struct str_map servers; ///< Our servers
  1658. // Events:
  1659. struct poller_fd tty_event; ///< Terminal input event
  1660. struct poller_fd signal_event; ///< Signal FD event
  1661. struct poller_timer flush_timer; ///< Flush all open files (e.g. logs)
  1662. struct poller_timer date_chg_tmr; ///< Print a date change
  1663. struct poller_timer autoaway_tmr; ///< Autoaway timer
  1664. struct poller poller; ///< Manages polled descriptors
  1665. bool quitting; ///< User requested quitting
  1666. bool polling; ///< The event loop is running
  1667. // Buffers:
  1668. struct buffer *buffers; ///< All our buffers in order
  1669. struct buffer *buffers_tail; ///< The tail of our buffers
  1670. struct buffer *global_buffer; ///< The global buffer
  1671. struct buffer *current_buffer; ///< The current buffer
  1672. struct buffer *last_buffer; ///< Last used buffer
  1673. // TODO: make buffer names fully unique like weechat does
  1674. struct str_map buffers_by_name; ///< Buffers by name
  1675. unsigned backlog_limit; ///< Limit for buffer lines
  1676. time_t last_displayed_msg_time; ///< Time of last displayed message
  1677. // Terminal:
  1678. iconv_t term_to_utf8; ///< Terminal encoding to UTF-8
  1679. iconv_t term_from_utf8; ///< UTF-8 to terminal encoding
  1680. struct input *input; ///< User interface
  1681. struct poller_idle prompt_event; ///< Deferred prompt refresh
  1682. struct poller_idle input_event; ///< Pending input event
  1683. struct strv pending_input; ///< Pending input lines
  1684. int *nick_palette; ///< A 256-color palette for nicknames
  1685. size_t nick_palette_len; ///< Number of entries in nick_palette
  1686. bool awaiting_mirc_escape; ///< Awaiting a mIRC attribute escape
  1687. bool in_bracketed_paste; ///< User is pasting some content
  1688. struct str input_buffer; ///< Buffered pasted content
  1689. bool running_backlog_helper; ///< Running a backlog helper
  1690. bool running_editor; ///< Running editor for the input
  1691. char *editor_filename; ///< The file being edited by user
  1692. int terminal_suspended; ///< Terminal suspension level
  1693. struct plugin *plugins; ///< Loaded plugins
  1694. struct hook *input_hooks; ///< Input hooks
  1695. struct hook *irc_hooks; ///< IRC hooks
  1696. struct hook *prompt_hooks; ///< Prompt hooks
  1697. struct hook *completion_hooks; ///< Autocomplete hooks
  1698. }
  1699. *g_ctx;
  1700. static struct ispect_field g_ctx_ispect[] =
  1701. {
  1702. ISPECT_MAP_REF( app_context, servers, false, server )
  1703. ISPECT_REF( app_context, buffers, true, buffer )
  1704. ISPECT_REF( app_context, global_buffer, false, buffer )
  1705. ISPECT_REF( app_context, current_buffer, false, buffer )
  1706. {}
  1707. };
  1708. static int *
  1709. filter_color_cube_for_acceptable_nick_colors (size_t *len)
  1710. {
  1711. // This is a pure function and we don't use threads, static storage is fine
  1712. static int table[6 * 6 * 6];
  1713. size_t len_counter = 0;
  1714. for (int x = 0; x < 6 * 6 * 6; x++)
  1715. {
  1716. int r = x / 36;
  1717. int g = (x / 6) % 6;
  1718. int b = (x % 6);
  1719. // Use the luma value of colours within the cube to filter colours that
  1720. // look okay-ish on terminals with both black and white backgrounds
  1721. double luma = 0.2126 * r / 6. + 0.7152 * g / 6. + 0.0722 * b / 6.;
  1722. if (luma >= .3 && luma <= .5)
  1723. table[len_counter++] = 16 + x;
  1724. }
  1725. *len = len_counter;
  1726. return table;
  1727. }
  1728. static bool
  1729. app_iconv_open (iconv_t *target, const char *to, const char *from)
  1730. {
  1731. if (ICONV_ACCEPTS_TRANSLIT)
  1732. {
  1733. char *to_real = xstrdup_printf ("%s//TRANSLIT", to);
  1734. *target = iconv_open (to_real, from);
  1735. free (to_real);
  1736. }
  1737. else
  1738. *target = iconv_open (to, from);
  1739. return *target != (iconv_t) -1;
  1740. }
  1741. static void
  1742. app_context_init (struct app_context *self)
  1743. {
  1744. memset (self, 0, sizeof *self);
  1745. self->config = config_make ();
  1746. poller_init (&self->poller);
  1747. self->servers = str_map_make ((str_map_free_fn) server_unref);
  1748. self->servers.key_xfrm = tolower_ascii_strxfrm;
  1749. self->buffers_by_name = str_map_make (NULL);
  1750. self->buffers_by_name.key_xfrm = tolower_ascii_strxfrm;
  1751. // So that we don't lose the logo shortly after startup
  1752. self->backlog_limit = 1000;
  1753. self->last_displayed_msg_time = time (NULL);
  1754. char *native = nl_langinfo (CODESET);
  1755. if (!app_iconv_open (&self->term_from_utf8, native, "UTF-8")
  1756. || !app_iconv_open (&self->term_to_utf8, "UTF-8", native))
  1757. exit_fatal ("creating the UTF-8 conversion object failed: %s",
  1758. strerror (errno));
  1759. self->input = input_new ();
  1760. self->input->user_data = self;
  1761. self->pending_input = strv_make ();
  1762. self->input_buffer = str_make ();
  1763. self->nick_palette =
  1764. filter_color_cube_for_acceptable_nick_colors (&self->nick_palette_len);
  1765. }
  1766. static void
  1767. app_context_free (struct app_context *self)
  1768. {
  1769. // Plugins can try to use of the other fields when destroyed
  1770. LIST_FOR_EACH (struct plugin, iter, self->plugins)
  1771. plugin_destroy (iter);
  1772. config_free (&self->config);
  1773. for (size_t i = 0; i < ATTR_COUNT; i++)
  1774. {
  1775. free (self->attrs_defaults[i]);
  1776. free (self->attrs[i]);
  1777. }
  1778. LIST_FOR_EACH (struct buffer, iter, self->buffers)
  1779. {
  1780. #ifdef HAVE_READLINE
  1781. // We can use the user interface here; see buffer_destroy()
  1782. CALL_ (self->input, buffer_destroy, iter->input_data);
  1783. iter->input_data = NULL;
  1784. #endif // HAVE_READLINE
  1785. buffer_unref (iter);
  1786. }
  1787. str_map_free (&self->buffers_by_name);
  1788. str_map_free (&self->servers);
  1789. poller_free (&self->poller);
  1790. iconv_close (self->term_from_utf8);
  1791. iconv_close (self->term_to_utf8);
  1792. CALL (self->input, destroy);
  1793. strv_free (&self->pending_input);
  1794. str_free (&self->input_buffer);
  1795. free (self->editor_filename);
  1796. }
  1797. static void
  1798. refresh_prompt (struct app_context *ctx)
  1799. {
  1800. // XXX: the need for this conditional could probably be resolved
  1801. // by some clever reordering
  1802. if (ctx->prompt_event.poller)
  1803. poller_idle_set (&ctx->prompt_event);
  1804. }
  1805. // --- Configuration -----------------------------------------------------------
  1806. static void
  1807. on_config_debug_mode_change (struct config_item *item)
  1808. {
  1809. g_debug_mode = item->value.boolean;
  1810. }
  1811. static void
  1812. on_config_show_all_prefixes_change (struct config_item *item)
  1813. {
  1814. struct app_context *ctx = item->user_data;
  1815. ctx->show_all_prefixes = item->value.boolean;
  1816. refresh_prompt (ctx);
  1817. }
  1818. static void on_config_backlog_limit_change (struct config_item *item);
  1819. static void on_config_attribute_change (struct config_item *item);
  1820. static void on_config_logging_change (struct config_item *item);
  1821. #define TRIVIAL_BOOLEAN_ON_CHANGE(name) \
  1822. static void \
  1823. on_config_ ## name ## _change (struct config_item *item) \
  1824. { \
  1825. struct app_context *ctx = item->user_data; \
  1826. ctx->name = item->value.boolean; \
  1827. }
  1828. TRIVIAL_BOOLEAN_ON_CHANGE (isolate_buffers)
  1829. TRIVIAL_BOOLEAN_ON_CHANGE (beep_on_highlight)
  1830. TRIVIAL_BOOLEAN_ON_CHANGE (word_wrapping)
  1831. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1832. static bool
  1833. config_validate_nonjunk_string
  1834. (const struct config_item *item, struct error **e)
  1835. {
  1836. if (item->type == CONFIG_ITEM_NULL)
  1837. return true;
  1838. hard_assert (config_item_type_is_string (item->type));
  1839. for (size_t i = 0; i < item->value.string.len; i++)
  1840. {
  1841. // Not even a tabulator
  1842. unsigned char c = item->value.string.str[i];
  1843. if (c < 32)
  1844. {
  1845. error_set (e, "control characters are not allowed");
  1846. return false;
  1847. }
  1848. }
  1849. return true;
  1850. }
  1851. static bool
  1852. config_validate_addresses
  1853. (const struct config_item *item, struct error **e)
  1854. {
  1855. if (item->type == CONFIG_ITEM_NULL)
  1856. return true;
  1857. if (!config_validate_nonjunk_string (item, e))
  1858. return false;
  1859. // Comma-separated list of "host[:port]" pairs
  1860. regex_t re;
  1861. int err = regcomp (&re, "^([^/:,]+(:[^/:,]+)?)?"
  1862. "(,([^/:,]+(:[^/:,]+)?)?)*$", REG_EXTENDED | REG_NOSUB);
  1863. hard_assert (!err);
  1864. bool result = !regexec (&re, item->value.string.str, 0, NULL, 0);
  1865. if (!result)
  1866. error_set (e, "invalid address list string");
  1867. regfree (&re);
  1868. return result;
  1869. }
  1870. static bool
  1871. config_validate_nonnegative
  1872. (const struct config_item *item, struct error **e)
  1873. {
  1874. if (item->type == CONFIG_ITEM_NULL)
  1875. return true;
  1876. hard_assert (item->type == CONFIG_ITEM_INTEGER);
  1877. if (item->value.integer >= 0)
  1878. return true;
  1879. error_set (e, "must be non-negative");
  1880. return false;
  1881. }
  1882. static struct config_schema g_config_server[] =
  1883. {
  1884. { .name = "nicks",
  1885. .comment = "IRC nickname",
  1886. .type = CONFIG_ITEM_STRING_ARRAY,
  1887. .validate = config_validate_nonjunk_string },
  1888. { .name = "username",
  1889. .comment = "IRC user name",
  1890. .type = CONFIG_ITEM_STRING,
  1891. .validate = config_validate_nonjunk_string },
  1892. { .name = "realname",
  1893. .comment = "IRC real name/e-mail",
  1894. .type = CONFIG_ITEM_STRING,
  1895. .validate = config_validate_nonjunk_string },
  1896. { .name = "addresses",
  1897. .comment = "Addresses of the IRC network (e.g. \"irc.net:6667\")",
  1898. .type = CONFIG_ITEM_STRING_ARRAY,
  1899. .validate = config_validate_addresses },
  1900. { .name = "password",
  1901. .comment = "Password to connect to the server, if any",
  1902. .type = CONFIG_ITEM_STRING,
  1903. .validate = config_validate_nonjunk_string },
  1904. // XXX: if we add support for new capabilities, the value stays unchanged
  1905. { .name = "capabilities",
  1906. .comment = "Capabilities to use if supported by server",
  1907. .type = CONFIG_ITEM_STRING_ARRAY,
  1908. .validate = config_validate_nonjunk_string,
  1909. .default_ = "\"multi-prefix,invite-notify,server-time,echo-message\"" },
  1910. { .name = "tls",
  1911. .comment = "Whether to use TLS",
  1912. .type = CONFIG_ITEM_BOOLEAN,
  1913. .default_ = "off" },
  1914. { .name = "tls_cert",
  1915. .comment = "Client TLS certificate (PEM)",
  1916. .type = CONFIG_ITEM_STRING },
  1917. { .name = "tls_verify",
  1918. .comment = "Whether to verify certificates",
  1919. .type = CONFIG_ITEM_BOOLEAN,
  1920. .default_ = "on" },
  1921. { .name = "tls_ca_file",
  1922. .comment = "OpenSSL CA bundle file",
  1923. .type = CONFIG_ITEM_STRING },
  1924. { .name = "tls_ca_path",
  1925. .comment = "OpenSSL CA bundle path",
  1926. .type = CONFIG_ITEM_STRING },
  1927. { .name = "tls_ciphers",
  1928. .comment = "OpenSSL cipher preference list",
  1929. .type = CONFIG_ITEM_STRING,
  1930. .default_ = "\"DEFAULT:!MEDIUM:!LOW\"" },
  1931. { .name = "autoconnect",
  1932. .comment = "Connect automatically on startup",
  1933. .type = CONFIG_ITEM_BOOLEAN,
  1934. .default_ = "on" },
  1935. { .name = "autojoin",
  1936. .comment = "Channels to join on start (e.g. \"#abc,#def key,#ghi\")",
  1937. .type = CONFIG_ITEM_STRING_ARRAY,
  1938. .validate = config_validate_nonjunk_string },
  1939. { .name = "command",
  1940. .comment = "Command to execute after a successful connect",
  1941. .type = CONFIG_ITEM_STRING },
  1942. { .name = "command_delay",
  1943. .comment = "Delay between executing \"command\" and joining channels",
  1944. .type = CONFIG_ITEM_INTEGER,
  1945. .validate = config_validate_nonnegative,
  1946. .default_ = "0" },
  1947. { .name = "reconnect",
  1948. .comment = "Whether to reconnect on error",
  1949. .type = CONFIG_ITEM_BOOLEAN,
  1950. .default_ = "on" },
  1951. { .name = "reconnect_delay",
  1952. .comment = "Time between reconnecting",
  1953. .type = CONFIG_ITEM_INTEGER,
  1954. .validate = config_validate_nonnegative,
  1955. .default_ = "5" },
  1956. { .name = "socks_host",
  1957. .comment = "Address of a SOCKS 4a/5 proxy",
  1958. .type = CONFIG_ITEM_STRING,
  1959. .validate = config_validate_nonjunk_string },
  1960. { .name = "socks_port",
  1961. .comment = "SOCKS port number",
  1962. .type = CONFIG_ITEM_INTEGER,
  1963. .validate = config_validate_nonnegative,
  1964. .default_ = "1080" },
  1965. { .name = "socks_username",
  1966. .comment = "SOCKS auth. username",
  1967. .type = CONFIG_ITEM_STRING },
  1968. { .name = "socks_password",
  1969. .comment = "SOCKS auth. password",
  1970. .type = CONFIG_ITEM_STRING },
  1971. {}
  1972. };
  1973. static struct config_schema g_config_behaviour[] =
  1974. {
  1975. { .name = "isolate_buffers",
  1976. .comment = "Don't leak messages from the server and global buffers",
  1977. .type = CONFIG_ITEM_BOOLEAN,
  1978. .default_ = "off",
  1979. .on_change = on_config_isolate_buffers_change },
  1980. { .name = "beep_on_highlight",
  1981. .comment = "Beep when highlighted or on a new invisible PM",
  1982. .type = CONFIG_ITEM_BOOLEAN,
  1983. .default_ = "on",
  1984. .on_change = on_config_beep_on_highlight_change },
  1985. { .name = "show_all_prefixes",
  1986. .comment = "Show all prefixes in front of nicknames",
  1987. .type = CONFIG_ITEM_BOOLEAN,
  1988. .default_ = "off",
  1989. .on_change = on_config_show_all_prefixes_change },
  1990. { .name = "word_wrapping",
  1991. .comment = "Enable simple word wrapping in buffers",
  1992. .type = CONFIG_ITEM_BOOLEAN,
  1993. .default_ = "on",
  1994. .on_change = on_config_word_wrapping_change },
  1995. { .name = "date_change_line",
  1996. .comment = "Input to strftime(3) for the date change line",
  1997. .type = CONFIG_ITEM_STRING,
  1998. .default_ = "\"%F\"" },
  1999. { .name = "logging",
  2000. .comment = "Log buffer contents to file",
  2001. .type = CONFIG_ITEM_BOOLEAN,
  2002. .default_ = "off",
  2003. .on_change = on_config_logging_change },
  2004. { .name = "save_on_quit",
  2005. .comment = "Save configuration before quitting",
  2006. .type = CONFIG_ITEM_BOOLEAN,
  2007. .default_ = "on" },
  2008. { .name = "debug_mode",
  2009. .comment = "Produce some debugging output",
  2010. .type = CONFIG_ITEM_BOOLEAN,
  2011. .default_ = "off",
  2012. .on_change = on_config_debug_mode_change },
  2013. // GNU screen has an ^O in its formatting attributes reset string,
  2014. // therefore we can't just pipe raw formatting to `less -R`.
  2015. // You can use the -r switch, however that makes `less` very confused
  2016. // about line wrapping, and the result is suboptimal.
  2017. { .name = "backlog_limit",
  2018. .comment = "Maximum number of lines stored in the backlog",
  2019. .type = CONFIG_ITEM_INTEGER,
  2020. .validate = config_validate_nonnegative,
  2021. .default_ = "1000",
  2022. .on_change = on_config_backlog_limit_change },
  2023. { .name = "backlog_helper",
  2024. .comment = "Shell command to display a buffer's history",
  2025. .type = CONFIG_ITEM_STRING,
  2026. .default_ = "\"LESSSECURE=1 less -M -R +G\"" },
  2027. { .name = "backlog_helper_strip_formatting",
  2028. .comment = "Strip formatting from backlog helper input",
  2029. .type = CONFIG_ITEM_BOOLEAN,
  2030. .default_ = "on" },
  2031. { .name = "reconnect_delay_growing",
  2032. .comment = "Growing factor for reconnect delay",
  2033. .type = CONFIG_ITEM_INTEGER,
  2034. .validate = config_validate_nonnegative,
  2035. .default_ = "2" },
  2036. { .name = "reconnect_delay_max",
  2037. .comment = "Maximum reconnect delay in seconds",
  2038. .type = CONFIG_ITEM_INTEGER,
  2039. .validate = config_validate_nonnegative,
  2040. .default_ = "600" },
  2041. { .name = "autoaway_message",
  2042. .comment = "Automated away message",
  2043. .type = CONFIG_ITEM_STRING,
  2044. .default_ = "\"I'm not here right now\"" },
  2045. { .name = "autoaway_delay",
  2046. .comment = "Delay from the last keypress in seconds",
  2047. .type = CONFIG_ITEM_INTEGER,
  2048. .validate = config_validate_nonnegative,
  2049. .default_ = "1800" },
  2050. { .name = "plugin_autoload",
  2051. .comment = "Plugins to automatically load on start",
  2052. .type = CONFIG_ITEM_STRING_ARRAY,
  2053. .validate = config_validate_nonjunk_string },
  2054. {}
  2055. };
  2056. static struct config_schema g_config_attributes[] =
  2057. {
  2058. #define XX(x, y, z) { .name = y, .comment = z, .type = CONFIG_ITEM_STRING, \
  2059. .on_change = on_config_attribute_change },
  2060. ATTR_TABLE (XX)
  2061. #undef XX
  2062. {}
  2063. };
  2064. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2065. static void
  2066. load_config_behaviour (struct config_item *subtree, void *user_data)
  2067. {
  2068. config_schema_apply_to_object (g_config_behaviour, subtree, user_data);
  2069. }
  2070. static void
  2071. load_config_attributes (struct config_item *subtree, void *user_data)
  2072. {
  2073. config_schema_apply_to_object (g_config_attributes, subtree, user_data);
  2074. }
  2075. static void
  2076. register_config_modules (struct app_context *ctx)
  2077. {
  2078. struct config *config = &ctx->config;
  2079. // The servers are loaded later when we can create buffers for them
  2080. config_register_module (config, "servers", NULL, NULL);
  2081. config_register_module (config, "aliases", NULL, NULL);
  2082. config_register_module (config, "plugins", NULL, NULL);
  2083. config_register_module (config, "behaviour", load_config_behaviour, ctx);
  2084. config_register_module (config, "attributes", load_config_attributes, ctx);
  2085. }
  2086. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2087. static const char *
  2088. get_config_string (struct config_item *root, const char *key)
  2089. {
  2090. struct config_item *item = config_item_get (root, key, NULL);
  2091. hard_assert (item);
  2092. if (item->type == CONFIG_ITEM_NULL)
  2093. return NULL;
  2094. hard_assert (config_item_type_is_string (item->type));
  2095. return item->value.string.str;
  2096. }
  2097. static bool
  2098. set_config_string
  2099. (struct config_item *root, const char *key, const char *value)
  2100. {
  2101. struct config_item *item = config_item_get (root, key, NULL);
  2102. hard_assert (item);
  2103. struct config_item *new_ = config_item_string_from_cstr (value);
  2104. struct error *e = NULL;
  2105. if (config_item_set_from (item, new_, &e))
  2106. return true;
  2107. config_item_destroy (new_);
  2108. print_error ("couldn't set `%s' in configuration: %s", key, e->message);
  2109. error_free (e);
  2110. return false;
  2111. }
  2112. static int64_t
  2113. get_config_integer (struct config_item *root, const char *key)
  2114. {
  2115. struct config_item *item = config_item_get (root, key, NULL);
  2116. hard_assert (item && item->type == CONFIG_ITEM_INTEGER);
  2117. return item->value.integer;
  2118. }
  2119. static bool
  2120. get_config_boolean (struct config_item *root, const char *key)
  2121. {
  2122. struct config_item *item = config_item_get (root, key, NULL);
  2123. hard_assert (item && item->type == CONFIG_ITEM_BOOLEAN);
  2124. return item->value.boolean;
  2125. }
  2126. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2127. static struct str_map *
  2128. get_servers_config (struct app_context *ctx)
  2129. {
  2130. return &config_item_get (ctx->config.root, "servers", NULL)->value.object;
  2131. }
  2132. static struct str_map *
  2133. get_aliases_config (struct app_context *ctx)
  2134. {
  2135. return &config_item_get (ctx->config.root, "aliases", NULL)->value.object;
  2136. }
  2137. static struct str_map *
  2138. get_plugins_config (struct app_context *ctx)
  2139. {
  2140. return &config_item_get (ctx->config.root, "plugins", NULL)->value.object;
  2141. }
  2142. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2143. static void
  2144. serialize_configuration (struct config_item *root, struct str *output)
  2145. {
  2146. str_append (output,
  2147. "# " PROGRAM_NAME " " PROGRAM_VERSION " configuration file\n"
  2148. "#\n"
  2149. "# Relative paths are searched for in ${XDG_CONFIG_HOME:-~/.config}\n"
  2150. "# /" PROGRAM_NAME " as well as in $XDG_CONFIG_DIRS/" PROGRAM_NAME "\n"
  2151. "#\n"
  2152. "# Everything is in UTF-8. Any custom comments will be overwritten.\n"
  2153. "\n");
  2154. config_item_write (root, true, output);
  2155. }
  2156. // --- Terminal output ---------------------------------------------------------
  2157. /// Default color pair
  2158. #define COLOR_DEFAULT -1
  2159. /// Bright versions of the basic color set
  2160. #define COLOR_BRIGHT(x) (COLOR_ ## x + 8)
  2161. /// Builds a color pair for 256-color terminals with a 16-color backup value
  2162. #define COLOR_256(name, c256) \
  2163. (((COLOR_ ## name) & 0xFFFF) | (((c256) & 0xFFFF) << 16))
  2164. typedef int (*terminal_printer_fn) (int);
  2165. static int
  2166. putchar_stderr (int c)
  2167. {
  2168. return fputc (c, stderr);
  2169. }
  2170. static terminal_printer_fn
  2171. get_attribute_printer (FILE *stream)
  2172. {
  2173. if (stream == stdout && g_terminal.stdout_is_tty)
  2174. return putchar;
  2175. if (stream == stderr && g_terminal.stderr_is_tty)
  2176. return putchar_stderr;
  2177. return NULL;
  2178. }
  2179. static void
  2180. vprint_attributed (struct app_context *ctx,
  2181. FILE *stream, intptr_t attribute, const char *fmt, va_list ap)
  2182. {
  2183. terminal_printer_fn printer = get_attribute_printer (stream);
  2184. if (!attribute)
  2185. printer = NULL;
  2186. if (printer)
  2187. tputs (ctx->attrs[attribute], 1, printer);
  2188. vfprintf (stream, fmt, ap);
  2189. if (printer)
  2190. tputs (ctx->attrs[ATTR_RESET], 1, printer);
  2191. }
  2192. static void
  2193. print_attributed (struct app_context *ctx,
  2194. FILE *stream, intptr_t attribute, const char *fmt, ...)
  2195. {
  2196. va_list ap;
  2197. va_start (ap, fmt);
  2198. vprint_attributed (ctx, stream, attribute, fmt, ap);
  2199. va_end (ap);
  2200. }
  2201. static void
  2202. log_message_attributed (void *user_data, const char *quote, const char *fmt,
  2203. va_list ap)
  2204. {
  2205. FILE *stream = stderr;
  2206. struct app_context *ctx = g_ctx;
  2207. CALL (ctx->input, hide);
  2208. print_attributed (ctx, stream, (intptr_t) user_data, "%s", quote);
  2209. vprint_attributed (ctx, stream, (intptr_t) user_data, fmt, ap);
  2210. fputs ("\n", stream);
  2211. CALL (ctx->input, show);
  2212. }
  2213. static ssize_t
  2214. attr_by_name (const char *name)
  2215. {
  2216. static const char *table[ATTR_COUNT] =
  2217. {
  2218. #define XX(x, y, z) [ATTR_ ## x] = y,
  2219. ATTR_TABLE (XX)
  2220. #undef XX
  2221. };
  2222. for (size_t i = 0; i < N_ELEMENTS (table); i++)
  2223. if (!strcmp (name, table[i]))
  2224. return i;
  2225. return -1;
  2226. }
  2227. static void
  2228. on_config_attribute_change (struct config_item *item)
  2229. {
  2230. struct app_context *ctx = item->user_data;
  2231. ssize_t id = attr_by_name (item->schema->name);
  2232. if (id != -1)
  2233. {
  2234. free (ctx->attrs[id]);
  2235. ctx->attrs[id] = xstrdup (item->type == CONFIG_ITEM_NULL
  2236. ? ctx->attrs_defaults[id]
  2237. : item->value.string.str);
  2238. }
  2239. }
  2240. static void
  2241. init_colors (struct app_context *ctx)
  2242. {
  2243. bool have_ti = init_terminal ();
  2244. char **defaults = ctx->attrs_defaults;
  2245. #define INIT_ATTR(id, ti) defaults[ATTR_ ## id] = xstrdup (have_ti ? (ti) : "")
  2246. INIT_ATTR (PROMPT, enter_bold_mode);
  2247. INIT_ATTR (RESET, exit_attribute_mode);
  2248. INIT_ATTR (DATE_CHANGE, enter_bold_mode);
  2249. INIT_ATTR (READ_MARKER, g_terminal.color_set_fg[COLOR_MAGENTA]);
  2250. INIT_ATTR (WARNING, g_terminal.color_set_fg[COLOR_YELLOW]);
  2251. INIT_ATTR (ERROR, g_terminal.color_set_fg[COLOR_RED]);
  2252. INIT_ATTR (EXTERNAL, g_terminal.color_set_fg[COLOR_WHITE]);
  2253. INIT_ATTR (TIMESTAMP, g_terminal.color_set_fg[COLOR_WHITE]);
  2254. INIT_ATTR (ACTION, g_terminal.color_set_fg[COLOR_RED]);
  2255. INIT_ATTR (USERHOST, g_terminal.color_set_fg[COLOR_CYAN]);
  2256. INIT_ATTR (JOIN, g_terminal.color_set_fg[COLOR_GREEN]);
  2257. INIT_ATTR (PART, g_terminal.color_set_fg[COLOR_RED]);
  2258. char *highlight = have_ti ? xstrdup_printf ("%s%s%s",
  2259. g_terminal.color_set_fg[COLOR_YELLOW],
  2260. g_terminal.color_set_bg[COLOR_MAGENTA],
  2261. enter_bold_mode) : NULL;
  2262. INIT_ATTR (HIGHLIGHT, highlight);
  2263. free (highlight);
  2264. #undef INIT_ATTR
  2265. // This prevents formatters from obtaining an attribute printer function
  2266. if (!have_ti)
  2267. {
  2268. g_terminal.stdout_is_tty = false;
  2269. g_terminal.stderr_is_tty = false;
  2270. }
  2271. g_log_message_real = log_message_attributed;
  2272. // Apply the default values so that we start with any formatting at all
  2273. config_schema_call_changed
  2274. (config_item_get (ctx->config.root, "attributes", NULL));
  2275. }
  2276. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2277. // A little tool that tries to make the most of the terminal's capabilities
  2278. // to set up text attributes. It mostly targets just terminal emulators as that
  2279. // is what people are using these days. At least no stupid ncurses limits us
  2280. // with color pairs.
  2281. enum
  2282. {
  2283. TEXT_BOLD = 1 << 0,
  2284. TEXT_ITALIC = 1 << 1,
  2285. TEXT_UNDERLINE = 1 << 2,
  2286. TEXT_INVERSE = 1 << 3,
  2287. TEXT_BLINK = 1 << 4
  2288. };
  2289. struct attr_printer
  2290. {
  2291. char **attrs; ///< Named attributes
  2292. FILE *stream; ///< Output stream
  2293. bool dirty; ///< Attributes are set
  2294. };
  2295. #define ATTR_PRINTER_INIT(ctx, stream) { ctx->attrs, stream, true }
  2296. static void
  2297. attr_printer_tputs (struct attr_printer *self, const char *attr)
  2298. {
  2299. terminal_printer_fn printer = get_attribute_printer (self->stream);
  2300. if (printer)
  2301. tputs (attr, 1, printer);
  2302. else
  2303. // We shouldn't really do this but we need it to
  2304. // output formatting to the backlog
  2305. fputs (attr, self->stream);
  2306. }
  2307. static void
  2308. attr_printer_reset (struct attr_printer *self)
  2309. {
  2310. if (self->dirty)
  2311. attr_printer_tputs (self, self->attrs[ATTR_RESET]);
  2312. self->dirty = false;
  2313. }
  2314. static void
  2315. attr_printer_apply_named (struct attr_printer *self, int attribute)
  2316. {
  2317. attr_printer_reset (self);
  2318. if (attribute != ATTR_RESET)
  2319. {
  2320. attr_printer_tputs (self, self->attrs[attribute]);
  2321. self->dirty = true;
  2322. }
  2323. }
  2324. // NOTE: commonly terminals have:
  2325. // 8 colors (worst, bright fg with BOLD, bg sometimes with BLINK)
  2326. // 16 colors (okayish, we have the full basic range guaranteed)
  2327. // 88 colors (the same plus a 4^3 RGB cube and a few shades of gray)
  2328. // 256 colors (best, like above but with a larger cube and more gray)
  2329. /// Interpolate from the 256-color palette to the 88-color one
  2330. static int
  2331. attr_printer_256_to_88 (int color)
  2332. {
  2333. // These colours are the same everywhere
  2334. if (color < 16)
  2335. return color;
  2336. // 24 -> 8 extra shades of gray
  2337. if (color >= 232)
  2338. return 80 + (color - 232) / 3;
  2339. // 6 * 6 * 6 cube -> 4 * 4 * 4 cube
  2340. int x[6] = { 0, 1, 1, 2, 2, 3 };
  2341. int index = color - 16;
  2342. return 16 +
  2343. ( x[ index / 36 ] << 8
  2344. | x[(index / 6) % 6 ] << 4
  2345. | x[(index % 6) ] );
  2346. }
  2347. static int
  2348. attr_printer_decode_color (int color, bool *is_bright)
  2349. {
  2350. int16_t c16 = color; hard_assert (c16 < 16);
  2351. int16_t c256 = color >> 16; hard_assert (c256 < 256);
  2352. *is_bright = false;
  2353. switch (max_colors)
  2354. {
  2355. case 8:
  2356. if (c16 >= 8)
  2357. {
  2358. c16 -= 8;
  2359. *is_bright = true;
  2360. }
  2361. case 16:
  2362. return c16;
  2363. case 88:
  2364. return c256 <= 0 ? c16 : attr_printer_256_to_88 (c256);
  2365. case 256:
  2366. return c256 <= 0 ? c16 : c256;
  2367. default:
  2368. // Unsupported palette
  2369. return -1;
  2370. }
  2371. }
  2372. static void
  2373. attr_printer_apply (struct attr_printer *self,
  2374. int text_attrs, int wanted_fg, int wanted_bg)
  2375. {
  2376. bool fg_is_bright;
  2377. int fg = attr_printer_decode_color (wanted_fg, &fg_is_bright);
  2378. bool bg_is_bright;
  2379. int bg = attr_printer_decode_color (wanted_bg, &bg_is_bright);
  2380. bool have_inverse = !!(text_attrs & TEXT_INVERSE);
  2381. if (have_inverse)
  2382. {
  2383. bool tmp = fg_is_bright;
  2384. fg_is_bright = bg_is_bright;
  2385. bg_is_bright = tmp;
  2386. }
  2387. // In 8 colour mode, some terminals don't support bright backgrounds.
  2388. // However, we can make use of the fact that the brightness change caused
  2389. // by the bold attribute is retained when inverting the colours.
  2390. // This has the downside of making the text bold when it's not supposed
  2391. // to be, and we still can't make both colours bright, so it's more of
  2392. // an interesting hack rather than anything else.
  2393. if (!fg_is_bright && bg_is_bright && have_inverse)
  2394. text_attrs |= TEXT_BOLD;
  2395. else if (!fg_is_bright && bg_is_bright
  2396. && !have_inverse && fg >= 0 && bg >= 0)
  2397. {
  2398. // As long as none of the colours is the default, we can swap them
  2399. int tmp = fg; fg = bg; bg = tmp;
  2400. text_attrs |= TEXT_BOLD | TEXT_INVERSE;
  2401. }
  2402. else
  2403. {
  2404. // This is what works on normal, decent terminals
  2405. if (fg_is_bright) text_attrs |= TEXT_BOLD;
  2406. if (bg_is_bright) text_attrs |= TEXT_BLINK;
  2407. }
  2408. attr_printer_reset (self);
  2409. if (text_attrs)
  2410. attr_printer_tputs (self, tparm (set_attributes,
  2411. 0, // standout
  2412. text_attrs & TEXT_UNDERLINE,
  2413. text_attrs & TEXT_INVERSE,
  2414. text_attrs & TEXT_BLINK,
  2415. 0, // dim
  2416. text_attrs & TEXT_BOLD,
  2417. 0, // blank
  2418. 0, // protect
  2419. 0)); // acs
  2420. if (enter_italics_mode && (text_attrs & TEXT_ITALIC))
  2421. attr_printer_tputs (self, enter_italics_mode);
  2422. if (fg >= 0)
  2423. attr_printer_tputs (self, g_terminal.color_set_fg[fg]);
  2424. if (bg >= 0)
  2425. attr_printer_tputs (self, g_terminal.color_set_bg[bg]);
  2426. self->dirty = true;
  2427. }
  2428. // --- Helpers -----------------------------------------------------------------
  2429. static int
  2430. irc_server_strcmp (struct server *s, const char *a, const char *b)
  2431. {
  2432. int x;
  2433. while (*a || *b)
  2434. if ((x = s->irc_tolower (*a++) - s->irc_tolower (*b++)))
  2435. return x;
  2436. return 0;
  2437. }
  2438. static int
  2439. irc_server_strncmp (struct server *s, const char *a, const char *b, size_t n)
  2440. {
  2441. int x;
  2442. while (n-- && (*a || *b))
  2443. if ((x = s->irc_tolower (*a++) - s->irc_tolower (*b++)))
  2444. return x;
  2445. return 0;
  2446. }
  2447. static char *
  2448. irc_cut_nickname (const char *prefix)
  2449. {
  2450. return cstr_cut_until (prefix, "!@");
  2451. }
  2452. static const char *
  2453. irc_find_userhost (const char *prefix)
  2454. {
  2455. const char *p = strchr (prefix, '!');
  2456. return p ? p + 1 : NULL;
  2457. }
  2458. static bool
  2459. irc_is_this_us (struct server *s, const char *prefix)
  2460. {
  2461. // This shouldn't be called before successfully registering.
  2462. // Better safe than sorry, though.
  2463. if (!s->irc_user)
  2464. return false;
  2465. char *nick = irc_cut_nickname (prefix);
  2466. bool result = !irc_server_strcmp (s, nick, s->irc_user->nickname);
  2467. free (nick);
  2468. return result;
  2469. }
  2470. static bool
  2471. irc_is_channel (struct server *s, const char *ident)
  2472. {
  2473. return *ident
  2474. && (!!strchr (s->irc_chantypes, *ident) ||
  2475. !!strchr (s->irc_idchan_prefixes, *ident));
  2476. }
  2477. // Message targets can be prefixed by a character filtering their targets
  2478. static const char *
  2479. irc_skip_statusmsg (struct server *s, const char *target)
  2480. {
  2481. return target + (*target && strchr (s->irc_statusmsg, *target));
  2482. }
  2483. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2484. // As of 2015, everything should be in UTF-8. And if it's not, we'll decode it
  2485. // as ISO Latin 1. This function should not be called on the whole message.
  2486. static char *
  2487. irc_to_utf8 (const char *text)
  2488. {
  2489. if (!text)
  2490. return NULL;
  2491. size_t len = strlen (text) + 1;
  2492. if (utf8_validate (text, len))
  2493. return xstrdup (text);
  2494. // Windows 1252 redefines several silly C1 control characters as glyphs
  2495. static const char c1[32][4] =
  2496. {
  2497. "\xe2\x82\xac", "\xc2\x81", "\xe2\x80\x9a", "\xc6\x92",
  2498. "\xe2\x80\x9e", "\xe2\x80\xa6", "\xe2\x80\xa0", "\xe2\x80\xa1",
  2499. "\xcb\x86", "\xe2\x80\xb0", "\xc5\xa0", "\xe2\x80\xb9",
  2500. "\xc5\x92", "\xc2\x8d", "\xc5\xbd", "\xc2\x8f",
  2501. "\xc2\x90", "\xe2\x80\x98", "\xe2\x80\x99", "\xe2\x80\x9c",
  2502. "\xe2\x80\x9d", "\xe2\x80\xa2", "\xe2\x80\x93", "\xe2\x80\x94",
  2503. "\xcb\x9c", "\xe2\x84\xa2", "\xc5\xa1", "\xe2\x80\xba",
  2504. "\xc5\x93", "\xc2\x9d", "\xc5\xbe", "\xc5\xb8",
  2505. };
  2506. struct str s = str_make ();
  2507. for (const char *p = text; *p; p++)
  2508. {
  2509. int c = *(unsigned char *) p;
  2510. if (c < 0x80)
  2511. str_append_c (&s, c);
  2512. else if (c < 0xA0)
  2513. str_append (&s, c1[c & 0x1f]);
  2514. else
  2515. str_append_data (&s,
  2516. (char[]) {0xc0 | (c >> 6), 0x80 | (c & 0x3f)}, 2);
  2517. }
  2518. return str_steal (&s);
  2519. }
  2520. // This function is used to output debugging IRC traffic to the terminal.
  2521. // It's far from ideal, as any non-UTF-8 text degrades the entire line to
  2522. // ISO Latin 1. But it should work good enough most of the time.
  2523. static char *
  2524. irc_to_term (struct app_context *ctx, const char *text)
  2525. {
  2526. char *utf8 = irc_to_utf8 (text);
  2527. char *term = iconv_xstrdup (ctx->term_from_utf8, utf8, -1, NULL);
  2528. free (utf8);
  2529. return term;
  2530. }
  2531. // --- Output formatter --------------------------------------------------------
  2532. // This complicated piece of code makes attributed text formatting simple.
  2533. // We use a printf-inspired syntax to push attributes and text to the object,
  2534. // then flush it either to a terminal, or a log file with formatting stripped.
  2535. //
  2536. // Format strings use a #-quoted notation, to differentiate from printf:
  2537. // #s inserts a string (expected to be in UTF-8)
  2538. // #d inserts a signed integer
  2539. // #l inserts a locale-encoded string
  2540. //
  2541. // #S inserts a string from the server with unknown encoding
  2542. // #m inserts a mIRC-formatted string (auto-resets at boundaries)
  2543. // #n cuts the nickname from a string and automatically colours it
  2544. // #N is like #n but also appends userhost, if present
  2545. //
  2546. // #a inserts named attributes (auto-resets)
  2547. // #r resets terminal attributes
  2548. // #c sets foreground color
  2549. // #C sets background color
  2550. //
  2551. // Modifiers:
  2552. // & free() the string argument after using it
  2553. static void
  2554. formatter_add_item (struct formatter *self, struct formatter_item template_)
  2555. {
  2556. if (template_.text)
  2557. template_.text = xstrdup (template_.text);
  2558. if (self->items_len == self->items_alloc)
  2559. self->items = xreallocarray
  2560. (self->items, sizeof *self->items, (self->items_alloc <<= 1));
  2561. self->items[self->items_len++] = template_;
  2562. }
  2563. #define FORMATTER_ADD_ITEM(self, type_, ...) formatter_add_item ((self), \
  2564. (struct formatter_item) { .type = FORMATTER_ITEM_ ## type_, __VA_ARGS__ })
  2565. #define FORMATTER_ADD_RESET(self) \
  2566. FORMATTER_ADD_ITEM ((self), ATTR, .attribute = ATTR_RESET)
  2567. #define FORMATTER_ADD_TEXT(self, text_) \
  2568. FORMATTER_ADD_ITEM ((self), TEXT, .text = (text_))
  2569. #define FORMATTER_ADD_SIMPLE(self, attribute_) \
  2570. FORMATTER_ADD_ITEM ((self), SIMPLE, .attribute = TEXT_ ## attribute_)
  2571. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2572. enum
  2573. {
  2574. MIRC_WHITE, MIRC_BLACK, MIRC_BLUE, MIRC_GREEN,
  2575. MIRC_L_RED, MIRC_RED, MIRC_PURPLE, MIRC_ORANGE,
  2576. MIRC_YELLOW, MIRC_L_GREEN, MIRC_CYAN, MIRC_L_CYAN,
  2577. MIRC_L_BLUE, MIRC_L_PURPLE, MIRC_GRAY, MIRC_L_GRAY,
  2578. };
  2579. // We use estimates from the 16 color terminal palette, or the 256 color cube,
  2580. // which is not always available. The mIRC orange colour is only in the cube.
  2581. static const int g_mirc_to_terminal[] =
  2582. {
  2583. [MIRC_WHITE] = COLOR_256 (BRIGHT (WHITE), 231),
  2584. [MIRC_BLACK] = COLOR_256 (BLACK, 16),
  2585. [MIRC_BLUE] = COLOR_256 (BLUE, 19),
  2586. [MIRC_GREEN] = COLOR_256 (GREEN, 34),
  2587. [MIRC_L_RED] = COLOR_256 (BRIGHT (RED), 196),
  2588. [MIRC_RED] = COLOR_256 (RED, 124),
  2589. [MIRC_PURPLE] = COLOR_256 (MAGENTA, 127),
  2590. [MIRC_ORANGE] = COLOR_256 (BRIGHT (YELLOW), 214),
  2591. [MIRC_YELLOW] = COLOR_256 (BRIGHT (YELLOW), 226),
  2592. [MIRC_L_GREEN] = COLOR_256 (BRIGHT (GREEN), 46),
  2593. [MIRC_CYAN] = COLOR_256 (CYAN, 37),
  2594. [MIRC_L_CYAN] = COLOR_256 (BRIGHT (CYAN), 51),
  2595. [MIRC_L_BLUE] = COLOR_256 (BRIGHT (BLUE), 21),
  2596. [MIRC_L_PURPLE] = COLOR_256 (BRIGHT (MAGENTA),201),
  2597. [MIRC_GRAY] = COLOR_256 (BRIGHT (BLACK), 244),
  2598. [MIRC_L_GRAY] = COLOR_256 (WHITE, 252),
  2599. };
  2600. static const char *
  2601. formatter_parse_mirc_color (struct formatter *self, const char *s)
  2602. {
  2603. if (!isdigit_ascii (*s))
  2604. {
  2605. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = -1);
  2606. FORMATTER_ADD_ITEM (self, BG_COLOR, .color = -1);
  2607. return s;
  2608. }
  2609. int fg = *s++ - '0';
  2610. if (isdigit_ascii (*s))
  2611. fg = fg * 10 + (*s++ - '0');
  2612. if (fg >= 0 && fg < 16)
  2613. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = g_mirc_to_terminal[fg]);
  2614. if (*s != ',' || !isdigit_ascii (s[1]))
  2615. return s;
  2616. s++;
  2617. int bg = *s++ - '0';
  2618. if (isdigit_ascii (*s))
  2619. bg = bg * 10 + (*s++ - '0');
  2620. if (bg >= 0 && bg < 16)
  2621. FORMATTER_ADD_ITEM (self, BG_COLOR, .color = g_mirc_to_terminal[bg]);
  2622. return s;
  2623. }
  2624. static void
  2625. formatter_parse_mirc (struct formatter *self, const char *s)
  2626. {
  2627. FORMATTER_ADD_RESET (self);
  2628. struct str buf = str_make ();
  2629. unsigned char c;
  2630. while ((c = *s++))
  2631. {
  2632. if (buf.len && c < 0x20)
  2633. {
  2634. FORMATTER_ADD_TEXT (self, buf.str);
  2635. str_reset (&buf);
  2636. }
  2637. switch (c)
  2638. {
  2639. case '\x02': FORMATTER_ADD_SIMPLE (self, BOLD); break;
  2640. case '\x1d': FORMATTER_ADD_SIMPLE (self, ITALIC); break;
  2641. case '\x1f': FORMATTER_ADD_SIMPLE (self, UNDERLINE); break;
  2642. case '\x16': FORMATTER_ADD_SIMPLE (self, INVERSE); break;
  2643. case '\x03':
  2644. s = formatter_parse_mirc_color (self, s);
  2645. break;
  2646. case '\x0f':
  2647. FORMATTER_ADD_RESET (self);
  2648. break;
  2649. default:
  2650. str_append_c (&buf, c);
  2651. }
  2652. }
  2653. if (buf.len)
  2654. FORMATTER_ADD_TEXT (self, buf.str);
  2655. str_free (&buf);
  2656. FORMATTER_ADD_RESET (self);
  2657. }
  2658. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2659. static void
  2660. formatter_parse_nick (struct formatter *self, const char *s)
  2661. {
  2662. // For outgoing messages; maybe we should add a special #t for them
  2663. // which would also make us not cut off the userhost part, ever
  2664. if (irc_is_channel (self->s, irc_skip_statusmsg (self->s, s)))
  2665. {
  2666. char *tmp = irc_to_utf8 (s);
  2667. FORMATTER_ADD_TEXT (self, tmp);
  2668. free (tmp);
  2669. return;
  2670. }
  2671. char *nick = irc_cut_nickname (s);
  2672. int color = siphash_wrapper (nick, strlen (nick)) % 7;
  2673. // Never use the black colour, could become transparent on black terminals;
  2674. // white is similarly excluded from the range
  2675. if (color == COLOR_BLACK)
  2676. color = (uint16_t) -1;
  2677. // Use a color from the 256-color cube if available
  2678. color |= self->ctx->nick_palette[siphash_wrapper (nick,
  2679. strlen (nick)) % self->ctx->nick_palette_len] << 16;
  2680. // We always use the default color for ourselves
  2681. if (self->s && irc_is_this_us (self->s, nick))
  2682. color = -1;
  2683. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = color);
  2684. char *x = irc_to_utf8 (nick);
  2685. free (nick);
  2686. FORMATTER_ADD_TEXT (self, x);
  2687. free (x);
  2688. // Need to reset the color afterwards
  2689. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = -1);
  2690. }
  2691. static void
  2692. formatter_parse_nick_full (struct formatter *self, const char *s)
  2693. {
  2694. formatter_parse_nick (self, s);
  2695. const char *userhost;
  2696. if (!(userhost = irc_find_userhost (s)))
  2697. return;
  2698. FORMATTER_ADD_TEXT (self, " (");
  2699. FORMATTER_ADD_ITEM (self, ATTR, .attribute = ATTR_USERHOST);
  2700. char *x = irc_to_utf8 (userhost);
  2701. FORMATTER_ADD_TEXT (self, x);
  2702. free (x);
  2703. FORMATTER_ADD_RESET (self);
  2704. FORMATTER_ADD_TEXT (self, ")");
  2705. }
  2706. static const char *
  2707. formatter_parse_field (struct formatter *self,
  2708. const char *field, struct str *buf, va_list *ap)
  2709. {
  2710. bool free_string = false;
  2711. char *s = NULL;
  2712. char *tmp = NULL;
  2713. int c;
  2714. restart:
  2715. switch ((c = *field++))
  2716. {
  2717. // We can push boring text content to the caller's buffer
  2718. // and let it flush the buffer only when it's actually needed
  2719. case 'd':
  2720. tmp = xstrdup_printf ("%d", va_arg (*ap, int));
  2721. str_append (buf, tmp);
  2722. free (tmp);
  2723. break;
  2724. case 's':
  2725. str_append (buf, (s = va_arg (*ap, char *)));
  2726. break;
  2727. case 'l':
  2728. if (!(tmp = iconv_xstrdup (self->ctx->term_to_utf8,
  2729. (s = va_arg (*ap, char *)), -1, NULL)))
  2730. print_error ("character conversion failed for: %s", "output");
  2731. else
  2732. str_append (buf, tmp);
  2733. free (tmp);
  2734. break;
  2735. case 'S':
  2736. tmp = irc_to_utf8 ((s = va_arg (*ap, char *)));
  2737. str_append (buf, tmp);
  2738. free (tmp);
  2739. break;
  2740. case 'm':
  2741. tmp = irc_to_utf8 ((s = va_arg (*ap, char *)));
  2742. formatter_parse_mirc (self, tmp);
  2743. free (tmp);
  2744. break;
  2745. case 'n':
  2746. formatter_parse_nick (self, (s = va_arg (*ap, char *)));
  2747. break;
  2748. case 'N':
  2749. formatter_parse_nick_full (self, (s = va_arg (*ap, char *)));
  2750. break;
  2751. case 'a':
  2752. FORMATTER_ADD_ITEM (self, ATTR, .attribute = va_arg (*ap, int));
  2753. break;
  2754. case 'c':
  2755. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = va_arg (*ap, int));
  2756. break;
  2757. case 'C':
  2758. FORMATTER_ADD_ITEM (self, BG_COLOR, .color = va_arg (*ap, int));
  2759. break;
  2760. case 'r':
  2761. FORMATTER_ADD_RESET (self);
  2762. break;
  2763. default:
  2764. if (c == '&' && !free_string)
  2765. free_string = true;
  2766. else if (c)
  2767. hard_assert (!"unexpected format specifier");
  2768. else
  2769. hard_assert (!"unexpected end of format string");
  2770. goto restart;
  2771. }
  2772. if (free_string)
  2773. free (s);
  2774. return field;
  2775. }
  2776. // I was unable to take a pointer of a bare "va_list" when it was passed in
  2777. // as a function argument, so it has to be a pointer from the beginning
  2778. static void
  2779. formatter_addv (struct formatter *self, const char *format, va_list *ap)
  2780. {
  2781. struct str buf = str_make ();
  2782. while (*format)
  2783. {
  2784. if (*format != '#' || *++format == '#')
  2785. {
  2786. str_append_c (&buf, *format++);
  2787. continue;
  2788. }
  2789. if (buf.len)
  2790. {
  2791. FORMATTER_ADD_TEXT (self, buf.str);
  2792. str_reset (&buf);
  2793. }
  2794. format = formatter_parse_field (self, format, &buf, ap);
  2795. }
  2796. if (buf.len)
  2797. FORMATTER_ADD_TEXT (self, buf.str);
  2798. str_free (&buf);
  2799. }
  2800. static void
  2801. formatter_add (struct formatter *self, const char *format, ...)
  2802. {
  2803. va_list ap;
  2804. va_start (ap, format);
  2805. formatter_addv (self, format, &ap);
  2806. va_end (ap);
  2807. }
  2808. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2809. struct line_char_attrs
  2810. {
  2811. int named; ///< Named attribute or -1
  2812. int text; ///< Text attributes
  2813. int fg; ///< Foreground color (-1 for default)
  2814. int bg; ///< Background color (-1 for default)
  2815. };
  2816. struct line_char
  2817. {
  2818. LIST_HEADER (struct line_char)
  2819. char bytes[MB_LEN_MAX]; ///< The character
  2820. size_t len; ///< Length of the character in bytes
  2821. wchar_t wide; ///< The character as a wchar_t
  2822. int width; ///< Width of the character in cells
  2823. struct line_char_attrs attrs; ///< Attributes
  2824. };
  2825. static struct line_char *
  2826. line_char_new (const char *mb, size_t mb_len, wchar_t wc)
  2827. {
  2828. struct line_char *self = xcalloc (1, sizeof *self);
  2829. memcpy (self->bytes, mb, (self->len = MIN (mb_len, sizeof self->bytes)));
  2830. self->width = wcwidth ((self->wide = wc));
  2831. // Typically various control characters
  2832. if (self->width < 0)
  2833. self->width = 0;
  2834. self->attrs.bg = self->attrs.fg = -1;
  2835. self->attrs.named = ATTR_RESET;
  2836. return self;
  2837. }
  2838. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2839. struct line_wrap_mark
  2840. {
  2841. struct line_char *start; ///< First character
  2842. int used; ///< Display cells used
  2843. };
  2844. static void
  2845. line_wrap_mark_push (struct line_wrap_mark *mark, struct line_char *c)
  2846. {
  2847. if (!mark->start)
  2848. mark->start = c;
  2849. mark->used += c->width;
  2850. }
  2851. struct line_wrap_state
  2852. {
  2853. struct line_char *result; ///< Head of result
  2854. struct line_char *result_tail; ///< Tail of result
  2855. int line_used; ///< Line length before marks
  2856. int line_max; ///< Maximum line length
  2857. struct line_wrap_mark chunk; ///< All buffered text
  2858. struct line_wrap_mark overflow; ///< Overflowing text
  2859. };
  2860. static void
  2861. line_wrap_flush_split (struct line_wrap_state *s, struct line_wrap_mark *before)
  2862. {
  2863. struct line_char *nl = line_char_new ("\n", 1, L'\n');
  2864. LIST_INSERT_WITH_TAIL (s->result, s->result_tail, nl, before->start);
  2865. s->line_used = before->used;
  2866. }
  2867. static void
  2868. line_wrap_flush (struct line_wrap_state *s, bool force_split)
  2869. {
  2870. if (!s->overflow.start)
  2871. s->line_used += s->chunk.used;
  2872. else if (force_split || s->chunk.used > s->line_max)
  2873. {
  2874. #ifdef WRAP_UNNECESSARILY
  2875. // When the line wraps at the end of the screen and a background colour
  2876. // is set, the terminal paints the entire new line with that colour.
  2877. // Explicitly inserting a newline with the default attributes fixes it.
  2878. line_wrap_flush_split (s, &s->overflow);
  2879. #else
  2880. // Splitting here breaks link searching mechanisms in some terminals,
  2881. // though, so we make a trade-off and let the chunk wrap naturally.
  2882. // Fuck terminals, really.
  2883. s->line_used = s->overflow.used;
  2884. #endif
  2885. }
  2886. else
  2887. // Print the chunk in its entirety on a new line
  2888. line_wrap_flush_split (s, &s->chunk);
  2889. memset (&s->chunk, 0, sizeof s->chunk);
  2890. memset (&s->overflow, 0, sizeof s->overflow);
  2891. }
  2892. static void
  2893. line_wrap_nl (struct line_wrap_state *s)
  2894. {
  2895. line_wrap_flush (s, true);
  2896. struct line_char *nl = line_char_new ("\n", 1, L'\n');
  2897. LIST_APPEND_WITH_TAIL (s->result, s->result_tail, nl);
  2898. s->line_used = 0;
  2899. }
  2900. static void
  2901. line_wrap_tab (struct line_wrap_state *s, struct line_char *c)
  2902. {
  2903. line_wrap_flush (s, true);
  2904. if (s->line_used >= s->line_max)
  2905. line_wrap_nl (s);
  2906. // Compute the number of characters needed to get to the next tab stop
  2907. int tab_width = ((s->line_used + 8) & ~7) - s->line_used;
  2908. // On overflow just fill the rest of the line with spaces
  2909. if (s->line_used + tab_width > s->line_max)
  2910. tab_width = s->line_max - s->line_used;
  2911. s->line_used += tab_width;
  2912. while (tab_width--)
  2913. {
  2914. struct line_char *space = line_char_new (" ", 1, L' ');
  2915. space->attrs = c->attrs;
  2916. LIST_APPEND_WITH_TAIL (s->result, s->result_tail, space);
  2917. }
  2918. }
  2919. static void
  2920. line_wrap_push_char (struct line_wrap_state *s, struct line_char *c)
  2921. {
  2922. // Note that when processing whitespace here, any non-WS chunk has already
  2923. // been flushed, and thus it matters little if we flush with force split
  2924. if (wcschr (L"\r\f\v", c->wide))
  2925. /* Skip problematic characters */;
  2926. else if (c->wide == L'\n')
  2927. line_wrap_nl (s);
  2928. else if (c->wide == L'\t')
  2929. line_wrap_tab (s, c);
  2930. else
  2931. goto use_as_is;
  2932. free (c);
  2933. return;
  2934. use_as_is:
  2935. if (s->overflow.start
  2936. || s->line_used + s->chunk.used + c->width > s->line_max)
  2937. {
  2938. if (s->overflow.used + c->width > s->line_max)
  2939. {
  2940. #ifdef WRAP_UNNECESSARILY
  2941. // If the overflow overflows, restart on a new line
  2942. line_wrap_nl (s);
  2943. #else
  2944. // See line_wrap_flush(), we would end up on a new line anyway
  2945. line_wrap_flush (s, true);
  2946. s->line_used = 0;
  2947. #endif
  2948. }
  2949. else
  2950. line_wrap_mark_push (&s->overflow, c);
  2951. }
  2952. line_wrap_mark_push (&s->chunk, c);
  2953. LIST_APPEND_WITH_TAIL (s->result, s->result_tail, c);
  2954. }
  2955. /// Basic word wrapping that respects wcwidth(3) and expands tabs.
  2956. /// Besides making text easier to read, it also fixes the problem with
  2957. /// formatting spilling over the entire new line on line wrap.
  2958. static struct line_char *
  2959. line_wrap (struct line_char *line, int max_width)
  2960. {
  2961. struct line_wrap_state s = { .line_max = max_width };
  2962. bool last_was_word_char = false;
  2963. LIST_FOR_EACH (struct line_char, c, line)
  2964. {
  2965. // Act on the right boundary of (\s*\S+) chunks
  2966. bool this_is_word_char = !wcschr (L" \t\r\n\f\v", c->wide);
  2967. if (last_was_word_char && !this_is_word_char)
  2968. line_wrap_flush (&s, false);
  2969. last_was_word_char = this_is_word_char;
  2970. LIST_UNLINK (line, c);
  2971. line_wrap_push_char (&s, c);
  2972. }
  2973. // Make sure to process the last word and return the modified list
  2974. line_wrap_flush (&s, false);
  2975. return s.result;
  2976. }
  2977. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2978. struct exploder
  2979. {
  2980. struct app_context *ctx; ///< Application context
  2981. struct line_char *result; ///< Result
  2982. struct line_char *result_tail; ///< Tail of result
  2983. struct line_char_attrs attrs; ///< Current attributes
  2984. };
  2985. static bool
  2986. explode_formatter_attr (struct exploder *self, struct formatter_item *item)
  2987. {
  2988. switch (item->type)
  2989. {
  2990. case FORMATTER_ITEM_ATTR:
  2991. self->attrs.named = item->attribute;
  2992. self->attrs.text = 0;
  2993. self->attrs.fg = -1;
  2994. self->attrs.bg = -1;
  2995. return true;
  2996. case FORMATTER_ITEM_SIMPLE:
  2997. self->attrs.named = -1;
  2998. self->attrs.text ^= item->attribute;
  2999. return true;
  3000. case FORMATTER_ITEM_FG_COLOR:
  3001. self->attrs.named = -1;
  3002. self->attrs.fg = item->color;
  3003. return true;
  3004. case FORMATTER_ITEM_BG_COLOR:
  3005. self->attrs.named = -1;
  3006. self->attrs.bg = item->color;
  3007. return true;
  3008. default:
  3009. return false;
  3010. }
  3011. }
  3012. static void
  3013. explode_text (struct exploder *self, const char *text)
  3014. {
  3015. // Throw away any potentially harmful control characters first
  3016. struct str filtered = str_make ();
  3017. for (const char *p = text; *p; p++)
  3018. if (!strchr ("\a\b\x1b", *p))
  3019. str_append_c (&filtered, *p);
  3020. size_t term_len = 0;
  3021. char *term = iconv_xstrdup (self->ctx->term_from_utf8,
  3022. filtered.str, filtered.len + 1, &term_len);
  3023. str_free (&filtered);
  3024. mbstate_t ps;
  3025. memset (&ps, 0, sizeof ps);
  3026. wchar_t wch;
  3027. size_t len, processed = 0;
  3028. while ((len = mbrtowc (&wch, term + processed, term_len - processed, &ps)))
  3029. {
  3030. hard_assert (len != (size_t) -2 && len != (size_t) -1);
  3031. processed += len;
  3032. struct line_char *c = line_char_new (term + processed - len, len, wch);
  3033. c->attrs = self->attrs;
  3034. LIST_APPEND_WITH_TAIL (self->result, self->result_tail, c);
  3035. }
  3036. free (term);
  3037. }
  3038. static struct line_char *
  3039. formatter_to_chars (struct formatter *formatter)
  3040. {
  3041. struct exploder self = { .ctx = formatter->ctx };
  3042. self.attrs.fg = self.attrs.bg = self.attrs.named = -1;
  3043. int attribute_ignore = 0;
  3044. for (size_t i = 0; i < formatter->items_len; i++)
  3045. {
  3046. struct formatter_item *iter = &formatter->items[i];
  3047. if (iter->type == FORMATTER_ITEM_TEXT)
  3048. explode_text (&self, iter->text);
  3049. else if (iter->type == FORMATTER_ITEM_IGNORE_ATTR)
  3050. attribute_ignore += iter->attribute;
  3051. else if (attribute_ignore <= 0
  3052. && !explode_formatter_attr (&self, iter))
  3053. hard_assert (!"unhandled formatter item type");
  3054. }
  3055. return self.result;
  3056. }
  3057. enum
  3058. {
  3059. FLUSH_OPT_RAW = (1 << 0), ///< Print raw attributes
  3060. FLUSH_OPT_NOWRAP = (1 << 1) ///< Do not wrap
  3061. };
  3062. static void
  3063. formatter_flush (struct formatter *self, FILE *stream, int flush_opts)
  3064. {
  3065. struct line_char *line = formatter_to_chars (self);
  3066. bool is_tty = !!get_attribute_printer (stream);
  3067. if (!is_tty && !(flush_opts & FLUSH_OPT_RAW))
  3068. {
  3069. LIST_FOR_EACH (struct line_char, c, line)
  3070. {
  3071. fwrite (c->bytes, c->len, 1, stream);
  3072. free (c);
  3073. }
  3074. return;
  3075. }
  3076. if (self->ctx->word_wrapping && !(flush_opts & FLUSH_OPT_NOWRAP))
  3077. line = line_wrap (line, g_terminal.columns);
  3078. struct attr_printer state = ATTR_PRINTER_INIT (self->ctx, stream);
  3079. struct line_char_attrs attrs = {}; // Won't compare equal to anything
  3080. LIST_FOR_EACH (struct line_char, c, line)
  3081. {
  3082. if (attrs.fg != c->attrs.fg
  3083. || attrs.bg != c->attrs.bg
  3084. || attrs.named != c->attrs.named
  3085. || attrs.text != c->attrs.text)
  3086. {
  3087. attrs = c->attrs;
  3088. if (attrs.named != -1)
  3089. attr_printer_apply_named (&state, attrs.named);
  3090. else
  3091. attr_printer_apply (&state, attrs.text, attrs.fg, attrs.bg);
  3092. }
  3093. fwrite (c->bytes, c->len, 1, stream);
  3094. free (c);
  3095. }
  3096. attr_printer_reset (&state);
  3097. }
  3098. // --- Buffers -----------------------------------------------------------------
  3099. static void
  3100. buffer_pop_excess_lines (struct app_context *ctx, struct buffer *self)
  3101. {
  3102. int to_delete = (int) self->lines_count - (int) ctx->backlog_limit;
  3103. while (to_delete-- > 0 && self->lines)
  3104. {
  3105. struct buffer_line *excess = self->lines;
  3106. LIST_UNLINK_WITH_TAIL (self->lines, self->lines_tail, excess);
  3107. buffer_line_destroy (excess);
  3108. self->lines_count--;
  3109. }
  3110. }
  3111. static void
  3112. on_config_backlog_limit_change (struct config_item *item)
  3113. {
  3114. struct app_context *ctx = item->user_data;
  3115. ctx->backlog_limit = MIN (item->value.integer, INT_MAX);
  3116. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  3117. buffer_pop_excess_lines (ctx, iter);
  3118. }
  3119. static void
  3120. buffer_update_time (struct app_context *ctx, time_t now, FILE *stream,
  3121. int flush_opts)
  3122. {
  3123. struct tm last, current;
  3124. if (!localtime_r (&ctx->last_displayed_msg_time, &last)
  3125. || !localtime_r (&now, &current))
  3126. {
  3127. // Strange but nonfatal
  3128. print_error ("%s: %s", "localtime_r", strerror (errno));
  3129. return;
  3130. }
  3131. ctx->last_displayed_msg_time = now;
  3132. if (last.tm_year == current.tm_year
  3133. && last.tm_mon == current.tm_mon
  3134. && last.tm_mday == current.tm_mday)
  3135. return;
  3136. char buf[64] = "";
  3137. const char *format =
  3138. get_config_string (ctx->config.root, "behaviour.date_change_line");
  3139. if (!strftime (buf, sizeof buf, format, &current))
  3140. {
  3141. print_error ("%s: %s", "strftime", strerror (errno));
  3142. return;
  3143. }
  3144. struct formatter f = formatter_make (ctx, NULL);
  3145. formatter_add (&f, "#a#s\n", ATTR_DATE_CHANGE, buf);
  3146. formatter_flush (&f, stream, flush_opts);
  3147. // Flush the trailing formatting reset item
  3148. fflush (stream);
  3149. formatter_free (&f);
  3150. }
  3151. static void
  3152. buffer_line_flush (struct buffer_line *line, struct formatter *f, FILE *output,
  3153. int flush_opts)
  3154. {
  3155. int flags = line->flags;
  3156. if (flags & BUFFER_LINE_INDENT) formatter_add (f, " ");
  3157. if (flags & BUFFER_LINE_STATUS) formatter_add (f, " - ");
  3158. if (flags & BUFFER_LINE_ERROR) formatter_add (f, "#a=!=#r ", ATTR_ERROR);
  3159. for (struct formatter_item *iter = line->items; iter->type; iter++)
  3160. formatter_add_item (f, *iter);
  3161. formatter_add (f, "\n");
  3162. formatter_flush (f, output, flush_opts);
  3163. formatter_free (f);
  3164. }
  3165. static void
  3166. buffer_line_write_time (struct formatter *f, struct buffer_line *line,
  3167. FILE *stream, int flush_opts)
  3168. {
  3169. // Normal timestamps don't include the date, this way the user won't be
  3170. // confused as to when an event has happened
  3171. buffer_update_time (f->ctx, line->when, stream, flush_opts);
  3172. struct tm current;
  3173. char buf[9];
  3174. if (!localtime_r (&line->when, &current))
  3175. print_error ("%s: %s", "localtime_r", strerror (errno));
  3176. else if (!strftime (buf, sizeof buf, "%T", &current))
  3177. print_error ("%s: %s", "strftime", "buffer too small");
  3178. else
  3179. formatter_add (f, "#a#s#r ", ATTR_TIMESTAMP, buf);
  3180. }
  3181. #define buffer_line_will_show_up(buffer, line) \
  3182. (!(buffer)->hide_unimportant || !((line)->flags & BUFFER_LINE_UNIMPORTANT))
  3183. static void
  3184. buffer_line_display (struct app_context *ctx,
  3185. struct buffer *buffer, struct buffer_line *line, bool is_external)
  3186. {
  3187. if (!buffer_line_will_show_up (buffer, line))
  3188. return;
  3189. CALL (ctx->input, hide);
  3190. struct formatter f = formatter_make (ctx, NULL);
  3191. buffer_line_write_time (&f, line, stdout, 0);
  3192. // Ignore all formatting for messages coming from other buffers, that is
  3193. // either from the global or server buffer. Instead print them in grey.
  3194. if (is_external)
  3195. {
  3196. formatter_add (&f, "#a", ATTR_EXTERNAL);
  3197. FORMATTER_ADD_ITEM (&f, IGNORE_ATTR, .attribute = 1);
  3198. }
  3199. buffer_line_flush (line, &f, stdout, 0);
  3200. // Flush the trailing formatting reset item
  3201. fflush (stdout);
  3202. CALL (ctx->input, show);
  3203. }
  3204. static void
  3205. buffer_line_write_to_backlog (struct app_context *ctx,
  3206. struct buffer_line *line, FILE *log_file, int flush_opts)
  3207. {
  3208. struct formatter f = formatter_make (ctx, NULL);
  3209. buffer_line_write_time (&f, line, log_file, flush_opts);
  3210. buffer_line_flush (line, &f, log_file, flush_opts);
  3211. }
  3212. static void
  3213. buffer_line_write_to_log (struct app_context *ctx,
  3214. struct buffer_line *line, FILE *log_file)
  3215. {
  3216. if (line->flags & BUFFER_LINE_SKIP_FILE)
  3217. return;
  3218. struct formatter f = formatter_make (ctx, NULL);
  3219. struct tm current;
  3220. char buf[20];
  3221. if (!gmtime_r (&line->when, &current))
  3222. print_error ("%s: %s", "gmtime_r", strerror (errno));
  3223. else if (!strftime (buf, sizeof buf, "%F %T", &current))
  3224. print_error ("%s: %s", "strftime", "buffer too small");
  3225. else
  3226. formatter_add (&f, "#s ", buf);
  3227. // The target is not a terminal, thus it won't wrap in spite of the 0
  3228. buffer_line_flush (line, &f, log_file, 0);
  3229. }
  3230. static void
  3231. log_formatter (struct app_context *ctx,
  3232. struct buffer *buffer, int flags, struct formatter *f)
  3233. {
  3234. if (!buffer)
  3235. buffer = ctx->global_buffer;
  3236. struct buffer_line *line = buffer_line_new (f);
  3237. line->flags = flags;
  3238. // TODO: allow providing custom time (IRCv3.2 server-time)
  3239. line->when = time (NULL);
  3240. buffer_pop_excess_lines (ctx, buffer);
  3241. LIST_APPEND_WITH_TAIL (buffer->lines, buffer->lines_tail, line);
  3242. buffer->lines_count++;
  3243. if (buffer->log_file)
  3244. buffer_line_write_to_log (ctx, line, buffer->log_file);
  3245. bool unseen_pm = buffer->type == BUFFER_PM
  3246. && buffer != ctx->current_buffer
  3247. && !(flags & BUFFER_LINE_UNIMPORTANT);
  3248. bool important = (flags & BUFFER_LINE_HIGHLIGHT) || unseen_pm;
  3249. if (ctx->beep_on_highlight && important)
  3250. // XXX: this may disturb any other foreground process
  3251. CALL (ctx->input, ding);
  3252. bool can_leak = false;
  3253. if ((buffer == ctx->global_buffer)
  3254. || (ctx->current_buffer->type == BUFFER_GLOBAL
  3255. && buffer->type == BUFFER_SERVER)
  3256. || (ctx->current_buffer->type != BUFFER_GLOBAL
  3257. && buffer == ctx->current_buffer->server->buffer))
  3258. can_leak = true;
  3259. bool displayed = true;
  3260. if (ctx->terminal_suspended > 0)
  3261. // Another process is using the terminal
  3262. displayed = false;
  3263. else if (buffer == ctx->current_buffer)
  3264. buffer_line_display (ctx, buffer, line, false);
  3265. else if (!ctx->isolate_buffers && can_leak)
  3266. buffer_line_display (ctx, buffer, line, true);
  3267. else
  3268. displayed = false;
  3269. // Advance the unread marker in active buffers but don't create a new one
  3270. if (!displayed
  3271. || (buffer == ctx->current_buffer && buffer->new_messages_count))
  3272. {
  3273. buffer->new_messages_count++;
  3274. if (flags & BUFFER_LINE_UNIMPORTANT)
  3275. buffer->new_unimportant_count++;
  3276. buffer->highlighted |= important;
  3277. }
  3278. if (!displayed)
  3279. refresh_prompt (ctx);
  3280. }
  3281. static void
  3282. log_full (struct app_context *ctx, struct server *s, struct buffer *buffer,
  3283. int flags, const char *format, ...)
  3284. {
  3285. va_list ap;
  3286. va_start (ap, format);
  3287. struct formatter f = formatter_make (ctx, s);
  3288. formatter_addv (&f, format, &ap);
  3289. log_formatter (ctx, buffer, flags, &f);
  3290. va_end (ap);
  3291. }
  3292. #define log_global(ctx, flags, ...) \
  3293. log_full ((ctx), NULL, (ctx)->global_buffer, flags, __VA_ARGS__)
  3294. #define log_server(s, buffer, flags, ...) \
  3295. log_full ((s)->ctx, s, (buffer), flags, __VA_ARGS__)
  3296. #define log_global_status(ctx, ...) \
  3297. log_global ((ctx), BUFFER_LINE_STATUS, __VA_ARGS__)
  3298. #define log_global_error(ctx, ...) \
  3299. log_global ((ctx), BUFFER_LINE_ERROR, __VA_ARGS__)
  3300. #define log_global_indent(ctx, ...) \
  3301. log_global ((ctx), BUFFER_LINE_INDENT, __VA_ARGS__)
  3302. #define log_server_status(s, buffer, ...) \
  3303. log_server ((s), (buffer), BUFFER_LINE_STATUS, __VA_ARGS__)
  3304. #define log_server_error(s, buffer, ...) \
  3305. log_server ((s), (buffer), BUFFER_LINE_ERROR, __VA_ARGS__)
  3306. #define log_global_debug(ctx, ...) \
  3307. BLOCK_START \
  3308. if (g_debug_mode) \
  3309. log_global ((ctx), 0, "(*) " __VA_ARGS__); \
  3310. BLOCK_END
  3311. #define log_server_debug(s, ...) \
  3312. BLOCK_START \
  3313. if (g_debug_mode) \
  3314. log_server ((s), (s)->buffer, 0, "(*) " __VA_ARGS__); \
  3315. BLOCK_END
  3316. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3317. // Lines that are used in more than one place
  3318. #define log_nick_self(s, buffer, new_) \
  3319. log_server ((s), (buffer), BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT, \
  3320. "You are now known as #n", (new_))
  3321. #define log_nick(s, buffer, old, new_) \
  3322. log_server ((s), (buffer), BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT, \
  3323. "#n is now known as #n", (old), (new_))
  3324. #define log_outcoming_notice(s, buffer, who, text) \
  3325. log_server_status ((s), (buffer), "#s(#n): #m", "Notice", (who), (text))
  3326. #define log_outcoming_privmsg(s, buffer, prefixes, who, text) \
  3327. log_server ((s), (buffer), 0, "<#s#n> #m", (prefixes), (who), (text))
  3328. #define log_outcoming_action(s, buffer, who, text) \
  3329. log_server ((s), (buffer), 0, " #a*#r #n #m", ATTR_ACTION, (who), (text))
  3330. #define log_outcoming_orphan_notice(s, target, text) \
  3331. log_server_status ((s), (s)->buffer, "Notice -> #n: #m", (target), (text))
  3332. #define log_outcoming_orphan_privmsg(s, target, text) \
  3333. log_server_status ((s), (s)->buffer, "MSG(#n): #m", (target), (text))
  3334. #define log_ctcp_query(s, target, tag) \
  3335. log_server_status ((s), (s)->buffer, "CTCP query to #S: #S", target, tag)
  3336. #define log_ctcp_reply(s, target, reply /* freed! */) \
  3337. log_server_status ((s), (s)->buffer, "CTCP reply to #S: #&S", target, reply)
  3338. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3339. static void
  3340. make_log_filename (const char *filename, struct str *output)
  3341. {
  3342. for (const char *p = filename; *p; p++)
  3343. // XXX: anything more to replace?
  3344. if (strchr ("/\\ ", *p))
  3345. str_append_c (output, '_');
  3346. else
  3347. str_append_c (output, tolower_ascii (*p));
  3348. }
  3349. static char *
  3350. buffer_get_log_path (struct buffer *buffer)
  3351. {
  3352. struct str path = str_make ();
  3353. get_xdg_home_dir (&path, "XDG_DATA_HOME", ".local/share");
  3354. str_append_printf (&path, "/%s/%s", PROGRAM_NAME, "logs");
  3355. (void) mkdir_with_parents (path.str, NULL);
  3356. // TODO: make sure global and server buffers don't collide with filenames
  3357. str_append_c (&path, '/');
  3358. make_log_filename (buffer->name, &path);
  3359. str_append (&path, ".log");
  3360. return str_steal (&path);
  3361. }
  3362. static void
  3363. buffer_open_log_file (struct app_context *ctx, struct buffer *buffer)
  3364. {
  3365. if (!ctx->logging || buffer->log_file)
  3366. return;
  3367. char *path = buffer_get_log_path (buffer);
  3368. if (!(buffer->log_file = fopen (path, "ab")))
  3369. log_global_error (ctx, "Couldn't open log file `#s': #l",
  3370. path, strerror (errno));
  3371. else
  3372. set_cloexec (fileno (buffer->log_file));
  3373. free (path);
  3374. }
  3375. static void
  3376. buffer_close_log_file (struct buffer *buffer)
  3377. {
  3378. if (buffer->log_file)
  3379. (void) fclose (buffer->log_file);
  3380. buffer->log_file = NULL;
  3381. }
  3382. static void
  3383. on_config_logging_change (struct config_item *item)
  3384. {
  3385. struct app_context *ctx = item->user_data;
  3386. ctx->logging = item->value.boolean;
  3387. for (struct buffer *buffer = ctx->buffers; buffer; buffer = buffer->next)
  3388. if (ctx->logging)
  3389. buffer_open_log_file (ctx, buffer);
  3390. else
  3391. buffer_close_log_file (buffer);
  3392. }
  3393. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3394. static struct buffer *
  3395. buffer_by_name (struct app_context *ctx, const char *name)
  3396. {
  3397. return str_map_find (&ctx->buffers_by_name, name);
  3398. }
  3399. static void
  3400. buffer_add (struct app_context *ctx, struct buffer *buffer)
  3401. {
  3402. hard_assert (!buffer_by_name (ctx, buffer->name));
  3403. str_map_set (&ctx->buffers_by_name, buffer->name, buffer);
  3404. LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
  3405. buffer_open_log_file (ctx, buffer);
  3406. // In theory this can't cause changes in the prompt
  3407. refresh_prompt (ctx);
  3408. }
  3409. static void
  3410. buffer_remove (struct app_context *ctx, struct buffer *buffer)
  3411. {
  3412. hard_assert (buffer != ctx->current_buffer);
  3413. hard_assert (buffer != ctx->global_buffer);
  3414. CALL_ (ctx->input, buffer_destroy, buffer->input_data);
  3415. buffer->input_data = NULL;
  3416. // And make sure to unlink the buffer from "irc_buffer_map"
  3417. struct server *s = buffer->server;
  3418. if (buffer->channel)
  3419. str_map_set (&s->irc_buffer_map, buffer->channel->name, NULL);
  3420. if (buffer->user)
  3421. str_map_set (&s->irc_buffer_map, buffer->user->nickname, NULL);
  3422. if (buffer == ctx->last_buffer)
  3423. ctx->last_buffer = NULL;
  3424. if (buffer->type == BUFFER_SERVER)
  3425. buffer->server->buffer = NULL;
  3426. str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
  3427. LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
  3428. buffer_unref (buffer);
  3429. refresh_prompt (ctx);
  3430. }
  3431. static void
  3432. buffer_print_read_marker (struct app_context *ctx, FILE *stream, int flush_opts)
  3433. {
  3434. struct formatter f = formatter_make (ctx, NULL);
  3435. formatter_add (&f, "#a-- -- -- ---\n", ATTR_READ_MARKER);
  3436. formatter_flush (&f, stream, flush_opts);
  3437. // Flush the trailing formatting reset item
  3438. fflush (stream);
  3439. formatter_free (&f);
  3440. }
  3441. static void
  3442. buffer_print_backlog (struct app_context *ctx, struct buffer *buffer)
  3443. {
  3444. // The prompt can take considerable time to redraw
  3445. CALL (ctx->input, hide);
  3446. // Simulate curses-like fullscreen buffers if the terminal allows it
  3447. if (g_terminal.initialized && clear_screen)
  3448. {
  3449. terminal_printer_fn printer = get_attribute_printer (stdout);
  3450. tputs (clear_screen, 1, printer);
  3451. if (cursor_to_ll)
  3452. tputs (cursor_to_ll, 1, printer);
  3453. else if (row_address)
  3454. tputs (tparm (row_address, g_terminal.lines - 1,
  3455. 0, 0, 0, 0, 0, 0, 0, 0), 1, printer);
  3456. else if (cursor_address)
  3457. tputs (tparm (cursor_address, g_terminal.lines - 1,
  3458. 0, 0, 0, 0, 0, 0, 0, 0), 1, printer);
  3459. fflush (stdout);
  3460. // We should update "last_displayed_msg_time" here just to be sure
  3461. // that the first date marker, if necessary, is shown, but in practice
  3462. // the value should always be from today when this function is called
  3463. }
  3464. else
  3465. {
  3466. char *buffer_name_localized =
  3467. iconv_xstrdup (ctx->term_from_utf8, buffer->name, -1, NULL);
  3468. print_status ("%s", buffer_name_localized);
  3469. free (buffer_name_localized);
  3470. }
  3471. // That is, minus the readline prompt
  3472. int display_limit = MAX (10, g_terminal.lines - 1);
  3473. int to_display = 0;
  3474. struct buffer_line *line;
  3475. for (line = buffer->lines_tail; line; line = line->prev)
  3476. {
  3477. to_display++;
  3478. if (buffer_line_will_show_up (buffer, line))
  3479. display_limit--;
  3480. if (!line->prev || display_limit <= 0)
  3481. break;
  3482. }
  3483. // Once we've found where we want to start with the backlog, print it
  3484. int until_marker = to_display - (int) buffer->new_messages_count;
  3485. for (; line; line = line->next)
  3486. {
  3487. if (until_marker-- == 0
  3488. && buffer->new_messages_count != buffer->lines_count)
  3489. buffer_print_read_marker (ctx, stdout, 0);
  3490. buffer_line_display (ctx, buffer, line, 0);
  3491. }
  3492. // So that it is obvious if the last line in the buffer is not from today
  3493. buffer_update_time (ctx, time (NULL), stdout, 0);
  3494. refresh_prompt (ctx);
  3495. CALL (ctx->input, show);
  3496. }
  3497. static void
  3498. buffer_activate (struct app_context *ctx, struct buffer *buffer)
  3499. {
  3500. if (ctx->current_buffer == buffer)
  3501. return;
  3502. if (ctx->current_buffer)
  3503. {
  3504. ctx->current_buffer->new_messages_count = 0;
  3505. ctx->current_buffer->new_unimportant_count = 0;
  3506. ctx->current_buffer->highlighted = false;
  3507. }
  3508. buffer_print_backlog (ctx, buffer);
  3509. CALL_ (ctx->input, buffer_switch, buffer->input_data);
  3510. // Now at last we can switch the pointers
  3511. ctx->last_buffer = ctx->current_buffer;
  3512. ctx->current_buffer = buffer;
  3513. refresh_prompt (ctx);
  3514. }
  3515. static void
  3516. buffer_merge (struct app_context *ctx,
  3517. struct buffer *buffer, struct buffer *merged)
  3518. {
  3519. // XXX: anything better to do? This situation is arguably rare and I'm
  3520. // not entirely sure what action to take.
  3521. log_full (ctx, NULL, buffer, BUFFER_LINE_STATUS,
  3522. "Buffer #s was merged into this buffer", merged->name);
  3523. // Find all lines from "merged" newer than the newest line in "buffer"
  3524. struct buffer_line *start = merged->lines;
  3525. if (buffer->lines_tail)
  3526. while (start && start->when < buffer->lines_tail->when)
  3527. start = start->next;
  3528. if (!start)
  3529. return;
  3530. // Count how many of them we have
  3531. size_t n = 0;
  3532. for (struct buffer_line *iter = start; iter; iter = iter->next)
  3533. n++;
  3534. struct buffer_line *tail = merged->lines_tail;
  3535. // Cut them from the original buffer
  3536. if (start == merged->lines)
  3537. merged->lines = NULL;
  3538. else if (start->prev)
  3539. start->prev->next = NULL;
  3540. if (start == merged->lines_tail)
  3541. merged->lines_tail = start->prev;
  3542. merged->lines_count -= n;
  3543. // And append them to current lines in the buffer
  3544. buffer->lines_tail->next = start;
  3545. start->prev = buffer->lines_tail;
  3546. buffer->lines_tail = tail;
  3547. buffer->lines_count += n;
  3548. log_full (ctx, NULL, buffer, BUFFER_LINE_STATUS | BUFFER_LINE_SKIP_FILE,
  3549. "End of merged content");
  3550. }
  3551. static void
  3552. buffer_rename (struct app_context *ctx,
  3553. struct buffer *buffer, const char *new_name)
  3554. {
  3555. struct buffer *collision = str_map_find (&ctx->buffers_by_name, new_name);
  3556. if (collision == buffer)
  3557. return;
  3558. hard_assert (!collision);
  3559. str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
  3560. str_map_set (&ctx->buffers_by_name, new_name, buffer);
  3561. buffer_close_log_file (buffer);
  3562. buffer_open_log_file (ctx, buffer);
  3563. free (buffer->name);
  3564. buffer->name = xstrdup (new_name);
  3565. // We might have renamed the current buffer
  3566. refresh_prompt (ctx);
  3567. }
  3568. static void
  3569. buffer_clear (struct buffer *buffer)
  3570. {
  3571. LIST_FOR_EACH (struct buffer_line, iter, buffer->lines)
  3572. buffer_line_destroy (iter);
  3573. buffer->lines = buffer->lines_tail = NULL;
  3574. buffer->lines_count = 0;
  3575. }
  3576. static struct buffer *
  3577. buffer_at_index (struct app_context *ctx, int n)
  3578. {
  3579. int i = 0;
  3580. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  3581. if (++i == n)
  3582. return iter;
  3583. return NULL;
  3584. }
  3585. static struct buffer *
  3586. buffer_next (struct app_context *ctx, int count)
  3587. {
  3588. struct buffer *new_buffer = ctx->current_buffer;
  3589. while (count-- > 0)
  3590. if (!(new_buffer = new_buffer->next))
  3591. new_buffer = ctx->buffers;
  3592. return new_buffer;
  3593. }
  3594. static struct buffer *
  3595. buffer_previous (struct app_context *ctx, int count)
  3596. {
  3597. struct buffer *new_buffer = ctx->current_buffer;
  3598. while (count-- > 0)
  3599. if (!(new_buffer = new_buffer->prev))
  3600. new_buffer = ctx->buffers_tail;
  3601. return new_buffer;
  3602. }
  3603. static bool
  3604. buffer_goto (struct app_context *ctx, int n)
  3605. {
  3606. struct buffer *buffer = buffer_at_index (ctx, n);
  3607. if (!buffer)
  3608. return false;
  3609. buffer_activate (ctx, buffer);
  3610. return true;
  3611. }
  3612. static int
  3613. buffer_count (struct app_context *ctx)
  3614. {
  3615. int total = 0;
  3616. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  3617. total++;
  3618. return total;
  3619. }
  3620. static void
  3621. buffer_move (struct app_context *ctx, struct buffer *buffer, int n)
  3622. {
  3623. hard_assert (n >= 1 && n <= buffer_count (ctx));
  3624. LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
  3625. struct buffer *following = ctx->buffers;
  3626. while (--n && following)
  3627. following = following->next;
  3628. LIST_INSERT_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer, following);
  3629. refresh_prompt (ctx);
  3630. }
  3631. static int
  3632. buffer_get_index (struct app_context *ctx, struct buffer *buffer)
  3633. {
  3634. int index = 1;
  3635. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  3636. {
  3637. if (iter == buffer)
  3638. return index;
  3639. index++;
  3640. }
  3641. return -1;
  3642. }
  3643. static void
  3644. buffer_remove_safe (struct app_context *ctx, struct buffer *buffer)
  3645. {
  3646. if (buffer == ctx->current_buffer)
  3647. buffer_activate (ctx, ctx->last_buffer
  3648. ? ctx->last_buffer
  3649. : buffer_next (ctx, 1));
  3650. buffer_remove (ctx, buffer);
  3651. }
  3652. static void
  3653. init_global_buffer (struct app_context *ctx)
  3654. {
  3655. struct buffer *global = ctx->global_buffer = buffer_new (ctx->input);
  3656. global->type = BUFFER_GLOBAL;
  3657. global->name = xstrdup (PROGRAM_NAME);
  3658. buffer_add (ctx, global);
  3659. buffer_activate (ctx, global);
  3660. }
  3661. // --- Users, channels ---------------------------------------------------------
  3662. static void
  3663. irc_user_on_destroy (void *object, void *user_data)
  3664. {
  3665. struct user *user = object;
  3666. struct server *s = user_data;
  3667. if (!s->rehashing)
  3668. str_map_set (&s->irc_users, user->nickname, NULL);
  3669. }
  3670. static struct user *
  3671. irc_make_user (struct server *s, char *nickname)
  3672. {
  3673. hard_assert (!str_map_find (&s->irc_users, nickname));
  3674. struct user *user = user_new ();
  3675. (void) user_weak_ref (user, irc_user_on_destroy, s);
  3676. user->nickname = nickname;
  3677. str_map_set (&s->irc_users, user->nickname, user);
  3678. return user;
  3679. }
  3680. struct user *
  3681. irc_get_or_make_user (struct server *s, const char *nickname)
  3682. {
  3683. struct user *user = str_map_find (&s->irc_users, nickname);
  3684. if (user)
  3685. return user_ref (user);
  3686. return irc_make_user (s, xstrdup (nickname));
  3687. }
  3688. static struct buffer *
  3689. irc_get_or_make_user_buffer (struct server *s, const char *nickname)
  3690. {
  3691. struct buffer *buffer = str_map_find (&s->irc_buffer_map, nickname);
  3692. if (buffer)
  3693. return buffer;
  3694. struct user *user = irc_get_or_make_user (s, nickname);
  3695. // Open a new buffer for the user
  3696. buffer = buffer_new (s->ctx->input);
  3697. buffer->type = BUFFER_PM;
  3698. buffer->name = xstrdup_printf ("%s.%s", s->name, nickname);
  3699. buffer->server = s;
  3700. buffer->user = user;
  3701. str_map_set (&s->irc_buffer_map, user->nickname, buffer);
  3702. buffer_add (s->ctx, buffer);
  3703. return buffer;
  3704. }
  3705. static void
  3706. irc_get_channel_user_prefix (struct server *s,
  3707. struct channel_user *channel_user, struct str *output)
  3708. {
  3709. if (s->ctx->show_all_prefixes)
  3710. str_append (output, channel_user->prefixes.str);
  3711. else if (channel_user->prefixes.len)
  3712. str_append_c (output, channel_user->prefixes.str[0]);
  3713. }
  3714. static bool
  3715. irc_channel_is_joined (struct channel *channel)
  3716. {
  3717. // TODO: find a better way of checking if we're on a channel
  3718. return !!channel->users_len;
  3719. }
  3720. // Note that this eats the user reference
  3721. static void
  3722. irc_channel_link_user (struct channel *channel, struct user *user,
  3723. const char *prefixes)
  3724. {
  3725. struct user_channel *user_channel = user_channel_new ();
  3726. user_channel->channel = channel;
  3727. LIST_PREPEND (user->channels, user_channel);
  3728. struct channel_user *channel_user = channel_user_new ();
  3729. channel_user->user = user;
  3730. str_append (&channel_user->prefixes, prefixes);
  3731. LIST_PREPEND (channel->users, channel_user);
  3732. channel->users_len++;
  3733. }
  3734. static void
  3735. irc_channel_unlink_user
  3736. (struct channel *channel, struct channel_user *channel_user)
  3737. {
  3738. // First destroy the user's weak references to the channel
  3739. struct user *user = channel_user->user;
  3740. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  3741. if (iter->channel == channel)
  3742. {
  3743. LIST_UNLINK (user->channels, iter);
  3744. user_channel_destroy (iter);
  3745. }
  3746. // Then just unlink the user from the channel
  3747. LIST_UNLINK (channel->users, channel_user);
  3748. channel_user_destroy (channel_user);
  3749. channel->users_len--;
  3750. }
  3751. static void
  3752. irc_channel_on_destroy (void *object, void *user_data)
  3753. {
  3754. struct channel *channel = object;
  3755. struct server *s = user_data;
  3756. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3757. irc_channel_unlink_user (channel, iter);
  3758. if (!s->rehashing)
  3759. str_map_set (&s->irc_channels, channel->name, NULL);
  3760. }
  3761. static struct channel *
  3762. irc_make_channel (struct server *s, char *name)
  3763. {
  3764. hard_assert (!str_map_find (&s->irc_channels, name));
  3765. struct channel *channel = channel_new ();
  3766. (void) channel_weak_ref (channel, irc_channel_on_destroy, s);
  3767. channel->name = name;
  3768. channel->topic = NULL;
  3769. str_map_set (&s->irc_channels, channel->name, channel);
  3770. return channel;
  3771. }
  3772. static struct channel_user *
  3773. irc_channel_get_user (struct channel *channel, struct user *user)
  3774. {
  3775. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3776. if (iter->user == user)
  3777. return iter;
  3778. return NULL;
  3779. }
  3780. static void
  3781. irc_remove_user_from_channel (struct user *user, struct channel *channel)
  3782. {
  3783. struct channel_user *channel_user = irc_channel_get_user (channel, user);
  3784. if (channel_user)
  3785. irc_channel_unlink_user (channel, channel_user);
  3786. }
  3787. static void
  3788. irc_left_channel (struct channel *channel)
  3789. {
  3790. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3791. irc_channel_unlink_user (channel, iter);
  3792. }
  3793. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3794. static void
  3795. remove_conflicting_buffer (struct server *s, struct buffer *buffer)
  3796. {
  3797. log_server_status (s, s->buffer,
  3798. "Removed buffer #s because of casemapping conflict", buffer->name);
  3799. if (s->ctx->current_buffer == buffer)
  3800. buffer_activate (s->ctx, s->buffer);
  3801. buffer_remove (s->ctx, buffer);
  3802. }
  3803. static void
  3804. irc_try_readd_user (struct server *s,
  3805. struct user *user, struct buffer *buffer)
  3806. {
  3807. if (str_map_find (&s->irc_users, user->nickname))
  3808. {
  3809. // Remove user from all channels and destroy any PM buffer
  3810. user_ref (user);
  3811. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  3812. irc_remove_user_from_channel (user, iter->channel);
  3813. if (buffer)
  3814. remove_conflicting_buffer (s, buffer);
  3815. user_unref (user);
  3816. }
  3817. else
  3818. {
  3819. str_map_set (&s->irc_users, user->nickname, user);
  3820. str_map_set (&s->irc_buffer_map, user->nickname, buffer);
  3821. }
  3822. }
  3823. static void
  3824. irc_try_readd_channel (struct server *s,
  3825. struct channel *channel, struct buffer *buffer)
  3826. {
  3827. if (str_map_find (&s->irc_channels, channel->name))
  3828. {
  3829. // Remove all users from channel and destroy any channel buffer
  3830. channel_ref (channel);
  3831. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3832. irc_channel_unlink_user (channel, iter);
  3833. if (buffer)
  3834. remove_conflicting_buffer (s, buffer);
  3835. channel_unref (channel);
  3836. }
  3837. else
  3838. {
  3839. str_map_set (&s->irc_channels, channel->name, channel);
  3840. str_map_set (&s->irc_buffer_map, channel->name, buffer);
  3841. }
  3842. }
  3843. static void
  3844. irc_rehash_and_fix_conflicts (struct server *s)
  3845. {
  3846. // Save the old maps and initialize new ones
  3847. struct str_map old_users = s->irc_users;
  3848. struct str_map old_channels = s->irc_channels;
  3849. struct str_map old_buffer_map = s->irc_buffer_map;
  3850. s->irc_users = str_map_make (NULL);
  3851. s->irc_channels = str_map_make (NULL);
  3852. s->irc_buffer_map = str_map_make (NULL);
  3853. s->irc_users .key_xfrm = s->irc_strxfrm;
  3854. s->irc_channels .key_xfrm = s->irc_strxfrm;
  3855. s->irc_buffer_map.key_xfrm = s->irc_strxfrm;
  3856. // Prevent channels and users from unsetting themselves
  3857. // from server maps upon removing the last reference to them
  3858. s->rehashing = true;
  3859. // XXX: to be perfectly sure, we should also check
  3860. // whether any users collide with channels and vice versa
  3861. // Our own user always takes priority, add him first
  3862. if (s->irc_user)
  3863. irc_try_readd_user (s, s->irc_user,
  3864. str_map_find (&old_buffer_map, s->irc_user->nickname));
  3865. struct str_map_iter iter;
  3866. struct user *user;
  3867. struct channel *channel;
  3868. iter = str_map_iter_make (&old_users);
  3869. while ((user = str_map_iter_next (&iter)))
  3870. irc_try_readd_user (s, user,
  3871. str_map_find (&old_buffer_map, user->nickname));
  3872. iter = str_map_iter_make (&old_channels);
  3873. while ((channel = str_map_iter_next (&iter)))
  3874. irc_try_readd_channel (s, channel,
  3875. str_map_find (&old_buffer_map, channel->name));
  3876. // Hopefully we've either moved or destroyed all the old content
  3877. s->rehashing = false;
  3878. str_map_free (&old_users);
  3879. str_map_free (&old_channels);
  3880. str_map_free (&old_buffer_map);
  3881. }
  3882. static void
  3883. irc_set_casemapping (struct server *s,
  3884. irc_tolower_fn tolower, irc_strxfrm_fn strxfrm)
  3885. {
  3886. if (tolower == s->irc_tolower
  3887. && strxfrm == s->irc_strxfrm)
  3888. return;
  3889. s->irc_tolower = tolower;
  3890. s->irc_strxfrm = strxfrm;
  3891. // Ideally we would never have to do this but I can't think of a workaround
  3892. irc_rehash_and_fix_conflicts (s);
  3893. }
  3894. // --- Core functionality ------------------------------------------------------
  3895. static bool
  3896. irc_is_connected (struct server *s)
  3897. {
  3898. return s->state != IRC_DISCONNECTED && s->state != IRC_CONNECTING;
  3899. }
  3900. static void
  3901. irc_update_poller (struct server *s, const struct pollfd *pfd)
  3902. {
  3903. int new_events = s->transport->get_poll_events (s);
  3904. hard_assert (new_events != 0);
  3905. if (!pfd || pfd->events != new_events)
  3906. poller_fd_set (&s->socket_event, new_events);
  3907. }
  3908. static void
  3909. irc_cancel_timers (struct server *s)
  3910. {
  3911. poller_timer_reset (&s->timeout_tmr);
  3912. poller_timer_reset (&s->ping_tmr);
  3913. poller_timer_reset (&s->reconnect_tmr);
  3914. poller_timer_reset (&s->autojoin_tmr);
  3915. }
  3916. static void
  3917. irc_reset_connection_timeouts (struct server *s)
  3918. {
  3919. poller_timer_set (&s->timeout_tmr, 3 * 60 * 1000);
  3920. poller_timer_set (&s->ping_tmr, (3 * 60 + 30) * 1000);
  3921. poller_timer_reset (&s->reconnect_tmr);
  3922. }
  3923. static int64_t
  3924. irc_get_reconnect_delay (struct server *s)
  3925. {
  3926. int64_t delay = get_config_integer (s->config, "reconnect_delay");
  3927. int64_t delay_factor = get_config_integer (s->ctx->config.root,
  3928. "behaviour.reconnect_delay_growing");
  3929. for (unsigned i = 0; i < s->reconnect_attempt; i++)
  3930. {
  3931. if (delay_factor && delay > INT64_MAX / delay_factor)
  3932. break;
  3933. delay *= delay_factor;
  3934. }
  3935. int64_t delay_max = get_config_integer (s->ctx->config.root,
  3936. "behaviour.reconnect_delay_max");
  3937. return MIN (delay, delay_max);
  3938. }
  3939. static void
  3940. irc_queue_reconnect (struct server *s)
  3941. {
  3942. // As long as the user wants us to, that is
  3943. if (!get_config_boolean (s->config, "reconnect"))
  3944. return;
  3945. // XXX: maybe add a state for when a connect is queued?
  3946. hard_assert (s->state == IRC_DISCONNECTED);
  3947. int64_t delay = irc_get_reconnect_delay (s);
  3948. s->reconnect_attempt++;
  3949. log_server_status (s, s->buffer,
  3950. "Trying to reconnect in #&s seconds...",
  3951. xstrdup_printf ("%" PRId64, delay));
  3952. poller_timer_set (&s->reconnect_tmr, delay * 1000);
  3953. }
  3954. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3955. static void irc_process_sent_message
  3956. (const struct irc_message *msg, struct server *s);
  3957. static void irc_send (struct server *s,
  3958. const char *format, ...) ATTRIBUTE_PRINTF (2, 3);
  3959. static void
  3960. irc_send (struct server *s, const char *format, ...)
  3961. {
  3962. if (!soft_assert (irc_is_connected (s)))
  3963. {
  3964. log_server_debug (s, "sending a message to a dead server connection");
  3965. return;
  3966. }
  3967. if (s->state == IRC_CLOSING
  3968. || s->state == IRC_HALF_CLOSED)
  3969. return;
  3970. va_list ap;
  3971. va_start (ap, format);
  3972. struct str str = str_make ();
  3973. str_append_vprintf (&str, format, ap);
  3974. va_end (ap);
  3975. log_server_debug (s, "#a<< \"#S\"#r", ATTR_PART, str.str);
  3976. struct irc_message msg;
  3977. irc_parse_message (&msg, str.str);
  3978. irc_process_sent_message (&msg, s);
  3979. irc_free_message (&msg);
  3980. str_append_str (&s->write_buffer, &str);
  3981. str_free (&str);
  3982. str_append (&s->write_buffer, "\r\n");
  3983. irc_update_poller (s, NULL);
  3984. }
  3985. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3986. static void
  3987. irc_real_shutdown (struct server *s)
  3988. {
  3989. hard_assert (irc_is_connected (s) && s->state != IRC_HALF_CLOSED);
  3990. if (s->transport
  3991. && s->transport->in_before_shutdown)
  3992. s->transport->in_before_shutdown (s);
  3993. while (shutdown (s->socket, SHUT_WR) == -1)
  3994. if (!soft_assert (errno == EINTR))
  3995. break;
  3996. s->state = IRC_HALF_CLOSED;
  3997. }
  3998. static void
  3999. irc_shutdown (struct server *s)
  4000. {
  4001. if (s->state == IRC_CLOSING
  4002. || s->state == IRC_HALF_CLOSED)
  4003. return;
  4004. // TODO: set a timer to cut the connection if we don't receive an EOF
  4005. s->state = IRC_CLOSING;
  4006. // Either there's still some data in the write buffer and we wait
  4007. // until they're sent, or we send an EOF to the server right away
  4008. if (!s->write_buffer.len)
  4009. irc_real_shutdown (s);
  4010. }
  4011. static void
  4012. irc_destroy_connector (struct server *s)
  4013. {
  4014. if (s->connector)
  4015. connector_free (s->connector);
  4016. free (s->connector);
  4017. s->connector = NULL;
  4018. if (s->socks_conn)
  4019. socks_connector_free (s->socks_conn);
  4020. free (s->socks_conn);
  4021. s->socks_conn = NULL;
  4022. // Not connecting anymore
  4023. s->state = IRC_DISCONNECTED;
  4024. }
  4025. static void
  4026. try_finish_quit (struct app_context *ctx)
  4027. {
  4028. if (!ctx->quitting)
  4029. return;
  4030. bool disconnected_all = true;
  4031. struct str_map_iter iter = str_map_iter_make (&ctx->servers);
  4032. struct server *s;
  4033. while ((s = str_map_iter_next (&iter)))
  4034. if (irc_is_connected (s))
  4035. disconnected_all = false;
  4036. if (disconnected_all)
  4037. ctx->polling = false;
  4038. }
  4039. static void
  4040. irc_destroy_transport (struct server *s)
  4041. {
  4042. if (s->transport
  4043. && s->transport->cleanup)
  4044. s->transport->cleanup (s);
  4045. s->transport = NULL;
  4046. poller_fd_reset (&s->socket_event);
  4047. xclose (s->socket);
  4048. s->socket = -1;
  4049. s->state = IRC_DISCONNECTED;
  4050. str_reset (&s->read_buffer);
  4051. str_reset (&s->write_buffer);
  4052. }
  4053. static void
  4054. irc_destroy_state (struct server *s)
  4055. {
  4056. struct str_map_iter iter = str_map_iter_make (&s->irc_channels);
  4057. struct channel *channel;
  4058. while ((channel = str_map_iter_next (&iter)))
  4059. irc_left_channel (channel);
  4060. if (s->irc_user)
  4061. {
  4062. user_unref (s->irc_user);
  4063. s->irc_user = NULL;
  4064. }
  4065. str_reset (&s->irc_user_mode);
  4066. free (s->irc_user_host);
  4067. s->irc_user_host = NULL;
  4068. s->cap_echo_message = false;
  4069. // Need to call this before server_init_specifics()
  4070. irc_set_casemapping (s, irc_tolower, irc_strxfrm);
  4071. server_free_specifics (s);
  4072. server_init_specifics (s);
  4073. }
  4074. static void
  4075. irc_disconnect (struct server *s)
  4076. {
  4077. hard_assert (irc_is_connected (s));
  4078. struct str_map_iter iter = str_map_iter_make (&s->irc_buffer_map);
  4079. struct buffer *buffer;
  4080. while ((buffer = str_map_iter_next (&iter)))
  4081. log_server (s, buffer, BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT,
  4082. "Disconnected from server");
  4083. irc_cancel_timers (s);
  4084. irc_destroy_transport (s);
  4085. irc_destroy_state (s);
  4086. // Take any relevant actions
  4087. if (s->ctx->quitting)
  4088. try_finish_quit (s->ctx);
  4089. else if (s->manual_disconnect)
  4090. s->manual_disconnect = false;
  4091. else
  4092. {
  4093. s->reconnect_attempt = 0;
  4094. irc_queue_reconnect (s);
  4095. }
  4096. refresh_prompt (s->ctx);
  4097. }
  4098. static void
  4099. irc_initiate_disconnect (struct server *s, const char *reason)
  4100. {
  4101. hard_assert (irc_is_connected (s));
  4102. // It can take a very long time for sending QUIT to take effect
  4103. if (s->manual_disconnect)
  4104. {
  4105. log_server_error (s, s->buffer, "%s: %s", "Disconnected from server",
  4106. "connection torn down early per user request");
  4107. irc_disconnect (s);
  4108. return;
  4109. }
  4110. if (reason)
  4111. irc_send (s, "QUIT :%s", reason);
  4112. else
  4113. // TODO: make the default QUIT message customizable
  4114. // -> global/per server/both?
  4115. // -> implement IRC output hooks for plugins?
  4116. irc_send (s, "QUIT :%s", PROGRAM_NAME " " PROGRAM_VERSION);
  4117. s->manual_disconnect = true;
  4118. irc_shutdown (s);
  4119. }
  4120. static void
  4121. initiate_quit (struct app_context *ctx, const char *message)
  4122. {
  4123. log_global_status (ctx, "Shutting down");
  4124. // Hide the user interface
  4125. CALL (ctx->input, hide);
  4126. // Initiate a connection close
  4127. struct str_map_iter iter = str_map_iter_make (&ctx->servers);
  4128. struct server *s;
  4129. while ((s = str_map_iter_next (&iter)))
  4130. {
  4131. // There may be a timer set to reconnect to the server
  4132. poller_timer_reset (&s->reconnect_tmr);
  4133. if (irc_is_connected (s))
  4134. irc_initiate_disconnect (s, message);
  4135. else if (s->state == IRC_CONNECTING)
  4136. irc_destroy_connector (s);
  4137. }
  4138. ctx->quitting = true;
  4139. try_finish_quit (ctx);
  4140. }
  4141. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4142. static void
  4143. on_irc_ping_timeout (void *user_data)
  4144. {
  4145. struct server *s = user_data;
  4146. log_server_error (s, s->buffer,
  4147. "%s: %s", "Disconnected from server", "timeout");
  4148. irc_disconnect (s);
  4149. }
  4150. static void
  4151. on_irc_timeout (void *user_data)
  4152. {
  4153. // Provoke a response from the server
  4154. struct server *s = user_data;
  4155. irc_send (s, "PING :%" PRIi64, (int64_t) time (NULL));
  4156. }
  4157. static void
  4158. on_irc_autojoin_timeout (void *user_data)
  4159. {
  4160. struct server *s = user_data;
  4161. // Since we may not have information from RPL_ISUPPORT yet,
  4162. // it's our safest bet to send the channels one at a time
  4163. struct str_map joins_sent = str_map_make (NULL);
  4164. // We don't know the casemapping yet either, however ASCII should do
  4165. joins_sent.key_xfrm = tolower_ascii_strxfrm;
  4166. // First join autojoin channels in their given order
  4167. const char *autojoin = get_config_string (s->config, "autojoin");
  4168. if (autojoin)
  4169. {
  4170. struct strv v = strv_make ();
  4171. cstr_split (autojoin, ",", true, &v);
  4172. for (size_t i = 0; i < v.len; i++)
  4173. {
  4174. irc_send (s, "JOIN %s", v.vector[i]);
  4175. str_map_set (&joins_sent, v.vector[i], (void *) 1);
  4176. }
  4177. strv_free (&v);
  4178. }
  4179. // Then also rejoin any channels from the last disconnect
  4180. struct str_map_iter iter = str_map_iter_make (&s->irc_channels);
  4181. struct channel *channel;
  4182. while ((channel = str_map_iter_next (&iter)))
  4183. {
  4184. struct str target = str_make ();
  4185. str_append (&target, channel->name);
  4186. const char *key;
  4187. if ((key = str_map_find (&channel->param_modes, "k")))
  4188. str_append_printf (&target, " %s", key);
  4189. // When a channel is both autojoined and rejoined, both keys are tried
  4190. if (!channel->left_manually
  4191. && !str_map_find (&joins_sent, target.str))
  4192. irc_send (s, "JOIN %s", target.str);
  4193. str_free (&target);
  4194. }
  4195. str_map_free (&joins_sent);
  4196. }
  4197. // --- Server I/O --------------------------------------------------------------
  4198. static char *
  4199. irc_process_hooks (struct server *s, char *input)
  4200. {
  4201. log_server_debug (s, "#a>> \"#S\"#r", ATTR_JOIN, input);
  4202. uint64_t hash = siphash_wrapper (input, strlen (input));
  4203. LIST_FOR_EACH (struct hook, iter, s->ctx->irc_hooks)
  4204. {
  4205. struct irc_hook *hook = (struct irc_hook *) iter;
  4206. if (!(input = hook->filter (hook, s, input)))
  4207. {
  4208. log_server_debug (s, "#a>= #s#r", ATTR_JOIN, "thrown away by hook");
  4209. return NULL;
  4210. }
  4211. uint64_t new_hash = siphash_wrapper (input, strlen (input));
  4212. if (new_hash != hash)
  4213. log_server_debug (s, "#a>= \"#S\"#r", ATTR_JOIN, input);
  4214. hash = new_hash;
  4215. }
  4216. return input;
  4217. }
  4218. static void irc_process_message
  4219. (const struct irc_message *msg, struct server *s);
  4220. static void
  4221. irc_process_buffer_custom (struct server *s, struct str *buf)
  4222. {
  4223. const char *start = buf->str, *end = start + buf->len;
  4224. for (const char *p = start; p + 1 < end; p++)
  4225. {
  4226. // Split the input on newlines
  4227. if (p[0] != '\r' || p[1] != '\n')
  4228. continue;
  4229. char *processed = irc_process_hooks (s, xstrndup (start, p - start));
  4230. start = p + 2;
  4231. if (!processed)
  4232. continue;
  4233. struct irc_message msg;
  4234. irc_parse_message (&msg, processed);
  4235. irc_process_message (&msg, s);
  4236. irc_free_message (&msg);
  4237. free (processed);
  4238. }
  4239. str_remove_slice (buf, 0, start - buf->str);
  4240. }
  4241. static enum socket_io_result
  4242. irc_try_read (struct server *s)
  4243. {
  4244. enum socket_io_result result = s->transport->try_read (s);
  4245. if (s->read_buffer.len >= (1 << 20))
  4246. {
  4247. // XXX: this is stupid; if anything, count it in dependence of time
  4248. log_server_error (s, s->buffer,
  4249. "The IRC server seems to spew out data frantically");
  4250. return SOCKET_IO_ERROR;
  4251. }
  4252. if (s->read_buffer.len)
  4253. irc_process_buffer_custom (s, &s->read_buffer);
  4254. return result;
  4255. }
  4256. static enum socket_io_result
  4257. irc_try_write (struct server *s)
  4258. {
  4259. enum socket_io_result result = s->transport->try_write (s);
  4260. if (result == SOCKET_IO_OK)
  4261. {
  4262. // If we're flushing the write buffer and our job is complete, we send
  4263. // an EOF to the server, changing the state to IRC_HALF_CLOSED
  4264. if (s->state == IRC_CLOSING && !s->write_buffer.len)
  4265. irc_real_shutdown (s);
  4266. }
  4267. return result;
  4268. }
  4269. static bool
  4270. irc_try_read_write (struct server *s)
  4271. {
  4272. enum socket_io_result read_result;
  4273. enum socket_io_result write_result;
  4274. if ((read_result = irc_try_read (s)) == SOCKET_IO_ERROR
  4275. || (write_result = irc_try_write (s)) == SOCKET_IO_ERROR)
  4276. {
  4277. log_server_error (s, s->buffer, "Server connection failed");
  4278. return false;
  4279. }
  4280. // FIXME: this may probably fire multiple times when we're flushing,
  4281. // we should probably store a flag next to the state
  4282. if (read_result == SOCKET_IO_EOF
  4283. || write_result == SOCKET_IO_EOF)
  4284. log_server_error (s, s->buffer, "Server closed the connection");
  4285. // If the write needs to read and we receive an EOF, we can't flush
  4286. if (write_result == SOCKET_IO_EOF)
  4287. return false;
  4288. if (read_result == SOCKET_IO_EOF)
  4289. {
  4290. // Eventually initiate shutdown to flush the write buffer
  4291. irc_shutdown (s);
  4292. // If there's nothing to write, we can disconnect now
  4293. if (s->state == IRC_HALF_CLOSED)
  4294. return false;
  4295. }
  4296. return true;
  4297. }
  4298. static void
  4299. on_irc_ready (const struct pollfd *pfd, struct server *s)
  4300. {
  4301. if (irc_try_read_write (s))
  4302. {
  4303. // XXX: shouldn't we rather wait for PONG messages?
  4304. irc_reset_connection_timeouts (s);
  4305. irc_update_poller (s, pfd);
  4306. }
  4307. else
  4308. // We don't want to keep the socket anymore
  4309. irc_disconnect (s);
  4310. }
  4311. // --- Plain transport ---------------------------------------------------------
  4312. static enum socket_io_result
  4313. transport_plain_try_read (struct server *s)
  4314. {
  4315. enum socket_io_result result =
  4316. socket_io_try_read (s->socket, &s->read_buffer);
  4317. if (result == SOCKET_IO_ERROR)
  4318. print_debug ("%s: %s", __func__, strerror (errno));
  4319. return result;
  4320. }
  4321. static enum socket_io_result
  4322. transport_plain_try_write (struct server *s)
  4323. {
  4324. enum socket_io_result result =
  4325. socket_io_try_write (s->socket, &s->write_buffer);
  4326. if (result == SOCKET_IO_ERROR)
  4327. print_debug ("%s: %s", __func__, strerror (errno));
  4328. return result;
  4329. }
  4330. static int
  4331. transport_plain_get_poll_events (struct server *s)
  4332. {
  4333. int events = POLLIN;
  4334. if (s->write_buffer.len)
  4335. events |= POLLOUT;
  4336. return events;
  4337. }
  4338. static struct transport g_transport_plain =
  4339. {
  4340. .try_read = transport_plain_try_read,
  4341. .try_write = transport_plain_try_write,
  4342. .get_poll_events = transport_plain_get_poll_events,
  4343. };
  4344. // --- TLS transport -----------------------------------------------------------
  4345. struct transport_tls_data
  4346. {
  4347. SSL_CTX *ssl_ctx; ///< SSL context
  4348. SSL *ssl; ///< SSL connection
  4349. bool ssl_rx_want_tx; ///< SSL_read() wants to write
  4350. bool ssl_tx_want_rx; ///< SSL_write() wants to read
  4351. };
  4352. /// The index in SSL_CTX user data for a reference to the server
  4353. static int g_transport_tls_data_index = -1;
  4354. static int
  4355. transport_tls_verify_callback (int preverify_ok, X509_STORE_CTX *ctx)
  4356. {
  4357. SSL *ssl = X509_STORE_CTX_get_ex_data
  4358. (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ());
  4359. struct server *s = SSL_CTX_get_ex_data
  4360. (SSL_get_SSL_CTX (ssl), g_transport_tls_data_index);
  4361. X509 *cert = X509_STORE_CTX_get_current_cert (ctx);
  4362. char *subject = X509_NAME_oneline (X509_get_subject_name (cert), NULL, 0);
  4363. char *issuer = X509_NAME_oneline (X509_get_issuer_name (cert), NULL, 0);
  4364. log_server_status (s, s->buffer, "Certificate subject: #s", subject);
  4365. log_server_status (s, s->buffer, "Certificate issuer: #s", issuer);
  4366. if (!preverify_ok)
  4367. {
  4368. log_server_error (s, s->buffer,
  4369. "Certificate verification failed: #s",
  4370. X509_verify_cert_error_string (X509_STORE_CTX_get_error (ctx)));
  4371. }
  4372. free (subject);
  4373. free (issuer);
  4374. return preverify_ok;
  4375. }
  4376. static bool
  4377. transport_tls_init_ca_set (SSL_CTX *ssl_ctx, const char *file, const char *path,
  4378. struct error **e)
  4379. {
  4380. ERR_clear_error ();
  4381. if (file || path)
  4382. {
  4383. if (SSL_CTX_load_verify_locations (ssl_ctx, file, path))
  4384. return true;
  4385. return error_set (e, "%s: %s",
  4386. "Failed to set locations for the CA certificate bundle",
  4387. ERR_reason_error_string (ERR_get_error ()));
  4388. }
  4389. if (!SSL_CTX_set_default_verify_paths (ssl_ctx))
  4390. return error_set (e, "%s: %s",
  4391. "Couldn't load the default CA certificate bundle",
  4392. ERR_reason_error_string (ERR_get_error ()));
  4393. return true;
  4394. }
  4395. static bool
  4396. transport_tls_init_ca (struct server *s, SSL_CTX *ssl_ctx, struct error **e)
  4397. {
  4398. const char *ca_file = get_config_string (s->config, "tls_ca_file");
  4399. const char *ca_path = get_config_string (s->config, "tls_ca_path");
  4400. char *full_ca_file = ca_file
  4401. ? resolve_filename (ca_file, resolve_relative_config_filename) : NULL;
  4402. char *full_ca_path = ca_path
  4403. ? resolve_filename (ca_path, resolve_relative_config_filename) : NULL;
  4404. bool ok = false;
  4405. if (ca_file && !full_ca_file)
  4406. error_set (e, "Couldn't find the CA bundle file");
  4407. else if (ca_path && !full_ca_path)
  4408. error_set (e, "Couldn't find the CA bundle path");
  4409. else
  4410. ok = transport_tls_init_ca_set (ssl_ctx, full_ca_file, full_ca_path, e);
  4411. free (full_ca_file);
  4412. free (full_ca_path);
  4413. return ok;
  4414. }
  4415. static bool
  4416. transport_tls_init_ctx (struct server *s, SSL_CTX *ssl_ctx, struct error **e)
  4417. {
  4418. bool verify = get_config_boolean (s->config, "tls_verify");
  4419. SSL_CTX_set_verify (ssl_ctx, verify ? SSL_VERIFY_PEER : SSL_VERIFY_NONE,
  4420. transport_tls_verify_callback);
  4421. if (g_transport_tls_data_index == -1)
  4422. g_transport_tls_data_index =
  4423. SSL_CTX_get_ex_new_index (0, "server", NULL, NULL, NULL);
  4424. SSL_CTX_set_ex_data (ssl_ctx, g_transport_tls_data_index, s);
  4425. const char *ciphers = get_config_string (s->config, "tls_ciphers");
  4426. if (ciphers && !SSL_CTX_set_cipher_list (ssl_ctx, ciphers))
  4427. log_server_error (s, s->buffer,
  4428. "Failed to select any cipher from the cipher list");
  4429. SSL_CTX_set_mode (ssl_ctx,
  4430. SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
  4431. // Disable deprecated protocols (see RFC 7568)
  4432. SSL_CTX_set_options (ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
  4433. // This seems to consume considerable amounts of memory while not giving
  4434. // that much in return; in addition to that, I'm not sure about security
  4435. // (see RFC 7525, section 3.3)
  4436. #ifdef SSL_OP_NO_COMPRESSION
  4437. SSL_CTX_set_options (ssl_ctx, SSL_OP_NO_COMPRESSION);
  4438. #endif // SSL_OP_NO_COMPRESSION
  4439. struct error *error = NULL;
  4440. if (!transport_tls_init_ca (s, ssl_ctx, &error))
  4441. {
  4442. if (verify)
  4443. {
  4444. error_propagate (e, error);
  4445. return false;
  4446. }
  4447. // Only inform the user if we're not actually verifying
  4448. log_server_error (s, s->buffer, "#s", error->message);
  4449. error_free (error);
  4450. }
  4451. return true;
  4452. }
  4453. static bool
  4454. transport_tls_init_cert (struct server *s, SSL *ssl, struct error **e)
  4455. {
  4456. const char *tls_cert = get_config_string (s->config, "tls_cert");
  4457. if (!tls_cert)
  4458. return true;
  4459. ERR_clear_error ();
  4460. bool result = false;
  4461. char *path = resolve_filename (tls_cert, resolve_relative_config_filename);
  4462. if (!path)
  4463. error_set (e, "%s: %s", "Cannot open file", tls_cert);
  4464. // XXX: perhaps we should read the file ourselves for better messages
  4465. else if (!SSL_use_certificate_file (ssl, path, SSL_FILETYPE_PEM)
  4466. || !SSL_use_PrivateKey_file (ssl, path, SSL_FILETYPE_PEM))
  4467. error_set (e, "%s: %s", "Setting the TLS client certificate failed",
  4468. ERR_reason_error_string (ERR_get_error ()));
  4469. else
  4470. result = true;
  4471. free (path);
  4472. return result;
  4473. }
  4474. static bool
  4475. transport_tls_init (struct server *s, const char *hostname, struct error **e)
  4476. {
  4477. ERR_clear_error ();
  4478. struct error *error = NULL;
  4479. SSL_CTX *ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
  4480. if (!ssl_ctx)
  4481. goto error_ssl_1;
  4482. if (!transport_tls_init_ctx (s, ssl_ctx, &error))
  4483. goto error_ssl_2;
  4484. SSL *ssl = SSL_new (ssl_ctx);
  4485. if (!ssl)
  4486. goto error_ssl_2;
  4487. if (!transport_tls_init_cert (s, ssl, &error))
  4488. {
  4489. // XXX: is this a reason to abort the connection?
  4490. log_server_error (s, s->buffer, "#s", error->message);
  4491. error_free (error);
  4492. error = NULL;
  4493. }
  4494. SSL_set_connect_state (ssl);
  4495. if (!SSL_set_fd (ssl, s->socket))
  4496. goto error_ssl_3;
  4497. // Enable SNI, FWIW; literal IP addresses aren't allowed
  4498. struct in6_addr dummy;
  4499. if (!inet_pton (AF_INET, hostname, &dummy)
  4500. && !inet_pton (AF_INET6, hostname, &dummy))
  4501. SSL_set_tlsext_host_name (ssl, hostname);
  4502. struct transport_tls_data *data = xcalloc (1, sizeof *data);
  4503. data->ssl_ctx = ssl_ctx;
  4504. data->ssl = ssl;
  4505. // Forces a handshake even if neither side wants to transmit data
  4506. data->ssl_rx_want_tx = true;
  4507. s->transport_data = data;
  4508. return true;
  4509. error_ssl_3:
  4510. SSL_free (ssl);
  4511. error_ssl_2:
  4512. SSL_CTX_free (ssl_ctx);
  4513. error_ssl_1:
  4514. if (!error)
  4515. error_set (&error, "%s: %s", "Could not initialize TLS",
  4516. ERR_reason_error_string (ERR_get_error ()));
  4517. error_propagate (e, error);
  4518. return false;
  4519. }
  4520. static void
  4521. transport_tls_cleanup (struct server *s)
  4522. {
  4523. struct transport_tls_data *data = s->transport_data;
  4524. if (data->ssl)
  4525. SSL_free (data->ssl);
  4526. if (data->ssl_ctx)
  4527. SSL_CTX_free (data->ssl_ctx);
  4528. free (data);
  4529. }
  4530. static enum socket_io_result
  4531. transport_tls_try_read (struct server *s)
  4532. {
  4533. struct transport_tls_data *data = s->transport_data;
  4534. if (data->ssl_tx_want_rx)
  4535. return SOCKET_IO_OK;
  4536. struct str *buf = &s->read_buffer;
  4537. data->ssl_rx_want_tx = false;
  4538. while (true)
  4539. {
  4540. ERR_clear_error ();
  4541. str_reserve (buf, 512);
  4542. int n_read = SSL_read (data->ssl, buf->str + buf->len,
  4543. buf->alloc - buf->len - 1 /* null byte */);
  4544. const char *error_info = NULL;
  4545. switch (xssl_get_error (data->ssl, n_read, &error_info))
  4546. {
  4547. case SSL_ERROR_NONE:
  4548. buf->str[buf->len += n_read] = '\0';
  4549. continue;
  4550. case SSL_ERROR_ZERO_RETURN:
  4551. return SOCKET_IO_EOF;
  4552. case SSL_ERROR_WANT_READ:
  4553. return SOCKET_IO_OK;
  4554. case SSL_ERROR_WANT_WRITE:
  4555. data->ssl_rx_want_tx = true;
  4556. return SOCKET_IO_OK;
  4557. case XSSL_ERROR_TRY_AGAIN:
  4558. continue;
  4559. default:
  4560. LOG_FUNC_FAILURE ("SSL_read", error_info);
  4561. return SOCKET_IO_ERROR;
  4562. }
  4563. }
  4564. }
  4565. static enum socket_io_result
  4566. transport_tls_try_write (struct server *s)
  4567. {
  4568. struct transport_tls_data *data = s->transport_data;
  4569. if (data->ssl_rx_want_tx)
  4570. return SOCKET_IO_OK;
  4571. struct str *buf = &s->write_buffer;
  4572. data->ssl_tx_want_rx = false;
  4573. while (buf->len)
  4574. {
  4575. ERR_clear_error ();
  4576. int n_written = SSL_write (data->ssl, buf->str, buf->len);
  4577. const char *error_info = NULL;
  4578. switch (xssl_get_error (data->ssl, n_written, &error_info))
  4579. {
  4580. case SSL_ERROR_NONE:
  4581. str_remove_slice (buf, 0, n_written);
  4582. continue;
  4583. case SSL_ERROR_ZERO_RETURN:
  4584. return SOCKET_IO_EOF;
  4585. case SSL_ERROR_WANT_WRITE:
  4586. return SOCKET_IO_OK;
  4587. case SSL_ERROR_WANT_READ:
  4588. data->ssl_tx_want_rx = true;
  4589. return SOCKET_IO_OK;
  4590. case XSSL_ERROR_TRY_AGAIN:
  4591. continue;
  4592. default:
  4593. LOG_FUNC_FAILURE ("SSL_write", error_info);
  4594. return SOCKET_IO_ERROR;
  4595. }
  4596. }
  4597. return SOCKET_IO_OK;
  4598. }
  4599. static int
  4600. transport_tls_get_poll_events (struct server *s)
  4601. {
  4602. struct transport_tls_data *data = s->transport_data;
  4603. int events = POLLIN;
  4604. if (s->write_buffer.len || data->ssl_rx_want_tx)
  4605. events |= POLLOUT;
  4606. // While we're waiting for an opposite event, we ignore the original
  4607. if (data->ssl_rx_want_tx) events &= ~POLLIN;
  4608. if (data->ssl_tx_want_rx) events &= ~POLLOUT;
  4609. return events;
  4610. }
  4611. static void
  4612. transport_tls_in_before_shutdown (struct server *s)
  4613. {
  4614. struct transport_tls_data *data = s->transport_data;
  4615. (void) SSL_shutdown (data->ssl);
  4616. }
  4617. static struct transport g_transport_tls =
  4618. {
  4619. .init = transport_tls_init,
  4620. .cleanup = transport_tls_cleanup,
  4621. .try_read = transport_tls_try_read,
  4622. .try_write = transport_tls_try_write,
  4623. .get_poll_events = transport_tls_get_poll_events,