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.

5504 lines
143KB

  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. /// Some arbitrary limit for the history file
  20. #define HISTORY_LIMIT 10000
  21. // A table of all attributes we use for output
  22. #define ATTR_TABLE(XX) \
  23. XX( PROMPT, "prompt", "Terminal attributes for the prompt" ) \
  24. XX( RESET, "reset", "String to reset terminal attributes" ) \
  25. XX( WARNING, "warning", "Terminal attributes for warnings" ) \
  26. XX( ERROR, "error", "Terminal attributes for errors" ) \
  27. XX( EXTERNAL, "external", "Terminal attributes for external lines" ) \
  28. XX( TIMESTAMP, "timestamp", "Terminal attributes for timestamps" ) \
  29. XX( HIGHLIGHT, "highlight", "Terminal attributes for highlights" ) \
  30. XX( ACTION, "action", "Terminal attributes for user actions" ) \
  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 <termios.h>
  53. #ifndef TIOCGWINSZ
  54. #include <sys/ioctl.h>
  55. #endif // ! TIOCGWINSZ
  56. #include <curses.h>
  57. #include <term.h>
  58. // Literally cancer
  59. #undef lines
  60. #undef columns
  61. #ifdef HAVE_READLINE
  62. #include <readline/readline.h>
  63. #include <readline/history.h>
  64. #endif // HAVE_READLINE
  65. #ifdef HAVE_EDITLINE
  66. #include <histedit.h>
  67. #endif // HAVE_EDITLINE
  68. // --- User interface ----------------------------------------------------------
  69. // I'm not sure which one of these backends is worse: whether it's GNU Readline
  70. // or BSD Editline. They both have their own annoying problems.
  71. struct input_buffer
  72. {
  73. #ifdef HAVE_READLINE
  74. HISTORY_STATE *history; ///< Saved history state
  75. char *saved_line; ///< Saved line content
  76. int saved_mark; ///< Saved mark
  77. #elif defined HAVE_EDITLINE
  78. HistoryW *history; ///< The history object
  79. wchar_t *saved_line; ///< Saved line content
  80. int saved_len; ///< Length of the saved line
  81. #endif // HAVE_EDITLINE
  82. int saved_point; ///< Saved cursor position
  83. };
  84. static struct input_buffer *
  85. input_buffer_new (void)
  86. {
  87. struct input_buffer *self = xcalloc (1, sizeof *self);
  88. #ifdef HAVE_EDITLINE
  89. self->history = history_winit ();
  90. HistEventW ev;
  91. history_w (self->history, &ev, H_SETSIZE, HISTORY_LIMIT);
  92. #endif // HAVE_EDITLINE
  93. return self;
  94. }
  95. static void
  96. input_buffer_destroy (struct input_buffer *self)
  97. {
  98. #ifdef HAVE_READLINE
  99. // Can't really free "history" from here
  100. #elif defined HAVE_EDITLINE
  101. history_wend (self->history);
  102. #endif // HAVE_EDITLINE
  103. free (self->saved_line);
  104. free (self);
  105. }
  106. struct input
  107. {
  108. bool active; ///< Are we a thing?
  109. #if defined HAVE_READLINE
  110. char *saved_line; ///< Saved line content
  111. int saved_point; ///< Saved cursor position
  112. int saved_mark; ///< Saved mark
  113. #elif defined HAVE_EDITLINE
  114. EditLine *editline; ///< The EditLine object
  115. char *(*saved_prompt) (EditLine *); ///< Saved prompt function
  116. char saved_char; ///< Saved char for the prompt
  117. #endif // HAVE_EDITLINE
  118. char *prompt; ///< The prompt we use
  119. int prompt_shown; ///< Whether the prompt is shown now
  120. struct input_buffer *current; ///< Current input buffer
  121. };
  122. static void
  123. input_init (struct input *self)
  124. {
  125. memset (self, 0, sizeof *self);
  126. }
  127. static void
  128. input_free (struct input *self)
  129. {
  130. #ifdef HAVE_READLINE
  131. free (self->saved_line);
  132. #endif // HAVE_READLINE
  133. free (self->prompt);
  134. }
  135. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  136. #ifdef HAVE_READLINE
  137. #define INPUT_START_IGNORE RL_PROMPT_START_IGNORE
  138. #define INPUT_END_IGNORE RL_PROMPT_END_IGNORE
  139. #define input_ding(self) rl_ding ()
  140. static void
  141. input_on_terminal_resized (struct input *self)
  142. {
  143. (void) self;
  144. // This fucks up big time on terminals with automatic wrapping such as
  145. // rxvt-unicode or newer VTE when the current line overflows, however we
  146. // can't do much about that
  147. rl_resize_terminal ();
  148. }
  149. static void
  150. input_on_readable (struct input *self)
  151. {
  152. (void) self;
  153. rl_callback_read_char ();
  154. }
  155. static void
  156. input_set_prompt (struct input *self, char *prompt)
  157. {
  158. free (self->prompt);
  159. self->prompt = prompt;
  160. // First reset the prompt to work around a bug in readline
  161. rl_set_prompt ("");
  162. if (self->prompt_shown)
  163. rl_redisplay ();
  164. rl_set_prompt (self->prompt);
  165. if (self->prompt_shown)
  166. rl_redisplay ();
  167. }
  168. static void
  169. input_erase (struct input *self)
  170. {
  171. (void) self;
  172. rl_set_prompt ("");
  173. rl_replace_line ("", 0);
  174. rl_point = rl_mark = 0;
  175. rl_redisplay ();
  176. }
  177. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  178. static int app_readline_init (void);
  179. static void on_readline_input (char *line);
  180. static void
  181. input_start (struct input *self, const char *program_name)
  182. {
  183. (void) program_name;
  184. using_history ();
  185. // This can cause memory leaks, or maybe even a segfault. Funny, eh?
  186. stifle_history (HISTORY_LIMIT);
  187. rl_startup_hook = app_readline_init;
  188. rl_catch_sigwinch = false;
  189. rl_callback_handler_install (self->prompt, on_readline_input);
  190. self->prompt_shown = 1;
  191. self->active = true;
  192. }
  193. static void
  194. input_stop (struct input *self)
  195. {
  196. if (self->prompt_shown > 0)
  197. input_erase (self);
  198. // This is okay as long as we're not called from within readline
  199. rl_callback_handler_remove ();
  200. self->active = false;
  201. }
  202. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  203. // The following part shows you why it's not a good idea to use
  204. // GNU Readline for this kind of software. Or for anything else, really.
  205. static void
  206. input_save_buffer (struct input *self, struct input_buffer *buffer)
  207. {
  208. (void) self;
  209. buffer->history = history_get_history_state ();
  210. buffer->saved_line = rl_copy_text (0, rl_end);
  211. buffer->saved_point = rl_point;
  212. buffer->saved_mark = rl_mark;
  213. }
  214. static void
  215. input_restore_buffer (struct input *self, struct input_buffer *buffer)
  216. {
  217. // Restore the target buffer's history
  218. if (buffer->history)
  219. {
  220. // history_get_history_state() just allocates a new HISTORY_STATE
  221. // and fills it with its current internal data. We don't need that
  222. // shell anymore after reviving it.
  223. history_set_history_state (buffer->history);
  224. free (buffer->history);
  225. buffer->history = NULL;
  226. }
  227. else
  228. {
  229. // This should get us a clean history while keeping the flags.
  230. // Note that we've either saved the previous history entries, or we've
  231. // cleared them altogether, so there should be nothing to leak.
  232. HISTORY_STATE *state = history_get_history_state ();
  233. state->offset = state->length = state->size = 0;
  234. history_set_history_state (state);
  235. free (state);
  236. }
  237. // Try to restore the target buffer's readline state
  238. if (buffer->saved_line)
  239. {
  240. rl_replace_line (buffer->saved_line, 0);
  241. rl_point = buffer->saved_point;
  242. rl_mark = buffer->saved_mark;
  243. free (buffer->saved_line);
  244. buffer->saved_line = NULL;
  245. if (self->prompt_shown)
  246. rl_redisplay ();
  247. }
  248. }
  249. static void
  250. input_switch_buffer (struct input *self, struct input_buffer *buffer)
  251. {
  252. // There could possibly be occurences of the current undo list in some
  253. // history entry. We either need to free the undo list, or move it
  254. // somewhere else to load back later, as the buffer we're switching to
  255. // has its own history state.
  256. rl_free_undo_list ();
  257. // Save this buffer's history so that it's independent for each buffer
  258. if (self->current)
  259. input_save_buffer (self, self->current);
  260. else
  261. // Just throw it away; there should always be an active buffer however
  262. #if RL_READLINE_VERSION >= 0x0603
  263. rl_clear_history ();
  264. #else // RL_READLINE_VERSION < 0x0603
  265. // At least something... this may leak undo entries
  266. clear_history ();
  267. #endif // RL_READLINE_VERSION < 0x0603
  268. input_restore_buffer (self, buffer);
  269. self->current = buffer;
  270. }
  271. static void
  272. input_destroy_buffer (struct input *self, struct input_buffer *buffer)
  273. {
  274. (void) self;
  275. // rl_clear_history, being the only way I know of to get rid of the complete
  276. // history including attached data, is a pretty recent addition. *sigh*
  277. #if RL_READLINE_VERSION >= 0x0603
  278. if (buffer->history)
  279. {
  280. // See input_switch_buffer() for why we need to do this BS
  281. rl_free_undo_list ();
  282. // This is probably the only way we can free the history fully
  283. HISTORY_STATE *state = history_get_history_state ();
  284. history_set_history_state (buffer->history);
  285. free (buffer->history);
  286. rl_clear_history ();
  287. history_set_history_state (state);
  288. free (state);
  289. }
  290. #endif // RL_READLINE_VERSION
  291. input_buffer_destroy (buffer);
  292. }
  293. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  294. static void
  295. input_save (struct input *self)
  296. {
  297. hard_assert (!self->saved_line);
  298. self->saved_point = rl_point;
  299. self->saved_mark = rl_mark;
  300. self->saved_line = rl_copy_text (0, rl_end);
  301. }
  302. static void
  303. input_restore (struct input *self)
  304. {
  305. hard_assert (self->saved_line);
  306. rl_set_prompt (self->prompt);
  307. rl_replace_line (self->saved_line, 0);
  308. rl_point = self->saved_point;
  309. rl_mark = self->saved_mark;
  310. free (self->saved_line);
  311. self->saved_line = NULL;
  312. }
  313. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  314. static void
  315. input_hide (struct input *self)
  316. {
  317. if (!self->active || self->prompt_shown-- < 1)
  318. return;
  319. input_save (self);
  320. input_erase (self);
  321. }
  322. static void
  323. input_show (struct input *self)
  324. {
  325. if (!self->active || ++self->prompt_shown < 1)
  326. return;
  327. input_restore (self);
  328. rl_redisplay ();
  329. }
  330. #endif // HAVE_READLINE
  331. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  332. #ifdef HAVE_EDITLINE
  333. #define INPUT_START_IGNORE '\x01'
  334. #define INPUT_END_IGNORE '\x01'
  335. static void app_editline_init (struct input *self);
  336. static void on_editline_input (struct input *self, char *line);
  337. static void
  338. input_ding (struct input *self)
  339. {
  340. (void) self;
  341. // XXX: this isn't probably very portable
  342. putc ('\a', stdout);
  343. }
  344. static void
  345. input_on_terminal_resized (struct input *self)
  346. {
  347. el_resize (self->editline);
  348. }
  349. static void
  350. input_redisplay (struct input *self)
  351. {
  352. // See rl_redisplay()
  353. // The character is VREPRINT (usually C-r)
  354. // TODO: read it from terminal info
  355. // XXX: could we potentially break UTF-8 with this?
  356. char x[] = { ('R' - 'A' + 1), 0 };
  357. el_push (self->editline, x);
  358. // We have to do this or it gets stuck and nothing is done
  359. (void) el_gets (self->editline, NULL);
  360. }
  361. static void
  362. input_set_prompt (struct input *self, char *prompt)
  363. {
  364. free (self->prompt);
  365. self->prompt = prompt;
  366. if (self->prompt_shown)
  367. input_redisplay (self);
  368. }
  369. static char *
  370. input_make_prompt (EditLine *editline)
  371. {
  372. struct input *self;
  373. el_get (editline, EL_CLIENTDATA, &self);
  374. return self->prompt;
  375. }
  376. static char *
  377. input_make_empty_prompt (EditLine *editline)
  378. {
  379. (void) editline;
  380. return "";
  381. }
  382. static void
  383. input_erase (struct input *self)
  384. {
  385. const LineInfoW *info = el_wline (self->editline);
  386. int len = info->lastchar - info->buffer;
  387. int point = info->cursor - info->buffer;
  388. el_cursor (self->editline, len - point);
  389. el_wdeletestr (self->editline, len);
  390. // XXX: this doesn't seem to save the escape character
  391. el_get (self->editline, EL_PROMPT, &self->saved_prompt, &self->saved_char);
  392. el_set (self->editline, EL_PROMPT, input_make_empty_prompt);
  393. input_redisplay (self);
  394. }
  395. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  396. static void
  397. input_start (struct input *self, const char *program_name)
  398. {
  399. self->editline = el_init (program_name, stdin, stdout, stderr);
  400. el_set (self->editline, EL_CLIENTDATA, self);
  401. el_set (self->editline, EL_PROMPT_ESC,
  402. input_make_prompt, INPUT_START_IGNORE);
  403. el_set (self->editline, EL_SIGNAL, false);
  404. el_set (self->editline, EL_UNBUFFERED, true);
  405. el_set (self->editline, EL_EDITOR, "emacs");
  406. app_editline_init (self);
  407. self->prompt_shown = 1;
  408. self->active = true;
  409. }
  410. static void
  411. input_stop (struct input *self)
  412. {
  413. if (self->prompt_shown > 0)
  414. input_erase (self);
  415. el_end (self->editline);
  416. self->editline = NULL;
  417. self->active = false;
  418. }
  419. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  420. static void
  421. input_save_buffer (struct input *self, struct input_buffer *buffer)
  422. {
  423. const LineInfoW *info = el_wline (self->editline);
  424. int len = info->lastchar - info->buffer;
  425. int point = info->cursor - info->buffer;
  426. wchar_t *line = calloc (sizeof *info->buffer, len + 1);
  427. memcpy (line, info->buffer, sizeof *info->buffer * len);
  428. el_cursor (self->editline, len - point);
  429. el_wdeletestr (self->editline, len);
  430. buffer->saved_line = line;
  431. buffer->saved_point = point;
  432. buffer->saved_len = len;
  433. }
  434. static void
  435. input_restore_buffer (struct input *self, struct input_buffer *buffer)
  436. {
  437. if (buffer->saved_line)
  438. {
  439. el_winsertstr (self->editline, buffer->saved_line);
  440. el_cursor (self->editline,
  441. -(buffer->saved_len - buffer->saved_point));
  442. free (buffer->saved_line);
  443. buffer->saved_line = NULL;
  444. }
  445. }
  446. static void
  447. input_switch_buffer (struct input *self, struct input_buffer *buffer)
  448. {
  449. if (self->current)
  450. input_save_buffer (self, self->current);
  451. input_restore_buffer (self, buffer);
  452. el_wset (self->editline, EL_HIST, history, buffer->history);
  453. self->current = buffer;
  454. }
  455. static void
  456. input_destroy_buffer (struct input *self, struct input_buffer *buffer)
  457. {
  458. (void) self;
  459. input_buffer_destroy (buffer);
  460. }
  461. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  462. static void
  463. input_save (struct input *self)
  464. {
  465. input_save_buffer (self, self->current);
  466. }
  467. static void
  468. input_restore (struct input *self)
  469. {
  470. input_restore_buffer (self, self->current);
  471. }
  472. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  473. static void
  474. input_hide (struct input *self)
  475. {
  476. if (!self->active || self->prompt_shown-- < 1)
  477. return;
  478. input_save (self);
  479. input_erase (self);
  480. }
  481. static void
  482. input_show (struct input *self)
  483. {
  484. if (!self->active || ++self->prompt_shown < 1)
  485. return;
  486. input_restore (self);
  487. // Would have used "saved_char" but it doesn't seem to work.
  488. // And it doesn't even when it does anyway (it seems to just strip it).
  489. el_set (self->editline,
  490. EL_PROMPT_ESC, input_make_prompt, INPUT_START_IGNORE);
  491. input_redisplay (self);
  492. }
  493. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  494. static void
  495. input_on_readable (struct input *self)
  496. {
  497. // We bind the return key to process it how we need to
  498. // el_gets() with EL_UNBUFFERED doesn't work with UTF-8,
  499. // we must use the wide-character interface
  500. int count = 0;
  501. const wchar_t *buf = el_wgets (self->editline, &count);
  502. if (!buf || count-- <= 0)
  503. return;
  504. // The character is VEOF (usually C-d)
  505. // TODO: read it from terminal info
  506. if (count == 0 && buf[0] == ('D' - 'A' + 1))
  507. input_ding (self);
  508. }
  509. #endif // HAVE_EDITLINE
  510. // --- Application data --------------------------------------------------------
  511. // All text stored in our data structures is encoded in UTF-8.
  512. // Or at least should be. The exception is IRC identifiers.
  513. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  514. // We need a few reference countable objects with support
  515. // for both strong and weak references
  516. /// Callback just before a reference counted object is destroyed
  517. typedef void (*destroy_cb_fn) (void *object, void *user_data);
  518. #define REF_COUNTABLE_HEADER \
  519. size_t ref_count; /**< Reference count */ \
  520. destroy_cb_fn on_destroy; /**< To remove any weak references */ \
  521. void *user_data; /**< User data for callbacks */
  522. #define REF_COUNTABLE_METHODS(name) \
  523. static struct name * \
  524. name ## _ref (struct name *self) \
  525. { \
  526. self->ref_count++; \
  527. return self; \
  528. } \
  529. \
  530. static void \
  531. name ## _unref (struct name *self) \
  532. { \
  533. if (--self->ref_count) \
  534. return; \
  535. if (self->on_destroy) \
  536. self->on_destroy (self, self->user_data); \
  537. name ## _destroy (self); \
  538. }
  539. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  540. struct user_channel
  541. {
  542. LIST_HEADER (struct user_channel)
  543. struct channel *channel; ///< Reference to channel
  544. };
  545. static struct user_channel *
  546. user_channel_new (void)
  547. {
  548. struct user_channel *self = xcalloc (1, sizeof *self);
  549. return self;
  550. }
  551. static void
  552. user_channel_destroy (struct user_channel *self)
  553. {
  554. // The "channel" reference is weak and this object should get
  555. // destroyed whenever the user stops being in the channel.
  556. free (self);
  557. }
  558. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  559. // We keep references to user information in channels and buffers,
  560. // and weak references in the name lookup table.
  561. struct user
  562. {
  563. REF_COUNTABLE_HEADER
  564. // TODO: eventually a reference to the server
  565. char *nickname; ///< Literal nickname
  566. // TODO: write code to poll for the away status
  567. bool away; ///< User is away
  568. struct user_channel *channels; ///< Channels the user is on
  569. };
  570. static struct user *
  571. user_new (void)
  572. {
  573. struct user *self = xcalloc (1, sizeof *self);
  574. self->ref_count = 1;
  575. return self;
  576. }
  577. static void
  578. user_destroy (struct user *self)
  579. {
  580. free (self->nickname);
  581. LIST_FOR_EACH (struct user_channel, iter, self->channels)
  582. user_channel_destroy (iter);
  583. free (self);
  584. }
  585. REF_COUNTABLE_METHODS (user)
  586. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  587. struct channel_user
  588. {
  589. LIST_HEADER (struct channel_user)
  590. struct user *user; ///< Reference to user
  591. char *modes; ///< Op/voice/... characters
  592. };
  593. static struct channel_user *
  594. channel_user_new (void)
  595. {
  596. struct channel_user *self = xcalloc (1, sizeof *self);
  597. return self;
  598. }
  599. static void
  600. channel_user_destroy (struct channel_user *self)
  601. {
  602. user_unref (self->user);
  603. free (self->modes);
  604. free (self);
  605. }
  606. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  607. // We keep references to channels in their buffers,
  608. // and weak references in their users and the name lookup table.
  609. // XXX: this doesn't really have to be reference countable
  610. struct channel
  611. {
  612. REF_COUNTABLE_HEADER
  613. // TODO: eventually a reference to the server
  614. char *name; ///< Channel name
  615. char *mode; ///< Channel mode
  616. char *topic; ///< Channel topic
  617. struct channel_user *users; ///< Channel users
  618. struct str_vector names_buf; ///< Buffer for RPL_NAMREPLY
  619. };
  620. static struct channel *
  621. channel_new (void)
  622. {
  623. struct channel *self = xcalloc (1, sizeof *self);
  624. self->ref_count = 1;
  625. str_vector_init (&self->names_buf);
  626. return self;
  627. }
  628. static void
  629. channel_destroy (struct channel *self)
  630. {
  631. free (self->name);
  632. free (self->mode);
  633. free (self->topic);
  634. // Owner has to make sure we have no users by now
  635. hard_assert (!self->users);
  636. str_vector_free (&self->names_buf);
  637. free (self);
  638. }
  639. REF_COUNTABLE_METHODS (channel)
  640. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  641. enum buffer_line_flags
  642. {
  643. BUFFER_LINE_HIGHLIGHT = 1 << 0 ///< The user was highlighted by this
  644. };
  645. enum buffer_line_type
  646. {
  647. BUFFER_LINE_PRIVMSG, ///< PRIVMSG
  648. BUFFER_LINE_ACTION, ///< PRIVMSG ACTION
  649. BUFFER_LINE_NOTICE, ///< NOTICE
  650. BUFFER_LINE_JOIN, ///< JOIN
  651. BUFFER_LINE_PART, ///< PART
  652. BUFFER_LINE_KICK, ///< KICK
  653. BUFFER_LINE_NICK, ///< NICK
  654. BUFFER_LINE_TOPIC, ///< TOPIC
  655. BUFFER_LINE_QUIT, ///< QUIT
  656. BUFFER_LINE_STATUS, ///< Whatever status messages
  657. BUFFER_LINE_ERROR ///< Whatever error messages
  658. };
  659. struct buffer_line_args
  660. {
  661. char *who; ///< Name of the origin or NULL (user)
  662. char *object; ///< Object of action
  663. char *text; ///< Text of message
  664. char *reason; ///< Reason for PART, KICK, QUIT
  665. };
  666. struct buffer_line
  667. {
  668. LIST_HEADER (struct buffer_line)
  669. // We use the "type" and "flags" mostly just as formatting hints
  670. enum buffer_line_type type; ///< Type of the event
  671. int flags; ///< Flags
  672. time_t when; ///< Time of the event
  673. struct buffer_line_args args; ///< Arguments
  674. };
  675. struct buffer_line *
  676. buffer_line_new (void)
  677. {
  678. struct buffer_line *self = xcalloc (1, sizeof *self);
  679. return self;
  680. }
  681. static void
  682. buffer_line_destroy (struct buffer_line *self)
  683. {
  684. free (self->args.who);
  685. free (self->args.object);
  686. free (self->args.text);
  687. free (self->args.reason);
  688. free (self);
  689. }
  690. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  691. enum buffer_type
  692. {
  693. BUFFER_GLOBAL, ///< Global information
  694. BUFFER_SERVER, ///< Server-related messages
  695. BUFFER_CHANNEL, ///< Channels
  696. BUFFER_PM ///< Private messages (query)
  697. };
  698. struct buffer
  699. {
  700. LIST_HEADER (struct buffer)
  701. enum buffer_type type; ///< Type of the buffer
  702. char *name; ///< The name of the buffer
  703. struct input_buffer *input_data; ///< User interface data
  704. // Buffer contents:
  705. struct buffer_line *lines; ///< All lines in this buffer
  706. struct buffer_line *lines_tail; ///< The tail of buffer lines
  707. unsigned lines_count; ///< How many lines we have
  708. unsigned unseen_messages_count; ///< # messages since last visited
  709. // Origin information:
  710. struct server *server; ///< Reference to server
  711. struct channel *channel; ///< Reference to channel
  712. struct user *user; ///< Reference to user
  713. };
  714. static struct buffer *
  715. buffer_new (void)
  716. {
  717. struct buffer *self = xcalloc (1, sizeof *self);
  718. self->input_data = input_buffer_new ();
  719. return self;
  720. }
  721. static void
  722. buffer_destroy (struct buffer *self)
  723. {
  724. free (self->name);
  725. if (self->input_data)
  726. input_buffer_destroy (self->input_data);
  727. LIST_FOR_EACH (struct buffer_line, iter, self->lines)
  728. buffer_line_destroy (iter);
  729. if (self->user)
  730. user_unref (self->user);
  731. if (self->channel)
  732. channel_unref (self->channel);
  733. free (self);
  734. }
  735. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  736. struct server
  737. {
  738. struct app_context *ctx; ///< Application context
  739. int irc_fd; ///< Socket FD of the server
  740. struct str read_buffer; ///< Input yet to be processed
  741. struct poller_fd irc_event; ///< IRC FD event
  742. bool irc_ready; ///< Whether we may send messages now
  743. SSL_CTX *ssl_ctx; ///< SSL context
  744. SSL *ssl; ///< SSL connection
  745. // TODO: an output queue to prevent excess floods (this will be needed
  746. // especially for away status polling)
  747. // XXX: there can be buffers for non-existent users
  748. // TODO: initialize key_strxfrm according to server properties;
  749. // note that collisions may arise on reconnecting
  750. // TODO: when disconnected, get rid of all users everywhere;
  751. // maybe also broadcast all buffers about the disconnection event
  752. // TODO: when getting connected again, rejoin all current channels
  753. struct buffer *buffer; ///< The buffer for this server
  754. struct str_map irc_users; ///< IRC user data
  755. struct str_map irc_channels; ///< IRC channel data
  756. struct str_map irc_buffer_map; ///< Maps IRC identifiers to buffers
  757. struct user *irc_user; ///< Our own user
  758. char *irc_user_mode; ///< Our current user mode
  759. char *irc_user_host; ///< Our current user@host
  760. // Events:
  761. struct poller_timer ping_tmr; ///< We should send a ping
  762. struct poller_timer timeout_tmr; ///< Connection seems to be dead
  763. struct poller_timer reconnect_tmr; ///< We should reconnect now
  764. };
  765. static void on_irc_ping_timeout (void *user_data);
  766. static void on_irc_timeout (void *user_data);
  767. static void on_irc_reconnect_timeout (void *user_data);
  768. static void
  769. server_init (struct server *self, struct poller *poller)
  770. {
  771. self->irc_fd = -1;
  772. str_init (&self->read_buffer);
  773. self->irc_ready = false;
  774. str_map_init (&self->irc_users);
  775. self->irc_users.key_xfrm = irc_strxfrm;
  776. str_map_init (&self->irc_channels);
  777. self->irc_channels.key_xfrm = irc_strxfrm;
  778. str_map_init (&self->irc_buffer_map);
  779. self->irc_buffer_map.key_xfrm = irc_strxfrm;
  780. poller_timer_init (&self->timeout_tmr, poller);
  781. self->timeout_tmr.dispatcher = on_irc_timeout;
  782. self->timeout_tmr.user_data = self;
  783. poller_timer_init (&self->ping_tmr, poller);
  784. self->ping_tmr.dispatcher = on_irc_ping_timeout;
  785. self->ping_tmr.user_data = self;
  786. poller_timer_init (&self->reconnect_tmr, poller);
  787. self->reconnect_tmr.dispatcher = on_irc_reconnect_timeout;
  788. self->reconnect_tmr.user_data = self;
  789. }
  790. static void
  791. server_free (struct server *self)
  792. {
  793. if (self->irc_fd != -1)
  794. {
  795. xclose (self->irc_fd);
  796. poller_fd_reset (&self->irc_event);
  797. }
  798. str_free (&self->read_buffer);
  799. if (self->ssl)
  800. SSL_free (self->ssl);
  801. if (self->ssl_ctx)
  802. SSL_CTX_free (self->ssl_ctx);
  803. if (self->irc_user)
  804. user_unref (self->irc_user);
  805. free (self->irc_user_mode);
  806. free (self->irc_user_host);
  807. str_map_free (&self->irc_users);
  808. str_map_free (&self->irc_channels);
  809. str_map_free (&self->irc_buffer_map);
  810. }
  811. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  812. struct app_context
  813. {
  814. // Configuration:
  815. struct config config; ///< Program configuration
  816. char *attrs[ATTR_COUNT]; ///< Terminal attributes
  817. bool no_colors; ///< Colour output mode
  818. bool reconnect; ///< Whether to reconnect on conn. fail.
  819. unsigned long reconnect_delay; ///< Reconnect delay in seconds
  820. bool isolate_buffers; ///< Isolate global/server buffers
  821. struct server server; ///< Our only server so far
  822. // Events:
  823. struct poller_fd tty_event; ///< Terminal input event
  824. struct poller_fd signal_event; ///< Signal FD event
  825. struct poller poller; ///< Manages polled descriptors
  826. bool quitting; ///< User requested quitting
  827. bool polling; ///< The event loop is running
  828. // Buffers:
  829. struct buffer *buffers; ///< All our buffers in order
  830. struct buffer *buffers_tail; ///< The tail of our buffers
  831. struct buffer *last_buffer; ///< Last used buffer
  832. // XXX: when we go multiserver, there will be collisions
  833. // TODO: make buffer names unique like weechat does
  834. struct str_map buffers_by_name; ///< Excludes GLOBAL and SERVER
  835. struct buffer *global_buffer; ///< The global buffer
  836. struct buffer *current_buffer; ///< The current buffer
  837. // TODO: So that we always output proper date change messages
  838. time_t last_displayed_msg_time; ///< Time of last displayed message
  839. // Terminal:
  840. iconv_t term_to_utf8; ///< Terminal encoding to UTF-8
  841. iconv_t term_from_utf8; ///< UTF-8 to terminal encoding
  842. iconv_t latin1_to_utf8; ///< ISO Latin 1 to UTF-8
  843. struct input input; ///< User interface
  844. }
  845. *g_ctx;
  846. static void
  847. app_context_init (struct app_context *self)
  848. {
  849. memset (self, 0, sizeof *self);
  850. config_init (&self->config);
  851. poller_init (&self->poller);
  852. server_init (&self->server, &self->poller);
  853. self->server.ctx = self;
  854. str_map_init (&self->buffers_by_name);
  855. self->buffers_by_name.key_xfrm = irc_strxfrm;
  856. self->last_displayed_msg_time = time (NULL);
  857. char *encoding = nl_langinfo (CODESET);
  858. #ifdef __linux__
  859. encoding = xstrdup_printf ("%s//TRANSLIT", encoding);
  860. #else // ! __linux__
  861. encoding = xstrdup (encoding);
  862. #endif // ! __linux__
  863. if ((self->term_from_utf8 =
  864. iconv_open (encoding, "UTF-8")) == (iconv_t) -1
  865. || (self->latin1_to_utf8 =
  866. iconv_open ("UTF-8", "ISO-8859-1")) == (iconv_t) -1
  867. || (self->term_to_utf8 =
  868. iconv_open ("UTF-8", nl_langinfo (CODESET))) == (iconv_t) -1)
  869. exit_fatal ("creating the UTF-8 conversion object failed: %s",
  870. strerror (errno));
  871. free (encoding);
  872. input_init (&self->input);
  873. }
  874. static void
  875. app_context_free (struct app_context *self)
  876. {
  877. config_free (&self->config);
  878. for (size_t i = 0; i < ATTR_COUNT; i++)
  879. free (self->attrs[i]);
  880. // FIXME: this doesn't free the history state
  881. LIST_FOR_EACH (struct buffer, iter, self->buffers)
  882. buffer_destroy (iter);
  883. str_map_free (&self->buffers_by_name);
  884. server_free (&self->server);
  885. poller_free (&self->poller);
  886. iconv_close (self->latin1_to_utf8);
  887. iconv_close (self->term_from_utf8);
  888. iconv_close (self->term_to_utf8);
  889. input_free (&self->input);
  890. }
  891. static void refresh_prompt (struct app_context *ctx);
  892. static char *irc_cut_nickname (const char *prefix);
  893. static const char *irc_find_userhost (const char *prefix);
  894. // --- Configuration -----------------------------------------------------------
  895. // TODO: eventually add "on_change" callbacks
  896. static bool
  897. config_validate_nonjunk_string
  898. (const struct config_item_ *item, struct error **e)
  899. {
  900. if (item->type == CONFIG_ITEM_NULL)
  901. return true;
  902. hard_assert (config_item_type_is_string (item->type));
  903. for (size_t i = 0; i < item->value.string.len; i++)
  904. {
  905. // Not even a tabulator
  906. unsigned char c = item->value.string.str[i];
  907. if (c < 32)
  908. {
  909. error_set (e, "control characters are not allowed");
  910. return false;
  911. }
  912. }
  913. return true;
  914. }
  915. static bool
  916. config_validate_nonnegative
  917. (const struct config_item_ *item, struct error **e)
  918. {
  919. if (item->type == CONFIG_ITEM_NULL)
  920. return true;
  921. hard_assert (item->type == CONFIG_ITEM_INTEGER);
  922. if (item->value.integer >= 0)
  923. return true;
  924. error_set (e, "must be non-negative");
  925. return false;
  926. }
  927. struct config_schema g_config_server[] =
  928. {
  929. { .name = "nickname",
  930. .comment = "IRC nickname",
  931. .type = CONFIG_ITEM_STRING,
  932. .validate = config_validate_nonjunk_string },
  933. { .name = "username",
  934. .comment = "IRC user name",
  935. .type = CONFIG_ITEM_STRING,
  936. .validate = config_validate_nonjunk_string },
  937. { .name = "realname",
  938. .comment = "IRC real name/e-mail",
  939. .type = CONFIG_ITEM_STRING,
  940. .validate = config_validate_nonjunk_string },
  941. { .name = "irc_host",
  942. .comment = "Address of the IRC server",
  943. .type = CONFIG_ITEM_STRING,
  944. .validate = config_validate_nonjunk_string },
  945. { .name = "irc_port",
  946. .comment = "Port of the IRC server",
  947. .type = CONFIG_ITEM_INTEGER,
  948. .validate = config_validate_nonnegative,
  949. .default_ = "6667" },
  950. { .name = "ssl",
  951. .comment = "Whether to use SSL/TLS",
  952. .type = CONFIG_ITEM_BOOLEAN,
  953. .default_ = "off" },
  954. { .name = "ssl_cert",
  955. .comment = "Client SSL certificate (PEM)",
  956. .type = CONFIG_ITEM_STRING },
  957. { .name = "ssl_verify",
  958. .comment = "Whether to verify certificates",
  959. .type = CONFIG_ITEM_BOOLEAN,
  960. .default_ = "on" },
  961. { .name = "ssl_ca_file",
  962. .comment = "OpenSSL CA bundle file",
  963. .type = CONFIG_ITEM_STRING },
  964. { .name = "ssl_ca_path",
  965. .comment = "OpenSSL CA bundle path",
  966. .type = CONFIG_ITEM_STRING },
  967. { .name = "autojoin",
  968. .comment = "Channels to join on start",
  969. .type = CONFIG_ITEM_STRING_ARRAY,
  970. .validate = config_validate_nonjunk_string },
  971. { .name = "reconnect",
  972. .comment = "Whether to reconnect on error",
  973. .type = CONFIG_ITEM_BOOLEAN,
  974. .default_ = "on" },
  975. { .name = "reconnect_delay",
  976. .comment = "Time between reconnecting",
  977. .type = CONFIG_ITEM_INTEGER,
  978. .default_ = "5" },
  979. { .name = "socks_host",
  980. .comment = "Address of a SOCKS 4a/5 proxy",
  981. .type = CONFIG_ITEM_STRING,
  982. .validate = config_validate_nonjunk_string },
  983. { .name = "socks_port",
  984. .comment = "SOCKS port number",
  985. .type = CONFIG_ITEM_INTEGER,
  986. .validate = config_validate_nonnegative,
  987. .default_ = "1080" },
  988. { .name = "socks_username",
  989. .comment = "SOCKS auth. username",
  990. .type = CONFIG_ITEM_STRING },
  991. { .name = "socks_password",
  992. .comment = "SOCKS auth. password",
  993. .type = CONFIG_ITEM_STRING },
  994. {}
  995. };
  996. struct config_schema g_config_behaviour[] =
  997. {
  998. { .name = "isolate_buffers",
  999. .comment = "Don't leak messages from the server and global buffers",
  1000. .type = CONFIG_ITEM_BOOLEAN,
  1001. .default_ = "off" },
  1002. {}
  1003. };
  1004. struct config_schema g_config_attributes[] =
  1005. {
  1006. #define XX(x, y, z) { .name = y, .comment = z, .type = CONFIG_ITEM_STRING },
  1007. ATTR_TABLE (XX)
  1008. #undef XX
  1009. {}
  1010. };
  1011. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1012. static void
  1013. load_config_server (struct config_item_ *subtree, void *user_data)
  1014. {
  1015. (void) user_data;
  1016. // This will eventually iterate over the object and create servers
  1017. config_schema_apply_to_object (g_config_server, subtree);
  1018. }
  1019. static void
  1020. load_config_behaviour (struct config_item_ *subtree, void *user_data)
  1021. {
  1022. (void) user_data;
  1023. config_schema_apply_to_object (g_config_behaviour, subtree);
  1024. }
  1025. static void
  1026. load_config_attributes (struct config_item_ *subtree, void *user_data)
  1027. {
  1028. (void) user_data;
  1029. config_schema_apply_to_object (g_config_attributes, subtree);
  1030. }
  1031. static void
  1032. register_config_modules (struct app_context *ctx)
  1033. {
  1034. struct config *config = &ctx->config;
  1035. config_register_module (config,
  1036. "server", load_config_server, ctx);
  1037. config_register_module (config,
  1038. "behaviour", load_config_behaviour, ctx);
  1039. config_register_module (config,
  1040. "attributes", load_config_attributes, ctx);
  1041. }
  1042. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1043. static const char *
  1044. get_config_string (struct app_context *ctx, const char *key)
  1045. {
  1046. struct config_item_ *item = config_item_get (ctx->config.root, key, NULL);
  1047. hard_assert (item);
  1048. if (item->type == CONFIG_ITEM_NULL)
  1049. return NULL;
  1050. hard_assert (config_item_type_is_string (item->type));
  1051. return item->value.string.str;
  1052. }
  1053. static bool
  1054. set_config_string (struct app_context *ctx, const char *key, const char *value)
  1055. {
  1056. struct config_item_ *item = config_item_get (ctx->config.root, key, NULL);
  1057. hard_assert (item);
  1058. struct str s;
  1059. str_init (&s);
  1060. str_append (&s, value);
  1061. struct config_item_ *new_ = config_item_string (&s);
  1062. str_free (&s);
  1063. struct error *e = NULL;
  1064. if (config_item_set_from (item, new_, &e))
  1065. return true;
  1066. config_item_destroy (new_);
  1067. print_error ("couldn't set `%s' in configuration: %s", key, e->message);
  1068. error_free (e);
  1069. return false;
  1070. }
  1071. static int64_t
  1072. get_config_integer (struct app_context *ctx, const char *key)
  1073. {
  1074. struct config_item_ *item = config_item_get (ctx->config.root, key, NULL);
  1075. hard_assert (item && item->type == CONFIG_ITEM_INTEGER);
  1076. return item->value.integer;
  1077. }
  1078. static bool
  1079. get_config_boolean (struct app_context *ctx, const char *key)
  1080. {
  1081. struct config_item_ *item = config_item_get (ctx->config.root, key, NULL);
  1082. hard_assert (item && item->type == CONFIG_ITEM_BOOLEAN);
  1083. return item->value.boolean;
  1084. }
  1085. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1086. static char *
  1087. write_configuration_file (const struct str *data, struct error **e)
  1088. {
  1089. struct str path;
  1090. str_init (&path);
  1091. get_xdg_home_dir (&path, "XDG_CONFIG_HOME", ".config");
  1092. str_append (&path, "/" PROGRAM_NAME);
  1093. if (!mkdir_with_parents (path.str, e))
  1094. goto error;
  1095. str_append (&path, "/" PROGRAM_NAME ".conf");
  1096. FILE *fp = fopen (path.str, "w");
  1097. if (!fp)
  1098. {
  1099. error_set (e, "could not open `%s' for writing: %s",
  1100. path.str, strerror (errno));
  1101. goto error;
  1102. }
  1103. errno = 0;
  1104. fwrite (data->str, data->len, 1, fp);
  1105. fclose (fp);
  1106. if (errno)
  1107. {
  1108. error_set (e, "writing to `%s' failed: %s", path.str, strerror (errno));
  1109. goto error;
  1110. }
  1111. return str_steal (&path);
  1112. error:
  1113. str_free (&path);
  1114. return NULL;
  1115. }
  1116. static void
  1117. serialize_configuration (struct app_context *ctx, struct str *output)
  1118. {
  1119. str_append (output,
  1120. "# " PROGRAM_NAME " " PROGRAM_VERSION " configuration file\n"
  1121. "#\n"
  1122. "# Relative paths are searched for in ${XDG_CONFIG_HOME:-~/.config}\n"
  1123. "# /" PROGRAM_NAME " as well as in $XDG_CONFIG_DIRS/" PROGRAM_NAME "\n"
  1124. "#\n"
  1125. "# Everything is in UTF-8. Any custom comments will be overwritten.\n"
  1126. "\n");
  1127. config_item_write (ctx->config.root, true, output);
  1128. }
  1129. // --- Attributed output -------------------------------------------------------
  1130. static struct
  1131. {
  1132. bool initialized; ///< Terminal is available
  1133. bool stdout_is_tty; ///< `stdout' is a terminal
  1134. bool stderr_is_tty; ///< `stderr' is a terminal
  1135. char *color_set_fg[8]; ///< Codes to set the foreground colour
  1136. char *color_set_bg[8]; ///< Codes to set the background colour
  1137. int lines; ///< Number of lines
  1138. int columns; ///< Number of columns
  1139. }
  1140. g_terminal;
  1141. static void
  1142. update_screen_size (void)
  1143. {
  1144. #ifdef TIOCGWINSZ
  1145. if (!g_terminal.stdout_is_tty)
  1146. return;
  1147. struct winsize size;
  1148. if (!ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &size))
  1149. {
  1150. char *row = getenv ("LINES");
  1151. char *col = getenv ("COLUMNS");
  1152. unsigned long tmp;
  1153. g_terminal.lines =
  1154. (row && xstrtoul (&tmp, row, 10)) ? tmp : size.ws_row;
  1155. g_terminal.columns =
  1156. (col && xstrtoul (&tmp, col, 10)) ? tmp : size.ws_col;
  1157. }
  1158. #endif // TIOCGWINSZ
  1159. }
  1160. static bool
  1161. init_terminal (void)
  1162. {
  1163. int tty_fd = -1;
  1164. if ((g_terminal.stderr_is_tty = isatty (STDERR_FILENO)))
  1165. tty_fd = STDERR_FILENO;
  1166. if ((g_terminal.stdout_is_tty = isatty (STDOUT_FILENO)))
  1167. tty_fd = STDOUT_FILENO;
  1168. int err;
  1169. if (tty_fd == -1 || setupterm (NULL, tty_fd, &err) == ERR)
  1170. return false;
  1171. // Make sure all terminal features used by us are supported
  1172. if (!set_a_foreground || !set_a_background
  1173. || !enter_bold_mode || !exit_attribute_mode)
  1174. {
  1175. del_curterm (cur_term);
  1176. return false;
  1177. }
  1178. g_terminal.lines = tigetnum ("lines");
  1179. g_terminal.columns = tigetnum ("cols");
  1180. update_screen_size ();
  1181. for (size_t i = 0; i < N_ELEMENTS (g_terminal.color_set_fg); i++)
  1182. {
  1183. g_terminal.color_set_fg[i] = xstrdup (tparm (set_a_foreground,
  1184. i, 0, 0, 0, 0, 0, 0, 0, 0));
  1185. g_terminal.color_set_bg[i] = xstrdup (tparm (set_a_background,
  1186. i, 0, 0, 0, 0, 0, 0, 0, 0));
  1187. }
  1188. return g_terminal.initialized = true;
  1189. }
  1190. static void
  1191. free_terminal (void)
  1192. {
  1193. if (!g_terminal.initialized)
  1194. return;
  1195. for (size_t i = 0; i < N_ELEMENTS (g_terminal.color_set_fg); i++)
  1196. {
  1197. free (g_terminal.color_set_fg[i]);
  1198. free (g_terminal.color_set_bg[i]);
  1199. }
  1200. del_curterm (cur_term);
  1201. }
  1202. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1203. typedef int (*terminal_printer_fn) (int);
  1204. static int
  1205. putchar_stderr (int c)
  1206. {
  1207. return fputc (c, stderr);
  1208. }
  1209. static terminal_printer_fn
  1210. get_attribute_printer (FILE *stream)
  1211. {
  1212. if (stream == stdout && g_terminal.stdout_is_tty)
  1213. return putchar;
  1214. if (stream == stderr && g_terminal.stderr_is_tty)
  1215. return putchar_stderr;
  1216. return NULL;
  1217. }
  1218. static void
  1219. vprint_attributed (struct app_context *ctx,
  1220. FILE *stream, intptr_t attribute, const char *fmt, va_list ap)
  1221. {
  1222. terminal_printer_fn printer = get_attribute_printer (stream);
  1223. if (!attribute)
  1224. printer = NULL;
  1225. if (printer)
  1226. tputs (ctx->attrs[attribute], 1, printer);
  1227. vfprintf (stream, fmt, ap);
  1228. if (printer)
  1229. tputs (ctx->attrs[ATTR_RESET], 1, printer);
  1230. }
  1231. static void
  1232. print_attributed (struct app_context *ctx,
  1233. FILE *stream, intptr_t attribute, const char *fmt, ...)
  1234. {
  1235. va_list ap;
  1236. va_start (ap, fmt);
  1237. vprint_attributed (ctx, stream, attribute, fmt, ap);
  1238. va_end (ap);
  1239. }
  1240. static void
  1241. log_message_attributed (void *user_data, const char *quote, const char *fmt,
  1242. va_list ap)
  1243. {
  1244. FILE *stream = stderr;
  1245. input_hide (&g_ctx->input);
  1246. print_attributed (g_ctx, stream, (intptr_t) user_data, "%s", quote);
  1247. vprint_attributed (g_ctx, stream, (intptr_t) user_data, fmt, ap);
  1248. fputs ("\n", stream);
  1249. input_show (&g_ctx->input);
  1250. }
  1251. static void
  1252. init_attribute (struct app_context *ctx, int id, const char *default_)
  1253. {
  1254. static const char *table[ATTR_COUNT] =
  1255. {
  1256. #define XX(x, y, z) [ATTR_ ## x] = "attributes." y,
  1257. ATTR_TABLE (XX)
  1258. #undef XX
  1259. };
  1260. const char *user = get_config_string (ctx, table[id]);
  1261. if (user)
  1262. ctx->attrs[id] = xstrdup (user);
  1263. else
  1264. ctx->attrs[id] = xstrdup (default_);
  1265. }
  1266. static void
  1267. init_colors (struct app_context *ctx)
  1268. {
  1269. bool have_ti = init_terminal ();
  1270. #define INIT_ATTR(id, ti) init_attribute (ctx, ATTR_ ## id, have_ti ? (ti) : "")
  1271. INIT_ATTR (PROMPT, enter_bold_mode);
  1272. INIT_ATTR (RESET, exit_attribute_mode);
  1273. INIT_ATTR (WARNING, g_terminal.color_set_fg[COLOR_YELLOW]);
  1274. INIT_ATTR (ERROR, g_terminal.color_set_fg[COLOR_RED]);
  1275. INIT_ATTR (EXTERNAL, g_terminal.color_set_fg[COLOR_WHITE]);
  1276. INIT_ATTR (TIMESTAMP, g_terminal.color_set_fg[COLOR_WHITE]);
  1277. INIT_ATTR (ACTION, g_terminal.color_set_fg[COLOR_RED]);
  1278. INIT_ATTR (JOIN, g_terminal.color_set_fg[COLOR_GREEN]);
  1279. INIT_ATTR (PART, g_terminal.color_set_fg[COLOR_RED]);
  1280. char *highlight = xstrdup_printf ("%s%s%s",
  1281. g_terminal.color_set_fg[COLOR_YELLOW],
  1282. g_terminal.color_set_bg[COLOR_MAGENTA],
  1283. enter_bold_mode);
  1284. INIT_ATTR (HIGHLIGHT, highlight);
  1285. free (highlight);
  1286. #undef INIT_ATTR
  1287. if (ctx->no_colors)
  1288. {
  1289. g_terminal.stdout_is_tty = false;
  1290. g_terminal.stderr_is_tty = false;
  1291. }
  1292. g_log_message_real = log_message_attributed;
  1293. }
  1294. // --- Signals -----------------------------------------------------------------
  1295. static int g_signal_pipe[2]; ///< A pipe used to signal... signals
  1296. /// Program termination has been requested by a signal
  1297. static volatile sig_atomic_t g_termination_requested;
  1298. /// The window has changed in size
  1299. static volatile sig_atomic_t g_winch_received;
  1300. static void
  1301. sigterm_handler (int signum)
  1302. {
  1303. (void) signum;
  1304. g_termination_requested = true;
  1305. int original_errno = errno;
  1306. if (write (g_signal_pipe[1], "t", 1) == -1)
  1307. soft_assert (errno == EAGAIN);
  1308. errno = original_errno;
  1309. }
  1310. static void
  1311. sigwinch_handler (int signum)
  1312. {
  1313. (void) signum;
  1314. g_winch_received = true;
  1315. int original_errno = errno;
  1316. if (write (g_signal_pipe[1], "w", 1) == -1)
  1317. soft_assert (errno == EAGAIN);
  1318. errno = original_errno;
  1319. }
  1320. static void
  1321. setup_signal_handlers (void)
  1322. {
  1323. if (pipe (g_signal_pipe) == -1)
  1324. exit_fatal ("%s: %s", "pipe", strerror (errno));
  1325. set_cloexec (g_signal_pipe[0]);
  1326. set_cloexec (g_signal_pipe[1]);
  1327. // So that the pipe cannot overflow; it would make write() block within
  1328. // the signal handler, which is something we really don't want to happen.
  1329. // The same holds true for read().
  1330. set_blocking (g_signal_pipe[0], false);
  1331. set_blocking (g_signal_pipe[1], false);
  1332. signal (SIGPIPE, SIG_IGN);
  1333. struct sigaction sa;
  1334. sa.sa_flags = SA_RESTART;
  1335. sa.sa_handler = sigwinch_handler;
  1336. sigemptyset (&sa.sa_mask);
  1337. if (sigaction (SIGWINCH, &sa, NULL) == -1)
  1338. exit_fatal ("sigaction: %s", strerror (errno));
  1339. sa.sa_handler = sigterm_handler;
  1340. if (sigaction (SIGINT, &sa, NULL) == -1
  1341. || sigaction (SIGTERM, &sa, NULL) == -1)
  1342. exit_fatal ("sigaction: %s", strerror (errno));
  1343. }
  1344. // --- Output formatter --------------------------------------------------------
  1345. // This complicated piece of code makes attributed text formatting simple.
  1346. // We use a printf-inspired syntax to push attributes and text to the object,
  1347. // then flush it either to a terminal, or a log file with formatting stripped.
  1348. //
  1349. // Format strings use a #-quoted notation, to differentiate from printf:
  1350. // #s inserts a string
  1351. // #d inserts a signed integer; also supports the #<N> and #0<N> notation
  1352. //
  1353. // #a inserts named attributes (auto-resets)
  1354. // #r resets terminal attributes
  1355. // #c sets foreground color
  1356. // #C sets background color
  1357. enum formatter_item_type
  1358. {
  1359. FORMATTER_ITEM_TEXT, ///< Text
  1360. FORMATTER_ITEM_ATTR, ///< Formatting attributes
  1361. FORMATTER_ITEM_FG_COLOR, ///< Foreground color
  1362. FORMATTER_ITEM_BG_COLOR ///< Background color
  1363. };
  1364. struct formatter_item
  1365. {
  1366. LIST_HEADER (struct formatter_item)
  1367. enum formatter_item_type type; ///< Type of this item
  1368. int color; ///< Color
  1369. int attribute; ///< Attribute ID
  1370. char *text; ///< Either text or an attribute string
  1371. };
  1372. static struct formatter_item *
  1373. formatter_item_new (void)
  1374. {
  1375. struct formatter_item *self = xcalloc (1, sizeof *self);
  1376. return self;
  1377. }
  1378. static void
  1379. formatter_item_destroy (struct formatter_item *self)
  1380. {
  1381. free (self->text);
  1382. free (self);
  1383. }
  1384. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1385. struct formatter
  1386. {
  1387. struct app_context *ctx; ///< Application context
  1388. bool ignore_new_attributes; ///< Whether to ignore new attributes
  1389. struct formatter_item *items; ///< Items
  1390. struct formatter_item *items_tail; ///< Tail of items
  1391. };
  1392. static void
  1393. formatter_init (struct formatter *self, struct app_context *ctx)
  1394. {
  1395. memset (self, 0, sizeof *self);
  1396. self->ctx = ctx;
  1397. }
  1398. static void
  1399. formatter_free (struct formatter *self)
  1400. {
  1401. LIST_FOR_EACH (struct formatter_item, iter, self->items)
  1402. formatter_item_destroy (iter);
  1403. }
  1404. static struct formatter_item *
  1405. formatter_add_blank (struct formatter *self)
  1406. {
  1407. struct formatter_item *item = formatter_item_new ();
  1408. LIST_APPEND_WITH_TAIL (self->items, self->items_tail, item);
  1409. return item;
  1410. }
  1411. static void
  1412. formatter_add_text (struct formatter *self, const char *text)
  1413. {
  1414. struct formatter_item *item = formatter_add_blank (self);
  1415. item->type = FORMATTER_ITEM_TEXT;
  1416. item->text = xstrdup (text);
  1417. }
  1418. static void
  1419. formatter_add_reset (struct formatter *self)
  1420. {
  1421. if (self->ignore_new_attributes)
  1422. return;
  1423. struct formatter_item *item = formatter_add_blank (self);
  1424. item->type = FORMATTER_ITEM_ATTR;
  1425. item->attribute = ATTR_RESET;
  1426. }
  1427. static void
  1428. formatter_add_attr (struct formatter *self, int attr_id)
  1429. {
  1430. if (self->ignore_new_attributes)
  1431. return;
  1432. struct formatter_item *item = formatter_add_blank (self);
  1433. item->type = FORMATTER_ITEM_ATTR;
  1434. item->attribute = attr_id;
  1435. }
  1436. static void
  1437. formatter_add_fg_color (struct formatter *self, int color)
  1438. {
  1439. if (self->ignore_new_attributes)
  1440. return;
  1441. struct formatter_item *item = formatter_add_blank (self);
  1442. item->type = FORMATTER_ITEM_FG_COLOR;
  1443. item->color = color;
  1444. }
  1445. static void
  1446. formatter_add_bg_color (struct formatter *self, int color)
  1447. {
  1448. if (self->ignore_new_attributes)
  1449. return;
  1450. struct formatter_item *item = formatter_add_blank (self);
  1451. item->type = FORMATTER_ITEM_BG_COLOR;
  1452. item->color = color;
  1453. }
  1454. static const char *
  1455. formatter_parse_field (struct formatter *self,
  1456. const char *field, struct str *buf, va_list *ap)
  1457. {
  1458. size_t width = 0;
  1459. bool zero_padded = false;
  1460. int c;
  1461. restart:
  1462. switch ((c = *field++))
  1463. {
  1464. char *s;
  1465. // We can push boring text content to the caller's buffer
  1466. // and let it flush the buffer only when it's actually needed
  1467. case 's':
  1468. s = va_arg (*ap, char *);
  1469. for (size_t len = strlen (s); len < width; len++)
  1470. str_append_c (buf, ' ');
  1471. str_append (buf, s);
  1472. break;
  1473. case 'd':
  1474. s = xstrdup_printf ("%d", va_arg (*ap, int));
  1475. for (size_t len = strlen (s); len < width; len++)
  1476. str_append_c (buf, " 0"[zero_padded]);
  1477. str_append (buf, s);
  1478. free (s);
  1479. break;
  1480. case 'a':
  1481. formatter_add_attr (self, va_arg (*ap, int));
  1482. break;
  1483. case 'c':
  1484. formatter_add_fg_color (self, va_arg (*ap, int));
  1485. break;
  1486. case 'C':
  1487. formatter_add_bg_color (self, va_arg (*ap, int));
  1488. break;
  1489. case 'r':
  1490. formatter_add_reset (self);
  1491. break;
  1492. default:
  1493. if (c == '0' && !zero_padded)
  1494. zero_padded = true;
  1495. else if (isdigit_ascii (c))
  1496. width = width * 10 + (c - '0');
  1497. else if (c)
  1498. hard_assert (!"unexpected format specifier");
  1499. else
  1500. hard_assert (!"unexpected end of format string");
  1501. goto restart;
  1502. }
  1503. return field;
  1504. }
  1505. static void
  1506. formatter_add (struct formatter *self, const char *format, ...)
  1507. {
  1508. struct str buf;
  1509. str_init (&buf);
  1510. va_list ap;
  1511. va_start (ap, format);
  1512. while (*format)
  1513. {
  1514. if (*format != '#' || *++format == '#')
  1515. {
  1516. str_append_c (&buf, *format++);
  1517. continue;
  1518. }
  1519. if (buf.len)
  1520. {
  1521. formatter_add_text (self, buf.str);
  1522. str_reset (&buf);
  1523. }
  1524. format = formatter_parse_field (self, format, &buf, &ap);
  1525. }
  1526. if (buf.len)
  1527. formatter_add_text (self, buf.str);
  1528. str_free (&buf);
  1529. va_end (ap);
  1530. }
  1531. static void
  1532. formatter_flush (struct formatter *self, FILE *stream)
  1533. {
  1534. terminal_printer_fn printer = get_attribute_printer (stream);
  1535. if (!printer)
  1536. {
  1537. LIST_FOR_EACH (struct formatter_item, iter, self->items)
  1538. if (iter->type == FORMATTER_ITEM_TEXT)
  1539. fputs (iter->text, stream);
  1540. return;
  1541. }
  1542. const char *attr_reset = self->ctx->attrs[ATTR_RESET];
  1543. tputs (attr_reset, 1, printer);
  1544. bool is_attributed = false;
  1545. LIST_FOR_EACH (struct formatter_item, iter, self->items)
  1546. {
  1547. switch (iter->type)
  1548. {
  1549. char *term;
  1550. case FORMATTER_ITEM_TEXT:
  1551. term = iconv_xstrdup
  1552. (self->ctx->term_from_utf8, iter->text, -1, NULL);
  1553. fputs (term, stream);
  1554. free (term);
  1555. break;
  1556. case FORMATTER_ITEM_ATTR:
  1557. if (is_attributed)
  1558. {
  1559. tputs (attr_reset, 1, printer);
  1560. is_attributed = false;
  1561. }
  1562. if (iter->attribute != ATTR_RESET)
  1563. {
  1564. tputs (self->ctx->attrs[iter->attribute], 1, printer);
  1565. is_attributed = true;
  1566. }
  1567. break;
  1568. case FORMATTER_ITEM_FG_COLOR:
  1569. tputs (g_terminal.color_set_fg[iter->color], 1, printer);
  1570. is_attributed = true;
  1571. break;
  1572. case FORMATTER_ITEM_BG_COLOR:
  1573. tputs (g_terminal.color_set_bg[iter->color], 1, printer);
  1574. is_attributed = true;
  1575. break;
  1576. }
  1577. }
  1578. if (is_attributed)
  1579. tputs (attr_reset, 1, printer);
  1580. }
  1581. // --- Buffers -----------------------------------------------------------------
  1582. static void
  1583. buffer_update_time (struct app_context *ctx, time_t now)
  1584. {
  1585. struct tm last, current;
  1586. if (!localtime_r (&ctx->last_displayed_msg_time, &last)
  1587. || !localtime_r (&now, &current))
  1588. {
  1589. // Strange but nonfatal
  1590. print_error ("%s: %s", "localtime_r", strerror (errno));
  1591. return;
  1592. }
  1593. ctx->last_displayed_msg_time = now;
  1594. if (last.tm_year == current.tm_year
  1595. && last.tm_mon == current.tm_mon
  1596. && last.tm_mday == current.tm_mday)
  1597. return;
  1598. char buf[32] = "";
  1599. if (soft_assert (strftime (buf, sizeof buf, "%F", &current)))
  1600. print_status ("%s", buf);
  1601. // Else the buffer was too small, which is pretty weird
  1602. }
  1603. static void
  1604. buffer_line_display (struct app_context *ctx,
  1605. struct buffer_line *line, bool is_external)
  1606. {
  1607. // Normal timestamps don't include the date, this way the user won't be
  1608. // confused as to when an event has happened
  1609. buffer_update_time (ctx, line->when);
  1610. struct buffer_line_args *a = &line->args;
  1611. char *nick = NULL;
  1612. const char *userhost = NULL;
  1613. int nick_color = -1;
  1614. int object_color = -1;
  1615. if (a->who)
  1616. {
  1617. nick = irc_cut_nickname (a->who);
  1618. userhost = irc_find_userhost (a->who);
  1619. nick_color = str_map_hash (nick, strlen (nick)) % 8;
  1620. }
  1621. if (a->object)
  1622. object_color = str_map_hash (a->object, strlen (a->object)) % 8;
  1623. struct formatter f;
  1624. formatter_init (&f, ctx);
  1625. struct tm current;
  1626. if (!localtime_r (&line->when, &current))
  1627. print_error ("%s: %s", "localtime_r", strerror (errno));
  1628. else
  1629. formatter_add (&f, "#a#02d:#02d:#02d#r ",
  1630. ATTR_TIMESTAMP, current.tm_hour, current.tm_min, current.tm_sec);
  1631. // Ignore all formatting for messages coming from other buffers, that is
  1632. // either from the global or server buffer. Instead print them in grey.
  1633. if (is_external)
  1634. {
  1635. formatter_add (&f, "#a", ATTR_EXTERNAL);
  1636. f.ignore_new_attributes = true;
  1637. }
  1638. // TODO: try to decode as much as possible using mIRC formatting;
  1639. // could either add a #m format specifier, or write a separate function
  1640. // to translate the formatting into formatter API calls
  1641. switch (line->type)
  1642. {
  1643. case BUFFER_LINE_PRIVMSG:
  1644. if (line->flags & BUFFER_LINE_HIGHLIGHT)
  1645. formatter_add (&f, "#a<#s>#r #s", ATTR_HIGHLIGHT, nick, a->text);
  1646. else
  1647. formatter_add (&f, "<#c#s#r> #s", nick_color, nick, a->text);
  1648. break;
  1649. case BUFFER_LINE_ACTION:
  1650. if (line->flags & BUFFER_LINE_HIGHLIGHT)
  1651. formatter_add (&f, " #a*#r ", ATTR_HIGHLIGHT);
  1652. else
  1653. formatter_add (&f, " #a*#r ", ATTR_ACTION);
  1654. formatter_add (&f, "#c#s#r #s", nick_color, nick, a->text);
  1655. break;
  1656. case BUFFER_LINE_NOTICE:
  1657. formatter_add (&f, " - ");
  1658. if (line->flags & BUFFER_LINE_HIGHLIGHT)
  1659. formatter_add (&f, "#a#s(#s)#r: #s",
  1660. ATTR_HIGHLIGHT, "Notice", nick, a->text);
  1661. else
  1662. formatter_add (&f, "#s(#c#s#r): #s",
  1663. "Notice", nick_color, nick, a->text);
  1664. break;
  1665. case BUFFER_LINE_JOIN:
  1666. formatter_add (&f, "#a-->#r ", ATTR_JOIN);
  1667. formatter_add (&f, "#c#s#r (#s) #a#s#r #s",
  1668. nick_color, nick, userhost,
  1669. ATTR_JOIN, "has joined", a->object);
  1670. break;
  1671. case BUFFER_LINE_PART:
  1672. formatter_add (&f, "#a<--#r ", ATTR_PART);
  1673. formatter_add (&f, "#c#s#r (#s) #a#s#r #s",
  1674. nick_color, nick, userhost,
  1675. ATTR_PART, "has left", a->object);
  1676. if (a->reason)
  1677. formatter_add (&f, " (#s)", a->reason);
  1678. break;
  1679. case BUFFER_LINE_KICK:
  1680. formatter_add (&f, "#a<--#r ", ATTR_PART);
  1681. formatter_add (&f, "#c#s#r (#s) #a#s#r #c#s#r",
  1682. nick_color, nick, userhost,
  1683. ATTR_PART, "has kicked", object_color, a->object);
  1684. if (a->reason)
  1685. formatter_add (&f, " (#s)", a->reason);
  1686. break;
  1687. case BUFFER_LINE_NICK:
  1688. formatter_add (&f, " - ");
  1689. if (a->who)
  1690. formatter_add (&f, "#c#s#r #s #c#s#r",
  1691. nick_color, nick,
  1692. "is now known as", object_color, a->object);
  1693. else
  1694. formatter_add (&f, "#s #s",
  1695. "You are now known as", a->object);
  1696. break;
  1697. case BUFFER_LINE_TOPIC:
  1698. formatter_add (&f, " - ");
  1699. formatter_add (&f, "#c#s#r #s \"#s\"",
  1700. nick_color, nick,
  1701. "has changed the topic to", a->text);
  1702. break;
  1703. case BUFFER_LINE_QUIT:
  1704. formatter_add (&f, "#a<--#r ", ATTR_PART);
  1705. formatter_add (&f, "#c#s#r (%s) #a#s#r",
  1706. nick_color, nick, userhost,
  1707. ATTR_PART, "has quit");
  1708. if (a->reason)
  1709. formatter_add (&f, " (#s)", a->reason);
  1710. break;
  1711. case BUFFER_LINE_STATUS:
  1712. formatter_add (&f, " - ");
  1713. formatter_add (&f, "#s", a->text);
  1714. break;
  1715. case BUFFER_LINE_ERROR:
  1716. formatter_add (&f, "#a=!=#r ", ATTR_ERROR);
  1717. formatter_add (&f, "#s", a->text);
  1718. }
  1719. free (nick);
  1720. input_hide (&ctx->input);
  1721. // TODO: write the line to a log file; note that the global and server
  1722. // buffers musn't collide with filenames
  1723. formatter_add (&f, "\n");
  1724. formatter_flush (&f, stdout);
  1725. formatter_free (&f);
  1726. input_show (&ctx->input);
  1727. }
  1728. static void
  1729. buffer_send_internal (struct app_context *ctx, struct buffer *buffer,
  1730. enum buffer_line_type type, int flags,
  1731. struct buffer_line_args a)
  1732. {
  1733. struct buffer_line *line = buffer_line_new ();
  1734. line->type = type;
  1735. line->flags = flags;
  1736. line->when = time (NULL);
  1737. line->args = a;
  1738. LIST_APPEND_WITH_TAIL (buffer->lines, buffer->lines_tail, line);
  1739. buffer->lines_count++;
  1740. if (buffer == ctx->current_buffer)
  1741. {
  1742. buffer_line_display (ctx, line, false);
  1743. return;
  1744. }
  1745. bool can_leak = false;
  1746. if ((buffer == ctx->global_buffer)
  1747. || (ctx->current_buffer->type == BUFFER_GLOBAL
  1748. && buffer->type == BUFFER_SERVER)
  1749. || (ctx->current_buffer->type != BUFFER_GLOBAL
  1750. && buffer == ctx->current_buffer->server->buffer))
  1751. can_leak = true;
  1752. if (!ctx->isolate_buffers && can_leak)
  1753. buffer_line_display (ctx, line, true);
  1754. else
  1755. {
  1756. buffer->unseen_messages_count++;
  1757. refresh_prompt (ctx);
  1758. }
  1759. }
  1760. #define buffer_send(ctx, buffer, type, flags, ...) \
  1761. buffer_send_internal ((ctx), (buffer), (type), (flags), \
  1762. (struct buffer_line_args) { __VA_ARGS__ })
  1763. #define buffer_send_status(ctx, buffer, ...) \
  1764. buffer_send (ctx, buffer, BUFFER_LINE_STATUS, 0, \
  1765. .text = xstrdup_printf (__VA_ARGS__))
  1766. #define buffer_send_error(ctx, buffer, ...) \
  1767. buffer_send (ctx, buffer, BUFFER_LINE_ERROR, 0, \
  1768. .text = xstrdup_printf (__VA_ARGS__))
  1769. static struct buffer *
  1770. buffer_by_name (struct app_context *ctx, const char *name)
  1771. {
  1772. return str_map_find (&ctx->buffers_by_name, name);
  1773. }
  1774. static void
  1775. buffer_add (struct app_context *ctx, struct buffer *buffer)
  1776. {
  1777. hard_assert (!buffer_by_name (ctx, buffer->name));
  1778. str_map_set (&ctx->buffers_by_name, buffer->name, buffer);
  1779. LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
  1780. // In theory this can't cause changes in the prompt
  1781. refresh_prompt (ctx);
  1782. }
  1783. static void
  1784. buffer_remove (struct app_context *ctx, struct buffer *buffer)
  1785. {
  1786. hard_assert (buffer != ctx->current_buffer);
  1787. // TODO: part from the channel if needed
  1788. input_destroy_buffer (&ctx->input, buffer->input_data);
  1789. buffer->input_data = NULL;
  1790. // And make sure to unlink the buffer from "irc_buffer_map"
  1791. struct server *s = buffer->server;
  1792. if (buffer->channel)
  1793. str_map_set (&s->irc_buffer_map, buffer->channel->name, NULL);
  1794. if (buffer->user)
  1795. str_map_set (&s->irc_buffer_map, buffer->user->nickname, NULL);
  1796. str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
  1797. LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
  1798. buffer_destroy (buffer);
  1799. if (buffer == ctx->last_buffer)
  1800. ctx->last_buffer = NULL;
  1801. // It's not a good idea to remove these buffers, but it's even a worse
  1802. // one to leave the pointers point to invalid memory
  1803. if (buffer == ctx->global_buffer)
  1804. ctx->global_buffer = NULL;
  1805. if (buffer == ctx->server.buffer)
  1806. ctx->server.buffer = NULL;
  1807. refresh_prompt (ctx);
  1808. }
  1809. static void
  1810. buffer_activate (struct app_context *ctx, struct buffer *buffer)
  1811. {
  1812. if (ctx->current_buffer == buffer)
  1813. return;
  1814. print_status ("%s", buffer->name);
  1815. // That is, minus the buffer switch line and the readline prompt
  1816. int to_display = MAX (10, g_terminal.lines - 2);
  1817. struct buffer_line *line = buffer->lines_tail;
  1818. while (line && line->prev && --to_display > 0)
  1819. line = line->prev;
  1820. // Once we've found where we want to start with the backlog, print it
  1821. for (; line; line = line->next)
  1822. buffer_line_display (ctx, line, false);
  1823. buffer->unseen_messages_count = 0;
  1824. input_switch_buffer (&ctx->input, buffer->input_data);
  1825. // Now at last we can switch the pointers
  1826. ctx->last_buffer = ctx->current_buffer;
  1827. ctx->current_buffer = buffer;
  1828. refresh_prompt (ctx);
  1829. }
  1830. static void
  1831. buffer_merge (struct app_context *ctx,
  1832. struct buffer *buffer, struct buffer *merged)
  1833. {
  1834. // TODO: try to merge the buffers as best as we can
  1835. }
  1836. static void
  1837. buffer_rename (struct app_context *ctx,
  1838. struct buffer *buffer, const char *new_name)
  1839. {
  1840. hard_assert (buffer->type == BUFFER_PM);
  1841. struct buffer *collision =
  1842. str_map_find (&buffer->server->irc_buffer_map, new_name);
  1843. if (collision)
  1844. {
  1845. // TODO: use full weechat-style buffer names
  1846. // to prevent name collisions with the global buffer
  1847. hard_assert (collision->type == BUFFER_PM);
  1848. // When there's a collision, there's not much else we can do
  1849. // other than somehow trying to merge them
  1850. buffer_merge (ctx, collision, buffer);
  1851. // TODO: log a status message about the merge
  1852. if (ctx->current_buffer == buffer)
  1853. buffer_activate (ctx, collision);
  1854. buffer_remove (ctx, buffer);
  1855. }
  1856. else
  1857. {
  1858. // Otherwise we just rename the buffer and that's it
  1859. str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
  1860. str_map_set (&ctx->buffers_by_name, new_name, buffer);
  1861. free (buffer->name);
  1862. buffer->name = xstrdup (new_name);
  1863. // We might have renamed the current buffer
  1864. refresh_prompt (ctx);
  1865. }
  1866. }
  1867. static struct buffer *
  1868. buffer_at_index (struct app_context *ctx, int n)
  1869. {
  1870. int i = 0;
  1871. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  1872. if (++i == n)
  1873. return iter;
  1874. return NULL;
  1875. }
  1876. static struct buffer *
  1877. buffer_next (struct app_context *ctx, int count)
  1878. {
  1879. struct buffer *new_buffer = ctx->current_buffer;
  1880. while (count-- > 0)
  1881. if (!(new_buffer = new_buffer->next))
  1882. new_buffer = ctx->buffers;
  1883. return new_buffer;
  1884. }
  1885. static struct buffer *
  1886. buffer_previous (struct app_context *ctx, int count)
  1887. {
  1888. struct buffer *new_buffer = ctx->current_buffer;
  1889. while (count-- > 0)
  1890. if (!(new_buffer = new_buffer->prev))
  1891. new_buffer = ctx->buffers_tail;
  1892. return new_buffer;
  1893. }
  1894. static bool
  1895. buffer_goto (struct app_context *ctx, int n)
  1896. {
  1897. struct buffer *buffer = buffer_at_index (ctx, n);
  1898. if (!buffer)
  1899. return false;
  1900. buffer_activate (ctx, buffer);
  1901. return true;
  1902. }
  1903. static int
  1904. buffer_get_index (struct app_context *ctx, struct buffer *buffer)
  1905. {
  1906. int index = 1;
  1907. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  1908. {
  1909. if (iter == buffer)
  1910. return index;
  1911. index++;
  1912. }
  1913. return -1;
  1914. }
  1915. static void
  1916. init_buffers (struct app_context *ctx)
  1917. {
  1918. // At the moment we have only two global everpresent buffers
  1919. struct buffer *global = ctx->global_buffer = buffer_new ();
  1920. struct buffer *server = ctx->server.buffer = buffer_new ();
  1921. global->type = BUFFER_GLOBAL;
  1922. global->name = xstrdup (PROGRAM_NAME);
  1923. server->type = BUFFER_SERVER;
  1924. server->name = xstrdup ("server");
  1925. server->server = &ctx->server;
  1926. LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, global);
  1927. LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, server);
  1928. }
  1929. // --- Users, channels ---------------------------------------------------------
  1930. static void
  1931. irc_user_on_destroy (void *object, void *user_data)
  1932. {
  1933. struct user *user = object;
  1934. struct server *s = user_data;
  1935. str_map_set (&s->irc_users, user->nickname, NULL);
  1936. }
  1937. static struct user *
  1938. irc_make_user (struct server *s, char *nickname)
  1939. {
  1940. hard_assert (!str_map_find (&s->irc_users, nickname));
  1941. struct user *user = user_new ();
  1942. user->on_destroy = irc_user_on_destroy;
  1943. user->user_data = s;
  1944. user->nickname = nickname;
  1945. str_map_set (&s->irc_users, user->nickname, user);
  1946. return user;
  1947. }
  1948. static struct buffer *
  1949. irc_get_or_make_user_buffer (struct server *s, const char *nickname)
  1950. {
  1951. struct buffer *buffer = str_map_find (&s->irc_buffer_map, nickname);
  1952. if (buffer)
  1953. return buffer;
  1954. struct user *user = str_map_find (&s->irc_users, nickname);
  1955. if (!user)
  1956. user = irc_make_user (s, xstrdup (nickname));
  1957. else
  1958. user = user_ref (user);
  1959. // Open a new buffer for the user
  1960. buffer = buffer_new ();
  1961. buffer->type = BUFFER_PM;
  1962. buffer->name = xstrdup (nickname);
  1963. buffer->server = s;
  1964. buffer->user = user;
  1965. LIST_APPEND_WITH_TAIL (s->ctx->buffers, s->ctx->buffers_tail, buffer);
  1966. str_map_set (&s->irc_buffer_map, user->nickname, buffer);
  1967. return buffer;
  1968. }
  1969. static void
  1970. irc_channel_unlink_user
  1971. (struct channel *channel, struct channel_user *channel_user)
  1972. {
  1973. // First destroy the user's weak references to the channel
  1974. struct user *user = channel_user->user;
  1975. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  1976. if (iter->channel == channel)
  1977. {
  1978. LIST_UNLINK (user->channels, iter);
  1979. user_channel_destroy (iter);
  1980. }
  1981. // Then just unlink the user from the channel
  1982. LIST_UNLINK (channel->users, channel_user);
  1983. channel_user_destroy (channel_user);
  1984. }
  1985. static void
  1986. irc_channel_on_destroy (void *object, void *user_data)
  1987. {
  1988. struct channel *channel = object;
  1989. struct server *s = user_data;
  1990. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  1991. irc_channel_unlink_user (channel, iter);
  1992. str_map_set (&s->irc_channels, channel->name, NULL);
  1993. }
  1994. static struct channel *
  1995. irc_make_channel (struct server *s, char *name)
  1996. {
  1997. hard_assert (!str_map_find (&s->irc_channels, name));
  1998. struct channel *channel = channel_new ();
  1999. channel->on_destroy = irc_channel_on_destroy;
  2000. channel->user_data = s;
  2001. channel->name = name;
  2002. channel->mode = xstrdup ("");
  2003. channel->topic = NULL;
  2004. str_map_set (&s->irc_channels, channel->name, channel);
  2005. return channel;
  2006. }
  2007. static void
  2008. irc_remove_user_from_channel (struct user *user, struct channel *channel)
  2009. {
  2010. LIST_FOR_EACH (struct channel_user, iter, channel->users)
  2011. if (iter->user == user)
  2012. irc_channel_unlink_user (channel, iter);
  2013. }
  2014. // --- Supporting code ---------------------------------------------------------
  2015. static bool irc_connect (struct server *s, bool *should_retry, struct error **);
  2016. static void irc_cancel_timers (struct server *s);
  2017. static void on_irc_reconnect_timeout (void *user_data);
  2018. static char *
  2019. irc_cut_nickname (const char *prefix)
  2020. {
  2021. return xstrndup (prefix, strcspn (prefix, "!@"));
  2022. }
  2023. static const char *
  2024. irc_find_userhost (const char *prefix)
  2025. {
  2026. const char *p = strchr (prefix, '!');
  2027. return p ? p + 1 : NULL;
  2028. }
  2029. static bool
  2030. irc_is_this_us (struct server *s, const char *prefix)
  2031. {
  2032. char *nick = irc_cut_nickname (prefix);
  2033. bool result = !irc_strcmp (nick, s->irc_user->nickname);
  2034. free (nick);
  2035. return result;
  2036. }
  2037. static bool
  2038. irc_is_channel (struct server *s, const char *ident)
  2039. {
  2040. (void) s; // TODO: parse prefixes from server features
  2041. return *ident && !!strchr ("#&+!", *ident);
  2042. }
  2043. static void
  2044. irc_shutdown (struct server *s)
  2045. {
  2046. // TODO: set a timer after which we cut the connection?
  2047. // Generally non-critical
  2048. if (s->ssl)
  2049. soft_assert (SSL_shutdown (s->ssl) != -1);
  2050. else
  2051. soft_assert (shutdown (s->irc_fd, SHUT_WR) == 0);
  2052. }
  2053. static void
  2054. try_finish_quit (struct app_context *ctx)
  2055. {
  2056. // TODO: multiserver
  2057. if (ctx->quitting && ctx->server.irc_fd == -1)
  2058. ctx->polling = false;
  2059. }
  2060. static void
  2061. initiate_quit (struct app_context *ctx)
  2062. {
  2063. // Destroy the user interface
  2064. input_stop (&ctx->input);
  2065. buffer_send_status (ctx, ctx->global_buffer, "Shutting down");
  2066. // Initiate a connection close
  2067. // TODO: multiserver
  2068. struct server *s = &ctx->server;
  2069. if (s->irc_fd != -1)
  2070. // XXX: when we go async, we'll have to flush output buffers first
  2071. irc_shutdown (s);
  2072. ctx->quitting = true;
  2073. try_finish_quit (ctx);
  2074. }
  2075. // As of 2015, everything should be in UTF-8. And if it's not, we'll decode it
  2076. // as ISO Latin 1. This function should not be called on the whole message.
  2077. static char *
  2078. irc_to_utf8 (struct app_context *ctx, const char *text)
  2079. {
  2080. size_t len = strlen (text) + 1;
  2081. if (utf8_validate (text, len))
  2082. return xstrdup (text);
  2083. return iconv_xstrdup (ctx->latin1_to_utf8, (char *) text, len, NULL);
  2084. }
  2085. // This function is used to output debugging IRC traffic to the terminal.
  2086. // It's far from ideal, as any non-UTF-8 text degrades the entire line to
  2087. // ISO Latin 1. But it should work good enough most of the time.
  2088. static char *
  2089. irc_to_term (struct app_context *ctx, const char *text)
  2090. {
  2091. char *utf8 = irc_to_utf8 (ctx, text);
  2092. char *term = iconv_xstrdup (ctx->term_from_utf8, utf8, -1, NULL);
  2093. free (utf8);
  2094. return term;
  2095. }
  2096. static bool irc_send (struct server *s,
  2097. const char *format, ...) ATTRIBUTE_PRINTF (2, 3);
  2098. static bool
  2099. irc_send (struct server *s, const char *format, ...)
  2100. {
  2101. if (!soft_assert (s->irc_fd != -1))
  2102. {
  2103. print_debug ("tried sending a message to a dead server connection");
  2104. return false;
  2105. }
  2106. va_list ap;
  2107. va_start (ap, format);
  2108. struct str str;
  2109. str_init (&str);
  2110. str_append_vprintf (&str, format, ap);
  2111. va_end (ap);
  2112. if (g_debug_mode)
  2113. {
  2114. input_hide (&s->ctx->input);
  2115. char *term = irc_to_term (s->ctx, str.str);
  2116. fprintf (stderr, "[IRC] <== \"%s\"\n", term);
  2117. free (term);
  2118. input_show (&s->ctx->input);
  2119. }
  2120. str_append (&str, "\r\n");
  2121. bool result = true;
  2122. if (s->ssl)
  2123. {
  2124. // TODO: call SSL_get_error() to detect if a clean shutdown has occured
  2125. if (SSL_write (s->ssl, str.str, str.len) != (int) str.len)
  2126. {
  2127. LOG_FUNC_FAILURE ("SSL_write",
  2128. ERR_error_string (ERR_get_error (), NULL));
  2129. result = false;
  2130. }
  2131. }
  2132. else if (write (s->irc_fd, str.str, str.len) != (ssize_t) str.len)
  2133. {
  2134. LOG_LIBC_FAILURE ("write");
  2135. result = false;
  2136. }
  2137. str_free (&str);
  2138. return result;
  2139. }
  2140. static bool
  2141. irc_initialize_ssl_ctx (struct server *s, struct error **e)
  2142. {
  2143. // XXX: maybe we should call SSL_CTX_set_options() for some workarounds
  2144. bool verify = get_config_boolean (s->ctx, "server.ssl_verify");
  2145. if (!verify)
  2146. SSL_CTX_set_verify (s->ssl_ctx, SSL_VERIFY_NONE, NULL);
  2147. const char *ca_file = get_config_string (s->ctx, "server.ca_file");
  2148. const char *ca_path = get_config_string (s->ctx, "server.ca_path");
  2149. struct error *error = NULL;
  2150. if (ca_file || ca_path)
  2151. {
  2152. if (SSL_CTX_load_verify_locations (s->ssl_ctx, ca_file, ca_path))
  2153. return true;
  2154. error_set (&error, "%s: %s",
  2155. "Failed to set locations for the CA certificate bundle",
  2156. ERR_reason_error_string (ERR_get_error ()));
  2157. goto ca_error;
  2158. }
  2159. if (!SSL_CTX_set_default_verify_paths (s->ssl_ctx))
  2160. {
  2161. error_set (&error, "%s: %s",
  2162. "Couldn't load the default CA certificate bundle",
  2163. ERR_reason_error_string (ERR_get_error ()));
  2164. goto ca_error;
  2165. }
  2166. return true;
  2167. ca_error:
  2168. if (verify)
  2169. {
  2170. error_propagate (e, error);
  2171. return false;
  2172. }
  2173. // Only inform the user if we're not actually verifying
  2174. buffer_send_error (s->ctx, s->buffer, "%s", error->message);
  2175. error_free (error);
  2176. return true;
  2177. }
  2178. static bool
  2179. irc_initialize_ssl (struct server *s, struct error **e)
  2180. {
  2181. const char *error_info = NULL;
  2182. s->ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
  2183. if (!s->ssl_ctx)
  2184. goto error_ssl_1;
  2185. if (!irc_initialize_ssl_ctx (s, e))
  2186. goto error_ssl_2;
  2187. s->ssl = SSL_new (s->ssl_ctx);
  2188. if (!s->ssl)
  2189. goto error_ssl_2;
  2190. const char *ssl_cert = get_config_string (s->ctx, "server.ssl_cert");
  2191. if (ssl_cert)
  2192. {
  2193. char *path = resolve_config_filename (ssl_cert);
  2194. if (!path)
  2195. buffer_send_error (s->ctx, s->ctx->global_buffer,
  2196. "%s: %s", "Cannot open file", ssl_cert);
  2197. // XXX: perhaps we should read the file ourselves for better messages
  2198. else if (!SSL_use_certificate_file (s->ssl, path, SSL_FILETYPE_PEM)
  2199. || !SSL_use_PrivateKey_file (s->ssl, path, SSL_FILETYPE_PEM))
  2200. buffer_send_error (s->ctx, s->ctx->global_buffer,
  2201. "%s: %s", "Setting the SSL client certificate failed",
  2202. ERR_error_string (ERR_get_error (), NULL));
  2203. free (path);
  2204. }
  2205. SSL_set_connect_state (s->ssl);
  2206. if (!SSL_set_fd (s->ssl, s->irc_fd))
  2207. goto error_ssl_3;
  2208. // Avoid SSL_write() returning SSL_ERROR_WANT_READ
  2209. SSL_set_mode (s->ssl, SSL_MODE_AUTO_RETRY);
  2210. switch (xssl_get_error (s->ssl, SSL_connect (s->ssl), &error_info))
  2211. {
  2212. case SSL_ERROR_NONE:
  2213. return true;
  2214. case SSL_ERROR_ZERO_RETURN:
  2215. error_info = "server closed the connection";
  2216. default:
  2217. break;
  2218. }
  2219. error_ssl_3:
  2220. SSL_free (s->ssl);
  2221. s->ssl = NULL;
  2222. error_ssl_2:
  2223. SSL_CTX_free (s->ssl_ctx);
  2224. s->ssl_ctx = NULL;
  2225. error_ssl_1:
  2226. // XXX: these error strings are really nasty; also there could be
  2227. // multiple errors on the OpenSSL stack.
  2228. if (!error_info)
  2229. error_info = ERR_error_string (ERR_get_error (), NULL);
  2230. error_set (e, "%s: %s", "could not initialize SSL", error_info);
  2231. return false;
  2232. }
  2233. static bool
  2234. irc_establish_connection (struct server *s,
  2235. const char *host, const char *port, struct error **e)
  2236. {
  2237. struct addrinfo gai_hints, *gai_result, *gai_iter;
  2238. memset (&gai_hints, 0, sizeof gai_hints);
  2239. gai_hints.ai_socktype = SOCK_STREAM;
  2240. int err = getaddrinfo (host, port, &gai_hints, &gai_result);
  2241. if (err)
  2242. {
  2243. error_set (e, "%s: %s: %s",
  2244. "connection failed", "getaddrinfo", gai_strerror (err));
  2245. return false;
  2246. }
  2247. int sockfd;
  2248. for (gai_iter = gai_result; gai_iter; gai_iter = gai_iter->ai_next)
  2249. {
  2250. sockfd = socket (gai_iter->ai_family,
  2251. gai_iter->ai_socktype, gai_iter->ai_protocol);
  2252. if (sockfd == -1)
  2253. continue;
  2254. set_cloexec (sockfd);
  2255. int yes = 1;
  2256. soft_assert (setsockopt (sockfd, SOL_SOCKET, SO_KEEPALIVE,
  2257. &yes, sizeof yes) != -1);
  2258. const char *real_host = host;
  2259. // Let's try to resolve the address back into a real hostname;
  2260. // we don't really need this, so we can let it quietly fail
  2261. char buf[NI_MAXHOST];
  2262. err = getnameinfo (gai_iter->ai_addr, gai_iter->ai_addrlen,
  2263. buf, sizeof buf, NULL, 0, NI_NUMERICHOST);
  2264. if (err)
  2265. LOG_FUNC_FAILURE ("getnameinfo", gai_strerror (err));
  2266. else
  2267. real_host = buf;
  2268. char *address = format_host_port_pair (real_host, port);
  2269. buffer_send_status (s->ctx, s->buffer, "Connecting to %s...", address);
  2270. free (address);
  2271. if (!connect (sockfd, gai_iter->ai_addr, gai_iter->ai_addrlen))
  2272. break;
  2273. xclose (sockfd);
  2274. }
  2275. freeaddrinfo (gai_result);
  2276. if (!gai_iter)
  2277. {
  2278. error_set (e, "connection failed");
  2279. return false;
  2280. }
  2281. s->irc_fd = sockfd;
  2282. return true;
  2283. }
  2284. // --- More readline funky stuff -----------------------------------------------
  2285. static char *
  2286. make_unseen_prefix (struct app_context *ctx)
  2287. {
  2288. struct str active_buffers;
  2289. str_init (&active_buffers);
  2290. size_t i = 0;
  2291. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  2292. {
  2293. i++;
  2294. if (!iter->unseen_messages_count)
  2295. continue;
  2296. if (active_buffers.len)
  2297. str_append_c (&active_buffers, ',');
  2298. str_append_printf (&active_buffers, "%zu", i);
  2299. }
  2300. if (active_buffers.len)
  2301. return str_steal (&active_buffers);
  2302. str_free (&active_buffers);
  2303. return NULL;
  2304. }
  2305. static void
  2306. make_prompt (struct app_context *ctx, struct str *output)
  2307. {
  2308. struct buffer *buffer = ctx->current_buffer;
  2309. if (!soft_assert (buffer))
  2310. return;
  2311. str_append_c (output, '[');
  2312. char *unseen_prefix = make_unseen_prefix (ctx);
  2313. if (unseen_prefix)
  2314. str_append_printf (output, "(%s) ", unseen_prefix);
  2315. free (unseen_prefix);
  2316. str_append_printf (output, "%d:%s",
  2317. buffer_get_index (ctx, buffer), buffer->name);
  2318. if (buffer->type == BUFFER_CHANNEL && *buffer->channel->mode)
  2319. str_append_printf (output, "(%s)", buffer->channel->mode);
  2320. if (buffer != ctx->global_buffer)
  2321. {
  2322. struct server *s = buffer->server;
  2323. str_append_c (output, ' ');
  2324. if (s->irc_fd == -1)
  2325. str_append (output, "(disconnected)");
  2326. else
  2327. {
  2328. str_append (output, s->irc_user->nickname);
  2329. if (*s->irc_user_mode)
  2330. str_append_printf (output, "(%s)", s->irc_user_mode);
  2331. }
  2332. }
  2333. str_append_c (output, ']');
  2334. }
  2335. static void
  2336. refresh_prompt (struct app_context *ctx)
  2337. {
  2338. bool have_attributes = !!get_attribute_printer (stdout);
  2339. struct str prompt;
  2340. str_init (&prompt);
  2341. make_prompt (ctx, &prompt);
  2342. str_append_c (&prompt, ' ');
  2343. if (have_attributes)
  2344. {
  2345. // XXX: to be completely correct, we should use tputs, but we cannot
  2346. input_set_prompt (&ctx->input, xstrdup_printf ("%c%s%c%s%c%s%c",
  2347. INPUT_START_IGNORE, ctx->attrs[ATTR_PROMPT],
  2348. INPUT_END_IGNORE,
  2349. prompt.str,
  2350. INPUT_START_IGNORE, ctx->attrs[ATTR_RESET],
  2351. INPUT_END_IGNORE));
  2352. }
  2353. else
  2354. input_set_prompt (&ctx->input, xstrdup (prompt.str));
  2355. str_free (&prompt);
  2356. }
  2357. // --- Input handling ----------------------------------------------------------
  2358. // TODO: we will need a proper mode parser; to be shared with kike
  2359. // TODO: we alse definitely need to parse server capability messages
  2360. static struct buffer *
  2361. irc_get_buffer_for_message (struct server *s,
  2362. const struct irc_message *msg, const char *target)
  2363. {
  2364. struct buffer *buffer = str_map_find (&s->irc_buffer_map, target);
  2365. if (irc_is_channel (s, target))
  2366. {
  2367. struct channel *channel = str_map_find (&s->irc_channels, target);
  2368. hard_assert ((channel && buffer) ||
  2369. (channel && !buffer) || (!channel && !buffer));
  2370. // This is weird
  2371. if (!channel)
  2372. return NULL;
  2373. }
  2374. else if (!buffer)
  2375. {
  2376. // Implying that the target is us
  2377. // Don't make user buffers for servers (they can send NOTICEs)
  2378. if (!irc_find_userhost (msg->prefix))
  2379. return s->buffer;
  2380. char *nickname = irc_cut_nickname (msg->prefix);
  2381. buffer = irc_get_or_make_user_buffer (s, nickname);
  2382. free (nickname);
  2383. }
  2384. return buffer;
  2385. }
  2386. static bool
  2387. irc_is_highlight (struct server *s, const char *message)
  2388. {
  2389. // Well, this is rather crude but it should make most users happy.
  2390. // Ideally we could do this at least in proper Unicode.
  2391. char *copy = xstrdup (message);
  2392. for (char *p = copy; *p; p++)
  2393. *p = irc_tolower (*p);
  2394. char *nick = xstrdup (s->irc_user->nickname);
  2395. for (char *p = nick; *p; p++)
  2396. *p = irc_tolower (*p);
  2397. // Special characters allowed in nicknames by RFC 2812: []\`_^{|} and -
  2398. // Also excluded from the ASCII: common user channel prefixes: +%@&~
  2399. const char *delimiters = ",.;:!?()<>/=#$* \t\r\n\v\f\"'";
  2400. bool result = false;
  2401. char *save = NULL;
  2402. for (char *token = strtok_r (copy, delimiters, &save);
  2403. token; token = strtok_r (NULL, delimiters, &save))
  2404. if (!strcmp (token, nick))
  2405. {
  2406. result = true;
  2407. break;
  2408. }
  2409. free (copy);
  2410. free (nick);
  2411. return result;
  2412. }
  2413. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2414. static void
  2415. irc_handle_join (struct server *s, const struct irc_message *msg)
  2416. {
  2417. if (!msg->prefix || msg->params.len < 1)
  2418. return;
  2419. const char *channel_name = msg->params.vector[0];
  2420. if (!irc_is_channel (s, channel_name))
  2421. return;
  2422. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  2423. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  2424. hard_assert ((channel && buffer) ||
  2425. (channel && !buffer) || (!channel && !buffer));
  2426. // We've joined a new channel
  2427. if (!channel && irc_is_this_us (s, msg->prefix))
  2428. {
  2429. buffer = buffer_new ();
  2430. buffer->type = BUFFER_CHANNEL;
  2431. buffer->name = xstrdup (channel_name);
  2432. buffer->server = s;
  2433. buffer->channel = channel =
  2434. irc_make_channel (s, xstrdup (channel_name));
  2435. LIST_APPEND_WITH_TAIL (s->ctx->buffers, s->ctx->buffers_tail, buffer);
  2436. str_map_set (&s->irc_buffer_map, channel->name, buffer);
  2437. buffer_activate (s->ctx, buffer);
  2438. }
  2439. // This is weird, ignoring
  2440. if (!channel)
  2441. return;
  2442. // Get or make a user object
  2443. char *nickname = irc_cut_nickname (msg->prefix);
  2444. struct user *user = str_map_find (&s->irc_users, nickname);
  2445. if (!user)
  2446. user = irc_make_user (s, nickname);
  2447. else
  2448. {
  2449. user = user_ref (user);
  2450. free (nickname);
  2451. }
  2452. // Link the user with the channel
  2453. struct user_channel *user_channel = user_channel_new ();
  2454. user_channel->channel = channel;
  2455. LIST_PREPEND (user->channels, user_channel);
  2456. struct channel_user *channel_user = channel_user_new ();
  2457. channel_user->user = user;
  2458. channel_user->modes = xstrdup ("");
  2459. LIST_PREPEND (channel->users, channel_user);
  2460. // Finally log the message
  2461. if (buffer)
  2462. {
  2463. buffer_send (s->ctx, buffer, BUFFER_LINE_JOIN, 0,
  2464. .who = irc_to_utf8 (s->ctx, msg->prefix),
  2465. .object = irc_to_utf8 (s->ctx, channel_name));
  2466. }
  2467. }
  2468. static void
  2469. irc_handle_kick (struct server *s, const struct irc_message *msg)
  2470. {
  2471. if (!msg->prefix || msg->params.len < 2)
  2472. return;
  2473. const char *channel_name = msg->params.vector[0];
  2474. const char *target = msg->params.vector[1];
  2475. if (!irc_is_channel (s, channel_name)
  2476. || irc_is_channel (s, target))
  2477. return;
  2478. const char *message = "";
  2479. if (msg->params.len > 2)
  2480. message = msg->params.vector[2];
  2481. struct user *user = str_map_find (&s->irc_users, target);
  2482. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  2483. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  2484. hard_assert ((channel && buffer) ||
  2485. (channel && !buffer) || (!channel && !buffer));
  2486. // It would be is weird for this to be false
  2487. if (user && channel)
  2488. irc_remove_user_from_channel (user, channel);
  2489. if (buffer)
  2490. {
  2491. buffer_send (s->ctx, buffer, BUFFER_LINE_KICK, 0,
  2492. .who = irc_to_utf8 (s->ctx, msg->prefix),
  2493. .object = irc_to_utf8 (s->ctx, target),
  2494. .reason = irc_to_utf8 (s->ctx, message));
  2495. }
  2496. }
  2497. static void
  2498. irc_handle_mode (struct server *s, const struct irc_message *msg)
  2499. {
  2500. // TODO: parse the mode change and apply it
  2501. // TODO: log a message
  2502. }
  2503. static void
  2504. irc_handle_nick (struct server *s, const struct irc_message *msg)
  2505. {
  2506. if (!msg->prefix || msg->params.len < 1)
  2507. return;
  2508. const char *new_nickname = msg->params.vector[0];
  2509. char *nickname = irc_cut_nickname (msg->prefix);
  2510. struct user *user = str_map_find (&s->irc_users, nickname);
  2511. free (nickname);
  2512. if (!user)
  2513. return;
  2514. // What the fuck
  2515. // TODO: probably log a message and force a reconnect
  2516. if (str_map_find (&s->irc_users, new_nickname))
  2517. return;
  2518. // Log a message in any PM buffer and rename it;
  2519. // we may even have one for ourselves
  2520. struct buffer *pm_buffer =
  2521. str_map_find (&s->irc_buffer_map, user->nickname);
  2522. if (pm_buffer)
  2523. {
  2524. str_map_set (&s->irc_buffer_map, new_nickname, pm_buffer);
  2525. str_map_set (&s->irc_buffer_map, user->nickname, NULL);
  2526. char *who = irc_is_this_us (s, msg->prefix)
  2527. ? irc_to_utf8 (s->ctx, msg->prefix)
  2528. : NULL;
  2529. buffer_send (s->ctx, pm_buffer, BUFFER_LINE_NICK, 0,
  2530. .who = who,
  2531. .object = irc_to_utf8 (s->ctx, new_nickname));
  2532. // TODO: use a full weechat-style buffer name here
  2533. buffer_rename (s->ctx, pm_buffer, new_nickname);
  2534. }
  2535. if (irc_is_this_us (s, msg->prefix))
  2536. {
  2537. // Log a message in all open buffers on this server
  2538. struct str_map_iter iter;
  2539. str_map_iter_init (&iter, &s->irc_buffer_map);
  2540. struct buffer *buffer;
  2541. while ((buffer = str_map_iter_next (&iter)))
  2542. {
  2543. // We've already done that
  2544. if (buffer == pm_buffer)
  2545. continue;
  2546. buffer_send (s->ctx, buffer, BUFFER_LINE_NICK, 0,
  2547. .object = irc_to_utf8 (s->ctx, new_nickname));
  2548. }
  2549. }
  2550. else
  2551. {
  2552. // Log a message in all channels the user is in
  2553. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  2554. {
  2555. struct buffer *buffer =
  2556. str_map_find (&s->irc_buffer_map, iter->channel->name);
  2557. hard_assert (buffer != NULL);
  2558. buffer_send (s->ctx, buffer, BUFFER_LINE_NICK, 0,
  2559. .who = irc_to_utf8 (s->ctx, msg->prefix),
  2560. .object = irc_to_utf8 (s->ctx, new_nickname));
  2561. }
  2562. }
  2563. // Finally rename the user
  2564. str_map_set (&s->irc_users, new_nickname, user_ref (user));
  2565. str_map_set (&s->irc_users, user->nickname, NULL);
  2566. free (user->nickname);
  2567. user->nickname = xstrdup (new_nickname);
  2568. // We might have renamed ourselves
  2569. refresh_prompt (s->ctx);
  2570. }
  2571. static void
  2572. irc_handle_ctcp_reply (struct server *s,
  2573. const struct irc_message *msg, struct ctcp_chunk *chunk)
  2574. {
  2575. char *nickname = irc_cut_nickname (msg->prefix);
  2576. char *nickname_utf8 = irc_to_utf8 (s->ctx, nickname);
  2577. char *tag_utf8 = irc_to_utf8 (s->ctx, chunk->tag.str);
  2578. char *text_utf8 = irc_to_utf8 (s->ctx, chunk->text.str);
  2579. buffer_send_status (s->ctx, s->buffer,
  2580. "CTCP reply from %s: %s %s", nickname_utf8, tag_utf8, text_utf8);
  2581. free (nickname);
  2582. free (nickname_utf8);
  2583. free (tag_utf8);
  2584. free (text_utf8);
  2585. }
  2586. static void
  2587. irc_handle_notice_text (struct server *s,
  2588. const struct irc_message *msg, struct str *text)
  2589. {
  2590. const char *target = msg->params.vector[0];
  2591. struct buffer *buffer = irc_get_buffer_for_message (s, msg, target);
  2592. if (buffer)
  2593. {
  2594. // TODO: some more obvious indication of highlights
  2595. int flags = irc_is_highlight (s, text->str)
  2596. ? BUFFER_LINE_HIGHLIGHT
  2597. : 0;
  2598. buffer_send (s->ctx, buffer, BUFFER_LINE_NOTICE, flags,
  2599. .who = irc_to_utf8 (s->ctx, msg->prefix),
  2600. .text = irc_to_utf8 (s->ctx, text->str));
  2601. }
  2602. }
  2603. static void
  2604. irc_handle_notice (struct server *s, const struct irc_message *msg)
  2605. {
  2606. if (!msg->prefix || msg->params.len < 2)
  2607. return;
  2608. // This ignores empty messages which we should never receive anyway
  2609. struct ctcp_chunk *chunks = ctcp_parse (msg->params.vector[1]);
  2610. LIST_FOR_EACH (struct ctcp_chunk, iter, chunks)
  2611. if (!iter->is_extended)
  2612. irc_handle_notice_text (s, msg, &iter->text);
  2613. else
  2614. irc_handle_ctcp_reply (s, msg, iter);
  2615. ctcp_destroy (chunks);
  2616. }
  2617. static void
  2618. irc_handle_part (struct server *s, const struct irc_message *msg)
  2619. {
  2620. if (!msg->prefix || msg->params.len < 1)
  2621. return;
  2622. const char *channel_name = msg->params.vector[0];
  2623. if (!irc_is_channel (s, channel_name))
  2624. return;
  2625. const char *message = "";
  2626. if (msg->params.len > 1)
  2627. message = msg->params.vector[1];
  2628. char *nickname = irc_cut_nickname (msg->prefix);
  2629. struct user *user = str_map_find (&s->irc_users, nickname);
  2630. free (nickname);
  2631. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  2632. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  2633. hard_assert ((channel && buffer) ||
  2634. (channel && !buffer) || (!channel && !buffer));
  2635. // It would be is weird for this to be false
  2636. if (user && channel)
  2637. irc_remove_user_from_channel (user, channel);
  2638. if (buffer)
  2639. {
  2640. buffer_send (s->ctx, buffer, BUFFER_LINE_PART, 0,
  2641. .who = irc_to_utf8 (s->ctx, msg->prefix),
  2642. .object = irc_to_utf8 (s->ctx, channel_name),
  2643. .reason = irc_to_utf8 (s->ctx, message));
  2644. }
  2645. }
  2646. static void
  2647. irc_handle_ping (struct server *s, const struct irc_message *msg)
  2648. {
  2649. if (msg->params.len)
  2650. irc_send (s, "PONG :%s", msg->params.vector[0]);
  2651. else
  2652. irc_send (s, "PONG");
  2653. }
  2654. static char *
  2655. ctime_now (char buf[26])
  2656. {
  2657. struct tm tm_;
  2658. time_t now = time (NULL);
  2659. if (!asctime_r (localtime_r (&now, &tm_), buf))
  2660. return NULL;
  2661. // Annoying thing
  2662. *strchr (buf, '\n') = '\0';
  2663. return buf;
  2664. }
  2665. static void irc_send_ctcp_reply (struct server *s, const char *recipient,
  2666. const char *format, ...) ATTRIBUTE_PRINTF (3, 4);
  2667. static void
  2668. irc_send_ctcp_reply (struct server *s,
  2669. const char *recipient, const char *format, ...)
  2670. {
  2671. struct str m;
  2672. str_init (&m);
  2673. va_list ap;
  2674. va_start (ap, format);
  2675. str_append_vprintf (&m, format, ap);
  2676. va_end (ap);
  2677. irc_send (s, "NOTICE %s :\x01%s\x01", recipient, m.str);
  2678. char *text_utf8 = irc_to_utf8 (s->ctx, m.str);
  2679. char *recipient_utf8 = irc_to_utf8 (s->ctx, recipient);
  2680. str_free (&m);
  2681. buffer_send_status (s->ctx, s->buffer,
  2682. "CTCP reply to %s: %s", recipient_utf8, text_utf8);
  2683. free (text_utf8);
  2684. free (recipient_utf8);
  2685. }
  2686. static void
  2687. irc_handle_ctcp_request (struct server *s,
  2688. const struct irc_message *msg, struct ctcp_chunk *chunk)
  2689. {
  2690. char *nickname = irc_cut_nickname (msg->prefix);
  2691. char *nickname_utf8 = irc_to_utf8 (s->ctx, nickname);
  2692. char *tag_utf8 = irc_to_utf8 (s->ctx, chunk->tag.str);
  2693. buffer_send_status (s->ctx, s->buffer,
  2694. "CTCP requested by %s: %s", nickname_utf8, tag_utf8);
  2695. const char *target = msg->params.vector[0];
  2696. const char *recipient = nickname;
  2697. if (irc_is_channel (s, target))
  2698. recipient = target;
  2699. if (!strcmp (chunk->tag.str, "CLIENTINFO"))
  2700. irc_send_ctcp_reply (s, recipient, "CLIENTINFO %s %s %s %s",
  2701. "PING", "VERSION", "TIME", "CLIENTINFO");
  2702. else if (!strcmp (chunk->tag.str, "PING"))
  2703. irc_send_ctcp_reply (s, recipient, "PING %s", chunk->text.str);
  2704. else if (!strcmp (chunk->tag.str, "VERSION"))
  2705. {
  2706. struct utsname info;
  2707. if (uname (&info))
  2708. LOG_LIBC_FAILURE ("uname");
  2709. else
  2710. irc_send_ctcp_reply (s, recipient, "VERSION %s %s on %s %s",
  2711. PROGRAM_NAME, PROGRAM_VERSION, info.sysname, info.machine);
  2712. }
  2713. else if (!strcmp (chunk->tag.str, "TIME"))
  2714. {
  2715. char buf[26];
  2716. if (!ctime_now (buf))
  2717. LOG_LIBC_FAILURE ("asctime_r");
  2718. else
  2719. irc_send_ctcp_reply (s, recipient, "TIME %s", buf);
  2720. }
  2721. free (nickname);
  2722. free (nickname_utf8);
  2723. free (tag_utf8);
  2724. }
  2725. static void
  2726. irc_handle_privmsg_text (struct server *s,
  2727. const struct irc_message *msg, struct str *text, bool is_action)
  2728. {
  2729. const char *target = msg->params.vector[0];
  2730. struct buffer *buffer = irc_get_buffer_for_message (s, msg, target);
  2731. if (buffer)
  2732. {
  2733. // TODO: some more obvious indication of highlights
  2734. int flags = irc_is_highlight (s, text->str)
  2735. ? BUFFER_LINE_HIGHLIGHT
  2736. : 0;
  2737. enum buffer_line_type type = is_action
  2738. ? BUFFER_LINE_ACTION
  2739. : BUFFER_LINE_PRIVMSG;
  2740. buffer_send (s->ctx, buffer, type, flags,
  2741. .who = irc_to_utf8 (s->ctx, msg->prefix),
  2742. .text = irc_to_utf8 (s->ctx, text->str));
  2743. }
  2744. }
  2745. static void
  2746. irc_handle_privmsg (struct server *s, const struct irc_message *msg)
  2747. {
  2748. if (!msg->prefix || msg->params.len < 2)
  2749. return;
  2750. // This ignores empty messages which we should never receive anyway
  2751. struct ctcp_chunk *chunks = ctcp_parse (msg->params.vector[1]);
  2752. LIST_FOR_EACH (struct ctcp_chunk, iter, chunks)
  2753. if (!iter->is_extended)
  2754. irc_handle_privmsg_text (s, msg, &iter->text, false);
  2755. else if (!strcmp (iter->tag.str, "ACTION"))
  2756. irc_handle_privmsg_text (s, msg, &iter->text, true);
  2757. else
  2758. irc_handle_ctcp_request (s, msg, iter);
  2759. ctcp_destroy (chunks);
  2760. }
  2761. static void
  2762. irc_handle_quit (struct server *s, const struct irc_message *msg)
  2763. {
  2764. if (!msg->prefix)
  2765. return;
  2766. // What the fuck
  2767. if (irc_is_this_us (s, msg->prefix))
  2768. return;
  2769. char *nickname = irc_cut_nickname (msg->prefix);
  2770. struct user *user = str_map_find (&s->irc_users, nickname);
  2771. free (nickname);
  2772. if (!user)
  2773. return;
  2774. const char *message = "";
  2775. if (msg->params.len > 0)
  2776. message = msg->params.vector[0];
  2777. // Log a message in any PM buffer
  2778. struct buffer *buffer =
  2779. str_map_find (&s->irc_buffer_map, user->nickname);
  2780. if (buffer)
  2781. {
  2782. buffer_send (s->ctx, buffer, BUFFER_LINE_QUIT, 0,
  2783. .who = irc_to_utf8 (s->ctx, msg->prefix),
  2784. .reason = irc_to_utf8 (s->ctx, message));
  2785. // TODO: set some kind of a flag in the buffer and when the user
  2786. // reappers on a channel (JOIN), log a "is back online" message.
  2787. // Also set this flag when we receive a "no such nick" numeric
  2788. // and reset it when we send something to the buffer.
  2789. }
  2790. // Log a message in all channels the user is in
  2791. LIST_FOR_EACH (struct user_channel, iter, user->channels)
  2792. {
  2793. buffer = str_map_find (&s->irc_buffer_map, iter->channel->name);
  2794. if (buffer)
  2795. buffer_send (s->ctx, buffer, BUFFER_LINE_QUIT, 0,
  2796. .who = irc_to_utf8 (s->ctx, msg->prefix),
  2797. .reason = irc_to_utf8 (s->ctx, message));
  2798. // This destroys "iter" which doesn't matter to us
  2799. irc_remove_user_from_channel (user, iter->channel);
  2800. }
  2801. }
  2802. static void
  2803. irc_handle_topic (struct server *s, const struct irc_message *msg)
  2804. {
  2805. if (!msg->prefix || msg->params.len < 2)
  2806. return;
  2807. const char *channel_name = msg->params.vector[0];
  2808. const char *topic = msg->params.vector[1];
  2809. if (!irc_is_channel (s, channel_name))
  2810. return;
  2811. struct channel *channel = str_map_find (&s->irc_channels, channel_name);
  2812. struct buffer *buffer = str_map_find (&s->irc_buffer_map, channel_name);
  2813. hard_assert ((channel && buffer) ||
  2814. (channel && !buffer) || (!channel && !buffer));
  2815. // It would be is weird for this to be false
  2816. if (channel)
  2817. {
  2818. free (channel->topic);
  2819. channel->topic = xstrdup (topic);
  2820. }
  2821. if (buffer)
  2822. {
  2823. buffer_send (s->ctx, buffer, BUFFER_LINE_TOPIC, 0,
  2824. .who = irc_to_utf8 (s->ctx, msg->prefix),
  2825. .text = irc_to_utf8 (s->ctx, topic));
  2826. }
  2827. }
  2828. static struct irc_handler
  2829. {
  2830. char *name;
  2831. void (*handler) (struct server *s, const struct irc_message *msg);
  2832. }
  2833. g_irc_handlers[] =
  2834. {
  2835. // This list needs to stay sorted
  2836. { "JOIN", irc_handle_join },
  2837. { "KICK", irc_handle_kick },
  2838. { "MODE", irc_handle_mode },
  2839. { "NICK", irc_handle_nick },
  2840. { "NOTICE", irc_handle_notice },
  2841. { "PART", irc_handle_part },
  2842. { "PING", irc_handle_ping },
  2843. { "PRIVMSG", irc_handle_privmsg },
  2844. { "QUIT", irc_handle_quit },
  2845. { "TOPIC", irc_handle_topic },
  2846. };
  2847. static int
  2848. irc_handler_cmp_by_name (const void *a, const void *b)
  2849. {
  2850. const struct irc_handler *first = a;
  2851. const struct irc_handler *second = b;
  2852. return strcasecmp_ascii (first->name, second->name);
  2853. }
  2854. static bool
  2855. irc_try_parse_word_for_userhost (struct server *s, const char *word)
  2856. {
  2857. regex_t re;
  2858. int err = regcomp (&re, "^[^!@]+!([^!@]+@[^!@]+)$", REG_EXTENDED);
  2859. if (!soft_assert (!err))
  2860. return false;
  2861. regmatch_t matches[2];
  2862. bool result = false;
  2863. if (!regexec (&re, word, 2, matches, 0))
  2864. {
  2865. free (s->irc_user_host);
  2866. s->irc_user_host = xstrndup (word + matches[1].rm_so,
  2867. matches[1].rm_eo - matches[1].rm_so);
  2868. result = true;
  2869. }
  2870. regfree (&re);
  2871. return result;
  2872. }
  2873. static void
  2874. irc_try_parse_welcome_for_userhost (struct server *s, const char *m)
  2875. {
  2876. struct str_vector v;
  2877. str_vector_init (&v);
  2878. split_str_ignore_empty (m, ' ', &v);
  2879. for (size_t i = 0; i < v.len; i++)
  2880. if (irc_try_parse_word_for_userhost (s, v.vector[i]))
  2881. break;
  2882. str_vector_free (&v);
  2883. }
  2884. static void
  2885. irc_process_numeric (struct server *s,
  2886. const struct irc_message *msg, unsigned long numeric)
  2887. {
  2888. // Numerics typically have human-readable information
  2889. // TODO: try to output certain replies in more specific buffers
  2890. // Get rid of the first parameter, if there's any at all,
  2891. // as it contains our nickname and is of no practical use to the user
  2892. struct str_vector copy;
  2893. str_vector_init (&copy);
  2894. str_vector_add_vector (&copy, msg->params.vector + !!msg->params.len);
  2895. // Join the parameter vector back, recode it to our internal encoding
  2896. // and send it to the server buffer
  2897. char *reconstructed = join_str_vector (&copy, ' ');
  2898. str_vector_free (&copy);
  2899. buffer_send (s->ctx, s->buffer, BUFFER_LINE_STATUS, 0,
  2900. .text = irc_to_utf8 (s->ctx, reconstructed));
  2901. free (reconstructed);
  2902. switch (numeric)
  2903. {
  2904. case IRC_RPL_WELCOME:
  2905. // We still issue a USERHOST anyway as this is in general unreliable
  2906. if (msg->params.len == 2)
  2907. irc_try_parse_welcome_for_userhost (s, msg->params.vector[1]);
  2908. break;
  2909. case IRC_RPL_ISUPPORT:
  2910. // TODO: parse this, mainly PREFIX; see
  2911. // http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
  2912. break;
  2913. case IRC_RPL_NAMREPLY:
  2914. // TODO: find the channel and if found, push nicks to names_buf
  2915. break;
  2916. case IRC_RPL_ENDOFNAMES:
  2917. // TODO: find the channel and if found, overwrite users;
  2918. // however take care to combine channel user modes
  2919. break;
  2920. case IRC_ERR_NICKNAMEINUSE:
  2921. // TODO: if not connected yet (irc_ready), use a different nick;
  2922. // either use a number suffix, or accept commas in "nickname" config
  2923. break;
  2924. }
  2925. }
  2926. static void
  2927. irc_process_message (const struct irc_message *msg,
  2928. const char *raw, void *user_data)
  2929. {
  2930. struct server *s = user_data;
  2931. if (g_debug_mode)
  2932. {
  2933. input_hide (&s->ctx->input);
  2934. char *term = irc_to_term (s->ctx, raw);
  2935. fprintf (stderr, "[IRC] ==> \"%s\"\n", term);
  2936. free (term);
  2937. input_show (&s->ctx->input);
  2938. }
  2939. // XXX: or is the 001 numeric enough? For what?
  2940. if (!s->irc_ready && (!strcasecmp (msg->command, "MODE")
  2941. || !strcasecmp (msg->command, "376") // RPL_ENDOFMOTD
  2942. || !strcasecmp (msg->command, "422"))) // ERR_NOMOTD
  2943. {
  2944. // XXX: should we really print this?
  2945. buffer_send_status (s->ctx, s->buffer, "Successfully connected");
  2946. s->irc_ready = true;
  2947. refresh_prompt (s->ctx);
  2948. // TODO: parse any response and store the result for us in app_context;
  2949. // this enables proper message splitting on output;
  2950. // we can also use WHOIS if it's not supported (optional by RFC 2812)
  2951. irc_send (s, "USERHOST %s", s->irc_user->nickname);
  2952. const char *autojoin = get_config_string (s->ctx, "server.autojoin");
  2953. if (autojoin)
  2954. irc_send (s, "JOIN :%s", autojoin);
  2955. }
  2956. struct irc_handler key = { .name = msg->command };
  2957. struct irc_handler *handler = bsearch (&key, g_irc_handlers,
  2958. N_ELEMENTS (g_irc_handlers), sizeof key, irc_handler_cmp_by_name);
  2959. if (handler)
  2960. handler->handler (s, msg);
  2961. unsigned long numeric;
  2962. if (xstrtoul (&numeric, msg->command, 10))
  2963. irc_process_numeric (s, msg, numeric);
  2964. }
  2965. // --- Message autosplitting magic ---------------------------------------------
  2966. // This is the most basic acceptable algorithm; something like ICU with proper
  2967. // locale specification would be needed to make it work better.
  2968. static size_t
  2969. wrap_text_for_single_line (const char *text, size_t text_len,
  2970. size_t line_len, struct str *output)
  2971. {
  2972. int eaten = 0;
  2973. // First try going word by word
  2974. const char *word_start;
  2975. const char *word_end = text + strcspn (text, " ");
  2976. size_t word_len = word_end - text;
  2977. while (line_len && word_len <= line_len)
  2978. {
  2979. if (word_len)
  2980. {
  2981. str_append_data (output, text, word_len);
  2982. text += word_len;
  2983. eaten += word_len;
  2984. line_len -= word_len;
  2985. }
  2986. // Find the next word's end
  2987. word_start = text + strspn (text, " ");
  2988. word_end = word_start + strcspn (word_start, " ");
  2989. word_len = word_end - text;
  2990. }
  2991. if (eaten)
  2992. // Discard whitespace between words if split
  2993. return eaten + (word_start - text);
  2994. // And if that doesn't help, cut the longest valid block of characters
  2995. while (true)
  2996. {
  2997. const char *next = utf8_next (text, text_len - eaten);
  2998. hard_assert (next);
  2999. size_t char_len = next - text;
  3000. if (char_len > line_len)
  3001. break;
  3002. str_append_data (output, text, char_len);
  3003. text += char_len;
  3004. eaten += char_len;
  3005. line_len -= char_len;
  3006. }
  3007. return eaten;
  3008. }
  3009. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3010. static bool
  3011. wrap_message (const char *message,
  3012. int line_max, struct str_vector *output, struct error **e)
  3013. {
  3014. if (line_max <= 0)
  3015. goto error;
  3016. for (size_t message_left = strlen (message); message_left; )
  3017. {
  3018. struct str m;
  3019. str_init (&m);
  3020. size_t eaten = wrap_text_for_single_line (message,
  3021. MIN ((size_t) line_max, message_left), message_left, &m);
  3022. if (!eaten)
  3023. {
  3024. str_free (&m);
  3025. goto error;
  3026. }
  3027. str_vector_add_owned (output, str_steal (&m));
  3028. message += eaten;
  3029. message_left -= eaten;
  3030. }
  3031. return true;
  3032. error:
  3033. // Well, that's just weird
  3034. error_set (e,
  3035. "Message splitting was unsuccessful as there was "
  3036. "too little room for UTF-8 characters");
  3037. return false;
  3038. }
  3039. /// Automatically splits messages that arrive at other clients with our prefix
  3040. /// so that they don't arrive cut off by the server
  3041. static bool
  3042. irc_autosplit_message (struct server *s, const char *message,
  3043. int fixed_part, struct str_vector *output, struct error **e)
  3044. {
  3045. // :<nick>!<user>@<host> <fixed-part><message>
  3046. int space_in_one_message = 0;
  3047. if (s->irc_user_host)
  3048. space_in_one_message = 510
  3049. - 1 - (int) strlen (s->irc_user->nickname)
  3050. - 1 - (int) strlen (s->irc_user_host)
  3051. - 1 - fixed_part;
  3052. // However we don't always have the full info for message splitting
  3053. if (!space_in_one_message)
  3054. str_vector_add (output, message);
  3055. else if (!wrap_message (message, space_in_one_message, output, e))
  3056. return false;
  3057. return true;
  3058. }
  3059. struct send_autosplit_args;
  3060. typedef void (*send_autosplit_logger_fn) (struct server *s,
  3061. struct send_autosplit_args *args, struct buffer *buffer, const char *line);
  3062. struct send_autosplit_args
  3063. {
  3064. const char *command; ///< E.g. PRIVMSG or NOTICE
  3065. const char *target; ///< User or channel
  3066. const char *message; ///< A message to be autosplit
  3067. send_autosplit_logger_fn logger; ///< Logger for all resulting lines
  3068. const char *prefix; ///< E.g. "\x01ACTION"
  3069. const char *suffix; ///< E.g. "\x01"
  3070. };
  3071. static void
  3072. send_autosplit_message (struct server *s, struct send_autosplit_args a)
  3073. {
  3074. struct buffer *buffer = str_map_find (&s->irc_buffer_map, a.target);
  3075. int fixed_part = strlen (a.command) + 1 + strlen (a.target) + 1 + 1
  3076. + strlen (a.prefix) + strlen (a.suffix);
  3077. struct str_vector lines;
  3078. str_vector_init (&lines);
  3079. struct error *e = NULL;
  3080. if (!irc_autosplit_message (s, a.message, fixed_part, &lines, &e))
  3081. {
  3082. buffer_send_error (s->ctx,
  3083. buffer ? buffer : s->buffer, "%s", e->message);
  3084. error_free (e);
  3085. goto end;
  3086. }
  3087. for (size_t i = 0; i < lines.len; i++)
  3088. {
  3089. irc_send (s, "%s %s :%s%s%s", a.command, a.target,
  3090. a.prefix, lines.vector[i], a.suffix);
  3091. a.logger (s, &a, buffer, lines.vector[i]);
  3092. }
  3093. end:
  3094. str_vector_free (&lines);
  3095. }
  3096. static void
  3097. log_outcoming_action (struct server *s,
  3098. struct send_autosplit_args *a, struct buffer *buffer, const char *line)
  3099. {
  3100. (void) a;
  3101. if (buffer)
  3102. buffer_send (s->ctx, buffer, BUFFER_LINE_ACTION, 0,
  3103. .who = irc_to_utf8 (s->ctx, s->irc_user->nickname),
  3104. .text = irc_to_utf8 (s->ctx, line));
  3105. // This can only be sent from a user or channel buffer
  3106. }
  3107. #define SEND_AUTOSPLIT_ACTION(s, target, message) \
  3108. send_autosplit_message ((s), (struct send_autosplit_args) \
  3109. { "PRIVMSG", (target), (message), log_outcoming_action, \
  3110. "\x01" "ACTION ", "\x01" })
  3111. static void
  3112. log_outcoming_privmsg (struct server *s,
  3113. struct send_autosplit_args *a, struct buffer *buffer, const char *line)
  3114. {
  3115. if (buffer)
  3116. buffer_send (s->ctx, buffer, BUFFER_LINE_PRIVMSG, 0,
  3117. .who = irc_to_utf8 (s->ctx, s->irc_user->nickname),
  3118. .text = irc_to_utf8 (s->ctx, line));
  3119. else
  3120. // TODO: fix logging and encoding
  3121. buffer_send (s->ctx, s->buffer, BUFFER_LINE_STATUS, 0,
  3122. .text = xstrdup_printf ("MSG(%s): %s", a->target, line));
  3123. }
  3124. #define SEND_AUTOSPLIT_PRIVMSG(s, target, message) \
  3125. send_autosplit_message ((s), (struct send_autosplit_args) \
  3126. { "PRIVMSG", (target), (message), log_outcoming_privmsg, "", "" })
  3127. static void
  3128. log_outcoming_notice (struct server *s,
  3129. struct send_autosplit_args *a, struct buffer *buffer, const char *line)
  3130. {
  3131. if (buffer)
  3132. buffer_send (s->ctx, buffer, BUFFER_LINE_NOTICE, 0,
  3133. .who = irc_to_utf8 (s->ctx, s->irc_user->nickname),
  3134. .text = irc_to_utf8 (s->ctx, line));
  3135. else
  3136. // TODO: fix logging and encoding
  3137. buffer_send (s->ctx, s->buffer, BUFFER_LINE_STATUS, 0,
  3138. .text = xstrdup_printf ("Notice -> %s: %s", a->target, line));
  3139. }
  3140. #define SEND_AUTOSPLIT_NOTICE(s, target, message) \
  3141. send_autosplit_message ((s), (struct send_autosplit_args) \
  3142. { "NOTICE", (target), (message), log_outcoming_notice, "", "" })
  3143. // --- Configuration dumper ----------------------------------------------------
  3144. struct config_dump_level
  3145. {
  3146. struct config_dump_level *next; ///< Next print level
  3147. const char *name; ///< Name of the object
  3148. };
  3149. struct config_dump_data
  3150. {
  3151. struct config_dump_level *head; ///< The first level
  3152. struct config_dump_level **tail; ///< Where to place further levels
  3153. struct str_vector *output; ///< Where to place new entries
  3154. };
  3155. static void config_dump_item
  3156. (struct config_item_ *item, struct config_dump_data *data);
  3157. static void
  3158. config_dump_children
  3159. (struct config_item_ *object, struct config_dump_data *data)
  3160. {
  3161. hard_assert (object->type = CONFIG_ITEM_OBJECT);
  3162. struct config_dump_level level;
  3163. level.next = NULL;
  3164. struct config_dump_level **prev_tail = data->tail;
  3165. *data->tail = &level;
  3166. data->tail = &level.next;
  3167. struct str_map_iter iter;
  3168. str_map_iter_init (&iter, &object->value.object);
  3169. struct config_item_ *child;
  3170. while ((child = str_map_iter_next (&iter)))
  3171. {
  3172. level.name = iter.link->key;
  3173. config_dump_item (child, data);
  3174. }
  3175. data->tail = prev_tail;
  3176. *data->tail = NULL;
  3177. }
  3178. static void
  3179. config_dump_item (struct config_item_ *item, struct config_dump_data *data)
  3180. {
  3181. struct str line;
  3182. str_init (&line);
  3183. struct config_dump_level *iter = data->head;
  3184. if (iter)
  3185. {
  3186. str_append (&line, iter->name);
  3187. iter = iter->next;
  3188. }
  3189. for (; iter; iter = iter->next)
  3190. str_append_printf (&line, ".%s", iter->name);
  3191. // Empty objects will show as such
  3192. if (item->type == CONFIG_ITEM_OBJECT
  3193. && item->value.object.len)
  3194. {
  3195. config_dump_children (item, data);
  3196. return;
  3197. }
  3198. // Don't bother writing out null values everywhere
  3199. struct config_schema *schema = item->schema;
  3200. bool has_default = schema && schema->default_;
  3201. if (item->type != CONFIG_ITEM_NULL || has_default)
  3202. {
  3203. str_append (&line, " = ");
  3204. struct str value;
  3205. str_init (&value);
  3206. config_item_write (item, false, &value);
  3207. str_append_str (&line, &value);
  3208. if (has_default && strcmp (schema->default_, value.str))
  3209. str_append_printf (&line, " (default: %s)", schema->default_);
  3210. else if (!has_default && item->type != CONFIG_ITEM_NULL)
  3211. str_append_printf (&line, " (default: %s)", "null");
  3212. str_free (&value);
  3213. }
  3214. str_vector_add_owned (data->output, str_steal (&line));
  3215. }
  3216. static void
  3217. config_dump (struct config_item_ *root, struct str_vector *output)
  3218. {
  3219. struct config_dump_data data;
  3220. data.head = NULL;
  3221. data.tail = &data.head;
  3222. data.output = output;
  3223. config_dump_item (root, &data);
  3224. }
  3225. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  3226. static int
  3227. str_vector_sort_cb (const void *a, const void *b)
  3228. {
  3229. return strcmp (*(const char **) a, *(const char **) b);
  3230. }
  3231. static void
  3232. str_vector_sort (struct str_vector *self)
  3233. {
  3234. qsort (self->vector, self->len, sizeof *self->vector, str_vector_sort_cb);
  3235. }
  3236. static void
  3237. dump_matching_options
  3238. (struct app_context *ctx, const char *mask, struct str_vector *output)
  3239. {
  3240. config_dump (ctx->config.root, output);
  3241. str_vector_sort (output);
  3242. // Filter out results by wildcard matching
  3243. for (size_t i = 0; i < output->len; i++)
  3244. {
  3245. // Yeah, I know
  3246. const char *line = output->vector[i];
  3247. char *key = xstrndup (line, strcspn (line, " "));
  3248. if (fnmatch (mask, key, 0))
  3249. str_vector_remove (output, i--);
  3250. free (key);
  3251. }
  3252. }
  3253. // --- User input handling -----------------------------------------------------
  3254. static bool handle_command_help (struct app_context *, char *);
  3255. /// Cuts the longest non-whitespace portion of text and advances the pointer
  3256. static char *
  3257. cut_word (char **s)
  3258. {
  3259. char *start = *s;
  3260. size_t word_len = strcspn (*s, " \t");
  3261. char *end = start + word_len;
  3262. *s = end + strspn (end, " \t");
  3263. *end = '\0';
  3264. return start;
  3265. }
  3266. static bool
  3267. try_handle_buffer_goto (struct app_context *ctx, const char *word)
  3268. {
  3269. unsigned long n;
  3270. if (!xstrtoul (&n, word, 10))
  3271. return false;
  3272. if (n > INT_MAX || !buffer_goto (ctx, n))
  3273. buffer_send_error (ctx, ctx->global_buffer,
  3274. "%s: %s", "no such buffer", word);
  3275. return true;
  3276. }
  3277. static struct buffer *
  3278. try_decode_buffer (struct app_context *ctx, const char *word)
  3279. {
  3280. unsigned long n;
  3281. struct buffer *buffer = NULL;
  3282. if (xstrtoul (&n, word, 10) && n <= INT_MAX)
  3283. buffer = buffer_at_index (ctx, n);
  3284. if (!buffer)
  3285. buffer = buffer_by_name (ctx, word);
  3286. // TODO: decode the global and server buffers, partial matches
  3287. return buffer;
  3288. }
  3289. static bool
  3290. server_command_check (struct app_context *ctx, const char *action)
  3291. {
  3292. if (ctx->current_buffer->type == BUFFER_GLOBAL)
  3293. buffer_send_error (ctx, ctx->current_buffer,
  3294. "Can't do this from a global buffer (%s)", action);
  3295. else
  3296. {
  3297. struct server *s = ctx->current_buffer->server;
  3298. if (s->irc_fd == -1)
  3299. buffer_send_error (ctx, s->buffer, "Not connected");
  3300. else
  3301. return true;
  3302. }
  3303. return false;
  3304. }
  3305. static void
  3306. show_buffers_list (struct app_context *ctx)
  3307. {
  3308. buffer_send_status (ctx, ctx->global_buffer, "%s", "");
  3309. buffer_send_status (ctx, ctx->global_buffer, "Buffers list:");
  3310. int i = 1;
  3311. LIST_FOR_EACH (struct buffer, iter, ctx->buffers)
  3312. buffer_send_status (ctx, ctx->global_buffer,
  3313. " [%d] %s", i++, iter->name);
  3314. }
  3315. static void
  3316. handle_buffer_close (struct app_context *ctx, char *arguments)
  3317. {
  3318. struct buffer *buffer = NULL;
  3319. const char *which = NULL;
  3320. if (!*arguments)
  3321. buffer = ctx->current_buffer;
  3322. else
  3323. buffer = try_decode_buffer (ctx, (which = cut_word (&arguments)));
  3324. if (!buffer)
  3325. buffer_send_error (ctx, ctx->global_buffer,
  3326. "%s: %s", "No such buffer", which);
  3327. else if (buffer == ctx->global_buffer)
  3328. buffer_send_error (ctx, ctx->global_buffer,
  3329. "Can't close the global buffer");
  3330. else if (buffer->type == BUFFER_SERVER)
  3331. buffer_send_error (ctx, ctx->global_buffer,
  3332. "Can't close a server buffer");
  3333. else
  3334. {
  3335. if (buffer == ctx->current_buffer)
  3336. buffer_activate (ctx, buffer_next (ctx, 1));
  3337. buffer_remove (ctx, buffer);
  3338. }
  3339. }
  3340. static bool
  3341. handle_command_buffer (struct app_context *ctx, char *arguments)
  3342. {
  3343. char *action = cut_word (&arguments);
  3344. if (try_handle_buffer_goto (ctx, action))
  3345. return true;
  3346. // XXX: also build a prefix map?
  3347. // TODO: some subcommand to print N last lines from the buffer
  3348. if (!strcasecmp_ascii (action, "list"))
  3349. show_buffers_list (ctx);
  3350. else if (!strcasecmp_ascii (action, "clear"))
  3351. {
  3352. // TODO
  3353. }
  3354. else if (!strcasecmp_ascii (action, "move"))
  3355. {
  3356. // TODO: unlink the buffer and link it back at index;
  3357. // we will probably need to extend liberty for this
  3358. }
  3359. else if (!strcasecmp_ascii (action, "close"))
  3360. handle_buffer_close (ctx, arguments);
  3361. else
  3362. return false;
  3363. return true;
  3364. }
  3365. static bool
  3366. replace_string_array
  3367. (struct config_item_ *item, struct str_vector *array, struct error **e)
  3368. {
  3369. char *changed = join_str_vector (array, ',');
  3370. struct str tmp = { .str = changed, .len = strlen (changed) };
  3371. bool result = config_item_set_from (item,
  3372. config_item_string_array (&tmp), e);
  3373. free (changed);
  3374. return result;
  3375. }
  3376. static bool
  3377. handle_command_set_add
  3378. (struct config_item_ *item, const char *value, struct error **e)
  3379. {
  3380. bool result = false;
  3381. struct str_vector items;
  3382. str_vector_init (&items);
  3383. split_str (item->value.string.str, ',', &items);
  3384. if (items.len == 1 && !*items.vector[0])
  3385. str_vector_reset (&items);
  3386. if (str_vector_find (&items, value) != -1)
  3387. error_set (e, "already present in the array: %s", value);
  3388. else
  3389. {
  3390. str_vector_add (&items, value);
  3391. result = replace_string_array (item, &items, e);
  3392. }
  3393. str_vector_free (&items);
  3394. return result;
  3395. }
  3396. static bool
  3397. handle_command_set_remove
  3398. (struct config_item_ *item, const char *value, struct error **e)
  3399. {
  3400. bool result = false;
  3401. struct str_vector items;
  3402. str_vector_init (&items);
  3403. split_str (item->value.string.str, ',', &items);
  3404. if (items.len == 1 && !*items.vector[0])
  3405. str_vector_reset (&items);
  3406. ssize_t i = str_vector_find (&items, value);
  3407. if (i == -1)
  3408. error_set (e, "not present in the array: %s", value);
  3409. else
  3410. {
  3411. str_vector_remove (&items, i);
  3412. result = replace_string_array (item, &items, e);
  3413. }
  3414. str_vector_free (&items);
  3415. return result;
  3416. }
  3417. static void
  3418. handle_command_set_assign_item (struct app_context *ctx,
  3419. char *key, struct config_item_ *new_, bool add, bool remove)
  3420. {
  3421. struct config_item_ *item =
  3422. config_item_get (ctx->config.root, key, NULL);
  3423. hard_assert (item);
  3424. struct error *e = NULL;
  3425. if ((add | remove) && item->type != CONFIG_ITEM_STRING_ARRAY)
  3426. // FIXME: it can also be null, which makes this message confusing
  3427. error_set (&e, "not a string array");
  3428. else if (add)
  3429. handle_command_set_add (item, new_->value.string.str, &e);
  3430. else if (remove)
  3431. handle_command_set_remove (item, new_->value.string.str, &e);
  3432. else
  3433. config_item_set_from (item, config_item_clone (new_), &e);
  3434. if (e)
  3435. {
  3436. buffer_send_error (ctx, ctx->global_buffer,
  3437. "Failed to set option \"%s\": %s", key, e->message);
  3438. error_free (e);
  3439. }
  3440. else
  3441. {
  3442. struct str_vector tmp;
  3443. str_vector_init (&tmp);
  3444. dump_matching_options (ctx, key, &tmp);
  3445. buffer_send_status (ctx, ctx->global_buffer,
  3446. "Option changed: %s", tmp.vector[0]);
  3447. str_vector_free (&tmp);
  3448. }
  3449. }
  3450. static bool
  3451. handle_command_set_assign
  3452. (struct app_context *ctx, struct str_vector *all, char *arguments)
  3453. {
  3454. char *op = cut_word (&arguments);
  3455. bool add = false;
  3456. bool remove = false;
  3457. if (!strcmp (op, "+=")) add = true;
  3458. else if (!strcmp (op, "-=")) remove = true;
  3459. else if (strcmp (op, "=")) return false;
  3460. if (!arguments)
  3461. return false;
  3462. struct error *e = NULL;
  3463. struct config_item_ *new_ =
  3464. config_item_parse (arguments, strlen (arguments), true, &e);
  3465. if (e)
  3466. {
  3467. buffer_send_error (ctx, ctx->global_buffer,
  3468. "Invalid value: %s", e->message);
  3469. error_free (e);
  3470. return true;
  3471. }
  3472. if ((add | remove) && !config_item_type_is_string (new_->type))
  3473. {
  3474. buffer_send_error (ctx, ctx->global_buffer,
  3475. "+= / -= operators need a string argument");
  3476. config_item_destroy (new_);
  3477. return true;
  3478. }
  3479. for (size_t i = 0; i < all->len; i++)
  3480. {
  3481. char *key = xstrndup (all->vector[i], strcspn (all->vector[i], " "));
  3482. handle_command_set_assign_item (ctx, key, new_, add, remove);
  3483. free (key);
  3484. }
  3485. config_item_destroy (new_);
  3486. return true;
  3487. }
  3488. static bool
  3489. handle_command_set (struct app_context *ctx, char *arguments)
  3490. {
  3491. char *option = "*";
  3492. if (*arguments)
  3493. option = cut_word (&arguments);
  3494. struct str_vector all;
  3495. str_vector_init (&all);
  3496. dump_matching_options (ctx, option, &all);
  3497. bool result = true;
  3498. if (!all.len)
  3499. buffer_send_error (ctx, ctx->global_buffer, "No matches: %s", option);
  3500. else if (!*arguments)
  3501. {
  3502. buffer_send_status (ctx, ctx->global_buffer, "%s", "");
  3503. for (size_t i = 0; i < all.len; i++)
  3504. buffer_send_status (ctx, ctx->global_buffer, "%s", all.vector[i]);
  3505. }
  3506. else
  3507. result = handle_command_set_assign (ctx, &all, arguments);
  3508. str_vector_free (&all);
  3509. return result;
  3510. }
  3511. static bool
  3512. handle_command_save (struct app_context *ctx, char *arguments)
  3513. {
  3514. if (*arguments)
  3515. return false;
  3516. struct str data;
  3517. str_init (&data);
  3518. serialize_configuration (ctx, &data);
  3519. struct error *e = NULL;
  3520. char *filename = write_configuration_file (&data, &e);
  3521. str_free (&data);
  3522. if (!filename)
  3523. {
  3524. buffer_send_error (ctx, ctx->global_buffer,
  3525. "%s: %s", "Saving configuration failed", e->message);
  3526. error_free (e);
  3527. }
  3528. else
  3529. buffer_send_status (ctx, ctx->global_buffer,
  3530. "Configuration written to `%s'", filename);
  3531. free (filename);
  3532. return true;
  3533. }
  3534. static bool
  3535. handle_command_msg (struct app_context *ctx, char *arguments)
  3536. {
  3537. if (!server_command_check (ctx, "send messages"))
  3538. return true;
  3539. if (!*arguments)
  3540. return false;
  3541. struct server *s = ctx->current_buffer->server;
  3542. char *target = cut_word (&arguments);
  3543. if (!*arguments)
  3544. buffer_send_error (ctx, s->buffer, "No text to send");
  3545. else
  3546. SEND_AUTOSPLIT_PRIVMSG (s, target, arguments);
  3547. return true;
  3548. }
  3549. static bool
  3550. handle_command_query (struct app_context *ctx, char *arguments)
  3551. {
  3552. if (!server_command_check (ctx, "send messages"))
  3553. return true;
  3554. if (!*arguments)
  3555. return false;
  3556. struct server *s = ctx->current_buffer->server;
  3557. char *target = cut_word (&arguments);
  3558. if (irc_is_channel (s, target))
  3559. buffer_send_error (ctx, s->buffer, "Cannot query a channel");
  3560. else if (!*arguments)
  3561. buffer_send_error (ctx, s->buffer, "No text to send");
  3562. else
  3563. {
  3564. buffer_activate (ctx, irc_get_or_make_user_buffer (s, target));
  3565. SEND_AUTOSPLIT_PRIVMSG (s, target, arguments);
  3566. }
  3567. return true;
  3568. }
  3569. static bool
  3570. handle_command_notice (struct app_context *ctx, char *arguments)
  3571. {
  3572. if (!server_command_check (ctx, "send messages"))
  3573. return true;
  3574. if (!*arguments)
  3575. return false;
  3576. struct server *s = ctx->current_buffer->server;
  3577. char *target = cut_word (&arguments);
  3578. if (!*arguments)
  3579. buffer_send_error (ctx, s->buffer, "No text to send");
  3580. else
  3581. SEND_AUTOSPLIT_NOTICE (s, target, arguments);
  3582. return true;
  3583. }
  3584. static bool
  3585. handle_command_ctcp (struct app_context *ctx, char *arguments)
  3586. {
  3587. if (!server_command_check (ctx, "send messages"))
  3588. return true;
  3589. if (!*arguments)
  3590. return false;
  3591. char *target = cut_word (&arguments);
  3592. if (!*arguments)
  3593. return false;
  3594. char *tag = cut_word (&arguments);
  3595. for (char *p = tag; *p; p++)
  3596. *p = toupper_ascii (*p);
  3597. struct server *s = ctx->current_buffer->server;
  3598. if (*arguments)
  3599. irc_send (s, "PRIVMSG %s :\x01%s %s\x01", target, tag, arguments);
  3600. else
  3601. irc_send (s, "PRIVMSG %s :\x01%s\x01", target, tag);
  3602. buffer_send_status (ctx, s->buffer,
  3603. "CTCP query to %s: %s", target, tag);
  3604. return true;
  3605. }
  3606. static bool
  3607. handle_command_me (struct app_context *ctx, char *arguments)
  3608. {
  3609. if (!server_command_check (ctx, "send messages"))
  3610. return true;
  3611. struct server *s = ctx->current_buffer->server;
  3612. if (ctx->current_buffer->type == BUFFER_CHANNEL)
  3613. SEND_AUTOSPLIT_ACTION (s,
  3614. ctx->current_buffer->channel->name, arguments);
  3615. else if (ctx->current_buffer->type == BUFFER_PM)
  3616. SEND_AUTOSPLIT_ACTION (s,
  3617. ctx->current_buffer->user->nickname, arguments);
  3618. else
  3619. buffer_send_error (ctx, s->buffer,
  3620. "Can't do this from a server buffer (%s)",
  3621. "send CTCP actions");
  3622. return true;
  3623. }
  3624. static bool
  3625. handle_command_quit (struct app_context *ctx, char *arguments)
  3626. {
  3627. // TODO: multiserver
  3628. struct server *s = &ctx->server;
  3629. if (s->irc_fd != -1)
  3630. {
  3631. if (*arguments)
  3632. irc_send (s, "QUIT :%s", arguments);
  3633. else
  3634. irc_send (s, "QUIT :%s", PROGRAM_NAME " " PROGRAM_VERSION);
  3635. }
  3636. initiate_quit (ctx);
  3637. return true;
  3638. }
  3639. static bool
  3640. handle_command_join (struct app_context *ctx, char *arguments)
  3641. {
  3642. if (!server_command_check (ctx, "join"))
  3643. return true;
  3644. struct server *s = ctx->current_buffer->server;
  3645. if (*arguments)
  3646. // TODO: check if the arguments are in the form of
  3647. // "channel(,channel)* key(,key)*"
  3648. irc_send (s, "JOIN %s", arguments);
  3649. else
  3650. {
  3651. if (ctx->current_buffer->type != BUFFER_CHANNEL)
  3652. buffer_send_error (ctx, ctx->current_buffer,
  3653. "%s: %s", "Can't join",
  3654. "no argument given and this buffer is not a channel");
  3655. // TODO: have a better way of checking if we're on the channel
  3656. else if (ctx->current_buffer->channel->users)
  3657. buffer_send_error (ctx, ctx->current_buffer,
  3658. "%s: %s", "Can't join",
  3659. "you already are on the channel");
  3660. else
  3661. // TODO: send the key if known
  3662. irc_send (s, "JOIN %s", ctx->current_buffer->channel->name);
  3663. }
  3664. return true;
  3665. }
  3666. static bool
  3667. handle_command_part (struct app_context *ctx, char *arguments)
  3668. {
  3669. if (!server_command_check (ctx, "part"))
  3670. return true;
  3671. struct server *s = ctx->current_buffer->server;
  3672. if (*arguments)
  3673. // TODO: check if the arguments are in the form of "channel(,channel)*"
  3674. // TODO: make sure to send the reason as one argument
  3675. irc_send (s, "PART %s", arguments);
  3676. else
  3677. {
  3678. if (ctx->current_buffer->type != BUFFER_CHANNEL)
  3679. buffer_send_error (ctx, ctx->current_buffer,
  3680. "%s: %s", "Can't part",
  3681. "no argument given and this buffer is not a channel");
  3682. // TODO: have a better way of checking if we're on the channel
  3683. else if (!ctx->current_buffer->channel->users)
  3684. buffer_send_error (ctx, ctx->current_buffer,
  3685. "%s: %s", "Can't join", "you're not on the channel");
  3686. else
  3687. irc_send (s, "PART %s", ctx->current_buffer->channel->name);
  3688. }
  3689. return true;
  3690. }
  3691. static bool
  3692. handle_command_connect (struct app_context *ctx, char *arguments)
  3693. {
  3694. // TODO: multiserver
  3695. struct server *s = &ctx->server;
  3696. if (s->irc_fd != -1)
  3697. {
  3698. buffer_send_error (ctx, s->buffer, "Already connected");
  3699. return true;
  3700. }
  3701. irc_cancel_timers (s);
  3702. on_irc_reconnect_timeout (s);
  3703. return true;
  3704. }
  3705. static bool
  3706. handle_command_list (struct app_context *ctx, char *arguments)
  3707. {
  3708. if (!server_command_check (ctx, "list channels"))
  3709. return true;
  3710. struct server *s = ctx->current_buffer->server;
  3711. if (*arguments)
  3712. irc_send (s, "LIST %s", arguments);
  3713. else
  3714. irc_send (s, "LIST");
  3715. return true;
  3716. }
  3717. static bool
  3718. handle_command_nick (struct app_context *ctx, char *arguments)
  3719. {
  3720. if (!server_command_check (ctx, "change nickname"))
  3721. return true;
  3722. if (!*arguments)
  3723. return false;
  3724. struct server *s = ctx->current_buffer->server;
  3725. irc_send (s, "NICK %s", cut_word (&arguments));
  3726. return true;
  3727. }
  3728. static bool
  3729. handle_command_quote (struct app_context *ctx, char *arguments)
  3730. {
  3731. if (!server_command_check (ctx, "quote"))
  3732. return true;
  3733. struct server *s = ctx->current_buffer->server;
  3734. irc_send (s, "%s", arguments);
  3735. return true;
  3736. }
  3737. static struct command_handler
  3738. {
  3739. const char *name;
  3740. bool (*handler) (struct app_context *ctx, char *arguments);
  3741. const char *description;
  3742. const char *usage;
  3743. }
  3744. g_command_handlers[] =
  3745. {
  3746. { "help", handle_command_help, "Show help",
  3747. "[<command> | <option>]" },
  3748. { "quit", handle_command_quit, "Quit the program",
  3749. "[<message>]" },
  3750. { "buffer", handle_command_buffer, "Manage buffers",
  3751. "list | clear | move | { close [<number> | <name>] } | <number>" },
  3752. { "set", handle_command_set, "Manage configuration",
  3753. "[<option>]" },
  3754. { "save", handle_command_save, "Save configuration",
  3755. "" },
  3756. { "msg", handle_command_msg, "Send message to a nick or channel",
  3757. "<target> <message>" },
  3758. { "query", handle_command_query, "Send a private message to a nick",
  3759. "<nick> <message>" },
  3760. { "notice", handle_command_notice, "Send notice to a nick or channel",
  3761. "<target> <message>" },
  3762. { "ctcp", handle_command_ctcp, "Send a CTCP query",
  3763. "<target> <tag>" },
  3764. { "me", handle_command_me, "Send a CTCP action",
  3765. "<message>" },
  3766. { "join", handle_command_join, "Join channels",
  3767. "[<channel>[,<channel>...]]" },
  3768. { "part", handle_command_part, "Leave channels",
  3769. "[<channel>[,<channel>...]]" },
  3770. #if 0
  3771. { "cycle", NULL, "", "" },
  3772. { "mode", NULL, "", "" },
  3773. { "topic", NULL, "", "" },
  3774. { "kick", NULL, "", "" },
  3775. { "kickban", NULL, "", "" },
  3776. { "ban", NULL, "", "" },
  3777. { "invite", NULL, "", "" },
  3778. #endif
  3779. { "connect", handle_command_connect, "Connect to the server",
  3780. "" },
  3781. { "list", handle_command_list, "List channels and their topic",
  3782. "[<channel>[,<channel>...]] [server]" },
  3783. #if 0
  3784. { "names", NULL, "", "" },
  3785. { "who", NULL, "", "" },
  3786. { "whois", NULL, "", "" },
  3787. { "motd", NULL, "", "" },
  3788. { "away", NULL, "", "" },
  3789. #endif
  3790. { "nick", handle_command_nick, "Change current nick",
  3791. "<nickname>" },
  3792. { "quote", handle_command_quote, "Send a raw command to the server",
  3793. "<command>" },
  3794. };
  3795. static bool
  3796. try_handle_command_help_option (struct app_context *ctx, const char *name)
  3797. {
  3798. struct config_item_ *item =
  3799. config_item_get (ctx->config.root, name, NULL);
  3800. if (!item)
  3801. return false;
  3802. struct config_schema *schema = item->schema;
  3803. if (!schema)
  3804. {
  3805. buffer_send_error (ctx, ctx->global_buffer,
  3806. "%s: %s", "This option has no schema", name);
  3807. return true;
  3808. }
  3809. buffer_send_status (ctx, ctx->global_buffer, "%s", "");
  3810. buffer_send_status (ctx, ctx->global_buffer,
  3811. "Option \"%s\":", name);
  3812. buffer_send_status (ctx, ctx->global_buffer,
  3813. " Description: %s", schema->comment);
  3814. buffer_send_status (ctx, ctx->global_buffer,
  3815. " Type: %s", config_item_type_name (schema->type));
  3816. buffer_send_status (ctx, ctx->global_buffer,
  3817. " Default: %s", schema->default_ ? schema->default_ : "null");
  3818. struct str tmp;
  3819. str_init (&tmp);
  3820. config_item_write (item, false, &tmp);
  3821. buffer_send_status (ctx, ctx->global_buffer,
  3822. " Current value: %s", tmp.str);
  3823. str_free (&tmp);
  3824. return true;
  3825. }
  3826. static bool
  3827. handle_command_help (struct app_context *ctx, char *arguments)
  3828. {
  3829. if (!*arguments)
  3830. {
  3831. buffer_send_status (ctx, ctx->global_buffer, "%s", "");
  3832. buffer_send_status (ctx, ctx->global_buffer, "Commands:");
  3833. for (size_t i = 0; i < N_ELEMENTS (g_command_handlers); i++)
  3834. {
  3835. struct command_handler *handler = &g_command_handlers[i];
  3836. buffer_send_status (ctx, ctx->global_buffer, " %s: %s",
  3837. handler->name, handler->description);
  3838. }
  3839. return true;
  3840. }
  3841. char *command = cut_word (&arguments);
  3842. for (size_t i = 0; i < N_ELEMENTS (g_command_handlers); i++)
  3843. {
  3844. struct command_handler *handler = &g_command_handlers[i];
  3845. if (strcasecmp_ascii (command, handler->name))
  3846. continue;
  3847. buffer_send_status (ctx, ctx->global_buffer, "%s", "");
  3848. buffer_send_status (ctx, ctx->global_buffer, "%s: %s",
  3849. handler->name, handler->description);
  3850. buffer_send_status (ctx, ctx->global_buffer, " Arguments: %s",
  3851. handler->usage);
  3852. return true;
  3853. }
  3854. if (!try_handle_command_help_option (ctx, command))
  3855. buffer_send_error (ctx, ctx->global_buffer,
  3856. "%s: %s", "No such command or option", command);
  3857. return true;
  3858. }
  3859. static int
  3860. command_handler_cmp_by_length (const void *a, const void *b)
  3861. {
  3862. const struct command_handler *first = a;
  3863. const struct command_handler *second = b;
  3864. return strlen (first->name) - strlen (second->name);
  3865. }
  3866. static void
  3867. init_partial_matching_user_command_map (struct str_map *partial)
  3868. {
  3869. // Trivially create a partial matching map
  3870. str_map_init (partial);
  3871. partial->key_xfrm = tolower_ascii_strxfrm;
  3872. // We process them from the longest to the shortest one,
  3873. // so that common prefixes favor shorter entries
  3874. struct command_handler *by_length[N_ELEMENTS (g_command_handlers)];
  3875. for (size_t i = 0; i < N_ELEMENTS (by_length); i++)
  3876. by_length[i] = &g_command_handlers[i];
  3877. qsort (by_length, N_ELEMENTS (by_length), sizeof *by_length,
  3878. command_handler_cmp_by_length);
  3879. for (size_t i = N_ELEMENTS (by_length); i--; )
  3880. {
  3881. char *copy = xstrdup (by_length[i]->name);
  3882. for (size_t part = strlen (copy); part; part--)
  3883. {
  3884. copy[part] = '\0';
  3885. str_map_set (partial, copy, by_length[i]);
  3886. }
  3887. free (copy);
  3888. }
  3889. }
  3890. static void
  3891. process_user_command (struct app_context *ctx, char *command)
  3892. {
  3893. static bool initialized = false;
  3894. static struct str_map partial;
  3895. if (!initialized)
  3896. {
  3897. init_partial_matching_user_command_map (&partial);
  3898. initialized = true;
  3899. }
  3900. char *name = cut_word (&command);
  3901. if (try_handle_buffer_goto (ctx, name))
  3902. return;
  3903. struct command_handler *handler = str_map_find (&partial, name);
  3904. if (!handler)
  3905. buffer_send_error (ctx, ctx->global_buffer,
  3906. "%s: %s", "No such command", name);
  3907. else if (!handler->handler (ctx, command))
  3908. buffer_send_error (ctx, ctx->global_buffer,
  3909. "%s: /%s %s", "Usage", handler->name, handler->usage);
  3910. }
  3911. static void
  3912. send_message_to_target (struct server *s,
  3913. const char *target, char *message, struct buffer *buffer)
  3914. {
  3915. if (s->irc_fd == -1)
  3916. {
  3917. buffer_send_error (s->ctx, buffer, "Not connected");
  3918. return;
  3919. }
  3920. SEND_AUTOSPLIT_PRIVMSG (s, target, message);
  3921. }
  3922. static void
  3923. send_message_to_current_buffer (struct app_context *ctx, char *message)
  3924. {
  3925. struct buffer *buffer = ctx->current_buffer;
  3926. hard_assert (buffer != NULL);
  3927. switch (buffer->type)
  3928. {
  3929. case BUFFER_GLOBAL:
  3930. case BUFFER_SERVER:
  3931. buffer_send_error (ctx, buffer, "This buffer is not a channel");
  3932. break;
  3933. case BUFFER_CHANNEL:
  3934. send_message_to_target (buffer->server,
  3935. buffer->channel->name, message, buffer);
  3936. break;
  3937. case BUFFER_PM:
  3938. send_message_to_target (buffer->server,
  3939. buffer->user->nickname, message, buffer);
  3940. break;
  3941. }
  3942. }
  3943. static void
  3944. process_input (struct app_context *ctx, char *user_input)
  3945. {
  3946. char *input;
  3947. size_t len;
  3948. if (!(input = iconv_xstrdup (ctx->term_to_utf8, user_input, -1, &len)))
  3949. print_error ("character conversion failed for `%s'", "user input");
  3950. else if (input[0] != '/')
  3951. send_message_to_current_buffer (ctx, input);
  3952. else if (input[1] == '/')
  3953. send_message_to_current_buffer (ctx, input + 1);
  3954. else
  3955. process_user_command (ctx, input + 1);
  3956. free (input);
  3957. }
  3958. // --- Supporting code (continued) ---------------------------------------------
  3959. enum irc_read_result
  3960. {
  3961. IRC_READ_OK, ///< Some data were read successfully
  3962. IRC_READ_EOF, ///< The server has closed connection
  3963. IRC_READ_AGAIN, ///< No more data at the moment
  3964. IRC_READ_ERROR ///< General connection failure
  3965. };
  3966. static enum irc_read_result
  3967. irc_fill_read_buffer_ssl (struct server *s, struct str *buf)
  3968. {
  3969. int n_read;
  3970. start:
  3971. n_read = SSL_read (s->ssl, buf->str + buf->len,
  3972. buf->alloc - buf->len - 1 /* null byte */);
  3973. const char *error_info = NULL;
  3974. switch (xssl_get_error (s->ssl, n_read, &error_info))
  3975. {
  3976. case SSL_ERROR_NONE:
  3977. buf->str[buf->len += n_read] = '\0';
  3978. return IRC_READ_OK;
  3979. case SSL_ERROR_ZERO_RETURN:
  3980. return IRC_READ_EOF;
  3981. case SSL_ERROR_WANT_READ:
  3982. return IRC_READ_AGAIN;
  3983. case SSL_ERROR_WANT_WRITE:
  3984. {
  3985. // Let it finish the handshake as we don't poll for writability;
  3986. // any errors are to be collected by SSL_read() in the next iteration
  3987. struct pollfd pfd = { .fd = s->irc_fd, .events = POLLOUT };
  3988. soft_assert (poll (&pfd, 1, 0) > 0);
  3989. goto start;
  3990. }
  3991. case XSSL_ERROR_TRY_AGAIN:
  3992. goto start;
  3993. default:
  3994. LOG_FUNC_FAILURE ("SSL_read", error_info);
  3995. return IRC_READ_ERROR;
  3996. }
  3997. }
  3998. static enum irc_read_result
  3999. irc_fill_read_buffer (struct server *s, struct str *buf)
  4000. {
  4001. ssize_t n_read;
  4002. start:
  4003. n_read = recv (s->irc_fd, buf->str + buf->len,
  4004. buf->alloc - buf->len - 1 /* null byte */, 0);
  4005. if (n_read > 0)
  4006. {
  4007. buf->str[buf->len += n_read] = '\0';
  4008. return IRC_READ_OK;
  4009. }
  4010. if (n_read == 0)
  4011. return IRC_READ_EOF;
  4012. if (errno == EAGAIN)
  4013. return IRC_READ_AGAIN;
  4014. if (errno == EINTR)
  4015. goto start;
  4016. LOG_LIBC_FAILURE ("recv");
  4017. return IRC_READ_ERROR;
  4018. }
  4019. static void
  4020. irc_cancel_timers (struct server *s)
  4021. {
  4022. poller_timer_reset (&s->timeout_tmr);
  4023. poller_timer_reset (&s->ping_tmr);
  4024. poller_timer_reset (&s->reconnect_tmr);
  4025. }
  4026. static void
  4027. irc_queue_reconnect (struct server *s)
  4028. {
  4029. // TODO: exponentional backoff
  4030. hard_assert (s->irc_fd == -1);
  4031. buffer_send_status (s->ctx, s->buffer,
  4032. "Trying to reconnect in %ld seconds...", s->ctx->reconnect_delay);
  4033. poller_timer_set (&s->reconnect_tmr, s->ctx->reconnect_delay * 1000);
  4034. }
  4035. static void
  4036. on_irc_reconnect_timeout (void *user_data)
  4037. {
  4038. struct server *s = user_data;
  4039. struct error *e = NULL;
  4040. bool should_retry = false;
  4041. if (irc_connect (s, &should_retry, &e))
  4042. return;
  4043. buffer_send_error (s->ctx, s->buffer, "%s", e->message);
  4044. error_free (e);
  4045. if (should_retry)
  4046. irc_queue_reconnect (s);
  4047. }
  4048. static void
  4049. on_irc_disconnected (struct server *s)
  4050. {
  4051. // Get rid of the dead socket and related things
  4052. if (s->ssl)
  4053. {
  4054. SSL_free (s->ssl);
  4055. s->ssl = NULL;
  4056. SSL_CTX_free (s->ssl_ctx);
  4057. s->ssl_ctx = NULL;
  4058. }
  4059. xclose (s->irc_fd);
  4060. s->irc_fd = -1;
  4061. s->irc_ready = false;
  4062. user_unref (s->irc_user);
  4063. s->irc_user = NULL;
  4064. free (s->irc_user_mode);
  4065. s->irc_user_mode = NULL;
  4066. free (s->irc_user_host);
  4067. s->irc_user_host = NULL;
  4068. s->irc_event.closed = true;
  4069. poller_fd_reset (&s->irc_event);
  4070. // All of our timers have lost their meaning now
  4071. irc_cancel_timers (s);
  4072. if (s->ctx->quitting)
  4073. try_finish_quit (s->ctx);
  4074. else if (!s->ctx->reconnect)
  4075. // XXX: not sure if we want this in a client
  4076. // FIXME: no, we don't, would need to be changed for multiserver anyway
  4077. initiate_quit (s->ctx);
  4078. else
  4079. irc_queue_reconnect (s);
  4080. }
  4081. static void
  4082. on_irc_ping_timeout (void *user_data)
  4083. {
  4084. struct server *s = user_data;
  4085. buffer_send_error (s->ctx, s->buffer, "Connection timeout");
  4086. on_irc_disconnected (s);
  4087. }
  4088. static void
  4089. on_irc_timeout (void *user_data)
  4090. {
  4091. // Provoke a response from the server
  4092. struct server *s = user_data;
  4093. irc_send (s, "PING :%s", get_config_string (s->ctx, "server.nickname"));
  4094. }
  4095. static void
  4096. irc_reset_connection_timeouts (struct server *s)
  4097. {
  4098. irc_cancel_timers (s);
  4099. poller_timer_set (&s->timeout_tmr, 3 * 60 * 1000);
  4100. poller_timer_set (&s->ping_tmr, (3 * 60 + 30) * 1000);
  4101. }
  4102. static void
  4103. on_irc_readable (const struct pollfd *fd, struct server *s)
  4104. {
  4105. if (fd->revents & ~(POLLIN | POLLHUP | POLLERR))
  4106. print_debug ("fd %d: unexpected revents: %d", fd->fd, fd->revents);
  4107. (void) set_blocking (s->irc_fd, false);
  4108. struct str *buf = &s->read_buffer;
  4109. enum irc_read_result (*fill_buffer)(struct server *, struct str *)
  4110. = s->ssl
  4111. ? irc_fill_read_buffer_ssl
  4112. : irc_fill_read_buffer;
  4113. bool disconnected = false;
  4114. while (true)
  4115. {
  4116. str_ensure_space (buf, 512);
  4117. switch (fill_buffer (s, buf))
  4118. {
  4119. case IRC_READ_AGAIN:
  4120. goto end;
  4121. case IRC_READ_ERROR:
  4122. buffer_send_error (s->ctx, s->buffer,
  4123. "Reading from the IRC server failed");
  4124. disconnected = true;
  4125. goto end;
  4126. case IRC_READ_EOF:
  4127. buffer_send_error (s->ctx, s->buffer,
  4128. "The IRC server closed the connection");
  4129. disconnected = true;
  4130. goto end;
  4131. case IRC_READ_OK:
  4132. break;
  4133. }
  4134. if (buf->len >= (1 << 20))
  4135. {
  4136. buffer_send_error (s->ctx, s->buffer,
  4137. "The IRC server seems to spew out data frantically");
  4138. irc_shutdown (s);
  4139. goto end;
  4140. }
  4141. }
  4142. end:
  4143. (void) set_blocking (s->irc_fd, true);
  4144. irc_process_buffer (buf, irc_process_message, s);
  4145. if (disconnected)
  4146. on_irc_disconnected (s);
  4147. else
  4148. irc_reset_connection_timeouts (s);
  4149. }
  4150. static bool
  4151. irc_connect (struct server *s, bool *should_retry, struct error **e)
  4152. {
  4153. // TODO: connect asynchronously so that we don't freeze
  4154. struct app_context *ctx = s->ctx;
  4155. *should_retry = true;
  4156. const char *irc_host = get_config_string (ctx, "server.irc_host");
  4157. int64_t irc_port_int = get_config_integer (ctx, "server.irc_port");
  4158. if (!get_config_string (ctx, "server.irc_host"))
  4159. {
  4160. error_set (e, "No hostname specified in configuration");
  4161. *should_retry = false;
  4162. return false;
  4163. }
  4164. const char *socks_host = get_config_string (ctx, "server.socks_host");
  4165. int64_t socks_port_int = get_config_integer (ctx, "server.socks_port");
  4166. const char *socks_username =
  4167. get_config_string (ctx, "server.socks_username");
  4168. const char *socks_password =
  4169. get_config_string (ctx, "server.socks_password");
  4170. // FIXME: use it as a number everywhere, there's no need for named services
  4171. // FIXME: memory leak
  4172. char *irc_port = xstrdup_printf ("%" PRIi64, irc_port_int);
  4173. char *socks_port = xstrdup_printf ("%" PRIi64, socks_port_int);
  4174. const char *nickname = get_config_string (ctx, "server.nickname");
  4175. const char *username = get_config_string (ctx, "server.username");
  4176. const char *realname = get_config_string (ctx, "server.realname");
  4177. // These are filled automatically if needed
  4178. hard_assert (nickname && username && realname);
  4179. bool use_ssl = get_config_boolean (ctx, "server.ssl");
  4180. if (socks_host)
  4181. {
  4182. char *address = format_host_port_pair (irc_host, irc_port);
  4183. char *socks_address = format_host_port_pair (socks_host, socks_port);
  4184. buffer_send_status (ctx, s->buffer,
  4185. "Connecting to %s via %s...", address, socks_address);
  4186. free (socks_address);
  4187. free (address);
  4188. struct error *error = NULL;
  4189. int fd = socks_connect (socks_host, socks_port, irc_host, irc_port,
  4190. socks_username, socks_password, &error);
  4191. if (fd == -1)
  4192. {
  4193. error_set (e, "%s: %s", "SOCKS connection failed", error->message);
  4194. error_free (error);
  4195. return false;
  4196. }
  4197. s->irc_fd = fd;
  4198. }
  4199. else if (!irc_establish_connection (s, irc_host, irc_port, e))
  4200. return false;
  4201. if (use_ssl && !irc_initialize_ssl (s, e))
  4202. {
  4203. xclose (s->irc_fd);
  4204. s->irc_fd = -1;
  4205. return false;
  4206. }
  4207. buffer_send_status (ctx, s->buffer, "Connection established");
  4208. poller_fd_init (&s->irc_event, &ctx->poller, s->irc_fd);
  4209. s->irc_event.dispatcher = (poller_fd_fn) on_irc_readable;
  4210. s->irc_event.user_data = s;
  4211. poller_fd_set (&s->irc_event, POLLIN);
  4212. irc_reset_connection_timeouts (s);
  4213. irc_send (s, "NICK %s", nickname);
  4214. // IRC servers may ignore the last argument if it's empty
  4215. irc_send (s, "USER %s 8 * :%s", username, *realname ? realname : " ");
  4216. // XXX: maybe we should wait for the first message from the server
  4217. // FIXME: the user may exist already after we've reconnected. Either
  4218. // make sure that there's no reference of this nick upon disconnection,
  4219. // or search in "irc_users" first... or something.
  4220. s->irc_user = irc_make_user (s, xstrdup (nickname));
  4221. s->irc_user_mode = xstrdup ("");
  4222. s->irc_user_host = NULL;
  4223. return true;
  4224. }
  4225. // --- User interface actions --------------------------------------------------
  4226. #ifdef HAVE_READLINE
  4227. static int
  4228. on_readline_goto_buffer (int count, int key)
  4229. {
  4230. (void) count;
  4231. int n = UNMETA (key) - '0';
  4232. if (n < 0 || n > 9)
  4233. return 0;
  4234. // There's no buffer zero
  4235. if (n == 0)
  4236. n = 10;
  4237. struct app_context *ctx = g_ctx;
  4238. if (ctx->last_buffer && buffer_get_index (ctx, ctx->current_buffer) == n)
  4239. // Fast switching between two buffers
  4240. buffer_activate (ctx, ctx->last_buffer);
  4241. else if (!buffer_goto (ctx, n))
  4242. input_ding (self);
  4243. return 0;
  4244. }
  4245. static int
  4246. on_readline_previous_buffer (int count, int key)
  4247. {
  4248. (void) key;
  4249. struct app_context *ctx = g_ctx;
  4250. buffer_activate (ctx, buffer_previous (ctx, count));
  4251. return 0;
  4252. }
  4253. static int
  4254. on_readline_next_buffer (int count, int key)
  4255. {
  4256. (void) key;
  4257. struct app_context *ctx = g_ctx;
  4258. buffer_activate (ctx, buffer_next (ctx, count));
  4259. return 0;
  4260. }
  4261. static int
  4262. on_readline_return (int count, int key)
  4263. {
  4264. (void) count;
  4265. (void) key;
  4266. struct input *self = &g_ctx->input;
  4267. // Let readline pass the line to our input handler
  4268. rl_done = 1;
  4269. // Hide the line, don't redisplay it
  4270. input_hide (self);
  4271. input_restore (self);
  4272. return 0;
  4273. }
  4274. static void
  4275. on_readline_input (char *line)
  4276. {
  4277. struct input *self = &g_ctx->input;
  4278. if (line)
  4279. {
  4280. if (*line)
  4281. add_history (line);
  4282. process_input (g_ctx, line);
  4283. free (line);
  4284. }
  4285. else
  4286. {
  4287. input_hide (self);
  4288. input_restore (self);
  4289. input_ding (self);
  4290. }
  4291. if (self->active)
  4292. // Readline automatically redisplays it
  4293. self->prompt_shown = 1;
  4294. }
  4295. static void
  4296. app_readline_bind_meta (char key, rl_command_func_t cb)
  4297. {
  4298. // This one seems to actually work
  4299. char keyseq[] = { '\\', 'e', key, 0 };
  4300. rl_bind_keyseq (keyseq, cb);
  4301. #if 0
  4302. // While this one only fucks up UTF-8
  4303. // Tested with urxvt and xterm, on Debian Jessie/Arch, default settings
  4304. // \M-<key> behaves exactly the same
  4305. rl_bind_key (META (key), cb);
  4306. #endif
  4307. }
  4308. static int
  4309. app_readline_init (void)
  4310. {
  4311. // XXX: maybe use rl_make_bare_keymap() and start from there;
  4312. // our dear user could potentionally rig things up in a way that might
  4313. // result in some funny unspecified behaviour
  4314. rl_add_defun ("previous-buffer", on_readline_previous_buffer, -1);
  4315. rl_add_defun ("next-buffer", on_readline_next_buffer, -1);
  4316. // Redefine M-0 through M-9 to switch buffers
  4317. for (int i = 0; i <= 9; i++)
  4318. app_readline_bind_meta ('0' + i, on_readline_goto_buffer);
  4319. rl_bind_keyseq ("\\C-p", rl_named_function ("previous-buffer"));
  4320. rl_bind_keyseq ("\\C-n", rl_named_function ("next-buffer"));
  4321. app_readline_bind_meta ('p', rl_named_function ("previous-history"));
  4322. app_readline_bind_meta ('n', rl_named_function ("next-history"));
  4323. // We need to hide the prompt first
  4324. rl_bind_key (RETURN, on_readline_return);
  4325. return 0;
  4326. }
  4327. #endif // HAVE_READLINE
  4328. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4329. #ifdef HAVE_EDITLINE
  4330. static unsigned char
  4331. on_editline_goto_buffer (EditLine *editline, int key)
  4332. {
  4333. (void) editline;
  4334. int n = key - '0';
  4335. if (n < 0 || n > 9)
  4336. return CC_ERROR;
  4337. // There's no buffer zero
  4338. if (n == 0)
  4339. n = 10;
  4340. struct app_context *ctx = g_ctx;
  4341. if (ctx->last_buffer && buffer_get_index (ctx, ctx->current_buffer) == n)
  4342. // Fast switching between two buffers
  4343. buffer_activate (ctx, ctx->last_buffer);
  4344. else if (!buffer_goto (ctx, n))
  4345. return CC_ERROR;
  4346. return CC_NORM;
  4347. }
  4348. static unsigned char
  4349. on_editline_previous_buffer (EditLine *editline, int key)
  4350. {
  4351. (void) editline;
  4352. (void) key;
  4353. struct app_context *ctx = g_ctx;
  4354. buffer_activate (ctx, buffer_previous (ctx, 1));
  4355. return CC_NORM;
  4356. }
  4357. static unsigned char
  4358. on_editline_next_buffer (EditLine *editline, int key)
  4359. {
  4360. (void) editline;
  4361. (void) key;
  4362. struct app_context *ctx = g_ctx;
  4363. buffer_activate (ctx, buffer_next (ctx, 1));
  4364. return CC_NORM;
  4365. }
  4366. static unsigned char
  4367. on_editline_return (EditLine *editline, int key)
  4368. {
  4369. (void) key;
  4370. struct input *self = &g_ctx->input;
  4371. const LineInfoW *info = el_wline (editline);
  4372. int len = info->lastchar - info->buffer;
  4373. int point = info->cursor - info->buffer;
  4374. wchar_t *line = calloc (sizeof *info->buffer, len + 1);
  4375. memcpy (line, info->buffer, sizeof *info->buffer * len);
  4376. // XXX: Editline seems to remember its position in history,
  4377. // so it's not going to work as you'd expect it to
  4378. if (*line)
  4379. {
  4380. HistEventW ev;
  4381. history_w (self->current->history, &ev, H_ENTER, line);
  4382. print_debug ("history: %d %ls", ev.num, ev.str);
  4383. }
  4384. // Convert it to multibyte to reflect the Readline interface
  4385. size_t needed = wcstombs (NULL, line, 0) + 1;
  4386. char converted[needed];
  4387. if (!needed || wcstombs (converted, line, needed) == (size_t) -1)
  4388. print_error ("encoding conversion failed");
  4389. else
  4390. process_input (g_ctx, converted);
  4391. free (line);
  4392. el_cursor (editline, len - point);
  4393. el_wdeletestr (editline, len);
  4394. return CC_REFRESH;
  4395. }
  4396. static void
  4397. app_editline_init (struct input *self)
  4398. {
  4399. el_set (self->editline, EL_ADDFN, "goto-buffer",
  4400. "Go to buffer", on_editline_goto_buffer);
  4401. el_set (self->editline, EL_ADDFN, "previous-buffer",
  4402. "Previous buffer", on_editline_previous_buffer);
  4403. el_set (self->editline, EL_ADDFN, "next-buffer",
  4404. "Next buffer", on_editline_next_buffer);
  4405. // Redefine M-0 through M-9 to switch buffers
  4406. for (size_t i = 0; i < 10; i++)
  4407. {
  4408. char keyseq[] = { 'M', '-', '0' + i, 0 };
  4409. el_set (self->editline, EL_BIND, keyseq, "goto-buffer", NULL);
  4410. }
  4411. el_set (self->editline, EL_BIND, "M-p", "ed-prev-history", NULL);
  4412. el_set (self->editline, EL_BIND, "M-n", "ed-next-history", NULL);
  4413. el_set (self->editline, EL_BIND, "^P", "previous-buffer", NULL);
  4414. el_set (self->editline, EL_BIND, "^N", "next-buffer", NULL);
  4415. // Source the user's defaults file
  4416. el_source (self->editline, NULL);
  4417. el_set (self->editline, EL_ADDFN, "send-line",
  4418. "Send line", on_editline_return);
  4419. el_set (self->editline, EL_BIND, "\n", "send-line", NULL);
  4420. }
  4421. #endif // HAVE_EDITLINE
  4422. // --- I/O event handlers ------------------------------------------------------
  4423. static void
  4424. on_signal_pipe_readable (const struct pollfd *fd, struct app_context *ctx)
  4425. {
  4426. char dummy;
  4427. (void) read (fd->fd, &dummy, 1);
  4428. if (g_termination_requested && !ctx->quitting)
  4429. {
  4430. // There may be a timer set to reconnect to the server
  4431. // TODO: multiserver
  4432. struct server *s = &ctx->server;
  4433. // TODO: a faster timer for quitting
  4434. irc_reset_connection_timeouts (s);
  4435. // FIXME: use a normal quit message
  4436. if (s->irc_fd != -1)
  4437. irc_send (s, "QUIT :Terminated by signal");
  4438. initiate_quit (ctx);
  4439. }
  4440. if (g_winch_received)
  4441. {
  4442. input_on_terminal_resized (&ctx->input);
  4443. update_screen_size ();
  4444. }
  4445. }
  4446. static void
  4447. on_tty_readable (const struct pollfd *fd, struct app_context *ctx)
  4448. {
  4449. (void) ctx;
  4450. if (fd->revents & ~(POLLIN | POLLHUP | POLLERR))
  4451. print_debug ("fd %d: unexpected revents: %d", fd->fd, fd->revents);
  4452. input_on_readable (&ctx->input);
  4453. }
  4454. // --- Configuration loading ---------------------------------------------------
  4455. static bool
  4456. autofill_user_info (struct app_context *ctx, struct error **e)
  4457. {
  4458. const char *nickname = get_config_string (ctx, "server.nickname");
  4459. const char *username = get_config_string (ctx, "server.username");
  4460. const char *realname = get_config_string (ctx, "server.realname");
  4461. if (nickname && username && realname)
  4462. return true;
  4463. // Read POSIX user info and fill the configuration if needed
  4464. struct passwd *pwd = getpwuid (geteuid ());
  4465. if (!pwd)
  4466. FAIL ("cannot retrieve user information: %s", strerror (errno));
  4467. // FIXME: set_config_strings() writes errors on its own
  4468. if (!nickname)
  4469. set_config_string (ctx, "server.nickname", pwd->pw_name);
  4470. if (!username)
  4471. set_config_string (ctx, "server.username", pwd->pw_name);
  4472. // Not all systems have the GECOS field but the vast majority does
  4473. if (!realname)
  4474. {
  4475. char *gecos = pwd->pw_gecos;
  4476. // The first comma, if any, ends the user's real name
  4477. char *comma = strchr (gecos, ',');
  4478. if (comma)
  4479. *comma = '\0';
  4480. set_config_string (ctx, "server.realname", gecos);
  4481. }
  4482. return true;
  4483. }
  4484. static bool
  4485. read_file (const char *filename, struct str *output, struct error **e)
  4486. {
  4487. FILE *fp = fopen (filename, "rb");
  4488. if (!fp)
  4489. {
  4490. error_set (e, "could not open `%s' for reading: %s",
  4491. filename, strerror (errno));
  4492. return false;
  4493. }
  4494. char buf[BUFSIZ];
  4495. size_t len;
  4496. while ((len = fread (buf, 1, sizeof buf, fp)) == sizeof buf)
  4497. str_append_data (output, buf, len);
  4498. str_append_data (output, buf, len);
  4499. bool success = !ferror (fp);
  4500. fclose (fp);
  4501. if (success)
  4502. return true;
  4503. error_set (e, "error while reading `%s': %s",
  4504. filename, strerror (errno));
  4505. return false;
  4506. }
  4507. static struct config_item_ *
  4508. load_configuration_file (const char *filename, struct error **e)
  4509. {
  4510. struct config_item_ *root = NULL;
  4511. struct str data;
  4512. str_init (&data);
  4513. if (!read_file (filename, &data, e))
  4514. goto end;
  4515. struct error *error = NULL;
  4516. if (!(root = config_item_parse (data.str, data.len, false, &error)))
  4517. {
  4518. error_set (e, "configuration parse error: %s", error->message);
  4519. error_free (error);
  4520. }
  4521. end:
  4522. str_free (&data);
  4523. return root;
  4524. }
  4525. static void
  4526. load_configuration (struct app_context *ctx)
  4527. {
  4528. struct config_item_ *root = NULL;
  4529. struct error *e = NULL;
  4530. char *filename = resolve_config_filename (PROGRAM_NAME ".conf");
  4531. if (filename)
  4532. root = load_configuration_file (filename, &e);
  4533. else
  4534. print_status ("configuration file not found");
  4535. free (filename);
  4536. if (e)
  4537. {
  4538. print_error ("%s", e->message);
  4539. error_free (e);
  4540. e = NULL;
  4541. }
  4542. config_load (&ctx->config, root ? root : config_item_object ());
  4543. if (!autofill_user_info (ctx, &e))
  4544. {
  4545. print_error ("%s: %s", "failed to fill in user details", e->message);
  4546. error_free (e);
  4547. }
  4548. ctx->reconnect =
  4549. get_config_boolean (ctx, "server.reconnect");
  4550. ctx->isolate_buffers =
  4551. get_config_boolean (ctx, "behaviour.isolate_buffers");
  4552. ctx->reconnect_delay =
  4553. get_config_integer (ctx, "server.reconnect_delay");
  4554. }
  4555. // --- Main program ------------------------------------------------------------
  4556. static void
  4557. init_poller_events (struct app_context *ctx)
  4558. {
  4559. poller_fd_init (&ctx->signal_event, &ctx->poller, g_signal_pipe[0]);
  4560. ctx->signal_event.dispatcher = (poller_fd_fn) on_signal_pipe_readable;
  4561. ctx->signal_event.user_data = ctx;
  4562. poller_fd_set (&ctx->signal_event, POLLIN);
  4563. poller_fd_init (&ctx->tty_event, &ctx->poller, STDIN_FILENO);
  4564. ctx->tty_event.dispatcher = (poller_fd_fn) on_tty_readable;
  4565. ctx->tty_event.user_data = ctx;
  4566. poller_fd_set (&ctx->tty_event, POLLIN);
  4567. }
  4568. int
  4569. main (int argc, char *argv[])
  4570. {
  4571. // We include a generated file from kike including this array we don't use;
  4572. // let's just keep it there and silence the compiler warning instead
  4573. (void) g_default_replies;
  4574. static const struct opt opts[] =
  4575. {
  4576. { 'd', "debug", NULL, 0, "run in debug mode" },
  4577. { 'h', "help", NULL, 0, "display this help and exit" },
  4578. { 'V', "version", NULL, 0, "output version information and exit" },
  4579. { 0, NULL, NULL, 0, NULL }
  4580. };
  4581. struct opt_handler oh;
  4582. opt_handler_init (&oh, argc, argv, opts, NULL, "Experimental IRC client.");
  4583. int c;
  4584. while ((c = opt_handler_get (&oh)) != -1)
  4585. switch (c)
  4586. {
  4587. case 'd':
  4588. g_debug_mode = true;
  4589. break;
  4590. case 'h':
  4591. opt_handler_usage (&oh, stdout);
  4592. exit (EXIT_SUCCESS);
  4593. case 'V':
  4594. printf (PROGRAM_NAME " " PROGRAM_VERSION "\n");
  4595. exit (EXIT_SUCCESS);
  4596. default:
  4597. print_error ("wrong options");
  4598. opt_handler_usage (&oh, stderr);
  4599. exit (EXIT_FAILURE);
  4600. }
  4601. opt_handler_free (&oh);
  4602. print_status (PROGRAM_NAME " " PROGRAM_VERSION " starting");
  4603. // We only need to convert to and from the terminal encoding
  4604. setlocale (LC_CTYPE, "");
  4605. struct app_context ctx;
  4606. app_context_init (&ctx);
  4607. g_ctx = &ctx;
  4608. SSL_library_init ();
  4609. atexit (EVP_cleanup);
  4610. SSL_load_error_strings ();
  4611. atexit (ERR_free_strings);
  4612. setup_signal_handlers ();
  4613. register_config_modules (&ctx);
  4614. load_configuration (&ctx);
  4615. init_colors (&ctx);
  4616. init_poller_events (&ctx);
  4617. init_buffers (&ctx);
  4618. buffer_activate (&ctx, ctx.server.buffer);
  4619. refresh_prompt (&ctx);
  4620. input_start (&ctx.input, argv[0]);
  4621. // Connect to the server ASAP
  4622. poller_timer_set (&ctx.server.reconnect_tmr, 0);
  4623. ctx.polling = true;
  4624. while (ctx.polling)
  4625. poller_run (&ctx.poller);
  4626. app_context_free (&ctx);
  4627. free_terminal ();
  4628. return EXIT_SUCCESS;
  4629. }