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.

9166 lines
242KB

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