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.

8823 lines
233KB

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