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.
 
 
 
 
 
 

13879 lines
377 KiB

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