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.

5949 lines
155KB

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