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.
 
 
 
 
 
 

5125 lines
134 KiB

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