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.

13461 lines
364KB

  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. struct user_channel
  1036. {
  1037. LIST_HEADER (struct user_channel)
  1038. struct channel *channel; ///< Reference to channel
  1039. };
  1040. static struct user_channel *
  1041. user_channel_new (void)
  1042. {
  1043. struct user_channel *self = xcalloc (1, sizeof *self);
  1044. return self;
  1045. }
  1046. static void
  1047. user_channel_destroy (struct user_channel *self)
  1048. {
  1049. // The "channel" reference is weak and this object should get
  1050. // destroyed whenever the user stops being in the channel.
  1051. free (self);
  1052. }
  1053. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1054. // We keep references to user information in channels and buffers,
  1055. // and weak references in the name lookup table.
  1056. struct user
  1057. {
  1058. REF_COUNTABLE_HEADER
  1059. char *nickname; ///< Literal nickname
  1060. // TODO: write code to poll for the away status
  1061. bool away; ///< User is away
  1062. struct user_channel *channels; ///< Channels the user is on
  1063. };
  1064. static struct user *
  1065. user_new (void)
  1066. {
  1067. struct user *self = xcalloc (1, sizeof *self);
  1068. self->ref_count = 1;
  1069. return self;
  1070. }
  1071. static void
  1072. user_destroy (struct user *self)
  1073. {
  1074. free (self->nickname);
  1075. LIST_FOR_EACH (struct user_channel, iter, self->channels)
  1076. user_channel_destroy (iter);
  1077. free (self);
  1078. }
  1079. REF_COUNTABLE_METHODS (user)
  1080. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1081. struct channel_user
  1082. {
  1083. LIST_HEADER (struct channel_user)
  1084. struct user *user; ///< Reference to user
  1085. struct str prefixes; ///< Ordered @+... characters
  1086. };
  1087. static struct channel_user *
  1088. channel_user_new (void)
  1089. {
  1090. struct channel_user *self = xcalloc (1, sizeof *self);
  1091. str_init (&self->prefixes);
  1092. return self;
  1093. }
  1094. static void
  1095. channel_user_destroy (struct channel_user *self)
  1096. {
  1097. user_unref (self->user);
  1098. str_free (&self->prefixes);
  1099. free (self);
  1100. }
  1101. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1102. // We keep references to channels in their buffers,
  1103. // and weak references in their users and the name lookup table.
  1104. struct channel
  1105. {
  1106. REF_COUNTABLE_HEADER
  1107. char *name; ///< Channel name
  1108. char *topic; ///< Channel topic
  1109. // XXX: write something like an ordered set of characters object?
  1110. struct str no_param_modes; ///< No parameter channel modes
  1111. struct str_map param_modes; ///< Parametrized channel modes
  1112. struct channel_user *users; ///< Channel users
  1113. struct str_vector names_buf; ///< Buffer for RPL_NAMREPLY
  1114. bool left_manually; ///< Don't rejoin on reconnect
  1115. };
  1116. static struct channel *
  1117. channel_new (void)
  1118. {
  1119. struct channel *self = xcalloc (1, sizeof *self);
  1120. self->ref_count = 1;
  1121. str_init (&self->no_param_modes);
  1122. str_map_init (&self->param_modes);
  1123. self->param_modes.free = free;
  1124. str_vector_init (&self->names_buf);
  1125. return self;
  1126. }
  1127. static void
  1128. channel_destroy (struct channel *self)
  1129. {
  1130. free (self->name);
  1131. free (self->topic);
  1132. str_free (&self->no_param_modes);
  1133. str_map_free (&self->param_modes);
  1134. // Owner has to make sure we have no users by now
  1135. hard_assert (!self->users);
  1136. str_vector_free (&self->names_buf);
  1137. free (self);
  1138. }
  1139. REF_COUNTABLE_METHODS (channel)
  1140. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1141. enum formatter_item_type
  1142. {
  1143. FORMATTER_ITEM_END, ///< Sentinel value for arrays
  1144. FORMATTER_ITEM_TEXT, ///< Text
  1145. FORMATTER_ITEM_ATTR, ///< Formatting attributes
  1146. FORMATTER_ITEM_FG_COLOR, ///< Foreground color
  1147. FORMATTER_ITEM_BG_COLOR, ///< Background color
  1148. FORMATTER_ITEM_SIMPLE, ///< Toggle mIRC formatting
  1149. FORMATTER_ITEM_IGNORE_ATTR ///< Un/set attribute ignoration
  1150. };
  1151. struct formatter_item
  1152. {
  1153. enum formatter_item_type type : 16; ///< Type of this item
  1154. int attribute : 16; ///< Attribute ID
  1155. int color; ///< Color
  1156. char *text; ///< String
  1157. };
  1158. static void
  1159. formatter_item_free (struct formatter_item *self)
  1160. {
  1161. free (self->text);
  1162. }
  1163. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1164. struct formatter
  1165. {
  1166. struct app_context *ctx; ///< Application context
  1167. struct server *s; ///< Server
  1168. struct formatter_item *items; ///< Items
  1169. size_t items_len; ///< Items used
  1170. size_t items_alloc; ///< Items allocated
  1171. };
  1172. static void
  1173. formatter_init (struct formatter *self,
  1174. struct app_context *ctx, struct server *s)
  1175. {
  1176. memset (self, 0, sizeof *self);
  1177. self->ctx = ctx;
  1178. self->s = s;
  1179. self->items = xcalloc (sizeof *self->items, (self->items_alloc = 16));
  1180. self->items_len = 0;
  1181. }
  1182. static void
  1183. formatter_free (struct formatter *self)
  1184. {
  1185. for (size_t i = 0; i < self->items_len; i++)
  1186. formatter_item_free (&self->items[i]);
  1187. free (self->items);
  1188. }
  1189. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1190. enum buffer_line_flags
  1191. {
  1192. BUFFER_LINE_STATUS = 1 << 0, ///< Status message
  1193. BUFFER_LINE_ERROR = 1 << 1, ///< Error message
  1194. BUFFER_LINE_HIGHLIGHT = 1 << 2, ///< The user was highlighted by this
  1195. BUFFER_LINE_SKIP_FILE = 1 << 3, ///< Don't log this to file
  1196. BUFFER_LINE_INDENT = 1 << 4, ///< Just indent the line
  1197. BUFFER_LINE_UNIMPORTANT = 1 << 5 ///< Joins, parts, similar spam
  1198. };
  1199. struct buffer_line
  1200. {
  1201. LIST_HEADER (struct buffer_line)
  1202. int flags; ///< Flags
  1203. time_t when; ///< Time of the event
  1204. struct formatter_item items[]; ///< Line data
  1205. };
  1206. /// Create a new buffer line stealing all data from the provided formatter
  1207. struct buffer_line *
  1208. buffer_line_new (struct formatter *f)
  1209. {
  1210. // We make space for one more item that gets initialized to all zeros,
  1211. // meaning FORMATTER_ITEM_END (because it's the first value in the enum)
  1212. size_t items_size = f->items_len * sizeof *f->items;
  1213. struct buffer_line *self =
  1214. xcalloc (1, sizeof *self + items_size + sizeof *self->items);
  1215. memcpy (self->items, f->items, items_size);
  1216. // We've stolen pointers from the formatter, let's destroy it altogether
  1217. free (f->items);
  1218. memset (f, 0, sizeof *f);
  1219. return self;
  1220. }
  1221. static void
  1222. buffer_line_destroy (struct buffer_line *self)
  1223. {
  1224. for (struct formatter_item *iter = self->items; iter->type; iter++)
  1225. formatter_item_free (iter);
  1226. free (self);
  1227. }
  1228. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1229. enum buffer_type
  1230. {
  1231. BUFFER_GLOBAL, ///< Global information
  1232. BUFFER_SERVER, ///< Server-related messages
  1233. BUFFER_CHANNEL, ///< Channels
  1234. BUFFER_PM ///< Private messages (query)
  1235. };
  1236. struct buffer
  1237. {
  1238. LIST_HEADER (struct buffer)
  1239. REF_COUNTABLE_HEADER
  1240. enum buffer_type type; ///< Type of the buffer
  1241. char *name; ///< The name of the buffer
  1242. struct input *input; ///< API for "input_data"
  1243. input_buffer_t input_data; ///< User interface data
  1244. // Buffer contents:
  1245. struct buffer_line *lines; ///< All lines in this buffer
  1246. struct buffer_line *lines_tail; ///< The tail of buffer lines
  1247. unsigned lines_count; ///< How many lines we have
  1248. unsigned new_messages_count; ///< # messages since last left
  1249. unsigned new_unimportant_count; ///< How much of that is unimportant
  1250. bool highlighted; ///< We've been highlighted
  1251. FILE *log_file; ///< Log file
  1252. // Origin information:
  1253. struct server *server; ///< Reference to server
  1254. struct channel *channel; ///< Reference to channel
  1255. struct user *user; ///< Reference to user
  1256. };
  1257. static struct buffer *
  1258. buffer_new (struct input *input)
  1259. {
  1260. struct buffer *self = xcalloc (1, sizeof *self);
  1261. self->ref_count = 1;
  1262. self->input = input;
  1263. self->input_data = CALL (input, buffer_new);
  1264. return self;
  1265. }
  1266. static void
  1267. buffer_destroy (struct buffer *self)
  1268. {
  1269. free (self->name);
  1270. if (self->input_data)
  1271. {
  1272. #ifdef HAVE_READLINE
  1273. // FIXME: can't really free "history" contents from here, as we cannot
  1274. // be sure that the user interface pointer is valid and usable
  1275. input_rl__buffer_destroy_wo_history (self->input_data);
  1276. #else // ! HAVE_READLINE
  1277. CALL_ (self->input, buffer_destroy, self->input_data);
  1278. #endif // ! HAVE_READLINE
  1279. }
  1280. LIST_FOR_EACH (struct buffer_line, iter, self->lines)
  1281. buffer_line_destroy (iter);
  1282. if (self->log_file)
  1283. (void) fclose (self->log_file);
  1284. if (self->user)
  1285. user_unref (self->user);
  1286. if (self->channel)
  1287. channel_unref (self->channel);
  1288. free (self);
  1289. }
  1290. REF_COUNTABLE_METHODS (buffer)
  1291. #define buffer_ref do_not_use_dangerous
  1292. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1293. // The only real purpose of this is to abstract away TLS
  1294. struct transport
  1295. {
  1296. /// Initialize the transport
  1297. bool (*init) (struct server *s, const char *hostname, struct error **e);
  1298. /// Destroy the user data pointer
  1299. void (*cleanup) (struct server *s);
  1300. /// The underlying socket may have become readable, update `read_buffer'
  1301. enum socket_io_result (*try_read) (struct server *s);
  1302. /// The underlying socket may have become writeable, flush `write_buffer'
  1303. enum socket_io_result (*try_write) (struct server *s);
  1304. /// Return event mask to use in the poller
  1305. int (*get_poll_events) (struct server *s);
  1306. /// Called just before closing the connection from our side
  1307. void (*in_before_shutdown) (struct server *s);
  1308. };
  1309. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1310. enum server_state
  1311. {
  1312. IRC_DISCONNECTED, ///< Not connected
  1313. IRC_CONNECTING, ///< Connecting to the server
  1314. IRC_CONNECTED, ///< Trying to register
  1315. IRC_REGISTERED, ///< We can chat now
  1316. IRC_CLOSING, ///< Flushing output before shutdown
  1317. IRC_HALF_CLOSED ///< Connection shutdown from our side
  1318. };
  1319. /// Convert an IRC identifier character to lower-case
  1320. typedef int (*irc_tolower_fn) (int);
  1321. /// Key conversion function for hashmap lookups
  1322. typedef size_t (*irc_strxfrm_fn) (char *, const char *, size_t);
  1323. struct server
  1324. {
  1325. REF_COUNTABLE_HEADER
  1326. struct app_context *ctx; ///< Application context
  1327. char *name; ///< Server identifier
  1328. struct buffer *buffer; ///< The buffer for this server
  1329. struct config_item *config; ///< Configuration root
  1330. // Connection:
  1331. enum server_state state; ///< Connection state
  1332. struct connector *connector; ///< Connection establisher
  1333. struct socks_connector *socks_conn; ///< SOCKS connection establisher
  1334. unsigned reconnect_attempt; ///< Number of reconnect attempt
  1335. bool manual_disconnect; ///< Don't reconnect after disconnect
  1336. int socket; ///< Socket FD of the server
  1337. struct str read_buffer; ///< Input yet to be processed
  1338. struct str write_buffer; ///< Outut yet to be be sent out
  1339. struct poller_fd socket_event; ///< We can read from the socket
  1340. struct transport *transport; ///< Transport method
  1341. void *transport_data; ///< Transport data
  1342. // Events:
  1343. struct poller_timer ping_tmr; ///< We should send a ping
  1344. struct poller_timer timeout_tmr; ///< Connection seems to be dead
  1345. struct poller_timer reconnect_tmr; ///< We should reconnect now
  1346. struct poller_timer autojoin_tmr; ///< Re/join channels as appropriate
  1347. // IRC:
  1348. // TODO: an output queue to prevent excess floods (this will be needed
  1349. // especially for away status polling)
  1350. bool rehashing; ///< Rehashing IRC identifiers
  1351. struct str_map irc_users; ///< IRC user data
  1352. struct str_map irc_channels; ///< IRC channel data
  1353. struct str_map irc_buffer_map; ///< Maps IRC identifiers to buffers
  1354. struct user *irc_user; ///< Our own user
  1355. int nick_counter; ///< Iterates "nicks" when registering
  1356. struct str irc_user_mode; ///< Our current user modes
  1357. char *irc_user_host; ///< Our current user@host
  1358. bool autoaway_active; ///< Autoaway is currently active
  1359. bool cap_echo_message; ///< Whether the server echos messages
  1360. // Server-specific information (from RPL_ISUPPORT):
  1361. irc_tolower_fn irc_tolower; ///< Server tolower()
  1362. irc_strxfrm_fn irc_strxfrm; ///< Server strxfrm()
  1363. char *irc_chantypes; ///< Channel types (name prefixes)
  1364. char *irc_idchan_prefixes; ///< Prefixes for "safe channels"
  1365. char *irc_statusmsg; ///< Prefixes for channel targets
  1366. char *irc_chanmodes_list; ///< Channel modes for lists
  1367. char *irc_chanmodes_param_always; ///< Channel modes with mandatory param
  1368. char *irc_chanmodes_param_when_set; ///< Channel modes with param when set
  1369. char *irc_chanmodes_param_never; ///< Channel modes without param
  1370. char *irc_chanuser_prefixes; ///< Channel user prefixes
  1371. char *irc_chanuser_modes; ///< Channel user modes
  1372. unsigned irc_max_modes; ///< Max parametrized modes per command
  1373. };
  1374. static void on_irc_timeout (void *user_data);
  1375. static void on_irc_ping_timeout (void *user_data);
  1376. static void on_irc_autojoin_timeout (void *user_data);
  1377. static void irc_initiate_connect (struct server *s);
  1378. static void
  1379. server_init_specifics (struct server *self)
  1380. {
  1381. // Defaults as per the RPL_ISUPPORT drafts, or RFC 1459
  1382. self->irc_tolower = irc_tolower;
  1383. self->irc_strxfrm = irc_strxfrm;
  1384. self->irc_chantypes = xstrdup ("#&");
  1385. self->irc_idchan_prefixes = xstrdup ("");
  1386. self->irc_statusmsg = xstrdup ("");
  1387. self->irc_chanmodes_list = xstrdup ("b");
  1388. self->irc_chanmodes_param_always = xstrdup ("k");
  1389. self->irc_chanmodes_param_when_set = xstrdup ("l");
  1390. self->irc_chanmodes_param_never = xstrdup ("imnpst");
  1391. self->irc_chanuser_prefixes = xstrdup ("@+");
  1392. self->irc_chanuser_modes = xstrdup ("ov");
  1393. self->irc_max_modes = 3;
  1394. }
  1395. static void
  1396. server_free_specifics (struct server *self)
  1397. {
  1398. free (self->irc_chantypes);
  1399. free (self->irc_idchan_prefixes);
  1400. free (self->irc_statusmsg);
  1401. free (self->irc_chanmodes_list);
  1402. free (self->irc_chanmodes_param_always);
  1403. free (self->irc_chanmodes_param_when_set);
  1404. free (self->irc_chanmodes_param_never);
  1405. free (self->irc_chanuser_prefixes);
  1406. free (self->irc_chanuser_modes);
  1407. }
  1408. static struct server *
  1409. server_new (struct poller *poller)
  1410. {
  1411. struct server *self = xcalloc (1, sizeof *self);
  1412. self->ref_count = 1;
  1413. self->socket = -1;
  1414. str_init (&self->read_buffer);
  1415. str_init (&self->write_buffer);
  1416. self->state = IRC_DISCONNECTED;
  1417. poller_timer_init (&self->timeout_tmr, poller);
  1418. self->timeout_tmr.dispatcher = on_irc_timeout;
  1419. self->timeout_tmr.user_data = self;
  1420. poller_timer_init (&self->ping_tmr, poller);
  1421. self->ping_tmr.dispatcher = on_irc_ping_timeout;
  1422. self->ping_tmr.user_data = self;
  1423. poller_timer_init (&self->reconnect_tmr, poller);
  1424. self->reconnect_tmr.dispatcher = (poller_timer_fn) irc_initiate_connect;
  1425. self->reconnect_tmr.user_data = self;
  1426. poller_timer_init (&self->autojoin_tmr, poller);
  1427. self->autojoin_tmr.dispatcher = on_irc_autojoin_timeout;
  1428. self->autojoin_tmr.user_data = self;
  1429. str_map_init (&self->irc_users);
  1430. self->irc_users.key_xfrm = irc_strxfrm;
  1431. str_map_init (&self->irc_channels);
  1432. self->irc_channels.key_xfrm = irc_strxfrm;
  1433. str_map_init (&self->irc_buffer_map);
  1434. self->irc_buffer_map.key_xfrm = irc_strxfrm;
  1435. str_init (&self->irc_user_mode);
  1436. server_init_specifics (self);
  1437. return self;
  1438. }
  1439. static void
  1440. server_destroy (struct server *self)
  1441. {
  1442. free (self->name);
  1443. if (self->connector)
  1444. {
  1445. connector_free (self->connector);
  1446. free (self->connector);
  1447. }
  1448. if (self->socks_conn)
  1449. {
  1450. socks_connector_free (self->socks_conn);
  1451. free (self->socks_conn);
  1452. }
  1453. if (self->transport
  1454. && self->transport->cleanup)
  1455. self->transport->cleanup (self);
  1456. if (self->socket != -1)
  1457. {
  1458. xclose (self->socket);
  1459. self->socket_event.closed = true;
  1460. poller_fd_reset (&self->socket_event);
  1461. }
  1462. str_free (&self->read_buffer);
  1463. str_free (&self->write_buffer);
  1464. poller_timer_reset (&self->ping_tmr);
  1465. poller_timer_reset (&self->timeout_tmr);
  1466. poller_timer_reset (&self->reconnect_tmr);
  1467. poller_timer_reset (&self->autojoin_tmr);
  1468. str_map_free (&self->irc_users);
  1469. str_map_free (&self->irc_channels);
  1470. str_map_free (&self->irc_buffer_map);
  1471. if (self->irc_user)
  1472. user_unref (self->irc_user);
  1473. str_free (&self->irc_user_mode);
  1474. free (self->irc_user_host);
  1475. server_free_specifics (self);
  1476. free (self);
  1477. }
  1478. REF_COUNTABLE_METHODS (server)
  1479. #define server_ref do_not_use_dangerous
  1480. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1481. struct plugin
  1482. {
  1483. LIST_HEADER (struct plugin)
  1484. char *name; ///< Name of the plugin
  1485. struct plugin_vtable *vtable; ///< Methods
  1486. };
  1487. struct plugin_vtable
  1488. {
  1489. /// Unregister and free the plugin including all relevant resources
  1490. void (*free) (struct plugin *self);
  1491. };
  1492. static void
  1493. plugin_destroy (struct plugin *self)
  1494. {
  1495. self->vtable->free (self);
  1496. free (self->name);
  1497. free (self);
  1498. }
  1499. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1500. // This is a bit ugly since insertion is O(n) and the need to get rid of the
  1501. // specific type because of list macros, however I don't currently posses any
  1502. // strictly better, ordered data structure
  1503. struct hook
  1504. {
  1505. LIST_HEADER (struct hook)
  1506. int priority; ///< The lesser the sooner
  1507. };
  1508. static struct hook *
  1509. hook_insert (struct hook *list, struct hook *item)
  1510. {
  1511. // Corner cases: list is empty or we precede everything
  1512. if (!list || item->priority < list->priority)
  1513. {
  1514. LIST_PREPEND (list, item);
  1515. return list;
  1516. }
  1517. // Otherwise fast-forward to the last entry that precedes us
  1518. struct hook *before = list;
  1519. while (before->next && before->next->priority < item->priority)
  1520. before = before->next;
  1521. // And link ourselves in between it and its successor
  1522. if ((item->next = before->next))
  1523. item->next->prev = item;
  1524. before->next = item;
  1525. item->prev = before;
  1526. return list;
  1527. }
  1528. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1529. struct input_hook
  1530. {
  1531. struct hook super; ///< Common hook fields
  1532. struct input_hook_vtable *vtable; ///< Methods
  1533. };
  1534. struct input_hook_vtable
  1535. {
  1536. /// Takes over the ownership of "input", returns either NULL if input
  1537. /// was thrown away, or a possibly modified version of it
  1538. char *(*filter) (struct input_hook *self,
  1539. struct buffer *buffer, char *input);
  1540. };
  1541. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1542. struct irc_hook
  1543. {
  1544. struct hook super; ///< Common hook fields
  1545. struct irc_hook_vtable *vtable; ///< Methods
  1546. };
  1547. struct irc_hook_vtable
  1548. {
  1549. /// Takes over the ownership of "message", returns either NULL if message
  1550. /// was thrown away, or a possibly modified version of it
  1551. char *(*filter) (struct irc_hook *self,
  1552. struct server *server, char *message);
  1553. };
  1554. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1555. struct completion_word
  1556. {
  1557. size_t start; ///< Offset to start of word
  1558. size_t end; ///< Offset to end of word
  1559. };
  1560. struct completion
  1561. {
  1562. char *line; ///< The line which is being completed
  1563. struct completion_word *words; ///< Word locations
  1564. size_t words_len; ///< Number of words
  1565. size_t words_alloc; ///< Number of words allocated
  1566. size_t location; ///< Which word is being completed
  1567. };
  1568. struct completion_hook
  1569. {
  1570. struct hook super; ///< Common hook fields
  1571. struct completion_hook_vtable *vtable;
  1572. };
  1573. struct completion_hook_vtable
  1574. {
  1575. /// Tries to add possible completions of "word" to "output"
  1576. void (*complete) (struct completion_hook *self,
  1577. struct completion *data, const char *word, struct str_vector *output);
  1578. };
  1579. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1580. struct app_context
  1581. {
  1582. char *attrs_defaults[ATTR_COUNT]; ///< Default terminal attributes
  1583. // Configuration:
  1584. struct config config; ///< Program configuration
  1585. char *attrs[ATTR_COUNT]; ///< Terminal attributes
  1586. bool isolate_buffers; ///< Isolate global/server buffers
  1587. bool beep_on_highlight; ///< Beep on highlight
  1588. bool logging; ///< Logging to file enabled
  1589. bool show_all_prefixes; ///< Show all prefixes before nicks
  1590. bool word_wrapping; ///< Enable simple word wrapping
  1591. struct str_map servers; ///< Our servers
  1592. // Events:
  1593. struct poller_fd tty_event; ///< Terminal input event
  1594. struct poller_fd signal_event; ///< Signal FD event
  1595. struct poller_timer flush_timer; ///< Flush all open files (e.g. logs)
  1596. struct poller_timer date_chg_tmr; ///< Print a date change
  1597. struct poller_timer autoaway_tmr; ///< Autoaway timer
  1598. struct poller poller; ///< Manages polled descriptors
  1599. bool quitting; ///< User requested quitting
  1600. bool polling; ///< The event loop is running
  1601. // Buffers:
  1602. struct buffer *buffers; ///< All our buffers in order
  1603. struct buffer *buffers_tail; ///< The tail of our buffers
  1604. struct buffer *global_buffer; ///< The global buffer
  1605. struct buffer *current_buffer; ///< The current buffer
  1606. struct buffer *last_buffer; ///< Last used buffer
  1607. // TODO: make buffer names fully unique like weechat does
  1608. struct str_map buffers_by_name; ///< Buffers by name
  1609. unsigned backlog_limit; ///< Limit for buffer lines
  1610. time_t last_displayed_msg_time; ///< Time of last displayed message
  1611. // Terminal:
  1612. iconv_t term_to_utf8; ///< Terminal encoding to UTF-8
  1613. iconv_t term_from_utf8; ///< UTF-8 to terminal encoding
  1614. struct input *input; ///< User interface
  1615. struct poller_idle input_event; ///< Pending input event
  1616. struct str_vector pending_input; ///< Pending input lines
  1617. int *nick_palette; ///< A 256-color palette for nicknames
  1618. size_t nick_palette_len; ///< Number of entries in nick_palette
  1619. bool awaiting_mirc_escape; ///< Awaiting a mIRC attribute escape
  1620. bool in_bracketed_paste; ///< User is pasting some content
  1621. struct str input_buffer; ///< Buffered pasted content
  1622. bool running_backlog_helper; ///< Running a backlog helper
  1623. bool running_editor; ///< Running editor for the input
  1624. char *editor_filename; ///< The file being edited by user
  1625. int terminal_suspended; ///< Terminal suspension level
  1626. struct plugin *plugins; ///< Loaded plugins
  1627. struct hook *input_hooks; ///< Input hooks
  1628. struct hook *irc_hooks; ///< IRC hooks
  1629. struct hook *completion_hooks; ///< Autocomplete hooks
  1630. }
  1631. *g_ctx;
  1632. static int *
  1633. filter_color_cube_for_acceptable_nick_colors (size_t *len)
  1634. {
  1635. // This is a pure function and we don't use threads, static storage is fine
  1636. static int table[6 * 6 * 6];
  1637. size_t len_counter = 0;
  1638. for (int x = 0; x < 6 * 6 * 6; x++)
  1639. {
  1640. int r = x / 36;
  1641. int g = (x / 6) % 6;
  1642. int b = (x % 6);
  1643. // Use the luma value of colours within the cube to filter colours that
  1644. // look okay-ish on terminals with both black and white backgrounds
  1645. double luma = 0.2126 * r / 6. + 0.7152 * g / 6. + 0.0722 * b / 6.;
  1646. if (luma >= .3 && luma <= .5)
  1647. table[len_counter++] = 16 + x;
  1648. }
  1649. *len = len_counter;
  1650. return table;
  1651. }
  1652. static bool
  1653. app_iconv_open (iconv_t *target, const char *to, const char *from)
  1654. {
  1655. if (ICONV_ACCEPTS_TRANSLIT)
  1656. {
  1657. char *to_real = xstrdup_printf ("%s//TRANSLIT", to);
  1658. *target = iconv_open (to_real, from);
  1659. free (to_real);
  1660. }
  1661. else
  1662. *target = iconv_open (to, from);
  1663. return *target != (iconv_t) -1;
  1664. }
  1665. static void
  1666. app_context_init (struct app_context *self)
  1667. {
  1668. memset (self, 0, sizeof *self);
  1669. config_init (&self->config);
  1670. poller_init (&self->poller);
  1671. str_map_init (&self->servers);
  1672. self->servers.free = (str_map_free_fn) server_unref;
  1673. self->servers.key_xfrm = tolower_ascii_strxfrm;
  1674. str_map_init (&self->buffers_by_name);
  1675. self->buffers_by_name.key_xfrm = tolower_ascii_strxfrm;
  1676. // So that we don't lose the logo shortly after startup
  1677. self->backlog_limit = 1000;
  1678. self->last_displayed_msg_time = time (NULL);
  1679. char *native = nl_langinfo (CODESET);
  1680. if (!app_iconv_open (&self->term_from_utf8, native, "UTF-8")
  1681. || !app_iconv_open (&self->term_to_utf8, "UTF-8", native))
  1682. exit_fatal ("creating the UTF-8 conversion object failed: %s",
  1683. strerror (errno));
  1684. self->input = input_new ();
  1685. self->input->user_data = self;
  1686. str_vector_init (&self->pending_input);
  1687. str_init (&self->input_buffer);
  1688. self->nick_palette =
  1689. filter_color_cube_for_acceptable_nick_colors (&self->nick_palette_len);
  1690. }
  1691. static void
  1692. app_context_free (struct app_context *self)
  1693. {
  1694. // Plugins can try to use of the other fields when destroyed
  1695. LIST_FOR_EACH (struct plugin, iter, self->plugins)
  1696. plugin_destroy (iter);
  1697. config_free (&self->config);
  1698. for (size_t i = 0; i < ATTR_COUNT; i++)
  1699. {
  1700. free (self->attrs_defaults[i]);
  1701. free (self->attrs[i]);
  1702. }
  1703. LIST_FOR_EACH (struct buffer, iter, self->buffers)
  1704. {
  1705. #ifdef HAVE_READLINE
  1706. // We can use the user interface here; see buffer_destroy()
  1707. CALL_ (self->input, buffer_destroy, iter->input_data);
  1708. iter->input_data = NULL;
  1709. #endif // HAVE_READLINE
  1710. buffer_unref (iter);
  1711. }
  1712. str_map_free (&self->buffers_by_name);
  1713. str_map_free (&self->servers);
  1714. poller_free (&self->poller);
  1715. iconv_close (self->term_from_utf8);
  1716. iconv_close (self->term_to_utf8);
  1717. CALL (self->input, destroy);
  1718. str_vector_free (&self->pending_input);
  1719. str_free (&self->input_buffer);
  1720. free (self->editor_filename);
  1721. }
  1722. static void refresh_prompt (struct app_context *ctx);
  1723. // --- Configuration -----------------------------------------------------------
  1724. static void
  1725. on_config_debug_mode_change (struct config_item *item)
  1726. {
  1727. g_debug_mode = item->value.boolean;
  1728. }
  1729. static void
  1730. on_config_show_all_prefixes_change (struct config_item *item)
  1731. {
  1732. struct app_context *ctx = item->user_data;
  1733. ctx->show_all_prefixes = item->value.boolean;
  1734. refresh_prompt (ctx);
  1735. }
  1736. static void on_config_backlog_limit_change (struct config_item *item);
  1737. static void on_config_attribute_change (struct config_item *item);
  1738. static void on_config_logging_change (struct config_item *item);
  1739. #define TRIVIAL_BOOLEAN_ON_CHANGE(name) \
  1740. static void \
  1741. on_config_ ## name ## _change (struct config_item *item) \
  1742. { \
  1743. struct app_context *ctx = item->user_data; \
  1744. ctx->name = item->value.boolean; \
  1745. }
  1746. TRIVIAL_BOOLEAN_ON_CHANGE (isolate_buffers)
  1747. TRIVIAL_BOOLEAN_ON_CHANGE (beep_on_highlight)
  1748. TRIVIAL_BOOLEAN_ON_CHANGE (word_wrapping)
  1749. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1750. static bool
  1751. config_validate_nonjunk_string
  1752. (const struct config_item *item, struct error **e)
  1753. {
  1754. if (item->type == CONFIG_ITEM_NULL)
  1755. return true;
  1756. hard_assert (config_item_type_is_string (item->type));
  1757. for (size_t i = 0; i < item->value.string.len; i++)
  1758. {
  1759. // Not even a tabulator
  1760. unsigned char c = item->value.string.str[i];
  1761. if (c < 32)
  1762. {
  1763. error_set (e, "control characters are not allowed");
  1764. return false;
  1765. }
  1766. }
  1767. return true;
  1768. }
  1769. static bool
  1770. config_validate_addresses
  1771. (const struct config_item *item, struct error **e)
  1772. {
  1773. if (item->type == CONFIG_ITEM_NULL)
  1774. return true;
  1775. if (!config_validate_nonjunk_string (item, e))
  1776. return false;
  1777. // Comma-separated list of "host[:port]" pairs
  1778. regex_t re;
  1779. int err = regcomp (&re, "^([^/:,]+(:[^/:,]+)?)?"
  1780. "(,([^/:,]+(:[^/:,]+)?)?)*$", REG_EXTENDED | REG_NOSUB);
  1781. hard_assert (!err);
  1782. bool result = !regexec (&re, item->value.string.str, 0, NULL, 0);
  1783. if (!result)
  1784. error_set (e, "invalid address list string");
  1785. regfree (&re);
  1786. return result;
  1787. }
  1788. static bool
  1789. config_validate_nonnegative
  1790. (const struct config_item *item, struct error **e)
  1791. {
  1792. if (item->type == CONFIG_ITEM_NULL)
  1793. return true;
  1794. hard_assert (item->type == CONFIG_ITEM_INTEGER);
  1795. if (item->value.integer >= 0)
  1796. return true;
  1797. error_set (e, "must be non-negative");
  1798. return false;
  1799. }
  1800. static struct config_schema g_config_server[] =
  1801. {
  1802. { .name = "nicks",
  1803. .comment = "IRC nickname",
  1804. .type = CONFIG_ITEM_STRING_ARRAY,
  1805. .validate = config_validate_nonjunk_string },
  1806. { .name = "username",
  1807. .comment = "IRC user name",
  1808. .type = CONFIG_ITEM_STRING,
  1809. .validate = config_validate_nonjunk_string },
  1810. { .name = "realname",
  1811. .comment = "IRC real name/e-mail",
  1812. .type = CONFIG_ITEM_STRING,
  1813. .validate = config_validate_nonjunk_string },
  1814. { .name = "addresses",
  1815. .comment = "Addresses of the IRC network (e.g. \"irc.net:6667\")",
  1816. .type = CONFIG_ITEM_STRING_ARRAY,
  1817. .validate = config_validate_addresses },
  1818. { .name = "password",
  1819. .comment = "Password to connect to the server, if any",
  1820. .type = CONFIG_ITEM_STRING,
  1821. .validate = config_validate_nonjunk_string },
  1822. // XXX: if we add support for new capabilities, the value stays unchanged
  1823. { .name = "capabilities",
  1824. .comment = "Capabilities to use if supported by server",
  1825. .type = CONFIG_ITEM_STRING_ARRAY,
  1826. .validate = config_validate_nonjunk_string,
  1827. .default_ = "\"multi-prefix,invite-notify,server-time,echo-message\"" },
  1828. { .name = "tls",
  1829. .comment = "Whether to use TLS",
  1830. .type = CONFIG_ITEM_BOOLEAN,
  1831. .default_ = "off" },
  1832. { .name = "tls_cert",
  1833. .comment = "Client TLS certificate (PEM)",
  1834. .type = CONFIG_ITEM_STRING },
  1835. { .name = "tls_verify",
  1836. .comment = "Whether to verify certificates",
  1837. .type = CONFIG_ITEM_BOOLEAN,
  1838. .default_ = "on" },
  1839. { .name = "tls_ca_file",
  1840. .comment = "OpenSSL CA bundle file",
  1841. .type = CONFIG_ITEM_STRING },
  1842. { .name = "tls_ca_path",
  1843. .comment = "OpenSSL CA bundle path",
  1844. .type = CONFIG_ITEM_STRING },
  1845. { .name = "tls_ciphers",
  1846. .comment = "OpenSSL cipher preference list",
  1847. .type = CONFIG_ITEM_STRING,
  1848. .default_ = "\"DEFAULT:!MEDIUM:!LOW\"" },
  1849. { .name = "autoconnect",
  1850. .comment = "Connect automatically on startup",
  1851. .type = CONFIG_ITEM_BOOLEAN,
  1852. .default_ = "on" },
  1853. { .name = "autojoin",
  1854. .comment = "Channels to join on start",
  1855. .type = CONFIG_ITEM_STRING_ARRAY,
  1856. .validate = config_validate_nonjunk_string },
  1857. { .name = "command",
  1858. .comment = "Command to execute after a successful connect",
  1859. .type = CONFIG_ITEM_STRING },
  1860. { .name = "command_delay",
  1861. .comment = "Delay between executing \"command\" and joining channels",
  1862. .type = CONFIG_ITEM_INTEGER,
  1863. .validate = config_validate_nonnegative,
  1864. .default_ = "0" },
  1865. { .name = "reconnect",
  1866. .comment = "Whether to reconnect on error",
  1867. .type = CONFIG_ITEM_BOOLEAN,
  1868. .default_ = "on" },
  1869. { .name = "reconnect_delay",
  1870. .comment = "Time between reconnecting",
  1871. .type = CONFIG_ITEM_INTEGER,
  1872. .validate = config_validate_nonnegative,
  1873. .default_ = "5" },
  1874. { .name = "socks_host",
  1875. .comment = "Address of a SOCKS 4a/5 proxy",
  1876. .type = CONFIG_ITEM_STRING,
  1877. .validate = config_validate_nonjunk_string },
  1878. { .name = "socks_port",
  1879. .comment = "SOCKS port number",
  1880. .type = CONFIG_ITEM_INTEGER,
  1881. .validate = config_validate_nonnegative,
  1882. .default_ = "1080" },
  1883. { .name = "socks_username",
  1884. .comment = "SOCKS auth. username",
  1885. .type = CONFIG_ITEM_STRING },
  1886. { .name = "socks_password",
  1887. .comment = "SOCKS auth. password",
  1888. .type = CONFIG_ITEM_STRING },
  1889. {}
  1890. };
  1891. static struct config_schema g_config_behaviour[] =
  1892. {
  1893. { .name = "isolate_buffers",
  1894. .comment = "Don't leak messages from the server and global buffers",
  1895. .type = CONFIG_ITEM_BOOLEAN,
  1896. .default_ = "off",
  1897. .on_change = on_config_isolate_buffers_change },
  1898. { .name = "beep_on_highlight",
  1899. .comment = "Beep when highlighted or on a new invisible PM",
  1900. .type = CONFIG_ITEM_BOOLEAN,
  1901. .default_ = "on",
  1902. .on_change = on_config_beep_on_highlight_change },
  1903. { .name = "show_all_prefixes",
  1904. .comment = "Show all prefixes in front of nicknames",
  1905. .type = CONFIG_ITEM_BOOLEAN,
  1906. .default_ = "off",
  1907. .on_change = on_config_show_all_prefixes_change },
  1908. { .name = "word_wrapping",
  1909. .comment = "Enable simple word wrapping in buffers",
  1910. .type = CONFIG_ITEM_BOOLEAN,
  1911. .default_ = "on",
  1912. .on_change = on_config_word_wrapping_change },
  1913. { .name = "date_change_line",
  1914. .comment = "Input to strftime(3) for the date change line",
  1915. .type = CONFIG_ITEM_STRING,
  1916. .default_ = "\"%F\"" },
  1917. { .name = "logging",
  1918. .comment = "Log buffer contents to file",
  1919. .type = CONFIG_ITEM_BOOLEAN,
  1920. .default_ = "off",
  1921. .on_change = on_config_logging_change },
  1922. { .name = "save_on_quit",
  1923. .comment = "Save configuration before quitting",
  1924. .type = CONFIG_ITEM_BOOLEAN,
  1925. .default_ = "on" },
  1926. { .name = "debug_mode",
  1927. .comment = "Produce some debugging output",
  1928. .type = CONFIG_ITEM_BOOLEAN,
  1929. .default_ = "off",
  1930. .on_change = on_config_debug_mode_change },
  1931. // GNU screen has an ^O in its formatting attributes reset string,
  1932. // therefore we can't just pipe raw formatting to `less -R`.
  1933. // You can use the -r switch, however that makes `less` very confused
  1934. // about line wrapping, and the result is suboptimal.
  1935. { .name = "backlog_limit",
  1936. .comment = "Maximum number of lines stored in the backlog",
  1937. .type = CONFIG_ITEM_INTEGER,
  1938. .validate = config_validate_nonnegative,
  1939. .default_ = "1000",
  1940. .on_change = on_config_backlog_limit_change },
  1941. { .name = "backlog_helper",
  1942. .comment = "Shell command to display a buffer's history",
  1943. .type = CONFIG_ITEM_STRING,
  1944. .default_ = "\"LESSSECURE=1 less -M -R +G\"" },
  1945. { .name = "backlog_helper_strip_formatting",
  1946. .comment = "Strip formatting from backlog helper input",
  1947. .type = CONFIG_ITEM_BOOLEAN,
  1948. .default_ = "on" },
  1949. { .name = "reconnect_delay_growing",
  1950. .comment = "Growing factor for reconnect delay",
  1951. .type = CONFIG_ITEM_INTEGER,
  1952. .validate = config_validate_nonnegative,
  1953. .default_ = "2" },
  1954. { .name = "reconnect_delay_max",
  1955. .comment = "Maximum reconnect delay in seconds",
  1956. .type = CONFIG_ITEM_INTEGER,
  1957. .validate = config_validate_nonnegative,
  1958. .default_ = "600" },
  1959. { .name = "autoaway_message",
  1960. .comment = "Automated away message",
  1961. .type = CONFIG_ITEM_STRING,
  1962. .default_ = "\"I'm not here right now\"" },
  1963. { .name = "autoaway_delay",
  1964. .comment = "Delay from the last keypress in seconds",
  1965. .type = CONFIG_ITEM_INTEGER,
  1966. .validate = config_validate_nonnegative,
  1967. .default_ = "1800" },
  1968. { .name = "plugin_autoload",
  1969. .comment = "Plugins to automatically load on start",
  1970. .type = CONFIG_ITEM_STRING_ARRAY,
  1971. .validate = config_validate_nonjunk_string },
  1972. {}
  1973. };
  1974. static struct config_schema g_config_attributes[] =
  1975. {
  1976. #define XX(x, y, z) { .name = y, .comment = z, .type = CONFIG_ITEM_STRING, \
  1977. .on_change = on_config_attribute_change },
  1978. ATTR_TABLE (XX)
  1979. #undef XX
  1980. {}
  1981. };
  1982. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1983. static void
  1984. load_config_behaviour (struct config_item *subtree, void *user_data)
  1985. {
  1986. config_schema_apply_to_object (g_config_behaviour, subtree, user_data);
  1987. }
  1988. static void
  1989. load_config_attributes (struct config_item *subtree, void *user_data)
  1990. {
  1991. config_schema_apply_to_object (g_config_attributes, subtree, user_data);
  1992. }
  1993. static void
  1994. register_config_modules (struct app_context *ctx)
  1995. {
  1996. struct config *config = &ctx->config;
  1997. // The servers are loaded later when we can create buffers for them
  1998. config_register_module (config, "servers", NULL, NULL);
  1999. config_register_module (config, "aliases", NULL, NULL);
  2000. config_register_module (config, "plugins", NULL, NULL);
  2001. config_register_module (config, "behaviour", load_config_behaviour, ctx);
  2002. config_register_module (config, "attributes", load_config_attributes, ctx);
  2003. }
  2004. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2005. static const char *
  2006. get_config_string (struct config_item *root, const char *key)
  2007. {
  2008. struct config_item *item = config_item_get (root, key, NULL);
  2009. hard_assert (item);
  2010. if (item->type == CONFIG_ITEM_NULL)
  2011. return NULL;
  2012. hard_assert (config_item_type_is_string (item->type));
  2013. return item->value.string.str;
  2014. }
  2015. static bool
  2016. set_config_string
  2017. (struct config_item *root, const char *key, const char *value)
  2018. {
  2019. struct config_item *item = config_item_get (root, key, NULL);
  2020. hard_assert (item);
  2021. struct config_item *new_ = config_item_string_from_cstr (value);
  2022. struct error *e = NULL;
  2023. if (config_item_set_from (item, new_, &e))
  2024. return true;
  2025. config_item_destroy (new_);
  2026. print_error ("couldn't set `%s' in configuration: %s", key, e->message);
  2027. error_free (e);
  2028. return false;
  2029. }
  2030. static int64_t
  2031. get_config_integer (struct config_item *root, const char *key)
  2032. {
  2033. struct config_item *item = config_item_get (root, key, NULL);
  2034. hard_assert (item && item->type == CONFIG_ITEM_INTEGER);
  2035. return item->value.integer;
  2036. }
  2037. static bool
  2038. get_config_boolean (struct config_item *root, const char *key)
  2039. {
  2040. struct config_item *item = config_item_get (root, key, NULL);
  2041. hard_assert (item && item->type == CONFIG_ITEM_BOOLEAN);
  2042. return item->value.boolean;
  2043. }
  2044. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2045. static struct str_map *
  2046. get_servers_config (struct app_context *ctx)
  2047. {
  2048. return &config_item_get (ctx->config.root, "servers", NULL)->value.object;
  2049. }
  2050. static struct str_map *
  2051. get_aliases_config (struct app_context *ctx)
  2052. {
  2053. return &config_item_get (ctx->config.root, "aliases", NULL)->value.object;
  2054. }
  2055. static struct str_map *
  2056. get_plugins_config (struct app_context *ctx)
  2057. {
  2058. return &config_item_get (ctx->config.root, "plugins", NULL)->value.object;
  2059. }
  2060. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2061. static void
  2062. serialize_configuration (struct config_item *root, struct str *output)
  2063. {
  2064. str_append (output,
  2065. "# " PROGRAM_NAME " " PROGRAM_VERSION " configuration file\n"
  2066. "#\n"
  2067. "# Relative paths are searched for in ${XDG_CONFIG_HOME:-~/.config}\n"
  2068. "# /" PROGRAM_NAME " as well as in $XDG_CONFIG_DIRS/" PROGRAM_NAME "\n"
  2069. "#\n"
  2070. "# Everything is in UTF-8. Any custom comments will be overwritten.\n"
  2071. "\n");
  2072. config_item_write (root, true, output);
  2073. }
  2074. // --- Terminal output ---------------------------------------------------------
  2075. /// Default color pair
  2076. #define COLOR_DEFAULT -1
  2077. /// Bright versions of the basic color set
  2078. #define COLOR_BRIGHT(x) (COLOR_ ## x + 8)
  2079. /// Builds a color pair for 256-color terminals with a 16-color backup value
  2080. #define COLOR_256(name, c256) \
  2081. (((COLOR_ ## name) & 0xFFFF) | (((c256) & 0xFFFF) << 16))
  2082. typedef int (*terminal_printer_fn) (int);
  2083. static int
  2084. putchar_stderr (int c)
  2085. {
  2086. return fputc (c, stderr);
  2087. }
  2088. static terminal_printer_fn
  2089. get_attribute_printer (FILE *stream)
  2090. {
  2091. if (stream == stdout && g_terminal.stdout_is_tty)
  2092. return putchar;
  2093. if (stream == stderr && g_terminal.stderr_is_tty)
  2094. return putchar_stderr;
  2095. return NULL;
  2096. }
  2097. static void
  2098. vprint_attributed (struct app_context *ctx,
  2099. FILE *stream, intptr_t attribute, const char *fmt, va_list ap)
  2100. {
  2101. terminal_printer_fn printer = get_attribute_printer (stream);
  2102. if (!attribute)
  2103. printer = NULL;
  2104. if (printer)
  2105. tputs (ctx->attrs[attribute], 1, printer);
  2106. vfprintf (stream, fmt, ap);
  2107. if (printer)
  2108. tputs (ctx->attrs[ATTR_RESET], 1, printer);
  2109. }
  2110. static void
  2111. print_attributed (struct app_context *ctx,
  2112. FILE *stream, intptr_t attribute, const char *fmt, ...)
  2113. {
  2114. va_list ap;
  2115. va_start (ap, fmt);
  2116. vprint_attributed (ctx, stream, attribute, fmt, ap);
  2117. va_end (ap);
  2118. }
  2119. static void
  2120. log_message_attributed (void *user_data, const char *quote, const char *fmt,
  2121. va_list ap)
  2122. {
  2123. FILE *stream = stderr;
  2124. struct app_context *ctx = g_ctx;
  2125. CALL (ctx->input, hide);
  2126. print_attributed (ctx, stream, (intptr_t) user_data, "%s", quote);
  2127. vprint_attributed (ctx, stream, (intptr_t) user_data, fmt, ap);
  2128. fputs ("\n", stream);
  2129. CALL (ctx->input, show);
  2130. }
  2131. static ssize_t
  2132. attr_by_name (const char *name)
  2133. {
  2134. static const char *table[ATTR_COUNT] =
  2135. {
  2136. #define XX(x, y, z) [ATTR_ ## x] = y,
  2137. ATTR_TABLE (XX)
  2138. #undef XX
  2139. };
  2140. for (size_t i = 0; i < N_ELEMENTS (table); i++)
  2141. if (!strcmp (name, table[i]))
  2142. return i;
  2143. return -1;
  2144. }
  2145. static void
  2146. on_config_attribute_change (struct config_item *item)
  2147. {
  2148. struct app_context *ctx = item->user_data;
  2149. ssize_t id = attr_by_name (item->schema->name);
  2150. if (id != -1)
  2151. {
  2152. free (ctx->attrs[id]);
  2153. ctx->attrs[id] = xstrdup (item->type == CONFIG_ITEM_NULL
  2154. ? ctx->attrs_defaults[id]
  2155. : item->value.string.str);
  2156. }
  2157. }
  2158. static void
  2159. init_colors (struct app_context *ctx)
  2160. {
  2161. bool have_ti = init_terminal ();
  2162. char **defaults = ctx->attrs_defaults;
  2163. #define INIT_ATTR(id, ti) defaults[ATTR_ ## id] = xstrdup (have_ti ? (ti) : "")
  2164. INIT_ATTR (PROMPT, enter_bold_mode);
  2165. INIT_ATTR (RESET, exit_attribute_mode);
  2166. INIT_ATTR (DATE_CHANGE, enter_bold_mode);
  2167. INIT_ATTR (READ_MARKER, g_terminal.color_set_fg[COLOR_MAGENTA]);
  2168. INIT_ATTR (WARNING, g_terminal.color_set_fg[COLOR_YELLOW]);
  2169. INIT_ATTR (ERROR, g_terminal.color_set_fg[COLOR_RED]);
  2170. INIT_ATTR (EXTERNAL, g_terminal.color_set_fg[COLOR_WHITE]);
  2171. INIT_ATTR (TIMESTAMP, g_terminal.color_set_fg[COLOR_WHITE]);
  2172. INIT_ATTR (ACTION, g_terminal.color_set_fg[COLOR_RED]);
  2173. INIT_ATTR (USERHOST, g_terminal.color_set_fg[COLOR_CYAN]);
  2174. INIT_ATTR (JOIN, g_terminal.color_set_fg[COLOR_GREEN]);
  2175. INIT_ATTR (PART, g_terminal.color_set_fg[COLOR_RED]);
  2176. char *highlight = have_ti ? xstrdup_printf ("%s%s%s",
  2177. g_terminal.color_set_fg[COLOR_YELLOW],
  2178. g_terminal.color_set_bg[COLOR_MAGENTA],
  2179. enter_bold_mode) : NULL;
  2180. INIT_ATTR (HIGHLIGHT, highlight);
  2181. free (highlight);
  2182. #undef INIT_ATTR
  2183. // This prevents formatters from obtaining an attribute printer function
  2184. if (!have_ti)
  2185. {
  2186. g_terminal.stdout_is_tty = false;
  2187. g_terminal.stderr_is_tty = false;
  2188. }
  2189. g_log_message_real = log_message_attributed;
  2190. // Apply the default values so that we start with any formatting at all
  2191. config_schema_call_changed
  2192. (config_item_get (ctx->config.root, "attributes", NULL));
  2193. }
  2194. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2195. // A little tool that tries to make the most of the terminal's capabilities
  2196. // to set up text attributes. It mostly targets just terminal emulators as that
  2197. // is what people are using these days. At least no stupid ncurses limits us
  2198. // with color pairs.
  2199. enum
  2200. {
  2201. ATTRIBUTE_BOLD = 1 << 0,
  2202. ATTRIBUTE_ITALIC = 1 << 1,
  2203. ATTRIBUTE_UNDERLINE = 1 << 2,
  2204. ATTRIBUTE_INVERSE = 1 << 3,
  2205. ATTRIBUTE_BLINK = 1 << 4
  2206. };
  2207. struct attribute_printer
  2208. {
  2209. struct app_context *ctx; ///< Application context
  2210. FILE *stream; ///< Output stream
  2211. bool dirty; ///< Attributes are set
  2212. int want; ///< Desired attributes
  2213. int want_foreground; ///< Desired foreground color
  2214. int want_background; ///< Desired background color
  2215. };
  2216. static void
  2217. attribute_printer_tputs (struct attribute_printer *self, const char *attr)
  2218. {
  2219. terminal_printer_fn printer = get_attribute_printer (self->stream);
  2220. if (printer)
  2221. tputs (attr, 1, printer);
  2222. else
  2223. // We shouldn't really do this but we need it to
  2224. // output formatting to the backlog
  2225. fputs (attr, self->stream);
  2226. }
  2227. static void
  2228. attribute_printer_reset (struct attribute_printer *self)
  2229. {
  2230. if (self->dirty)
  2231. attribute_printer_tputs (self, self->ctx->attrs[ATTR_RESET]);
  2232. self->dirty = false;
  2233. }
  2234. static void
  2235. attribute_printer_init (struct attribute_printer *self,
  2236. struct app_context *ctx, FILE *stream)
  2237. {
  2238. self->ctx = ctx;
  2239. self->stream = stream;
  2240. self->dirty = true;
  2241. self->want = 0;
  2242. self->want_foreground = -1;
  2243. self->want_background = -1;
  2244. }
  2245. static void
  2246. attribute_printer_apply (struct attribute_printer *self, int attribute)
  2247. {
  2248. attribute_printer_reset (self);
  2249. if (attribute != ATTR_RESET)
  2250. {
  2251. attribute_printer_tputs (self, self->ctx->attrs[attribute]);
  2252. self->dirty = true;
  2253. }
  2254. }
  2255. // NOTE: commonly terminals have:
  2256. // 8 colors (worst, bright fg with BOLD, bg sometimes with BLINK)
  2257. // 16 colors (okayish, we have the full basic range guaranteed)
  2258. // 88 colors (the same plus a 4^3 RGB cube and a few shades of gray)
  2259. // 256 colors (best, like above but with a larger cube and more gray)
  2260. /// Interpolate from the 256-color palette to the 88-color one
  2261. static int
  2262. attribute_printer_256_to_88 (int color)
  2263. {
  2264. // These colours are the same everywhere
  2265. if (color < 16)
  2266. return color;
  2267. // 24 -> 8 extra shades of gray
  2268. if (color >= 232)
  2269. return 80 + (color - 232) / 3;
  2270. // 6 * 6 * 6 cube -> 4 * 4 * 4 cube
  2271. int x[6] = { 0, 1, 1, 2, 2, 3 };
  2272. int index = color - 16;
  2273. return 16 +
  2274. ( x[ index / 36 ] << 8
  2275. | x[(index / 6) % 6 ] << 4
  2276. | x[(index % 6) ] );
  2277. }
  2278. static int
  2279. attribute_printer_decode_color (int color, bool *is_bright)
  2280. {
  2281. int16_t c16 = color; hard_assert (c16 < 16);
  2282. int16_t c256 = color >> 16; hard_assert (c256 < 256);
  2283. *is_bright = false;
  2284. switch (max_colors)
  2285. {
  2286. case 8:
  2287. if (c16 >= 8)
  2288. {
  2289. c16 -= 8;
  2290. *is_bright = true;
  2291. }
  2292. case 16:
  2293. return c16;
  2294. case 88:
  2295. return c256 <= 0 ? c16 : attribute_printer_256_to_88 (c256);
  2296. case 256:
  2297. return c256 <= 0 ? c16 : c256;
  2298. default:
  2299. // Unsupported palette
  2300. return -1;
  2301. }
  2302. }
  2303. static void
  2304. attribute_printer_update (struct attribute_printer *self)
  2305. {
  2306. bool fg_is_bright;
  2307. int fg = attribute_printer_decode_color
  2308. (self->want_foreground, &fg_is_bright);
  2309. bool bg_is_bright;
  2310. int bg = attribute_printer_decode_color
  2311. (self->want_background, &bg_is_bright);
  2312. int attributes = self->want;
  2313. bool have_inverse = !!(attributes & ATTRIBUTE_INVERSE);
  2314. if (have_inverse)
  2315. {
  2316. bool tmp = fg_is_bright;
  2317. fg_is_bright = bg_is_bright;
  2318. bg_is_bright = tmp;
  2319. }
  2320. // In 8 colour mode, some terminals don't support bright backgrounds.
  2321. // However, we can make use of the fact that the brightness change caused
  2322. // by the bold attribute is retained when inverting the colours.
  2323. // This has the downside of making the text bold when it's not supposed
  2324. // to be, and we still can't make both colours bright, so it's more of
  2325. // an interesting hack rather than anything else.
  2326. if (!fg_is_bright && bg_is_bright && have_inverse)
  2327. attributes |= ATTRIBUTE_BOLD;
  2328. else if (!fg_is_bright && bg_is_bright
  2329. && !have_inverse && fg >= 0 && bg >= 0)
  2330. {
  2331. // As long as none of the colours is the default, we can swap them
  2332. int tmp = fg; fg = bg; bg = tmp;
  2333. attributes |= ATTRIBUTE_BOLD | ATTRIBUTE_INVERSE;
  2334. }
  2335. else
  2336. {
  2337. // This is what works on normal, decent terminals
  2338. if (fg_is_bright) attributes |= ATTRIBUTE_BOLD;
  2339. if (bg_is_bright) attributes |= ATTRIBUTE_BLINK;
  2340. }
  2341. attribute_printer_reset (self);
  2342. if (attributes)
  2343. attribute_printer_tputs (self, tparm (set_attributes,
  2344. 0, // standout
  2345. attributes & ATTRIBUTE_UNDERLINE,
  2346. attributes & ATTRIBUTE_INVERSE,
  2347. attributes & ATTRIBUTE_BLINK,
  2348. 0, // dim
  2349. attributes & ATTRIBUTE_BOLD,
  2350. 0, // blank
  2351. 0, // protect
  2352. 0)); // acs
  2353. if (enter_italics_mode && (attributes & ATTRIBUTE_ITALIC))
  2354. attribute_printer_tputs (self, enter_italics_mode);
  2355. if (fg >= 0)
  2356. attribute_printer_tputs (self, g_terminal.color_set_fg[fg]);
  2357. if (bg >= 0)
  2358. attribute_printer_tputs (self, g_terminal.color_set_bg[bg]);
  2359. self->dirty = true;
  2360. }
  2361. // --- Helpers -----------------------------------------------------------------
  2362. static int
  2363. irc_server_strcmp (struct server *s, const char *a, const char *b)
  2364. {
  2365. int x;
  2366. while (*a || *b)
  2367. if ((x = s->irc_tolower (*a++) - s->irc_tolower (*b++)))
  2368. return x;
  2369. return 0;
  2370. }
  2371. static int
  2372. irc_server_strncmp (struct server *s, const char *a, const char *b, size_t n)
  2373. {
  2374. int x;
  2375. while (n-- && (*a || *b))
  2376. if ((x = s->irc_tolower (*a++) - s->irc_tolower (*b++)))
  2377. return x;
  2378. return 0;
  2379. }
  2380. static char *
  2381. irc_cut_nickname (const char *prefix)
  2382. {
  2383. return cstr_cut_until (prefix, "!@");
  2384. }
  2385. static const char *
  2386. irc_find_userhost (const char *prefix)
  2387. {
  2388. const char *p = strchr (prefix, '!');
  2389. return p ? p + 1 : NULL;
  2390. }
  2391. static bool
  2392. irc_is_this_us (struct server *s, const char *prefix)
  2393. {
  2394. // This shouldn't be called before successfully registering.
  2395. // Better safe than sorry, though.
  2396. if (!s->irc_user)
  2397. return false;
  2398. char *nick = irc_cut_nickname (prefix);
  2399. bool result = !irc_server_strcmp (s, nick, s->irc_user->nickname);
  2400. free (nick);
  2401. return result;
  2402. }
  2403. static bool
  2404. irc_is_channel (struct server *s, const char *ident)
  2405. {
  2406. return *ident
  2407. && (!!strchr (s->irc_chantypes, *ident) ||
  2408. !!strchr (s->irc_idchan_prefixes, *ident));
  2409. }
  2410. // Message targets can be prefixed by a character filtering their targets
  2411. static const char *
  2412. irc_skip_statusmsg (struct server *s, const char *target)
  2413. {
  2414. return target + (*target && strchr (s->irc_statusmsg, *target));
  2415. }
  2416. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2417. // As of 2015, everything should be in UTF-8. And if it's not, we'll decode it
  2418. // as ISO Latin 1. This function should not be called on the whole message.
  2419. static char *
  2420. irc_to_utf8 (const char *text)
  2421. {
  2422. if (!text)
  2423. return NULL;
  2424. size_t len = strlen (text) + 1;
  2425. if (utf8_validate (text, len))
  2426. return xstrdup (text);
  2427. // Windows 1252 redefines several silly C1 control characters as glyphs
  2428. static const char *c1[32] =
  2429. {
  2430. "\xe2\x82\xac", "\xc2\x81", "\xe2\x80\x9a", "\xc6\x92",
  2431. "\xe2\x80\x9e", "\xe2\x80\xa6", "\xe2\x80\xa0", "\xe2\x80\xa1",
  2432. "\xcb\x86", "\xe2\x80\xb0", "\xc5\xa0", "\xe2\x80\xb9",
  2433. "\xc5\x92", "\xc2\x8d", "\xc5\xbd", "\xc2\x8f",
  2434. "\xc2\x90", "\xe2\x80\x98", "\xe2\x80\x99", "\xe2\x80\x9c",
  2435. "\xe2\x80\x9d", "\xe2\x80\xa2", "\xe2\x80\x93", "\xe2\x80\x94",
  2436. "\xcb\x9c", "\xe2\x84\xa2", "\xc5\xa1", "\xe2\x80\xba",
  2437. "\xc5\x93", "\xc2\x9d", "\xc5\xbe", "\xc5\xb8",
  2438. };
  2439. struct str s;
  2440. str_init (&s);
  2441. for (const char *p = text; *p; p++)
  2442. {
  2443. int c = *(unsigned char *) p;
  2444. if (c < 0x80)
  2445. str_append_c (&s, c);
  2446. else if (c < 0xA0)
  2447. str_append (&s, c1[c & 0x1f]);
  2448. else
  2449. str_append_data (&s,
  2450. (char[]) {0xc0 | (c >> 6), 0x80 | (c & 0x3f)}, 2);
  2451. }
  2452. return str_steal (&s);
  2453. }
  2454. // This function is used to output debugging IRC traffic to the terminal.
  2455. // It's far from ideal, as any non-UTF-8 text degrades the entire line to
  2456. // ISO Latin 1. But it should work good enough most of the time.
  2457. static char *
  2458. irc_to_term (struct app_context *ctx, const char *text)
  2459. {
  2460. char *utf8 = irc_to_utf8 (text);
  2461. char *term = iconv_xstrdup (ctx->term_from_utf8, utf8, -1, NULL);
  2462. free (utf8);
  2463. return term;
  2464. }
  2465. // --- Output formatter --------------------------------------------------------
  2466. // This complicated piece of code makes attributed text formatting simple.
  2467. // We use a printf-inspired syntax to push attributes and text to the object,
  2468. // then flush it either to a terminal, or a log file with formatting stripped.
  2469. //
  2470. // Format strings use a #-quoted notation, to differentiate from printf:
  2471. // #s inserts a string (expected to be in UTF-8)
  2472. // #d inserts a signed integer
  2473. // #l inserts a locale-encoded string
  2474. //
  2475. // #S inserts a string from the server with unknown encoding
  2476. // #m inserts a mIRC-formatted string (auto-resets at boundaries)
  2477. // #n cuts the nickname from a string and automatically colours it
  2478. // #N is like #n but also appends userhost, if present
  2479. //
  2480. // #a inserts named attributes (auto-resets)
  2481. // #r resets terminal attributes
  2482. // #c sets foreground color
  2483. // #C sets background color
  2484. //
  2485. // Modifiers:
  2486. // & free() the string argument after using it
  2487. static void
  2488. formatter_add_item (struct formatter *self, struct formatter_item template_)
  2489. {
  2490. if (template_.text)
  2491. template_.text = xstrdup (template_.text);
  2492. if (self->items_len == self->items_alloc)
  2493. self->items = xreallocarray
  2494. (self->items, sizeof *self->items, (self->items_alloc <<= 1));
  2495. self->items[self->items_len++] = template_;
  2496. }
  2497. #define FORMATTER_ADD_ITEM(self, type_, ...) formatter_add_item ((self), \
  2498. (struct formatter_item) { .type = FORMATTER_ITEM_ ## type_, __VA_ARGS__ })
  2499. #define FORMATTER_ADD_RESET(self) \
  2500. FORMATTER_ADD_ITEM ((self), ATTR, .attribute = ATTR_RESET)
  2501. #define FORMATTER_ADD_TEXT(self, text_) \
  2502. FORMATTER_ADD_ITEM ((self), TEXT, .text = (text_))
  2503. #define FORMATTER_ADD_SIMPLE(self, attribute_) \
  2504. FORMATTER_ADD_ITEM ((self), SIMPLE, .attribute = ATTRIBUTE_ ## attribute_)
  2505. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2506. enum
  2507. {
  2508. MIRC_WHITE, MIRC_BLACK, MIRC_BLUE, MIRC_GREEN,
  2509. MIRC_L_RED, MIRC_RED, MIRC_PURPLE, MIRC_ORANGE,
  2510. MIRC_YELLOW, MIRC_L_GREEN, MIRC_CYAN, MIRC_L_CYAN,
  2511. MIRC_L_BLUE, MIRC_L_PURPLE, MIRC_GRAY, MIRC_L_GRAY,
  2512. };
  2513. // We use estimates from the 16 color terminal palette, or the 256 color cube,
  2514. // which is not always available. The mIRC orange colour is only in the cube.
  2515. static const int g_mirc_to_terminal[] =
  2516. {
  2517. [MIRC_WHITE] = COLOR_256 (BRIGHT (WHITE), 231),
  2518. [MIRC_BLACK] = COLOR_256 (BLACK, 16),
  2519. [MIRC_BLUE] = COLOR_256 (BLUE, 19),
  2520. [MIRC_GREEN] = COLOR_256 (GREEN, 34),
  2521. [MIRC_L_RED] = COLOR_256 (BRIGHT (RED), 196),
  2522. [MIRC_RED] = COLOR_256 (RED, 124),
  2523. [MIRC_PURPLE] = COLOR_256 (MAGENTA, 127),
  2524. [MIRC_ORANGE] = COLOR_256 (BRIGHT (YELLOW), 214),
  2525. [MIRC_YELLOW] = COLOR_256 (BRIGHT (YELLOW), 226),
  2526. [MIRC_L_GREEN] = COLOR_256 (BRIGHT (GREEN), 46),
  2527. [MIRC_CYAN] = COLOR_256 (CYAN, 37),
  2528. [MIRC_L_CYAN] = COLOR_256 (BRIGHT (CYAN), 51),
  2529. [MIRC_L_BLUE] = COLOR_256 (BRIGHT (BLUE), 21),
  2530. [MIRC_L_PURPLE] = COLOR_256 (BRIGHT (MAGENTA),201),
  2531. [MIRC_GRAY] = COLOR_256 (BRIGHT (BLACK), 244),
  2532. [MIRC_L_GRAY] = COLOR_256 (WHITE, 252),
  2533. };
  2534. static const char *
  2535. formatter_parse_mirc_color (struct formatter *self, const char *s)
  2536. {
  2537. if (!isdigit_ascii (*s))
  2538. {
  2539. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = -1);
  2540. FORMATTER_ADD_ITEM (self, BG_COLOR, .color = -1);
  2541. return s;
  2542. }
  2543. int fg = *s++ - '0';
  2544. if (isdigit_ascii (*s))
  2545. fg = fg * 10 + (*s++ - '0');
  2546. if (fg >= 0 && fg < 16)
  2547. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = g_mirc_to_terminal[fg]);
  2548. if (*s != ',' || !isdigit_ascii (s[1]))
  2549. return s;
  2550. s++;
  2551. int bg = *s++ - '0';
  2552. if (isdigit_ascii (*s))
  2553. bg = bg * 10 + (*s++ - '0');
  2554. if (bg >= 0 && bg < 16)
  2555. FORMATTER_ADD_ITEM (self, BG_COLOR, .color = g_mirc_to_terminal[bg]);
  2556. return s;
  2557. }
  2558. static void
  2559. formatter_parse_mirc (struct formatter *self, const char *s)
  2560. {
  2561. struct str buf;
  2562. str_init (&buf);
  2563. FORMATTER_ADD_RESET (self);
  2564. unsigned char c;
  2565. while ((c = *s++))
  2566. {
  2567. if (buf.len && c < 0x20)
  2568. {
  2569. FORMATTER_ADD_TEXT (self, buf.str);
  2570. str_reset (&buf);
  2571. }
  2572. switch (c)
  2573. {
  2574. case '\x02': FORMATTER_ADD_SIMPLE (self, BOLD); break;
  2575. case '\x1d': FORMATTER_ADD_SIMPLE (self, ITALIC); break;
  2576. case '\x1f': FORMATTER_ADD_SIMPLE (self, UNDERLINE); break;
  2577. case '\x16': FORMATTER_ADD_SIMPLE (self, INVERSE); break;
  2578. case '\x03':
  2579. s = formatter_parse_mirc_color (self, s);
  2580. break;
  2581. case '\x0f':
  2582. FORMATTER_ADD_RESET (self);
  2583. break;
  2584. default:
  2585. str_append_c (&buf, c);
  2586. }
  2587. }
  2588. if (buf.len)
  2589. FORMATTER_ADD_TEXT (self, buf.str);
  2590. str_free (&buf);
  2591. FORMATTER_ADD_RESET (self);
  2592. }
  2593. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2594. static void
  2595. formatter_parse_nick (struct formatter *self, char *s)
  2596. {
  2597. // For outgoing messages; maybe we should add a special #t for them
  2598. // which would also make us not cut off the userhost part, ever
  2599. if (irc_is_channel (self->s, irc_skip_statusmsg (self->s, s)))
  2600. {
  2601. char *tmp = irc_to_utf8 (s);
  2602. FORMATTER_ADD_TEXT (self, tmp);
  2603. free (tmp);
  2604. return;
  2605. }
  2606. char *nick = irc_cut_nickname (s);
  2607. int color = siphash_wrapper (nick, strlen (nick)) % 7;
  2608. // Never use the black colour, could become transparent on black terminals;
  2609. // white is similarly excluded from the range
  2610. if (color == COLOR_BLACK)
  2611. color = (uint16_t) -1;
  2612. // Use a color from the 256-color cube if available
  2613. color |= self->ctx->nick_palette[siphash_wrapper (nick,
  2614. strlen (nick)) % self->ctx->nick_palette_len] << 16;
  2615. // We always use the default color for ourselves
  2616. if (self->s && irc_is_this_us (self->s, nick))
  2617. color = -1;
  2618. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = color);
  2619. char *x = irc_to_utf8 (nick);
  2620. free (nick);
  2621. FORMATTER_ADD_TEXT (self, x);
  2622. free (x);
  2623. // Need to reset the color afterwards
  2624. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = -1);
  2625. }
  2626. static void
  2627. formatter_parse_nick_full (struct formatter *self, char *s)
  2628. {
  2629. formatter_parse_nick (self, s);
  2630. const char *userhost;
  2631. if (!(userhost = irc_find_userhost (s)))
  2632. return;
  2633. FORMATTER_ADD_TEXT (self, " (");
  2634. FORMATTER_ADD_ITEM (self, ATTR, .attribute = ATTR_USERHOST);
  2635. char *x = irc_to_utf8 (userhost);
  2636. FORMATTER_ADD_TEXT (self, x);
  2637. free (x);
  2638. FORMATTER_ADD_RESET (self);
  2639. FORMATTER_ADD_TEXT (self, ")");
  2640. }
  2641. static const char *
  2642. formatter_parse_field (struct formatter *self,
  2643. const char *field, struct str *buf, va_list *ap)
  2644. {
  2645. bool free_string = false;
  2646. char *s = NULL;
  2647. char *tmp = NULL;
  2648. int c;
  2649. restart:
  2650. switch ((c = *field++))
  2651. {
  2652. // We can push boring text content to the caller's buffer
  2653. // and let it flush the buffer only when it's actually needed
  2654. case 'd':
  2655. tmp = xstrdup_printf ("%d", va_arg (*ap, int));
  2656. str_append (buf, tmp);
  2657. free (tmp);
  2658. break;
  2659. case 's':
  2660. str_append (buf, (s = va_arg (*ap, char *)));
  2661. break;
  2662. case 'l':
  2663. if (!(tmp = iconv_xstrdup (self->ctx->term_to_utf8,
  2664. (s = va_arg (*ap, char *)), -1, NULL)))
  2665. print_error ("character conversion failed for: %s", "output");
  2666. else
  2667. str_append (buf, tmp);
  2668. free (tmp);
  2669. break;
  2670. case 'S':
  2671. tmp = irc_to_utf8 ((s = va_arg (*ap, char *)));
  2672. str_append (buf, tmp);
  2673. free (tmp);
  2674. break;
  2675. case 'm':
  2676. tmp = irc_to_utf8 ((s = va_arg (*ap, char *)));
  2677. formatter_parse_mirc (self, tmp);
  2678. free (tmp);
  2679. break;
  2680. case 'n':
  2681. formatter_parse_nick (self, (s = va_arg (*ap, char *)));
  2682. break;
  2683. case 'N':
  2684. formatter_parse_nick_full (self, (s = va_arg (*ap, char *)));
  2685. break;
  2686. case 'a':
  2687. FORMATTER_ADD_ITEM (self, ATTR, .attribute = va_arg (*ap, int));
  2688. break;
  2689. case 'c':
  2690. FORMATTER_ADD_ITEM (self, FG_COLOR, .color = va_arg (*ap, int));
  2691. break;
  2692. case 'C':
  2693. FORMATTER_ADD_ITEM (self, BG_COLOR, .color = va_arg (*ap, int));
  2694. break;
  2695. case 'r':
  2696. FORMATTER_ADD_RESET (self);
  2697. break;
  2698. default:
  2699. if (c == '&' && !free_string)
  2700. free_string = true;
  2701. else if (c)
  2702. hard_assert (!"unexpected format specifier");
  2703. else
  2704. hard_assert (!"unexpected end of format string");
  2705. goto restart;
  2706. }
  2707. if (free_string)
  2708. free (s);
  2709. return field;
  2710. }
  2711. // I was unable to take a pointer of a bare "va_list" when it was passed in
  2712. // as a function argument, so it has to be a pointer from the beginning
  2713. static void
  2714. formatter_addv (struct formatter *self, const char *format, va_list *ap)
  2715. {
  2716. struct str buf;
  2717. str_init (&buf);
  2718. while (*format)
  2719. {
  2720. if (*format != '#' || *++format == '#')
  2721. {
  2722. str_append_c (&buf, *format++);
  2723. continue;
  2724. }
  2725. if (buf.len)
  2726. {
  2727. FORMATTER_ADD_TEXT (self, buf.str);
  2728. str_reset (&buf);
  2729. }
  2730. format = formatter_parse_field (self, format, &buf, ap);
  2731. }
  2732. if (buf.len)
  2733. FORMATTER_ADD_TEXT (self, buf.str);
  2734. str_free (&buf);
  2735. }
  2736. static void
  2737. formatter_add (struct formatter *self, const char *format, ...)
  2738. {
  2739. va_list ap;
  2740. va_start (ap, format);
  2741. formatter_addv (self, format, &ap);
  2742. va_end (ap);
  2743. }
  2744. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2745. struct line_char_attrs
  2746. {
  2747. int named; ///< Named attribute or -1
  2748. int text; ///< Text attributes
  2749. int fg; ///< Foreground color (-1 for default)
  2750. int bg; ///< Background color (-1 for default)
  2751. };
  2752. struct line_char
  2753. {
  2754. LIST_HEADER (struct line_char)
  2755. char bytes[MB_LEN_MAX]; ///< The character
  2756. size_t len; ///< Length of the character in bytes
  2757. wchar_t wide; ///< The character as a wchar_t
  2758. int width; ///< Width of the character in cells
  2759. struct line_char_attrs attrs; ///< Attributes
  2760. };
  2761. static struct line_char *
  2762. line_char_new (const char *mb, size_t mb_len, wchar_t wc)
  2763. {
  2764. struct line_char *self = xcalloc (1, sizeof *self);
  2765. memcpy (self->bytes, mb, (self->len = MIN (mb_len, sizeof self->bytes)));
  2766. self->width = wcwidth ((self->wide = wc));
  2767. // Typically various control characters
  2768. if (self->width < 0)
  2769. self->width = 0;
  2770. self->attrs.bg = self->attrs.fg = -1;
  2771. self->attrs.named = ATTR_RESET;
  2772. return self;
  2773. }
  2774. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2775. struct line_wrap_mark
  2776. {
  2777. struct line_char *start; ///< First character
  2778. int used; ///< Display cells used
  2779. };
  2780. static void
  2781. line_wrap_mark_push (struct line_wrap_mark *mark, struct line_char *c)
  2782. {
  2783. if (!mark->start)
  2784. mark->start = c;
  2785. mark->used += c->width;
  2786. }
  2787. struct line_wrap_state
  2788. {
  2789. struct line_char *result; ///< Head of result
  2790. struct line_char *result_tail; ///< Tail of result
  2791. int line_used; ///< Line length before marks
  2792. int line_max; ///< Maximum line length
  2793. struct line_wrap_mark chunk; ///< All buffered text
  2794. struct line_wrap_mark overflow; ///< Overflowing text
  2795. };
  2796. static void
  2797. line_wrap_flush_split (struct line_wrap_state *s, struct line_wrap_mark *before)
  2798. {
  2799. struct line_char *nl = line_char_new ("\n", 1, L'\n');
  2800. LIST_INSERT_WITH_TAIL (s->result, s->result_tail, nl, before->start);
  2801. s->line_used = before->used;
  2802. }
  2803. static void
  2804. line_wrap_flush (struct line_wrap_state *s, bool force_split)
  2805. {
  2806. if (!s->overflow.start)
  2807. s->line_used += s->chunk.used;
  2808. else if (force_split || s->chunk.used > s->line_max)
  2809. {
  2810. #ifdef WRAP_UNNECESSARILY
  2811. // When the line wraps at the end of the screen and a background colour
  2812. // is set, the terminal paints the entire new line with that colour.
  2813. // Explicitly inserting a newline with the default attributes fixes it.
  2814. line_wrap_flush_split (s, &s->overflow);
  2815. #else
  2816. // Splitting here breaks link searching mechanisms in some terminals,
  2817. // though, so we make a trade-off and let the chunk wrap naturally.
  2818. // Fuck terminals, really.
  2819. s->line_used = s->overflow.used;
  2820. #endif
  2821. }
  2822. else
  2823. // Print the chunk in its entirety on a new line
  2824. line_wrap_flush_split (s, &s->chunk);
  2825. memset (&s->chunk, 0, sizeof s->chunk);
  2826. memset (&s->overflow, 0, sizeof s->overflow);
  2827. }
  2828. static void
  2829. line_wrap_nl (struct line_wrap_state *s)
  2830. {
  2831. line_wrap_flush (s, true);
  2832. struct line_char *nl = line_char_new ("\n", 1, L'\n');
  2833. LIST_APPEND_WITH_TAIL (s->result, s->result_tail, nl);
  2834. s->line_used = 0;
  2835. }
  2836. static void
  2837. line_wrap_tab (struct line_wrap_state *s, struct line_char *c)
  2838. {
  2839. line_wrap_flush (s, true);
  2840. if (s->line_used >= s->line_max)
  2841. line_wrap_nl (s);
  2842. // Compute the number of characters needed to get to the next tab stop
  2843. int tab_width = ((s->line_used + 8) & ~7) - s->line_used;
  2844. // On overflow just fill the rest of the line with spaces
  2845. if (s->line_used + tab_width > s->line_max)
  2846. tab_width = s->line_max - s->line_used;
  2847. s->line_used += tab_width;
  2848. while (tab_width--)
  2849. {
  2850. struct line_char *space = line_char_new (" ", 1, L' ');
  2851. space->attrs = c->attrs;
  2852. LIST_APPEND_WITH_TAIL (s->result, s->result_tail, space);
  2853. }
  2854. }
  2855. static void
  2856. line_wrap_push_char (struct line_wrap_state *s, struct line_char *c)
  2857. {
  2858. // Note that when processing whitespace here, any non-WS chunk has already
  2859. // been flushed, and thus it matters little if we flush with force split
  2860. if (wcschr (L"\r\f\v", c->wide))
  2861. /* Skip problematic characters */;
  2862. else if (c->wide == L'\n')
  2863. line_wrap_nl (s);
  2864. else if (c->wide == L'\t')
  2865. line_wrap_tab (s, c);
  2866. else
  2867. goto use_as_is;
  2868. free (c);
  2869. return;
  2870. use_as_is:
  2871. if (s->overflow.start
  2872. || s->line_used + s->chunk.used + c->width > s->line_max)
  2873. {
  2874. if (s->overflow.used + c->width > s->line_max)
  2875. {
  2876. #ifdef WRAP_UNNECESSARILY
  2877. // If the overflow overflows, restart on a new line
  2878. line_wrap_nl (s);
  2879. #else
  2880. // See line_wrap_flush(), we would end up on a new line anyway
  2881. line_wrap_flush (s, true);
  2882. s->line_used = 0;
  2883. #endif
  2884. }
  2885. else
  2886. line_wrap_mark_push (&s->overflow, c);
  2887. }
  2888. line_wrap_mark_push (&s->chunk, c);
  2889. LIST_APPEND_WITH_TAIL (s->result, s->result_tail, c);
  2890. }
  2891. /// Basic word wrapping that respects wcwidth(3) and expands tabs.
  2892. /// Besides making text easier to read, it also fixes the problem with
  2893. /// formatting spilling over the entire new line on line wrap.
  2894. static struct line_char *
  2895. line_wrap (struct line_char *line, int max_width)
  2896. {
  2897. struct line_wrap_state s = { .line_max = max_width };
  2898. bool last_was_word_char = false;
  2899. LIST_FOR_EACH (struct line_char, c, line)
  2900. {
  2901. // Act on the right boundary of (\s*\S+) chunks
  2902. bool this_is_word_char = !wcschr (L" \t\r\n\f\v", c->wide);
  2903. if (last_was_word_char && !this_is_word_char)
  2904. line_wrap_flush (&s, false);
  2905. last_was_word_char = this_is_word_char;
  2906. LIST_UNLINK (line, c);
  2907. line_wrap_push_char (&s, c);
  2908. }
  2909. // Make sure to process the last word and return the modified list
  2910. line_wrap_flush (&s, false);
  2911. return s.result;
  2912. }
  2913. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2914. struct exploder
  2915. {
  2916. struct app_context *ctx; ///< Application context
  2917. struct line_char *result; ///< Result
  2918. struct line_char *result_tail; ///< Tail of result
  2919. struct line_char_attrs attrs; ///< Current attributes
  2920. };
  2921. static bool
  2922. explode_formatter_attr (struct exploder *self, struct formatter_item *item)
  2923. {
  2924. switch (item->type)
  2925. {
  2926. case FORMATTER_ITEM_ATTR:
  2927. self->attrs.named = item->attribute;
  2928. self->attrs.text = 0;
  2929. self->attrs.fg = -1;
  2930. self->attrs.bg = -1;
  2931. return true;
  2932. case FORMATTER_ITEM_SIMPLE:
  2933. self->attrs.named = -1;
  2934. self->attrs.text ^= item->attribute;
  2935. return true;
  2936. case FORMATTER_ITEM_FG_COLOR:
  2937. self->attrs.named = -1;
  2938. self->attrs.fg = item->color;
  2939. return true;
  2940. case FORMATTER_ITEM_BG_COLOR:
  2941. self->attrs.named = -1;
  2942. self->attrs.bg = item->color;
  2943. return true;
  2944. default:
  2945. return false;
  2946. }
  2947. }
  2948. static void
  2949. explode_text (struct exploder *self, const char *text)
  2950. {
  2951. size_t term_len = 0;
  2952. char *term = iconv_xstrdup (self->ctx->term_from_utf8,
  2953. (char *) text, -1, &term_len);
  2954. mbstate_t ps;
  2955. memset (&ps, 0, sizeof ps);
  2956. wchar_t wch;
  2957. size_t len, processed = 0;
  2958. while ((len = mbrtowc (&wch, term + processed, term_len - processed, &ps)))
  2959. {
  2960. hard_assert (len != (size_t) -2 && len != (size_t) -1);
  2961. processed += len;
  2962. // Throw away any potentially harmful control characters
  2963. // XXX: this is likely to break shift state encodings
  2964. if (wcschr (L"\a\b\x1b", wch))
  2965. continue;
  2966. struct line_char *c = line_char_new (term + processed - len, len, wch);
  2967. c->attrs = self->attrs;
  2968. LIST_APPEND_WITH_TAIL (self->result, self->result_tail, c);
  2969. }
  2970. free (term);
  2971. }
  2972. static struct line_char *
  2973. formatter_to_chars (struct formatter *formatter)
  2974. {
  2975. struct exploder self = { .ctx = formatter->ctx };
  2976. self.attrs.fg = self.attrs.bg = self.attrs.named = -1;
  2977. int attribute_ignore = 0;
  2978. for (size_t i = 0; i < formatter->items_len; i++)
  2979. {
  2980. struct formatter_item *iter = &formatter->items[i];
  2981. if (iter->type == FORMATTER_ITEM_TEXT)
  2982. explode_text (&self, iter->text);
  2983. else if (iter->type == FORMATTER_ITEM_IGNORE_ATTR)
  2984. attribute_ignore += iter->attribute;
  2985. else if (attribute_ignore <= 0
  2986. && !explode_formatter_attr (&self, iter))
  2987. hard_assert (!"unhandled formatter item type");
  2988. }
  2989. return self.result;
  2990. }
  2991. enum
  2992. {
  2993. FLUSH_OPT_RAW = (1 << 0), ///< Print raw attributes
  2994. FLUSH_OPT_NOWRAP = (1 << 1) ///< Do not wrap
  2995. };
  2996. static void
  2997. formatter_flush (struct formatter *self, FILE *stream, int flush_opts)
  2998. {
  2999. struct line_char *line = formatter_to_chars (self);
  3000. bool is_tty = !!get_attribute_printer (stream);
  3001. if (!is_tty && !(flush_opts & FLUSH_OPT_RAW))
  3002. {
  3003. LIST_FOR_EACH (struct line_char, c, line)
  3004. {
  3005. fwrite (c->bytes, c->len, 1, stream);
  3006. free (c);
  3007. }
  3008. return;
  3009. }
  3010. if (self->ctx->word_wrapping && !(flush_opts & FLUSH_OPT_NOWRAP))
  3011. line = line_wrap (line, g_terminal.columns);
  3012. // TODO: rewrite the sloppily hacked mess around attribute_printer;
  3013. // so far I just didn't want to break everything at once
  3014. struct attribute_printer state;
  3015. attribute_printer_init (&state, self->ctx, stream);
  3016. attribute_printer_reset (&state);
  3017. struct line_char_attrs attrs =
  3018. { .fg = -1, .bg = -1, .named = ATTR_RESET, .text = 0 };
  3019. LIST_FOR_EACH (struct line_char, c, line)
  3020. {
  3021. if (attrs.fg != c->attrs.fg
  3022. || attrs.bg != c->attrs.bg
  3023. || attrs.named != c->attrs.named
  3024. || attrs.text != c->attrs.text)
  3025. {
  3026. if (c->attrs.named != -1)
  3027. attribute_printer_apply (&state, c->attrs.named);
  3028. else
  3029. {
  3030. state.want = c->attrs.text;
  3031. state.want_foreground = c->attrs.fg;
  3032. state.want_background = c->attrs.bg;
  3033. attribute_printer_reset (&state);
  3034. attribute_printer_update (&state);
  3035. }
  3036. attrs = c->attrs;
  3037. }
  3038. fwrite (c->bytes, c->len, 1, stream);
  3039. free (c);
  3040. }
  3041. attribute_printer_reset (&state);
  3042. }
  3043. // --- Buffers -----------------------------------------------------------------
  3044. static void
  3045. buffer_pop_excess_lines (struct app_context *ctx, struct buffer *self)
  3046. {
  3047. int to_delete = (int) self->lines_count - (int) ctx->backlog_limit;
  3048. while (to_delete-- > 0 && self->lines)
  3049. {
  3050. struct buffer_line *excess = self->lines;
  3051. LIST_UNLINK_WITH_TAIL (self->lines, self->lines_tail, excess);
  3052. buffer_line_destroy (excess);
  3053. self->lines_count--;
  3054. }
  3055. }
  3056. static void
  3057. on_config_backlog_limit_change (struct config_item *item)
  3058. {
  3059. struct app_context *ctx = item->user_data;
  3060. ctx->backlog_limit = MIN (item->value.integer, INT_MAX);
  3061. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  3062. buffer_pop_excess_lines (ctx, iter);
  3063. }
  3064. static void
  3065. buffer_update_time (struct app_context *ctx, time_t now, FILE *stream,
  3066. int flush_opts)
  3067. {
  3068. struct tm last, current;
  3069. if (!localtime_r (&ctx->last_displayed_msg_time, &last)
  3070. || !localtime_r (&now, &current))
  3071. {
  3072. // Strange but nonfatal
  3073. print_error ("%s: %s", "localtime_r", strerror (errno));
  3074. return;
  3075. }
  3076. ctx->last_displayed_msg_time = now;
  3077. if (last.tm_year == current.tm_year
  3078. && last.tm_mon == current.tm_mon
  3079. && last.tm_mday == current.tm_mday)
  3080. return;
  3081. char buf[64] = "";
  3082. const char *format =
  3083. get_config_string (ctx->config.root, "behaviour.date_change_line");
  3084. if (!strftime (buf, sizeof buf, format, &current))
  3085. {
  3086. print_error ("%s: %s", "strftime", strerror (errno));
  3087. return;
  3088. }
  3089. struct formatter f;
  3090. formatter_init (&f, ctx, NULL);
  3091. formatter_add (&f, "#a#s\n", ATTR_DATE_CHANGE, buf);
  3092. formatter_flush (&f, stream, flush_opts);
  3093. // Flush the trailing formatting reset item
  3094. fflush (stream);
  3095. formatter_free (&f);
  3096. }
  3097. static void
  3098. buffer_line_flush (struct buffer_line *line, struct formatter *f, FILE *output,
  3099. int flush_opts)
  3100. {
  3101. int flags = line->flags;
  3102. if (flags & BUFFER_LINE_INDENT) formatter_add (f, " ");
  3103. if (flags & BUFFER_LINE_STATUS) formatter_add (f, " - ");
  3104. if (flags & BUFFER_LINE_ERROR) formatter_add (f, "#a=!=#r ", ATTR_ERROR);
  3105. for (struct formatter_item *iter = line->items; iter->type; iter++)
  3106. formatter_add_item (f, *iter);
  3107. formatter_add (f, "\n");
  3108. formatter_flush (f, output, flush_opts);
  3109. formatter_free (f);
  3110. }
  3111. static void
  3112. buffer_line_write_time (struct formatter *f, struct buffer_line *line,
  3113. FILE *stream, int flush_opts)
  3114. {
  3115. // Normal timestamps don't include the date, this way the user won't be
  3116. // confused as to when an event has happened
  3117. buffer_update_time (f->ctx, line->when, stream, flush_opts);
  3118. struct tm current;
  3119. char buf[9];
  3120. if (!localtime_r (&line->when, &current))
  3121. print_error ("%s: %s", "localtime_r", strerror (errno));
  3122. else if (!strftime (buf, sizeof buf, "%T", &current))
  3123. print_error ("%s: %s", "strftime", "buffer too small");
  3124. else
  3125. formatter_add (f, "#a#s#r ", ATTR_TIMESTAMP, buf);
  3126. }
  3127. static void
  3128. buffer_line_display (struct app_context *ctx,
  3129. struct buffer_line *line, bool is_external)
  3130. {
  3131. CALL (ctx->input, hide);
  3132. struct formatter f;
  3133. formatter_init (&f, ctx, NULL);
  3134. buffer_line_write_time (&f, line, stdout, 0);
  3135. // Ignore all formatting for messages coming from other buffers, that is
  3136. // either from the global or server buffer. Instead print them in grey.
  3137. if (is_external)
  3138. {
  3139. formatter_add (&f, "#a", ATTR_EXTERNAL);
  3140. FORMATTER_ADD_ITEM (&f, IGNORE_ATTR, .attribute = 1);
  3141. }
  3142. buffer_line_flush (line, &f, stdout, 0);
  3143. // Flush the trailing formatting reset item
  3144. fflush (stdout);
  3145. CALL (ctx->input, show);
  3146. }
  3147. static void
  3148. buffer_line_write_to_backlog (struct app_context *ctx,
  3149. struct buffer_line *line, FILE *log_file, int flush_opts)
  3150. {
  3151. struct formatter f;
  3152. formatter_init (&f, ctx, NULL);
  3153. buffer_line_write_time (&f, line, log_file, flush_opts);
  3154. buffer_line_flush (line, &f, log_file, flush_opts);
  3155. }
  3156. static void
  3157. buffer_line_write_to_log (struct app_context *ctx,
  3158. struct buffer_line *line, FILE *log_file)
  3159. {
  3160. if (line->flags & BUFFER_LINE_SKIP_FILE)
  3161. return;
  3162. struct formatter f;
  3163. formatter_init (&f, ctx, NULL);
  3164. struct tm current;
  3165. char buf[20];
  3166. if (!gmtime_r (&line->when, &current))
  3167. print_error ("%s: %s", "gmtime_r", strerror (errno));
  3168. else if (!strftime (buf, sizeof buf, "%F %T", &current))
  3169. print_error ("%s: %s", "strftime", "buffer too small");
  3170. else
  3171. formatter_add (&f, "#s ", buf);
  3172. // The target is not a terminal, thus it won't wrap in spite of the 0
  3173. buffer_line_flush (line, &f, log_file, 0);
  3174. }
  3175. static void
  3176. log_formatter (struct app_context *ctx,
  3177. struct buffer *buffer, int flags, struct formatter *f)
  3178. {
  3179. if (!buffer)
  3180. buffer = ctx->global_buffer;
  3181. struct buffer_line *line = buffer_line_new (f);
  3182. line->flags = flags;
  3183. // TODO: allow providing custom time (IRCv3.2 server-time)
  3184. line->when = time (NULL);
  3185. buffer_pop_excess_lines (ctx, buffer);
  3186. LIST_APPEND_WITH_TAIL (buffer->lines, buffer->lines_tail, line);
  3187. buffer->lines_count++;
  3188. if (buffer->log_file)
  3189. buffer_line_write_to_log (ctx, line, buffer->log_file);
  3190. bool unseen_pm = buffer->type == BUFFER_PM
  3191. && buffer != ctx->current_buffer
  3192. && !(flags & BUFFER_LINE_UNIMPORTANT);
  3193. bool important = (flags & BUFFER_LINE_HIGHLIGHT) || unseen_pm;
  3194. if (ctx->beep_on_highlight && important)
  3195. CALL (ctx->input, ding);
  3196. bool can_leak = false;
  3197. if ((buffer == ctx->global_buffer)
  3198. || (ctx->current_buffer->type == BUFFER_GLOBAL
  3199. && buffer->type == BUFFER_SERVER)
  3200. || (ctx->current_buffer->type != BUFFER_GLOBAL
  3201. && buffer == ctx->current_buffer->server->buffer))
  3202. can_leak = true;
  3203. bool displayed = true;
  3204. if (ctx->terminal_suspended > 0)
  3205. // Another process is using the terminal
  3206. displayed = false;
  3207. else if (buffer == ctx->current_buffer)
  3208. buffer_line_display (ctx, line, false);
  3209. else if (!ctx->isolate_buffers && can_leak)
  3210. buffer_line_display (ctx, line, true);
  3211. else
  3212. displayed = false;
  3213. // Advance the unread marker in active buffers but don't create a new one
  3214. if (!displayed
  3215. || (buffer == ctx->current_buffer && buffer->new_messages_count))
  3216. {
  3217. buffer->new_messages_count++;
  3218. if (flags & BUFFER_LINE_UNIMPORTANT)
  3219. buffer->new_unimportant_count++;
  3220. buffer->highlighted |= important;
  3221. }
  3222. if (!displayed)
  3223. refresh_prompt (ctx);
  3224. }
  3225. static void
  3226. log_full (struct app_context *ctx, struct server *s, struct buffer *buffer,
  3227. int flags, const char *format, ...)
  3228. {
  3229. va_list ap;
  3230. va_start (ap, format);
  3231. struct formatter f;
  3232. formatter_init (&f, ctx, s);
  3233. formatter_addv (&f, format, &ap);
  3234. log_formatter (ctx, buffer, flags, &f);
  3235. va_end (ap);
  3236. }
  3237. #define log_global(ctx, flags, ...) \
  3238. log_full ((ctx), NULL, (ctx)->global_buffer, flags, __VA_ARGS__)
  3239. #define log_server(s, buffer, flags, ...) \
  3240. log_full ((s)->ctx, s, (buffer), flags, __VA_ARGS__)
  3241. #define log_global_status(ctx, ...) \
  3242. log_global ((ctx), BUFFER_LINE_STATUS, __VA_ARGS__)
  3243. #define log_global_error(ctx, ...) \
  3244. log_global ((ctx), BUFFER_LINE_ERROR, __VA_ARGS__)
  3245. #define log_global_indent(ctx, ...) \
  3246. log_global ((ctx), BUFFER_LINE_INDENT, __VA_ARGS__)
  3247. #define log_server_status(s, buffer, ...) \
  3248. log_server ((s), (buffer), BUFFER_LINE_STATUS, __VA_ARGS__)
  3249. #define log_server_error(s, buffer, ...) \
  3250. log_server ((s), (buffer), BUFFER_LINE_ERROR, __VA_ARGS__)
  3251. #define log_global_debug(ctx, ...) \
  3252. BLOCK_START \
  3253. if (g_debug_mode) \
  3254. log_global ((ctx), 0, "(*) " __VA_ARGS__); \
  3255. BLOCK_END
  3256. #define log_server_debug(s, ...) \
  3257. BLOCK_START \
  3258. if (g_debug_mode) \
  3259. log_server ((s), (s)->buffer, 0, "(*) " __VA_ARGS__); \
  3260. BLOCK_END
  3261. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3262. // Lines that are used in more than one place
  3263. #define log_nick_self(s, buffer, new_) \
  3264. log_server ((s), (buffer), BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT, \
  3265. "You are now known as #n", (new_))
  3266. #define log_nick(s, buffer, old, new_) \
  3267. log_server ((s), (buffer), BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT, \
  3268. "#n is now known as #n", (old), (new_))
  3269. #define log_outcoming_notice(s, buffer, who, text) \
  3270. log_server_status ((s), (buffer), "#s(#n): #m", "Notice", (who), (text))
  3271. #define log_outcoming_privmsg(s, buffer, prefixes, who, text) \
  3272. log_server ((s), (buffer), 0, "<#s#n> #m", (prefixes), (who), (text))
  3273. #define log_outcoming_action(s, buffer, who, text) \
  3274. log_server ((s), (buffer), 0, " #a*#r #n #m", ATTR_ACTION, (who), (text))
  3275. #define log_outcoming_orphan_notice(s, target, text) \
  3276. log_server_status ((s), (s)->buffer, "Notice -> #n: #m", (target), (text))
  3277. #define log_outcoming_orphan_privmsg(s, target, text) \
  3278. log_server_status ((s), (s)->buffer, "MSG(#n): #m", (target), (text))
  3279. #define log_ctcp_query(s, target, tag) \
  3280. log_server_status ((s), (s)->buffer, "CTCP query to #S: #S", target, tag)
  3281. #define log_ctcp_reply(s, target, reply /* freed! */) \
  3282. log_server_status ((s), (s)->buffer, "CTCP reply to #S: #&S", target, reply)
  3283. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3284. static void
  3285. make_log_filename (const char *filename, struct str *output)
  3286. {
  3287. for (const char *p = filename; *p; p++)
  3288. // XXX: anything more to replace?
  3289. if (strchr ("/\\ ", *p))
  3290. str_append_c (output, '_');
  3291. else
  3292. str_append_c (output, tolower_ascii (*p));
  3293. }
  3294. static char *
  3295. buffer_get_log_path (struct buffer *buffer)
  3296. {
  3297. struct str path;
  3298. str_init (&path);
  3299. get_xdg_home_dir (&path, "XDG_DATA_HOME", ".local/share");
  3300. str_append_printf (&path, "/%s/%s", PROGRAM_NAME, "logs");
  3301. (void) mkdir_with_parents (path.str, NULL);
  3302. // TODO: make sure global and server buffers don't collide with filenames
  3303. str_append_c (&path, '/');
  3304. make_log_filename (buffer->name, &path);
  3305. str_append (&path, ".log");
  3306. return str_steal (&path);
  3307. }
  3308. static void
  3309. buffer_open_log_file (struct app_context *ctx, struct buffer *buffer)
  3310. {
  3311. if (!ctx->logging || buffer->log_file)
  3312. return;
  3313. char *path = buffer_get_log_path (buffer);
  3314. if (!(buffer->log_file = fopen (path, "ab")))
  3315. log_global_error (ctx, "Couldn't open log file `#s': #l",
  3316. path, strerror (errno));
  3317. else
  3318. set_cloexec (fileno (buffer->log_file));
  3319. free (path);
  3320. }
  3321. static void
  3322. buffer_close_log_file (struct buffer *buffer)
  3323. {
  3324. if (buffer->log_file)
  3325. (void) fclose (buffer->log_file);
  3326. buffer->log_file = NULL;
  3327. }
  3328. static void
  3329. on_config_logging_change (struct config_item *item)
  3330. {
  3331. struct app_context *ctx = item->user_data;
  3332. ctx->logging = item->value.boolean;
  3333. for (struct buffer *buffer = ctx->buffers; buffer; buffer = buffer->next)
  3334. if (ctx->logging)
  3335. buffer_open_log_file (ctx, buffer);
  3336. else
  3337. buffer_close_log_file (buffer);
  3338. }
  3339. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3340. static struct buffer *
  3341. buffer_by_name (struct app_context *ctx, const char *name)
  3342. {
  3343. return str_map_find (&ctx->buffers_by_name, name);
  3344. }
  3345. static void
  3346. buffer_add (struct app_context *ctx, struct buffer *buffer)
  3347. {
  3348. hard_assert (!buffer_by_name (ctx, buffer->name));
  3349. str_map_set (&ctx->buffers_by_name, buffer->name, buffer);
  3350. LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
  3351. buffer_open_log_file (ctx, buffer);
  3352. // In theory this can't cause changes in the prompt
  3353. refresh_prompt (ctx);
  3354. }
  3355. static void
  3356. buffer_remove (struct app_context *ctx, struct buffer *buffer)
  3357. {
  3358. hard_assert (buffer != ctx->current_buffer);
  3359. hard_assert (buffer != ctx->global_buffer);
  3360. CALL_ (ctx->input, buffer_destroy, buffer->input_data);
  3361. buffer->input_data = NULL;
  3362. // And make sure to unlink the buffer from "irc_buffer_map"
  3363. struct server *s = buffer->server;
  3364. if (buffer->channel)
  3365. str_map_set (&s->irc_buffer_map, buffer->channel->name, NULL);
  3366. if (buffer->user)
  3367. str_map_set (&s->irc_buffer_map, buffer->user->nickname, NULL);
  3368. if (buffer == ctx->last_buffer)
  3369. ctx->last_buffer = NULL;
  3370. if (buffer->type == BUFFER_SERVER)
  3371. buffer->server->buffer = NULL;
  3372. str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
  3373. LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
  3374. buffer_unref (buffer);
  3375. refresh_prompt (ctx);
  3376. }
  3377. static void
  3378. buffer_print_read_marker (struct app_context *ctx, FILE *stream, int flush_opts)
  3379. {
  3380. struct formatter f;
  3381. formatter_init (&f, ctx, NULL);
  3382. formatter_add (&f, "#a-- -- -- ---\n", ATTR_READ_MARKER);
  3383. formatter_flush (&f, stream, flush_opts);
  3384. // Flush the trailing formatting reset item
  3385. fflush (stream);
  3386. formatter_free (&f);
  3387. }
  3388. static void
  3389. buffer_print_backlog (struct app_context *ctx, struct buffer *buffer)
  3390. {
  3391. // The prompt can take considerable time to redraw
  3392. CALL (ctx->input, hide);
  3393. // That is, minus the readline prompt
  3394. int display_limit = MAX (10, g_terminal.lines - 1);
  3395. // Simulate curses-like fullscreen buffers if the terminal allows it
  3396. if (g_terminal.initialized && clear_screen)
  3397. {
  3398. terminal_printer_fn printer = get_attribute_printer (stdout);
  3399. tputs (clear_screen, 1, printer);
  3400. if (cursor_to_ll)
  3401. tputs (cursor_to_ll, 1, printer);
  3402. else if (row_address)
  3403. tputs (tparm (row_address, g_terminal.lines - 1,
  3404. 0, 0, 0, 0, 0, 0, 0, 0), 1, printer);
  3405. else if (cursor_address)
  3406. tputs (tparm (cursor_address, g_terminal.lines - 1,
  3407. 0, 0, 0, 0, 0, 0, 0, 0), 1, printer);
  3408. fflush (stdout);
  3409. // We should update "last_displayed_msg_time" here just to be sure
  3410. // that the first date marker, if necessary, is shown, but in practice
  3411. // the value should always be from today when this function is called
  3412. }
  3413. else
  3414. {
  3415. char *buffer_name_localized =
  3416. iconv_xstrdup (ctx->term_from_utf8, buffer->name, -1, NULL);
  3417. print_status ("%s", buffer_name_localized);
  3418. free (buffer_name_localized);
  3419. }
  3420. struct buffer_line *line = buffer->lines_tail;
  3421. int to_display = line != NULL;
  3422. for (; line && line->prev && --display_limit > 0; line = line->prev)
  3423. to_display++;
  3424. // Once we've found where we want to start with the backlog, print it
  3425. int until_marker = to_display - (int) buffer->new_messages_count;
  3426. for (; line; line = line->next)
  3427. {
  3428. if (until_marker-- == 0
  3429. && buffer->new_messages_count != buffer->lines_count)
  3430. buffer_print_read_marker (ctx, stdout, 0);
  3431. buffer_line_display (ctx, line, 0);
  3432. }
  3433. // So that it is obvious if the last line in the buffer is not from today
  3434. buffer_update_time (ctx, time (NULL), stdout, 0);
  3435. refresh_prompt (ctx);
  3436. CALL (ctx->input, show);
  3437. }
  3438. static void
  3439. buffer_activate (struct app_context *ctx, struct buffer *buffer)
  3440. {
  3441. if (ctx->current_buffer == buffer)
  3442. return;
  3443. if (ctx->current_buffer)
  3444. {
  3445. ctx->current_buffer->new_messages_count = 0;
  3446. ctx->current_buffer->new_unimportant_count = 0;
  3447. ctx->current_buffer->highlighted = false;
  3448. }
  3449. buffer_print_backlog (ctx, buffer);
  3450. CALL_ (ctx->input, buffer_switch, buffer->input_data);
  3451. // Now at last we can switch the pointers
  3452. ctx->last_buffer = ctx->current_buffer;
  3453. ctx->current_buffer = buffer;
  3454. refresh_prompt (ctx);
  3455. }
  3456. static void
  3457. buffer_merge (struct app_context *ctx,
  3458. struct buffer *buffer, struct buffer *merged)
  3459. {
  3460. // XXX: anything better to do? This situation is arguably rare and I'm
  3461. // not entirely sure what action to take.
  3462. log_full (ctx, NULL, buffer, BUFFER_LINE_STATUS,
  3463. "Buffer #s was merged into this buffer", merged->name);
  3464. // Find all lines from "merged" newer than the newest line in "buffer"
  3465. struct buffer_line *start = merged->lines;
  3466. if (buffer->lines_tail)
  3467. while (start && start->when < buffer->lines_tail->when)
  3468. start = start->next;
  3469. if (!start)
  3470. return;
  3471. // Count how many of them we have
  3472. size_t n = 0;
  3473. for (struct buffer_line *iter = start; iter; iter = iter->next)
  3474. n++;
  3475. struct buffer_line *tail = merged->lines_tail;
  3476. // Cut them from the original buffer
  3477. if (start == merged->lines)
  3478. merged->lines = NULL;
  3479. else if (start->prev)
  3480. start->prev->next = NULL;
  3481. if (start == merged->lines_tail)
  3482. merged->lines_tail = start->prev;
  3483. merged->lines_count -= n;
  3484. // And append them to current lines in the buffer
  3485. buffer->lines_tail->next = start;
  3486. start->prev = buffer->lines_tail;
  3487. buffer->lines_tail = tail;
  3488. buffer->lines_count += n;
  3489. log_full (ctx, NULL, buffer, BUFFER_LINE_STATUS | BUFFER_LINE_SKIP_FILE,
  3490. "End of merged content");
  3491. }
  3492. static void
  3493. buffer_rename (struct app_context *ctx,
  3494. struct buffer *buffer, const char *new_name)
  3495. {
  3496. struct buffer *collision = str_map_find (&ctx->buffers_by_name, new_name);
  3497. if (collision == buffer)
  3498. return;
  3499. hard_assert (!collision);
  3500. str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
  3501. str_map_set (&ctx->buffers_by_name, new_name, buffer);
  3502. buffer_close_log_file (buffer);
  3503. buffer_open_log_file (ctx, buffer);
  3504. free (buffer->name);
  3505. buffer->name = xstrdup (new_name);
  3506. // We might have renamed the current buffer
  3507. refresh_prompt (ctx);
  3508. }
  3509. static void
  3510. buffer_clear (struct buffer *buffer)
  3511. {
  3512. LIST_FOR_EACH (struct buffer_line, iter, buffer->lines)
  3513. buffer_line_destroy (iter);
  3514. buffer->lines = buffer->lines_tail = NULL;
  3515. buffer->lines_count = 0;
  3516. }
  3517. static struct buffer *
  3518. buffer_at_index (struct app_context *ctx, int n)
  3519. {
  3520. int i = 0;
  3521. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  3522. if (++i == n)
  3523. return iter;
  3524. return NULL;
  3525. }
  3526. static struct buffer *
  3527. buffer_next (struct app_context *ctx, int count)
  3528. {
  3529. struct buffer *new_buffer = ctx->current_buffer;
  3530. while (count-- > 0)
  3531. if (!(new_buffer = new_buffer->next))
  3532. new_buffer = ctx->buffers;
  3533. return new_buffer;
  3534. }
  3535. static struct buffer *
  3536. buffer_previous (struct app_context *ctx, int count)
  3537. {
  3538. struct buffer *new_buffer = ctx->current_buffer;
  3539. while (count-- > 0)
  3540. if (!(new_buffer = new_buffer->prev))
  3541. new_buffer = ctx->buffers_tail;
  3542. return new_buffer;
  3543. }
  3544. static bool
  3545. buffer_goto (struct app_context *ctx, int n)
  3546. {
  3547. struct buffer *buffer = buffer_at_index (ctx, n);
  3548. if (!buffer)
  3549. return false;
  3550. buffer_activate (ctx, buffer);
  3551. return true;
  3552. }
  3553. static int
  3554. buffer_count (struct app_context *ctx)
  3555. {
  3556. int total = 0;
  3557. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  3558. total++;
  3559. return total;
  3560. }
  3561. static void
  3562. buffer_move (struct app_context *ctx, struct buffer *buffer, int n)
  3563. {
  3564. hard_assert (n >= 1 && n <= buffer_count (ctx));
  3565. LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
  3566. struct buffer *following = ctx->buffers;
  3567. while (--n && following)
  3568. following = following->next;
  3569. LIST_INSERT_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer, following);
  3570. refresh_prompt (ctx);
  3571. }
  3572. static int
  3573. buffer_get_index (struct app_context *ctx, struct buffer *buffer)
  3574. {
  3575. int index = 1;
  3576. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  3577. {
  3578. if (iter == buffer)
  3579. return index;
  3580. index++;
  3581. }
  3582. return -1;
  3583. }
  3584. static void
  3585. buffer_remove_safe (struct app_context *ctx, struct buffer *buffer)
  3586. {
  3587. if (buffer == ctx->current_buffer)
  3588. buffer_activate (ctx, ctx->last_buffer
  3589. ? ctx->last_buffer
  3590. : buffer_next (ctx, 1));
  3591. buffer_remove (ctx, buffer);
  3592. }
  3593. static void
  3594. init_global_buffer (struct app_context *ctx)
  3595. {
  3596. struct buffer *global = ctx->global_buffer = buffer_new (ctx->input);
  3597. global->type = BUFFER_GLOBAL;
  3598. global->name = xstrdup (PROGRAM_NAME);
  3599. buffer_add (ctx, global);
  3600. buffer_activate (ctx, global);
  3601. }
  3602. // --- Users, channels ---------------------------------------------------------
  3603. static void
  3604. irc_user_on_destroy (void *object, void *user_data)
  3605. {
  3606. struct user *user = object;
  3607. struct server *s = user_data;
  3608. if (!s->rehashing)
  3609. str_map_set (&s->irc_users, user->nickname, NULL);
  3610. }
  3611. static struct user *
  3612. irc_make_user (struct server *s, char *nickname)
  3613. {
  3614. hard_assert (!str_map_find (&s->irc_users, nickname));
  3615. struct user *user = user_new ();
  3616. (void) user_weak_ref (user, irc_user_on_destroy, s);
  3617. user->nickname = nickname;
  3618. str_map_set (&s->irc_users, user->nickname, user);
  3619. return user;
  3620. }
  3621. struct user *
  3622. irc_get_or_make_user (struct server *s, const char *nickname)
  3623. {
  3624. struct user *user = str_map_find (&s->irc_users, nickname);
  3625. if (user)
  3626. return user_ref (user);
  3627. return irc_make_user (s, xstrdup (nickname));
  3628. }
  3629. static struct buffer *
  3630. irc_get_or_make_user_buffer (struct server *s, const char *nickname)
  3631. {
  3632. struct buffer *buffer = str_map_find (&s->irc_buffer_map, nickname);
  3633. if (buffer)
  3634. return buffer;
  3635. struct user *user = irc_get_or_make_user (s, nickname);
  3636. // Open a new buffer for the user
  3637. buffer = buffer_new (s->ctx->input);
  3638. buffer->type = BUFFER_PM;
  3639. buffer->name = xstrdup_printf ("%s.%s", s->name, nickname);
  3640. buffer->server = s;
  3641. buffer->user = user;
  3642. str_map_set (&s->irc_buffer_map, user->nickname, buffer);
  3643. buffer_add (s->ctx, buffer);
  3644. return buffer;
  3645. }
  3646. static void
  3647. irc_get_channel_user_prefix (struct server *s,
  3648. struct channel_user *channel_user, struct str *output)
  3649. {
  3650. if (s->ctx->show_all_prefixes)
  3651. str_append (output, channel_user->prefixes.str);
  3652. else if (channel_user->prefixes.len)
  3653. str_append_c (output, channel_user->prefixes.str[0]);
  3654. }
  3655. static bool
  3656. irc_channel_is_joined (struct channel *channel)
  3657. {
  3658. // TODO: find a better way of checking if we're on a channel
  3659. return !!channel->users;
  3660. }
  3661. // Note that this eats the user reference
  3662. static void
  3663. irc_channel_link_user (struct channel *channel, struct user *user,
  3664. const char *prefixes)
  3665. {
  3666. struct user_channel *user_channel = user_channel_new ();
  3667. user_channel->channel = channel;
  3668. LIST_PREPEND (user->channels, user_channel);
  3669. struct channel_user *channel_user = channel_user_new ();
  3670. channel_user->user = user;
  3671. str_append (&channel_user->prefixes, prefixes);
  3672. LIST_PREPEND (channel->users, channel_user);
  3673. }
  3674. static void
  3675. irc_channel_unlink_user
  3676. (struct channel *channel, struct channel_user *channel_user)
  3677. {
  3678. // First destroy the user's weak references to the channel
  3679. struct user *user = channel_user->user;
  3680. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  3681. if (iter->channel == channel)
  3682. {
  3683. LIST_UNLINK (user->channels, iter);
  3684. user_channel_destroy (iter);
  3685. }
  3686. // Then just unlink the user from the channel
  3687. LIST_UNLINK (channel->users, channel_user);
  3688. channel_user_destroy (channel_user);
  3689. }
  3690. static void
  3691. irc_channel_on_destroy (void *object, void *user_data)
  3692. {
  3693. struct channel *channel = object;
  3694. struct server *s = user_data;
  3695. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3696. irc_channel_unlink_user (channel, iter);
  3697. if (!s->rehashing)
  3698. str_map_set (&s->irc_channels, channel->name, NULL);
  3699. }
  3700. static struct channel *
  3701. irc_make_channel (struct server *s, char *name)
  3702. {
  3703. hard_assert (!str_map_find (&s->irc_channels, name));
  3704. struct channel *channel = channel_new ();
  3705. (void) channel_weak_ref (channel, irc_channel_on_destroy, s);
  3706. channel->name = name;
  3707. channel->topic = NULL;
  3708. str_map_set (&s->irc_channels, channel->name, channel);
  3709. return channel;
  3710. }
  3711. static struct channel_user *
  3712. irc_channel_get_user (struct channel *channel, struct user *user)
  3713. {
  3714. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3715. if (iter->user == user)
  3716. return iter;
  3717. return NULL;
  3718. }
  3719. static void
  3720. irc_remove_user_from_channel (struct user *user, struct channel *channel)
  3721. {
  3722. struct channel_user *channel_user = irc_channel_get_user (channel, user);
  3723. if (channel_user)
  3724. irc_channel_unlink_user (channel, channel_user);
  3725. }
  3726. static void
  3727. irc_left_channel (struct channel *channel)
  3728. {
  3729. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3730. irc_channel_unlink_user (channel, iter);
  3731. }
  3732. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3733. static void
  3734. remove_conflicting_buffer (struct server *s, struct buffer *buffer)
  3735. {
  3736. log_server_status (s, s->buffer,
  3737. "Removed buffer #s because of casemapping conflict", buffer->name);
  3738. if (s->ctx->current_buffer == buffer)
  3739. buffer_activate (s->ctx, s->buffer);
  3740. buffer_remove (s->ctx, buffer);
  3741. }
  3742. static void
  3743. irc_try_readd_user (struct server *s,
  3744. struct user *user, struct buffer *buffer)
  3745. {
  3746. if (str_map_find (&s->irc_users, user->nickname))
  3747. {
  3748. // Remove user from all channels and destroy any PM buffer
  3749. user_ref (user);
  3750. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  3751. irc_remove_user_from_channel (user, iter->channel);
  3752. if (buffer)
  3753. remove_conflicting_buffer (s, buffer);
  3754. user_unref (user);
  3755. }
  3756. else
  3757. {
  3758. str_map_set (&s->irc_users, user->nickname, user);
  3759. str_map_set (&s->irc_buffer_map, user->nickname, buffer);
  3760. }
  3761. }
  3762. static void
  3763. irc_try_readd_channel (struct server *s,
  3764. struct channel *channel, struct buffer *buffer)
  3765. {
  3766. if (str_map_find (&s->irc_channels, channel->name))
  3767. {
  3768. // Remove all users from channel and destroy any channel buffer
  3769. channel_ref (channel);
  3770. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  3771. irc_channel_unlink_user (channel, iter);
  3772. if (buffer)
  3773. remove_conflicting_buffer (s, buffer);
  3774. channel_unref (channel);
  3775. }
  3776. else
  3777. {
  3778. str_map_set (&s->irc_channels, channel->name, channel);
  3779. str_map_set (&s->irc_buffer_map, channel->name, buffer);
  3780. }
  3781. }
  3782. static void
  3783. irc_rehash_and_fix_conflicts (struct server *s)
  3784. {
  3785. // Save the old maps and initialize new ones
  3786. struct str_map old_users = s->irc_users;
  3787. struct str_map old_channels = s->irc_channels;
  3788. struct str_map old_buffer_map = s->irc_buffer_map;
  3789. str_map_init (&s->irc_users);
  3790. str_map_init (&s->irc_channels);
  3791. str_map_init (&s->irc_buffer_map);
  3792. s->irc_users .key_xfrm = s->irc_strxfrm;
  3793. s->irc_channels .key_xfrm = s->irc_strxfrm;
  3794. s->irc_buffer_map.key_xfrm = s->irc_strxfrm;
  3795. // Prevent channels and users from unsetting themselves
  3796. // from server maps upon removing the last reference to them
  3797. s->rehashing = true;
  3798. // XXX: to be perfectly sure, we should also check
  3799. // whether any users collide with channels and vice versa
  3800. // Our own user always takes priority, add him first
  3801. if (s->irc_user)
  3802. irc_try_readd_user (s, s->irc_user,
  3803. str_map_find (&old_buffer_map, s->irc_user->nickname));
  3804. struct str_map_iter iter;
  3805. struct user *user;
  3806. struct channel *channel;
  3807. str_map_iter_init (&iter, &old_users);
  3808. while ((user = str_map_iter_next (&iter)))
  3809. irc_try_readd_user (s, user,
  3810. str_map_find (&old_buffer_map, user->nickname));
  3811. str_map_iter_init (&iter, &old_channels);
  3812. while ((channel = str_map_iter_next (&iter)))
  3813. irc_try_readd_channel (s, channel,
  3814. str_map_find (&old_buffer_map, channel->name));
  3815. // Hopefully we've either moved or destroyed all the old content
  3816. s->rehashing = false;
  3817. str_map_free (&old_users);
  3818. str_map_free (&old_channels);
  3819. str_map_free (&old_buffer_map);
  3820. }
  3821. static void
  3822. irc_set_casemapping (struct server *s,
  3823. irc_tolower_fn tolower, irc_strxfrm_fn strxfrm)
  3824. {
  3825. if (tolower == s->irc_tolower
  3826. && strxfrm == s->irc_strxfrm)
  3827. return;
  3828. s->irc_tolower = tolower;
  3829. s->irc_strxfrm = strxfrm;
  3830. // Ideally we would never have to do this but I can't think of a workaround
  3831. irc_rehash_and_fix_conflicts (s);
  3832. }
  3833. // --- Core functionality ------------------------------------------------------
  3834. static bool
  3835. irc_is_connected (struct server *s)
  3836. {
  3837. return s->state != IRC_DISCONNECTED && s->state != IRC_CONNECTING;
  3838. }
  3839. static void
  3840. irc_update_poller (struct server *s, const struct pollfd *pfd)
  3841. {
  3842. int new_events = s->transport->get_poll_events (s);
  3843. hard_assert (new_events != 0);
  3844. if (!pfd || pfd->events != new_events)
  3845. poller_fd_set (&s->socket_event, new_events);
  3846. }
  3847. static void
  3848. irc_cancel_timers (struct server *s)
  3849. {
  3850. poller_timer_reset (&s->timeout_tmr);
  3851. poller_timer_reset (&s->ping_tmr);
  3852. poller_timer_reset (&s->reconnect_tmr);
  3853. poller_timer_reset (&s->autojoin_tmr);
  3854. }
  3855. static void
  3856. irc_reset_connection_timeouts (struct server *s)
  3857. {
  3858. poller_timer_set (&s->timeout_tmr, 3 * 60 * 1000);
  3859. poller_timer_set (&s->ping_tmr, (3 * 60 + 30) * 1000);
  3860. poller_timer_reset (&s->reconnect_tmr);
  3861. }
  3862. static int64_t
  3863. irc_get_reconnect_delay (struct server *s)
  3864. {
  3865. int64_t delay = get_config_integer (s->config, "reconnect_delay");
  3866. int64_t delay_factor = get_config_integer (s->ctx->config.root,
  3867. "behaviour.reconnect_delay_growing");
  3868. for (unsigned i = 0; i < s->reconnect_attempt; i++)
  3869. {
  3870. if (delay_factor && delay > INT64_MAX / delay_factor)
  3871. break;
  3872. delay *= delay_factor;
  3873. }
  3874. int64_t delay_max = get_config_integer (s->ctx->config.root,
  3875. "behaviour.reconnect_delay_max");
  3876. return MIN (delay, delay_max);
  3877. }
  3878. static void
  3879. irc_queue_reconnect (struct server *s)
  3880. {
  3881. // As long as the user wants us to, that is
  3882. if (!get_config_boolean (s->config, "reconnect"))
  3883. return;
  3884. // XXX: maybe add a state for when a connect is queued?
  3885. hard_assert (s->state == IRC_DISCONNECTED);
  3886. int64_t delay = irc_get_reconnect_delay (s);
  3887. s->reconnect_attempt++;
  3888. log_server_status (s, s->buffer,
  3889. "Trying to reconnect in #&s seconds...",
  3890. xstrdup_printf ("%" PRId64, delay));
  3891. poller_timer_set (&s->reconnect_tmr, delay * 1000);
  3892. }
  3893. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3894. static void irc_process_sent_message
  3895. (const struct irc_message *msg, struct server *s);
  3896. static void irc_send (struct server *s,
  3897. const char *format, ...) ATTRIBUTE_PRINTF (2, 3);
  3898. static void
  3899. irc_send (struct server *s, const char *format, ...)
  3900. {
  3901. if (!soft_assert (irc_is_connected (s)))
  3902. {
  3903. log_server_debug (s, "sending a message to a dead server connection");
  3904. return;
  3905. }
  3906. if (s->state == IRC_CLOSING
  3907. || s->state == IRC_HALF_CLOSED)
  3908. return;
  3909. va_list ap;
  3910. va_start (ap, format);
  3911. struct str str;
  3912. str_init (&str);
  3913. str_append_vprintf (&str, format, ap);
  3914. va_end (ap);
  3915. log_server_debug (s, "#a<< \"#S\"#r", ATTR_PART, str.str);
  3916. struct irc_message msg;
  3917. irc_parse_message (&msg, str.str);
  3918. irc_process_sent_message (&msg, s);
  3919. irc_free_message (&msg);
  3920. str_append_str (&s->write_buffer, &str);
  3921. str_free (&str);
  3922. str_append (&s->write_buffer, "\r\n");
  3923. irc_update_poller (s, NULL);
  3924. }
  3925. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3926. static void
  3927. irc_real_shutdown (struct server *s)
  3928. {
  3929. hard_assert (irc_is_connected (s) && s->state != IRC_HALF_CLOSED);
  3930. if (s->transport
  3931. && s->transport->in_before_shutdown)
  3932. s->transport->in_before_shutdown (s);
  3933. while (shutdown (s->socket, SHUT_WR) == -1)
  3934. if (!soft_assert (errno == EINTR))
  3935. break;
  3936. s->state = IRC_HALF_CLOSED;
  3937. }
  3938. static void
  3939. irc_shutdown (struct server *s)
  3940. {
  3941. if (s->state == IRC_CLOSING
  3942. || s->state == IRC_HALF_CLOSED)
  3943. return;
  3944. // TODO: set a timer to cut the connection if we don't receive an EOF
  3945. s->state = IRC_CLOSING;
  3946. // Either there's still some data in the write buffer and we wait
  3947. // until they're sent, or we send an EOF to the server right away
  3948. if (!s->write_buffer.len)
  3949. irc_real_shutdown (s);
  3950. }
  3951. static void
  3952. irc_destroy_connector (struct server *s)
  3953. {
  3954. if (s->connector)
  3955. connector_free (s->connector);
  3956. free (s->connector);
  3957. s->connector = NULL;
  3958. if (s->socks_conn)
  3959. socks_connector_free (s->socks_conn);
  3960. free (s->socks_conn);
  3961. s->socks_conn = NULL;
  3962. // Not connecting anymore
  3963. s->state = IRC_DISCONNECTED;
  3964. }
  3965. static void
  3966. try_finish_quit (struct app_context *ctx)
  3967. {
  3968. if (!ctx->quitting)
  3969. return;
  3970. struct str_map_iter iter;
  3971. str_map_iter_init (&iter, &ctx->servers);
  3972. bool disconnected_all = true;
  3973. struct server *s;
  3974. while ((s = str_map_iter_next (&iter)))
  3975. if (irc_is_connected (s))
  3976. disconnected_all = false;
  3977. if (disconnected_all)
  3978. ctx->polling = false;
  3979. }
  3980. static void
  3981. initiate_quit (struct app_context *ctx)
  3982. {
  3983. log_global_status (ctx, "Shutting down");
  3984. // Hide the user interface
  3985. CALL (ctx->input, hide);
  3986. // Initiate a connection close
  3987. struct str_map_iter iter;
  3988. str_map_iter_init (&iter, &ctx->servers);
  3989. struct server *s;
  3990. while ((s = str_map_iter_next (&iter)))
  3991. {
  3992. // There may be a timer set to reconnect to the server
  3993. poller_timer_reset (&s->reconnect_tmr);
  3994. if (irc_is_connected (s))
  3995. {
  3996. irc_shutdown (s);
  3997. s->manual_disconnect = true;
  3998. }
  3999. else if (s->state == IRC_CONNECTING)
  4000. irc_destroy_connector (s);
  4001. }
  4002. ctx->quitting = true;
  4003. try_finish_quit (ctx);
  4004. }
  4005. static void
  4006. irc_destroy_transport (struct server *s)
  4007. {
  4008. if (s->transport
  4009. && s->transport->cleanup)
  4010. s->transport->cleanup (s);
  4011. s->transport = NULL;
  4012. xclose (s->socket);
  4013. s->socket = -1;
  4014. s->state = IRC_DISCONNECTED;
  4015. s->socket_event.closed = true;
  4016. poller_fd_reset (&s->socket_event);
  4017. str_reset (&s->read_buffer);
  4018. str_reset (&s->write_buffer);
  4019. }
  4020. static void
  4021. irc_destroy_state (struct server *s)
  4022. {
  4023. struct str_map_iter iter;
  4024. str_map_iter_init (&iter, &s->irc_channels);
  4025. struct channel *channel;
  4026. while ((channel = str_map_iter_next (&iter)))
  4027. irc_left_channel (channel);
  4028. if (s->irc_user)
  4029. {
  4030. user_unref (s->irc_user);
  4031. s->irc_user = NULL;
  4032. }
  4033. str_reset (&s->irc_user_mode);
  4034. free (s->irc_user_host);
  4035. s->irc_user_host = NULL;
  4036. s->cap_echo_message = false;
  4037. // Need to call this before server_init_specifics()
  4038. irc_set_casemapping (s, irc_tolower, irc_strxfrm);
  4039. server_free_specifics (s);
  4040. server_init_specifics (s);
  4041. }
  4042. static void
  4043. irc_disconnect (struct server *s)
  4044. {
  4045. hard_assert (irc_is_connected (s));
  4046. struct str_map_iter iter;
  4047. str_map_iter_init (&iter, &s->irc_buffer_map);
  4048. struct buffer *buffer;
  4049. while ((buffer = str_map_iter_next (&iter)))
  4050. log_server (s, buffer, BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT,
  4051. "Disconnected from server");
  4052. irc_cancel_timers (s);
  4053. irc_destroy_transport (s);
  4054. irc_destroy_state (s);
  4055. // Take any relevant actions
  4056. if (s->ctx->quitting)
  4057. try_finish_quit (s->ctx);
  4058. else if (s->manual_disconnect)
  4059. s->manual_disconnect = false;
  4060. else
  4061. {
  4062. s->reconnect_attempt = 0;
  4063. irc_queue_reconnect (s);
  4064. }
  4065. refresh_prompt (s->ctx);
  4066. }
  4067. static void
  4068. irc_initiate_disconnect (struct server *s, const char *reason)
  4069. {
  4070. hard_assert (irc_is_connected (s));
  4071. s->manual_disconnect = true;
  4072. if (reason)
  4073. irc_send (s, "QUIT :%s", reason);
  4074. else
  4075. irc_send (s, "QUIT :%s", PROGRAM_NAME " " PROGRAM_VERSION);
  4076. }
  4077. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4078. static void
  4079. on_irc_ping_timeout (void *user_data)
  4080. {
  4081. struct server *s = user_data;
  4082. log_server_error (s, s->buffer, "Connection timeout");
  4083. irc_disconnect (s);
  4084. }
  4085. static void
  4086. on_irc_timeout (void *user_data)
  4087. {
  4088. // Provoke a response from the server
  4089. struct server *s = user_data;
  4090. irc_send (s, "PING :%" PRIi64, (int64_t) time (NULL));
  4091. }
  4092. static void
  4093. on_irc_autojoin_timeout (void *user_data)
  4094. {
  4095. struct server *s = user_data;
  4096. // Since we may not have information from RPL_ISUPPORT yet,
  4097. // it's our safest bet to send the channels one at a time
  4098. struct str_map joins_sent;
  4099. str_map_init (&joins_sent);
  4100. // We don't know the casemapping yet either, however ASCII should do
  4101. joins_sent.key_xfrm = tolower_ascii_strxfrm;
  4102. // First join autojoin channels in their given order
  4103. const char *autojoin = get_config_string (s->config, "autojoin");
  4104. if (autojoin)
  4105. {
  4106. struct str_vector v;
  4107. str_vector_init (&v);
  4108. cstr_split (autojoin, ",", &v);
  4109. for (size_t i = 0; i < v.len; i++)
  4110. {
  4111. irc_send (s, "JOIN :%s", v.vector[i]);
  4112. str_map_set (&joins_sent, v.vector[i], (void *) 1);
  4113. }
  4114. str_vector_free (&v);
  4115. }
  4116. // Then also rejoin any channels from the last disconnect
  4117. struct str_map_iter iter;
  4118. str_map_iter_init (&iter, &s->irc_channels);
  4119. struct channel *channel;
  4120. while ((channel = str_map_iter_next (&iter)))
  4121. if (!channel->left_manually
  4122. && !str_map_find (&joins_sent, channel->name))
  4123. irc_send (s, "JOIN :%s", channel->name);
  4124. str_map_free (&joins_sent);
  4125. }
  4126. // --- Server I/O --------------------------------------------------------------
  4127. static char *
  4128. irc_process_hooks (struct server *s, char *input)
  4129. {
  4130. log_server_debug (s, "#a>> \"#S\"#r", ATTR_JOIN, input);
  4131. uint64_t hash = siphash_wrapper (input, strlen (input));
  4132. LIST_FOR_EACH (struct hook, iter, s->ctx->irc_hooks)
  4133. {
  4134. struct irc_hook *hook = (struct irc_hook *) iter;
  4135. if (!(input = hook->vtable->filter (hook, s, input)))
  4136. {
  4137. log_server_debug (s, "#a>= #s#r", ATTR_JOIN, "thrown away by hook");
  4138. return NULL;
  4139. }
  4140. uint64_t new_hash = siphash_wrapper (input, strlen (input));
  4141. if (new_hash != hash)
  4142. log_server_debug (s, "#a>= \"#S\"#r", ATTR_JOIN, input);
  4143. hash = new_hash;
  4144. }
  4145. return input;
  4146. }
  4147. static void irc_process_message
  4148. (const struct irc_message *msg, struct server *s);
  4149. static void
  4150. irc_process_buffer_custom (struct server *s, struct str *buf)
  4151. {
  4152. const char *start = buf->str, *end = start + buf->len;
  4153. for (const char *p = start; p + 1 < end; p++)
  4154. {
  4155. // Split the input on newlines
  4156. if (p[0] != '\r' || p[1] != '\n')
  4157. continue;
  4158. char *processed = irc_process_hooks (s, xstrndup (start, p - start));
  4159. start = p + 2;
  4160. if (!processed)
  4161. continue;
  4162. struct irc_message msg;
  4163. irc_parse_message (&msg, processed);
  4164. irc_process_message (&msg, s);
  4165. irc_free_message (&msg);
  4166. free (processed);
  4167. }
  4168. str_remove_slice (buf, 0, start - buf->str);
  4169. }
  4170. static enum socket_io_result
  4171. irc_try_read (struct server *s)
  4172. {
  4173. enum socket_io_result result = s->transport->try_read (s);
  4174. if (s->read_buffer.len >= (1 << 20))
  4175. {
  4176. // XXX: this is stupid; if anything, count it in dependence of time
  4177. log_server_error (s, s->buffer,
  4178. "The IRC server seems to spew out data frantically");
  4179. return SOCKET_IO_ERROR;
  4180. }
  4181. if (s->read_buffer.len)
  4182. irc_process_buffer_custom (s, &s->read_buffer);
  4183. return result;
  4184. }
  4185. static enum socket_io_result
  4186. irc_try_write (struct server *s)
  4187. {
  4188. enum socket_io_result result = s->transport->try_write (s);
  4189. if (result == SOCKET_IO_OK)
  4190. {
  4191. // If we're flushing the write buffer and our job is complete, we send
  4192. // an EOF to the server, changing the state to IRC_HALF_CLOSED
  4193. if (s->state == IRC_CLOSING && !s->write_buffer.len)
  4194. irc_real_shutdown (s);
  4195. }
  4196. return result;
  4197. }
  4198. static bool
  4199. irc_try_read_write (struct server *s)
  4200. {
  4201. enum socket_io_result read_result;
  4202. enum socket_io_result write_result;
  4203. if ((read_result = irc_try_read (s)) == SOCKET_IO_ERROR
  4204. || (write_result = irc_try_write (s)) == SOCKET_IO_ERROR)
  4205. {
  4206. log_server_error (s, s->buffer, "Server connection failed");
  4207. return false;
  4208. }
  4209. // FIXME: this may probably fire multiple times when we're flushing,
  4210. // we should probably store a flag next to the state
  4211. if (read_result == SOCKET_IO_EOF
  4212. || write_result == SOCKET_IO_EOF)
  4213. log_server_error (s, s->buffer, "Server closed the connection");
  4214. // If the write needs to read and we receive an EOF, we can't flush
  4215. if (write_result == SOCKET_IO_EOF)
  4216. return false;
  4217. if (read_result == SOCKET_IO_EOF)
  4218. {
  4219. // Eventually initiate shutdown to flush the write buffer
  4220. irc_shutdown (s);
  4221. // If there's nothing to write, we can disconnect now
  4222. if (s->state == IRC_HALF_CLOSED)
  4223. return false;
  4224. }
  4225. return true;
  4226. }
  4227. static void
  4228. on_irc_ready (const struct pollfd *pfd, struct server *s)
  4229. {
  4230. if (irc_try_read_write (s))
  4231. {
  4232. // XXX: shouldn't we rather wait for PONG messages?
  4233. irc_reset_connection_timeouts (s);
  4234. irc_update_poller (s, pfd);
  4235. }
  4236. else
  4237. // We don't want to keep the socket anymore
  4238. irc_disconnect (s);
  4239. }
  4240. // --- Plain transport ---------------------------------------------------------
  4241. static enum socket_io_result
  4242. transport_plain_try_read (struct server *s)
  4243. {
  4244. struct error *e = NULL;
  4245. enum socket_io_result result =
  4246. socket_io_try_read (s->socket, &s->read_buffer, &e);
  4247. if (e)
  4248. {
  4249. print_debug ("%s: %s", __func__, e->message);
  4250. error_free (e);
  4251. }
  4252. return result;
  4253. }
  4254. static enum socket_io_result
  4255. transport_plain_try_write (struct server *s)
  4256. {
  4257. struct error *e = NULL;
  4258. enum socket_io_result result =
  4259. socket_io_try_write (s->socket, &s->write_buffer, &e);
  4260. if (e)
  4261. {
  4262. print_debug ("%s: %s", __func__, e->message);
  4263. error_free (e);
  4264. }
  4265. return result;
  4266. }
  4267. static int
  4268. transport_plain_get_poll_events (struct server *s)
  4269. {
  4270. int events = POLLIN;
  4271. if (s->write_buffer.len)
  4272. events |= POLLOUT;
  4273. return events;
  4274. }
  4275. static struct transport g_transport_plain =
  4276. {
  4277. .try_read = transport_plain_try_read,
  4278. .try_write = transport_plain_try_write,
  4279. .get_poll_events = transport_plain_get_poll_events,
  4280. };
  4281. // --- TLS transport -----------------------------------------------------------
  4282. struct transport_tls_data
  4283. {
  4284. SSL_CTX *ssl_ctx; ///< SSL context
  4285. SSL *ssl; ///< SSL connection
  4286. bool ssl_rx_want_tx; ///< SSL_read() wants to write
  4287. bool ssl_tx_want_rx; ///< SSL_write() wants to read
  4288. };
  4289. /// The index in SSL_CTX user data for a reference to the server
  4290. static int g_transport_tls_data_index = -1;
  4291. static int
  4292. transport_tls_verify_callback (int preverify_ok, X509_STORE_CTX *ctx)
  4293. {
  4294. SSL *ssl = X509_STORE_CTX_get_ex_data
  4295. (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ());
  4296. struct server *s = SSL_CTX_get_ex_data
  4297. (SSL_get_SSL_CTX (ssl), g_transport_tls_data_index);
  4298. X509 *cert = X509_STORE_CTX_get_current_cert (ctx);
  4299. char *subject = X509_NAME_oneline (X509_get_subject_name (cert), NULL, 0);
  4300. char *issuer = X509_NAME_oneline (X509_get_issuer_name (cert), NULL, 0);
  4301. log_server_status (s, s->buffer, "Certificate subject: #s", subject);
  4302. log_server_status (s, s->buffer, "Certificate issuer: #s", issuer);
  4303. if (!preverify_ok)
  4304. {
  4305. log_server_error (s, s->buffer,
  4306. "Certificate verification failed: #s",
  4307. X509_verify_cert_error_string (X509_STORE_CTX_get_error (ctx)));
  4308. }
  4309. free (subject);
  4310. free (issuer);
  4311. return preverify_ok;
  4312. }
  4313. static bool
  4314. transport_tls_init_ca_set (SSL_CTX *ssl_ctx, const char *file, const char *path,
  4315. struct error **e)
  4316. {
  4317. ERR_clear_error ();
  4318. if (file || path)
  4319. {
  4320. if (SSL_CTX_load_verify_locations (ssl_ctx, file, path))
  4321. return true;
  4322. FAIL ("%s: %s", "Failed to set locations for the CA certificate bundle",
  4323. ERR_reason_error_string (ERR_get_error ()));
  4324. }
  4325. if (!SSL_CTX_set_default_verify_paths (ssl_ctx))
  4326. FAIL ("%s: %s", "Couldn't load the default CA certificate bundle",
  4327. ERR_reason_error_string (ERR_get_error ()));
  4328. return true;
  4329. }
  4330. static bool
  4331. transport_tls_init_ca (struct server *s, SSL_CTX *ssl_ctx, struct error **e)
  4332. {
  4333. const char *ca_file = get_config_string (s->config, "tls_ca_file");
  4334. const char *ca_path = get_config_string (s->config, "tls_ca_path");
  4335. char *full_ca_file = ca_file
  4336. ? resolve_filename (ca_file, resolve_relative_config_filename) : NULL;
  4337. char *full_ca_path = ca_path
  4338. ? resolve_filename (ca_path, resolve_relative_config_filename) : NULL;
  4339. bool ok = false;
  4340. if (ca_file && !full_ca_file)
  4341. error_set (e, "Couldn't find the CA bundle file");
  4342. else if (ca_path && !full_ca_path)
  4343. error_set (e, "Couldn't find the CA bundle path");
  4344. else
  4345. ok = transport_tls_init_ca_set (ssl_ctx, full_ca_file, full_ca_path, e);
  4346. free (full_ca_file);
  4347. free (full_ca_path);
  4348. return ok;
  4349. }
  4350. static bool
  4351. transport_tls_init_ctx (struct server *s, SSL_CTX *ssl_ctx, struct error **e)
  4352. {
  4353. bool verify = get_config_boolean (s->config, "tls_verify");
  4354. SSL_CTX_set_verify (ssl_ctx, verify ? SSL_VERIFY_PEER : SSL_VERIFY_NONE,
  4355. transport_tls_verify_callback);
  4356. if (g_transport_tls_data_index == -1)
  4357. g_transport_tls_data_index =
  4358. SSL_CTX_get_ex_new_index (0, "server", NULL, NULL, NULL);
  4359. SSL_CTX_set_ex_data (ssl_ctx, g_transport_tls_data_index, s);
  4360. const char *ciphers = get_config_string (s->config, "tls_ciphers");
  4361. if (ciphers && !SSL_CTX_set_cipher_list (ssl_ctx, ciphers))
  4362. log_server_error (s, s->buffer,
  4363. "Failed to select any cipher from the cipher list");
  4364. SSL_CTX_set_mode (ssl_ctx,
  4365. SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
  4366. // Disable deprecated protocols (see RFC 7568)
  4367. SSL_CTX_set_options (ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
  4368. // This seems to consume considerable amounts of memory while not giving
  4369. // that much in return; in addition to that, I'm not sure about security
  4370. // (see RFC 7525, section 3.3)
  4371. #ifdef SSL_OP_NO_COMPRESSION
  4372. SSL_CTX_set_options (ssl_ctx, SSL_OP_NO_COMPRESSION);
  4373. #endif // SSL_OP_NO_COMPRESSION
  4374. struct error *error = NULL;
  4375. if (!transport_tls_init_ca (s, ssl_ctx, &error))
  4376. {
  4377. if (verify)
  4378. {
  4379. error_propagate (e, error);
  4380. return false;
  4381. }
  4382. // Only inform the user if we're not actually verifying
  4383. log_server_error (s, s->buffer, "#s", error->message);
  4384. error_free (error);
  4385. }
  4386. return true;
  4387. }
  4388. static bool
  4389. transport_tls_init_cert (struct server *s, SSL *ssl, struct error **e)
  4390. {
  4391. const char *tls_cert = get_config_string (s->config, "tls_cert");
  4392. if (!tls_cert)
  4393. return true;
  4394. ERR_clear_error ();
  4395. bool result = false;
  4396. char *path = resolve_filename (tls_cert, resolve_relative_config_filename);
  4397. if (!path)
  4398. error_set (e, "%s: %s", "Cannot open file", tls_cert);
  4399. // XXX: perhaps we should read the file ourselves for better messages
  4400. else if (!SSL_use_certificate_file (ssl, path, SSL_FILETYPE_PEM)
  4401. || !SSL_use_PrivateKey_file (ssl, path, SSL_FILETYPE_PEM))
  4402. error_set (e, "%s: %s", "Setting the TLS client certificate failed",
  4403. ERR_reason_error_string (ERR_get_error ()));
  4404. else
  4405. result = true;
  4406. free (path);
  4407. return result;
  4408. }
  4409. static bool
  4410. transport_tls_init (struct server *s, const char *hostname, struct error **e)
  4411. {
  4412. ERR_clear_error ();
  4413. struct error *error = NULL;
  4414. SSL_CTX *ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
  4415. if (!ssl_ctx)
  4416. goto error_ssl_1;
  4417. if (!transport_tls_init_ctx (s, ssl_ctx, &error))
  4418. goto error_ssl_2;
  4419. SSL *ssl = SSL_new (ssl_ctx);
  4420. if (!ssl)
  4421. goto error_ssl_2;
  4422. if (!transport_tls_init_cert (s, ssl, &error))
  4423. {
  4424. // XXX: is this a reason to abort the connection?
  4425. log_server_error (s, s->buffer, "#s", error->message);
  4426. error_free (error);
  4427. error = NULL;
  4428. }
  4429. SSL_set_connect_state (ssl);
  4430. if (!SSL_set_fd (ssl, s->socket))
  4431. goto error_ssl_3;
  4432. // Enable SNI, FWIW; literal IP addresses aren't allowed
  4433. struct in6_addr dummy;
  4434. if (!inet_pton (AF_INET, hostname, &dummy)
  4435. && !inet_pton (AF_INET6, hostname, &dummy))
  4436. SSL_set_tlsext_host_name (ssl, hostname);
  4437. struct transport_tls_data *data = xcalloc (1, sizeof *data);
  4438. data->ssl_ctx = ssl_ctx;
  4439. data->ssl = ssl;
  4440. // Forces a handshake even if neither side wants to transmit data
  4441. data->ssl_rx_want_tx = true;
  4442. s->transport_data = data;
  4443. return true;
  4444. error_ssl_3:
  4445. SSL_free (ssl);
  4446. error_ssl_2:
  4447. SSL_CTX_free (ssl_ctx);
  4448. error_ssl_1:
  4449. if (!error)
  4450. error_set (&error, "%s: %s", "Could not initialize TLS",
  4451. ERR_reason_error_string (ERR_get_error ()));
  4452. error_propagate (e, error);
  4453. return false;
  4454. }
  4455. static void
  4456. transport_tls_cleanup (struct server *s)
  4457. {
  4458. struct transport_tls_data *data = s->transport_data;
  4459. if (data->ssl)
  4460. SSL_free (data->ssl);
  4461. if (data->ssl_ctx)
  4462. SSL_CTX_free (data->ssl_ctx);
  4463. free (data);
  4464. }
  4465. static enum socket_io_result
  4466. transport_tls_try_read (struct server *s)
  4467. {
  4468. struct transport_tls_data *data = s->transport_data;
  4469. if (data->ssl_tx_want_rx)
  4470. return SOCKET_IO_OK;
  4471. struct str *buf = &s->read_buffer;
  4472. data->ssl_rx_want_tx = false;
  4473. while (true)
  4474. {
  4475. ERR_clear_error ();
  4476. str_ensure_space (buf, 512);
  4477. int n_read = SSL_read (data->ssl, buf->str + buf->len,
  4478. buf->alloc - buf->len - 1 /* null byte */);
  4479. const char *error_info = NULL;
  4480. switch (xssl_get_error (data->ssl, n_read, &error_info))
  4481. {
  4482. case SSL_ERROR_NONE:
  4483. buf->str[buf->len += n_read] = '\0';
  4484. continue;
  4485. case SSL_ERROR_ZERO_RETURN:
  4486. return SOCKET_IO_EOF;
  4487. case SSL_ERROR_WANT_READ:
  4488. return SOCKET_IO_OK;
  4489. case SSL_ERROR_WANT_WRITE:
  4490. data->ssl_rx_want_tx = true;
  4491. return SOCKET_IO_OK;
  4492. case XSSL_ERROR_TRY_AGAIN:
  4493. continue;
  4494. default:
  4495. LOG_FUNC_FAILURE ("SSL_read", error_info);
  4496. return SOCKET_IO_ERROR;
  4497. }
  4498. }
  4499. }
  4500. static enum socket_io_result
  4501. transport_tls_try_write (struct server *s)
  4502. {
  4503. struct transport_tls_data *data = s->transport_data;
  4504. if (data->ssl_rx_want_tx)
  4505. return SOCKET_IO_OK;
  4506. struct str *buf = &s->write_buffer;
  4507. data->ssl_tx_want_rx = false;
  4508. while (buf->len)
  4509. {
  4510. ERR_clear_error ();
  4511. int n_written = SSL_write (data->ssl, buf->str, buf->len);
  4512. const char *error_info = NULL;
  4513. switch (xssl_get_error (data->ssl, n_written, &error_info))
  4514. {
  4515. case SSL_ERROR_NONE:
  4516. str_remove_slice (buf, 0, n_written);
  4517. continue;
  4518. case SSL_ERROR_ZERO_RETURN:
  4519. return SOCKET_IO_EOF;
  4520. case SSL_ERROR_WANT_WRITE:
  4521. return SOCKET_IO_OK;
  4522. case SSL_ERROR_WANT_READ:
  4523. data->ssl_tx_want_rx = true;
  4524. return SOCKET_IO_OK;
  4525. case XSSL_ERROR_TRY_AGAIN:
  4526. continue;
  4527. default:
  4528. LOG_FUNC_FAILURE ("SSL_write", error_info);
  4529. return SOCKET_IO_ERROR;
  4530. }
  4531. }
  4532. return SOCKET_IO_OK;
  4533. }
  4534. static int
  4535. transport_tls_get_poll_events (struct server *s)
  4536. {
  4537. struct transport_tls_data *data = s->transport_data;
  4538. int events = POLLIN;
  4539. if (s->write_buffer.len || data->ssl_rx_want_tx)
  4540. events |= POLLOUT;
  4541. // While we're waiting for an opposite event, we ignore the original
  4542. if (data->ssl_rx_want_tx) events &= ~POLLIN;
  4543. if (data->ssl_tx_want_rx) events &= ~POLLOUT;
  4544. return events;
  4545. }
  4546. static void
  4547. transport_tls_in_before_shutdown (struct server *s)
  4548. {
  4549. struct transport_tls_data *data = s->transport_data;
  4550. (void) SSL_shutdown (data->ssl);
  4551. }
  4552. static struct transport g_transport_tls =
  4553. {
  4554. .init = transport_tls_init,
  4555. .cleanup = transport_tls_cleanup,
  4556. .try_read = transport_tls_try_read,
  4557. .try_write = transport_tls_try_write,
  4558. .get_poll_events = transport_tls_get_poll_events,
  4559. .in_before_shutdown = transport_tls_in_before_shutdown,
  4560. };
  4561. // --- Connection establishment ------------------------------------------------
  4562. static bool
  4563. irc_autofill_user_info (struct server *s, struct error **e)
  4564. {
  4565. const char *nicks = get_config_string (s->config, "nicks");
  4566. const char *username = get_config_string (s->config, "username");
  4567. const char *realname = get_config_string (s->config, "realname");
  4568. if (nicks && *nicks && username && *username && realname)
  4569. return true;
  4570. // Read POSIX user info and fill the configuration if needed
  4571. struct passwd *pwd = getpwuid (geteuid ());
  4572. if (!pwd)
  4573. FAIL ("cannot retrieve user information: %s", strerror (errno));
  4574. // FIXME: set_config_strings() writes errors on its own
  4575. if (!nicks || !*nicks)
  4576. set_config_string (s->config, "nicks", pwd->pw_name);
  4577. if (!username || !*username)
  4578. set_config_string (s->config, "username", pwd->pw_name);
  4579. // Not all systems have the GECOS field but the vast majority does
  4580. if (!realname)
  4581. {
  4582. char *gecos = pwd->pw_gecos;
  4583. // The first comma, if any, ends the user's real name
  4584. char *comma = strchr (gecos, ',');
  4585. if (comma)
  4586. *comma = '\0';
  4587. set_config_string (s->config, "realname", gecos);
  4588. }
  4589. return true;
  4590. }
  4591. static char *
  4592. irc_fetch_next_nickname (struct server *s)
  4593. {
  4594. struct str_vector v;
  4595. str_vector_init (&v);
  4596. cstr_split_ignore_empty (get_config_string (s->config, "nicks"), ',', &v);
  4597. char *result = NULL;
  4598. if (s->nick_counter >= 0 && (size_t) s->nick_counter < v.len)
  4599. result = xstrdup (v.vector[s->nick_counter++]);
  4600. if ((size_t) s->nick_counter >= v.len)
  4601. // Exhausted all nicknames
  4602. s->nick_counter = -1;
  4603. str_vector_free (&v);
  4604. return result;
  4605. }
  4606. static void
  4607. irc_register (struct server *s)
  4608. {
  4609. // Fill in user information automatically if needed
  4610. irc_autofill_user_info (s, NULL);
  4611. const char *username = get_config_string (s->config, "username");
  4612. const char *realname = get_config_string (s->config, "realname");
  4613. hard_assert (username && realname);
  4614. // Start IRCv3.1 capability negotiation;
  4615. // at worst the server will ignore this or send a harmless error message
  4616. irc_send (s, "CAP LS");
  4617. const char *password = get_config_string (s->config, "password");
  4618. if (password)
  4619. irc_send (s, "PASS :%s", password);
  4620. s->nick_counter = 0;
  4621. char *nickname = irc_fetch_next_nickname (s);
  4622. if (nickname)
  4623. irc_send (s, "NICK :%s", nickname);
  4624. else
  4625. log_server_error (s, s->buffer, "No nicks present in configuration");
  4626. free (nickname);
  4627. // IRC servers may ignore the last argument if it's empty
  4628. irc_send (s, "USER %s 8 * :%s", username, *realname ? realname : " ");
  4629. }
  4630. static void
  4631. irc_finish_connection (struct server *s, int socket, const char *hostname)
  4632. {
  4633. struct app_context *ctx = s->ctx;
  4634. // Most of our output comes from the user one full command at a time and we
  4635. // use output buffering, so it makes a lot of sense to avoid these delays
  4636. int yes = 1;
  4637. soft_assert (setsockopt (socket, IPPROTO_TCP, TCP_NODELAY,
  4638. &yes, sizeof yes) != -1);
  4639. set_blocking (socket, false);
  4640. s->socket = socket;
  4641. s->transport = get_config_boolean (s->config, "tls")
  4642. ? &g_transport_tls
  4643. : &g_transport_plain;
  4644. struct error *e = NULL;
  4645. if (s->transport->init && !s->transport->init (s, hostname, &e))
  4646. {
  4647. log_server_error (s, s->buffer, "Connection failed: #s", e->message);
  4648. error_free (e);
  4649. xclose (s->socket);
  4650. s->socket = -1;
  4651. s->transport = NULL;
  4652. return;
  4653. }
  4654. log_server_status (s, s->buffer, "Connection established");
  4655. s->state = IRC_CONNECTED;
  4656. poller_fd_init (&s->socket_event, &ctx->poller, s->socket);
  4657. s->socket_event.dispatcher = (poller_fd_fn) on_irc_ready;
  4658. s->socket_event.user_data = s;
  4659. irc_update_poller (s, NULL);
  4660. irc_reset_connection_timeouts (s);
  4661. irc_register (s);
  4662. refresh_prompt (s->ctx);
  4663. }
  4664. static void
  4665. irc_split_host_port (char *s, char **host, char **port)
  4666. {
  4667. char *colon = strrchr (s, ':');
  4668. if (colon)
  4669. {
  4670. *colon = '\0';
  4671. *port = ++colon;
  4672. }
  4673. else
  4674. *port = "6667";
  4675. // Unwrap IPv6 addresses in format_host_port_pair() format
  4676. size_t host_end = strlen (s) - 1;
  4677. if (*s == '[' && s[host_end] == ']')
  4678. s++[host_end] = '\0';
  4679. *host = s;
  4680. }
  4681. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4682. static void
  4683. irc_on_connector_connecting (void *user_data, const char *address)
  4684. {
  4685. struct server *s = user_data;
  4686. log_server_status (s, s->buffer, "Connecting to #s...", address);
  4687. }
  4688. static void
  4689. irc_on_connector_error (void *user_data, const char *error)
  4690. {
  4691. struct server *s = user_data;
  4692. log_server_error (s, s->buffer, "Connection failed: #s", error);
  4693. }
  4694. static void
  4695. irc_on_connector_failure (void *user_data)
  4696. {
  4697. struct server *s = user_data;
  4698. irc_destroy_connector (s);
  4699. irc_queue_reconnect (s);
  4700. }
  4701. static void
  4702. irc_on_connector_connected (void *user_data, int socket, const char *hostname)
  4703. {
  4704. struct server *s = user_data;
  4705. char *hostname_copy = xstrdup (hostname);
  4706. irc_destroy_connector (s);
  4707. irc_finish_connection (s, socket, hostname_copy);
  4708. free (hostname_copy);
  4709. }
  4710. static void
  4711. irc_setup_connector (struct server *s, const struct str_vector *addresses)
  4712. {
  4713. struct connector *connector = xmalloc (sizeof *connector);
  4714. connector_init (connector, &s->ctx->poller);
  4715. s->connector = connector;
  4716. connector->user_data = s;
  4717. connector->on_connecting = irc_on_connector_connecting;
  4718. connector->on_error = irc_on_connector_error;
  4719. connector->on_connected = irc_on_connector_connected;
  4720. connector->on_failure = irc_on_connector_failure;
  4721. for (size_t i = 0; i < addresses->len; i++)
  4722. {
  4723. char *host, *port;
  4724. irc_split_host_port (addresses->vector[i], &host, &port);
  4725. connector_add_target (connector, host, port);
  4726. }
  4727. }
  4728. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4729. // TODO: see if we can further merge code for the two connectors, for example
  4730. // by making SOCKS 4A and 5 mere plugins for the connector, or by using
  4731. // a virtual interface common to them both (seems more likely)
  4732. static void
  4733. irc_on_socks_connecting (void *user_data,
  4734. const char *address, const char *via, const char *version)
  4735. {
  4736. struct server *s = user_data;
  4737. log_server_status (s, s->buffer,
  4738. "Connecting to #s via #s (#s)...", address, via, version);
  4739. }
  4740. static bool
  4741. irc_setup_connector_socks (struct server *s,
  4742. const struct str_vector *addresses, struct error **e)
  4743. {
  4744. const char *socks_host = get_config_string (s->config, "socks_host");
  4745. int64_t socks_port_int = get_config_integer (s->config, "socks_port");
  4746. if (!socks_host)
  4747. return false;
  4748. struct socks_connector *connector = xmalloc (sizeof *connector);
  4749. socks_connector_init (connector, &s->ctx->poller);
  4750. s->socks_conn = connector;
  4751. connector->user_data = s;
  4752. connector->on_connecting = irc_on_socks_connecting;
  4753. connector->on_error = irc_on_connector_error;
  4754. connector->on_connected = irc_on_connector_connected;
  4755. connector->on_failure = irc_on_connector_failure;
  4756. for (size_t i = 0; i < addresses->len; i++)
  4757. {
  4758. char *host, *port;
  4759. irc_split_host_port (addresses->vector[i], &host, &port);
  4760. if (!socks_connector_add_target (connector, host, port, e))
  4761. return false;
  4762. }
  4763. char *service = xstrdup_printf ("%" PRIi64, socks_port_int);
  4764. socks_connector_run (connector, socks_host, service,
  4765. get_config_string (s->config, "socks_username"),
  4766. get_config_string (s->config, "socks_password"));
  4767. free (service);
  4768. // The SOCKS connector can have already failed; we mustn't return true then
  4769. if (!s->socks_conn)
  4770. FAIL ("SOCKS connection failed");
  4771. return true;
  4772. }
  4773. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4774. static void
  4775. irc_initiate_connect (struct server *s)
  4776. {
  4777. hard_assert (s->state == IRC_DISCONNECTED);
  4778. const char *addresses = get_config_string (s->config, "addresses");
  4779. if (!addresses || !addresses[strspn (addresses, ",")])
  4780. {
  4781. // No sense in trying to reconnect
  4782. log_server_error (s, s->buffer,
  4783. "No addresses specified in configuration");
  4784. return;
  4785. }
  4786. struct str_vector servers;
  4787. str_vector_init (&servers);
  4788. cstr_split_ignore_empty (addresses, ',', &servers);
  4789. struct error *e = NULL;
  4790. if (!irc_setup_connector_socks (s, &servers, &e) && !e)
  4791. irc_setup_connector (s, &servers);
  4792. str_vector_free (&servers);
  4793. if (e)
  4794. {
  4795. irc_destroy_connector (s);
  4796. log_server_error (s, s->buffer, "#s", e->message);
  4797. error_free (e);
  4798. irc_queue_reconnect (s);
  4799. }
  4800. else if (s->state != IRC_CONNECTED)
  4801. s->state = IRC_CONNECTING;
  4802. }
  4803. // --- Input prompt ------------------------------------------------------------
  4804. static void
  4805. make_unseen_prefix (struct app_context *ctx, struct str *active_buffers)
  4806. {
  4807. size_t buffer_no = 0;
  4808. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  4809. {
  4810. buffer_no++;
  4811. if (!(iter->new_messages_count - iter->new_unimportant_count)
  4812. || iter == ctx->current_buffer)
  4813. continue;
  4814. if (active_buffers->len)
  4815. str_append_c (active_buffers, ',');
  4816. if (iter->highlighted)
  4817. str_append_c (active_buffers, '!');
  4818. str_append_printf (active_buffers, "%zu", buffer_no);
  4819. }
  4820. }
  4821. static void
  4822. make_chanmode_postfix (struct channel *channel, struct str *modes)
  4823. {
  4824. if (channel->no_param_modes.len)
  4825. str_append (modes, channel->no_param_modes.str);
  4826. struct str_map_iter iter;
  4827. str_map_iter_init (&iter, &channel->param_modes);
  4828. char *param;
  4829. while ((param = str_map_iter_next (&iter)))
  4830. str_append_c (modes, iter.link->key[0]);
  4831. }
  4832. static void
  4833. make_server_postfix_registered (struct buffer *buffer, struct str *output)
  4834. {
  4835. struct server *s = buffer->server;
  4836. if (buffer->type == BUFFER_CHANNEL)
  4837. {
  4838. struct server *s = buffer->server;
  4839. struct channel_user *channel_user =
  4840. irc_channel_get_user (buffer->channel, s->irc_user);
  4841. if (channel_user)
  4842. irc_get_channel_user_prefix (s, channel_user, output);
  4843. }
  4844. str_append (output, s->irc_user->nickname);
  4845. if (s->irc_user_mode.len)
  4846. str_append_printf (output, "(%s)", s->irc_user_mode.str);
  4847. }
  4848. static void
  4849. make_server_postfix (struct buffer *buffer, struct str *output)
  4850. {
  4851. struct server *s = buffer->server;
  4852. str_append_c (output, ' ');
  4853. if (!irc_is_connected (s))
  4854. str_append (output, "(disconnected)");
  4855. else if (s->state != IRC_REGISTERED)
  4856. str_append (output, "(unregistered)");
  4857. else
  4858. make_server_postfix_registered (buffer, output);
  4859. }
  4860. static void
  4861. make_prompt (struct app_context *ctx, struct str *output)
  4862. {
  4863. struct buffer *buffer = ctx->current_buffer;
  4864. if (!buffer)
  4865. return;
  4866. str_append_c (output, '[');
  4867. struct str active_buffers;
  4868. str_init (&active_buffers);
  4869. make_unseen_prefix (ctx, &active_buffers);
  4870. if (active_buffers.len)
  4871. str_append_printf (output, "(%s) ", active_buffers.str);
  4872. str_free (&active_buffers);
  4873. str_append_printf (output, "%d:%s",
  4874. buffer_get_index (ctx, buffer), buffer->name);
  4875. if (buffer->type == BUFFER_CHANNEL)
  4876. {
  4877. struct str modes;
  4878. str_init (&modes);
  4879. make_chanmode_postfix (buffer->channel, &modes);
  4880. if (modes.len)
  4881. str_append_printf (output, "(+%s)", modes.str);
  4882. str_free (&modes);
  4883. }
  4884. if (buffer != ctx->global_buffer)
  4885. make_server_postfix (buffer, output);
  4886. str_append_c (output, ']');
  4887. }
  4888. static void
  4889. input_maybe_set_prompt (struct input *self, char *new_prompt)
  4890. {
  4891. // Redisplay can be an expensive operation
  4892. const char *prompt = CALL (self, get_prompt);
  4893. if (prompt && !strcmp (new_prompt, prompt))
  4894. free (new_prompt);
  4895. else
  4896. CALL_ (self, set_prompt, new_prompt);
  4897. }
  4898. static void
  4899. refresh_prompt (struct app_context *ctx)
  4900. {
  4901. bool have_attributes = !!get_attribute_printer (stdout);
  4902. struct str prompt;
  4903. str_init (&prompt);
  4904. make_prompt (ctx, &prompt);
  4905. str_append_c (&prompt, ' ');
  4906. char *localized = iconv_xstrdup (ctx->term_from_utf8, prompt.str, -1, NULL);
  4907. str_free (&prompt);
  4908. if (have_attributes)
  4909. {
  4910. // XXX: to be completely correct, we should use tputs, but we cannot
  4911. input_maybe_set_prompt (ctx->input, xstrdup_printf ("%c%s%c%s%c%s%c",
  4912. INPUT_START_IGNORE, ctx->attrs[ATTR_PROMPT],
  4913. INPUT_END_IGNORE,
  4914. localized,
  4915. INPUT_START_IGNORE, ctx->attrs[ATTR_RESET],
  4916. INPUT_END_IGNORE));
  4917. free (localized);
  4918. }
  4919. else
  4920. input_maybe_set_prompt (ctx->input, localized);
  4921. }
  4922. // --- Helpers -----------------------------------------------------------------
  4923. static struct buffer *
  4924. irc_get_buffer_for_message (struct server *s,
  4925. const struct irc_message *msg, const char *target)
  4926. {
  4927. // TODO: display such messages differently
  4928. target = irc_skip_statusmsg (s, target);
  4929. struct buffer *buffer = str_map_find (&s->irc_buffer_map, target);
  4930. if (irc_is_channel (s, target))
  4931. {
  4932. struct channel *channel = str_map_find (&s->irc_channels, target);
  4933. hard_assert ((channel && buffer) ||
  4934. (channel && !buffer) || (!channel && !buffer));
  4935. // This is weird
  4936. if (!channel)
  4937. return NULL;
  4938. }
  4939. else if (!buffer)
  4940. {
  4941. // Outgoing messages needn't have a prefix, no buffer associated
  4942. if (!msg->prefix)
  4943. return NULL;
  4944. // Don't make user buffers for servers (they can send NOTICEs)
  4945. if (!irc_find_userhost (msg->prefix))
  4946. return s->buffer;
  4947. char *nickname = irc_cut_nickname (msg->prefix);
  4948. if (irc_is_this_us (s, target))
  4949. buffer = irc_get_or_make_user_buffer (s, nickname);
  4950. free (nickname);
  4951. // With the IRCv3.2 echo-message capability, we can receive messages
  4952. // as they are delivered to the target; in that case we return NULL
  4953. // and the caller should check the origin
  4954. }
  4955. return buffer;
  4956. }
  4957. static bool
  4958. irc_is_highlight (struct server *s, const char *message)
  4959. {
  4960. // This may be called by notices before even successfully registering
  4961. if (!s->irc_user)
  4962. return false;