| 
						 
						
						
						
						 
					 | 
					 | 
					@@ -1,7 +1,7 @@
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					/*
 | 
					 | 
					 | 
					 | 
					/*
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 * degesch.c: a terminal-based IRC client
 | 
					 | 
					 | 
					 | 
					 * degesch.c: a terminal-based IRC client
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 *
 | 
					 | 
					 | 
					 | 
					 *
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 * Copyright (c) 2015 - 2020, Přemysl Eric Janouch <p@janouch.name>
 | 
					 | 
					 | 
					 | 
					 * Copyright (c) 2015 - 2021, Přemysl Eric Janouch <p@janouch.name>
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 *
 | 
					 | 
					 | 
					 | 
					 *
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 * Permission to use, copy, modify, and/or distribute this software for any
 | 
					 | 
					 | 
					 | 
					 * Permission to use, copy, modify, and/or distribute this software for any
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 * purpose with or without fee is hereby granted.
 | 
					 | 
					 | 
					 | 
					 * purpose with or without fee is hereby granted.
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -51,6 +51,7 @@ enum
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "common.c"
 | 
					 | 
					 | 
					 | 
					#include "common.c"
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "kike-replies.c"
 | 
					 | 
					 | 
					 | 
					#include "kike-replies.c"
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					#include <math.h>
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include <langinfo.h>
 | 
					 | 
					 | 
					 | 
					#include <langinfo.h>
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include <locale.h>
 | 
					 | 
					 | 
					 | 
					#include <locale.h>
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include <pwd.h>
 | 
					 | 
					 | 
					 | 
					#include <pwd.h>
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -1718,8 +1719,10 @@ struct server
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						char *irc_user_host;                ///< Our current user@host
 | 
					 | 
					 | 
					 | 
						char *irc_user_host;                ///< Our current user@host
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						bool autoaway_active;               ///< Autoaway is currently active
 | 
					 | 
					 | 
					 | 
						bool autoaway_active;               ///< Autoaway is currently active
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						struct strv cap_ls_buf;             ///< Buffer for IRCv3.2 CAP LS
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						bool cap_echo_message;              ///< Whether the server echoes messages
 | 
					 | 
					 | 
					 | 
						bool cap_echo_message;              ///< Whether the server echoes messages
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						bool cap_away_notify;               ///< Whether we get AWAY notifications
 | 
					 | 
					 | 
					 | 
						bool cap_away_notify;               ///< Whether we get AWAY notifications
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						bool cap_sasl;                      ///< Whether SASL is available
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// Server-specific information (from RPL_ISUPPORT):
 | 
					 | 
					 | 
					 | 
						// Server-specific information (from RPL_ISUPPORT):
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -1730,6 +1733,9 @@ struct server
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						char *irc_idchan_prefixes;          ///< Prefixes for "safe channels"
 | 
					 | 
					 | 
					 | 
						char *irc_idchan_prefixes;          ///< Prefixes for "safe channels"
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						char *irc_statusmsg;                ///< Prefixes for channel targets
 | 
					 | 
					 | 
					 | 
						char *irc_statusmsg;                ///< Prefixes for channel targets
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						char irc_extban_prefix;             ///< EXTBAN prefix or \0
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						char *irc_extban_types;             ///< EXTBAN types
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						char *irc_chanmodes_list;           ///< Channel modes for lists
 | 
					 | 
					 | 
					 | 
						char *irc_chanmodes_list;           ///< Channel modes for lists
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						char *irc_chanmodes_param_always;   ///< Channel modes with mandatory param
 | 
					 | 
					 | 
					 | 
						char *irc_chanmodes_param_always;   ///< Channel modes with mandatory param
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						char *irc_chanmodes_param_when_set; ///< Channel modes with param when set
 | 
					 | 
					 | 
					 | 
						char *irc_chanmodes_param_when_set; ///< Channel modes with param when set
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -1778,6 +1784,9 @@ server_init_specifics (struct server *self)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						self->irc_idchan_prefixes           = xstrdup ("");
 | 
					 | 
					 | 
					 | 
						self->irc_idchan_prefixes           = xstrdup ("");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						self->irc_statusmsg                 = xstrdup ("");
 | 
					 | 
					 | 
					 | 
						self->irc_statusmsg                 = xstrdup ("");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						self->irc_extban_prefix             = 0;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						self->irc_extban_types              = xstrdup ("");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						self->irc_chanmodes_list            = xstrdup ("b");
 | 
					 | 
					 | 
					 | 
						self->irc_chanmodes_list            = xstrdup ("b");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						self->irc_chanmodes_param_always    = xstrdup ("k");
 | 
					 | 
					 | 
					 | 
						self->irc_chanmodes_param_always    = xstrdup ("k");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						self->irc_chanmodes_param_when_set  = xstrdup ("l");
 | 
					 | 
					 | 
					 | 
						self->irc_chanmodes_param_when_set  = xstrdup ("l");
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -1796,6 +1805,8 @@ server_free_specifics (struct server *self)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						free (self->irc_idchan_prefixes);
 | 
					 | 
					 | 
					 | 
						free (self->irc_idchan_prefixes);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						free (self->irc_statusmsg);
 | 
					 | 
					 | 
					 | 
						free (self->irc_statusmsg);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						free (self->irc_extban_types);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						free (self->irc_chanmodes_list);
 | 
					 | 
					 | 
					 | 
						free (self->irc_chanmodes_list);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						free (self->irc_chanmodes_param_always);
 | 
					 | 
					 | 
					 | 
						free (self->irc_chanmodes_param_always);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						free (self->irc_chanmodes_param_when_set);
 | 
					 | 
					 | 
					 | 
						free (self->irc_chanmodes_param_when_set);
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -1841,6 +1852,7 @@ server_new (struct poller *poller)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						self->irc_user_mode = str_make ();
 | 
					 | 
					 | 
					 | 
						self->irc_user_mode = str_make ();
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						self->cap_ls_buf = strv_make ();
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						server_init_specifics (self);
 | 
					 | 
					 | 
					 | 
						server_init_specifics (self);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						return self;
 | 
					 | 
					 | 
					 | 
						return self;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -1887,6 +1899,7 @@ server_destroy (struct server *self)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						str_free (&self->irc_user_mode);
 | 
					 | 
					 | 
					 | 
						str_free (&self->irc_user_mode);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						free (self->irc_user_host);
 | 
					 | 
					 | 
					 | 
						free (self->irc_user_host);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						strv_free (&self->cap_ls_buf);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						server_free_specifics (self);
 | 
					 | 
					 | 
					 | 
						server_free_specifics (self);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						free (self);
 | 
					 | 
					 | 
					 | 
						free (self);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -2104,17 +2117,23 @@ filter_color_cube_for_acceptable_nick_colors (size_t *len)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// This is a pure function and we don't use threads, static storage is fine
 | 
					 | 
					 | 
					 | 
						// This is a pure function and we don't use threads, static storage is fine
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						static int table[6 * 6 * 6];
 | 
					 | 
					 | 
					 | 
						static int table[6 * 6 * 6];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						size_t len_counter = 0;
 | 
					 | 
					 | 
					 | 
						size_t len_counter = 0;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						for (int x = 0; x < 6 * 6 * 6; x++)
 | 
					 | 
					 | 
					 | 
						for (int x = 0; x < (int) N_ELEMENTS (table); x++)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							// FIXME this isn't exactly right, the values aren't linear
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							int r =  x / 36;
 | 
					 | 
					 | 
					 | 
							int r =  x / 36;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							int g = (x / 6) % 6;
 | 
					 | 
					 | 
					 | 
							int g = (x / 6) % 6;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							int b = (x % 6);
 | 
					 | 
					 | 
					 | 
							int b = (x % 6);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							// Use the luma value of colours within the cube to filter colours that
 | 
					 | 
					 | 
					 | 
							// The first step is 95/255, the rest are 40/255,
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							// look okay-ish on terminals with both black and white backgrounds
 | 
					 | 
					 | 
					 | 
							// as an approximation we can double the first step
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							double luma = 0.2126 * r / 6. + 0.7152 * g / 6. + 0.0722 * b / 6.;
 | 
					 | 
					 | 
					 | 
							double linear_R = pow ((r + !!r) / 6., 2.2);
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							if (luma >= .3 && luma <= .5)
 | 
					 | 
					 | 
					 | 
							double linear_G = pow ((g + !!g) / 6., 2.2);
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							double linear_B = pow ((b + !!b) / 6., 2.2);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							// Use the relative luminance of colours within the cube to filter
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							// colours that look okay-ish on terminals with both black and white
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							// backgrounds (use the test-nick-colors script to calibrate)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							double Y = 0.2126 * linear_R + 0.7152 * linear_G + 0.0722 * linear_B;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							if (Y >= .25 && Y <= .4)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								table[len_counter++] = 16 + x;
 | 
					 | 
					 | 
					 | 
								table[len_counter++] = 16 + x;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						}
 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						*len = len_counter;
 | 
					 | 
					 | 
					 | 
						*len = len_counter;
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -2337,7 +2356,7 @@ static struct config_schema g_config_server[] =
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  .type      = CONFIG_ITEM_STRING_ARRAY,
 | 
					 | 
					 | 
					 | 
						  .type      = CONFIG_ITEM_STRING_ARRAY,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  .validate  = config_validate_nonjunk_string,
 | 
					 | 
					 | 
					 | 
						  .validate  = config_validate_nonjunk_string,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  .default_  = "\"multi-prefix,invite-notify,server-time,echo-message,"
 | 
					 | 
					 | 
					 | 
						  .default_  = "\"multi-prefix,invite-notify,server-time,echo-message,"
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							"message-tags,away-notify\"" },
 | 
					 | 
					 | 
					 | 
							"message-tags,away-notify,cap-notify,chghost\"" },
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ .name      = "tls",
 | 
					 | 
					 | 
					 | 
						{ .name      = "tls",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  .comment   = "Whether to use TLS",
 | 
					 | 
					 | 
					 | 
						  .comment   = "Whether to use TLS",
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -3028,6 +3047,20 @@ irc_skip_statusmsg (struct server *s, const char *target)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						return target + (*target && strchr (s->irc_statusmsg, *target));
 | 
					 | 
					 | 
					 | 
						return target + (*target && strchr (s->irc_statusmsg, *target));
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					static bool
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					irc_is_extban (struct server *s, const char *target)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						// Some servers have a prefix, and some support negation
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (s->irc_extban_prefix && *target++ != s->irc_extban_prefix)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							return false;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (*target == '~')
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							target++;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						// XXX: we don't know if it's supposed to have an argument, or not
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						return *target && strchr (s->irc_extban_types, *target++)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							&& strchr (":\0", *target);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
					 | 
					 | 
					 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					// As of 2020, everything should be in UTF-8.  And if it's not, we'll decode it
 | 
					 | 
					 | 
					 | 
					// As of 2020, everything should be in UTF-8.  And if it's not, we'll decode it
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -4031,6 +4064,13 @@ log_full (struct app_context *ctx, struct server *s, struct buffer *buffer,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						log_server ((s), (buffer), BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT,   \
 | 
					 | 
					 | 
					 | 
						log_server ((s), (buffer), BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT,   \
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							"#n is now known as #n", (old), (new_))
 | 
					 | 
					 | 
					 | 
							"#n is now known as #n", (old), (new_))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					#define log_chghost_self(s, buffer, new_)                                      \
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						log_server ((s), (buffer), BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT,   \
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							"You are now #N", (new_))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					#define log_chghost(s, buffer, old, new_)                                      \
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						log_server ((s), (buffer), BUFFER_LINE_STATUS | BUFFER_LINE_UNIMPORTANT,   \
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							"#N is now #N", (old), (new_))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#define log_outcoming_notice(s, buffer, who, text)                             \
 | 
					 | 
					 | 
					 | 
					#define log_outcoming_notice(s, buffer, who, text)                             \
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						log_server_status ((s), (buffer), "#s(#n): #m", "Notice", (who), (text))
 | 
					 | 
					 | 
					 | 
						log_server_status ((s), (buffer), "#s(#n): #m", "Notice", (who), (text))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#define log_outcoming_privmsg(s, buffer, prefixes, who, text)                  \
 | 
					 | 
					 | 
					 | 
					#define log_outcoming_privmsg(s, buffer, prefixes, who, text)                  \
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -4083,6 +4123,13 @@ buffer_open_log_file (struct app_context *ctx, struct buffer *buffer)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							return;
 | 
					 | 
					 | 
					 | 
							return;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// TODO: should we try to reopen files wrt. case mapping?
 | 
					 | 
					 | 
					 | 
						// TODO: should we try to reopen files wrt. case mapping?
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//  - Need to read the whole directory and look for matches:
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//    irc_server_strcmp(buffer->s, d_name, make_log_filename())
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//    remember to strip the ".log" suffix from d_name, case-sensitively.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//  - The tolower_ascii() in make_log_filename() is a perfect overlap,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//    it may stay as-is.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//  - buffer_get_log_path() will need to return a FILE *,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//    or an error that includes the below message.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						char *path = buffer_get_log_path (buffer);
 | 
					 | 
					 | 
					 | 
						char *path = buffer_get_log_path (buffer);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (!(buffer->log_file = fopen (path, "ab")))
 | 
					 | 
					 | 
					 | 
						if (!(buffer->log_file = fopen (path, "ab")))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							log_global_error (ctx, "Couldn't open log file `#s': #l",
 | 
					 | 
					 | 
					 | 
							log_global_error (ctx, "Couldn't open log file `#s': #l",
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -4956,7 +5003,10 @@ irc_destroy_state (struct server *s)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						str_reset (&s->irc_user_mode);
 | 
					 | 
					 | 
					 | 
						str_reset (&s->irc_user_mode);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						cstr_set (&s->irc_user_host, NULL);
 | 
					 | 
					 | 
					 | 
						cstr_set (&s->irc_user_host, NULL);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						strv_reset (&s->cap_ls_buf);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						s->cap_away_notify = false;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						s->cap_echo_message = false;
 | 
					 | 
					 | 
					 | 
						s->cap_echo_message = false;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						s->cap_sasl = false;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// Need to call this before server_init_specifics()
 | 
					 | 
					 | 
					 | 
						// Need to call this before server_init_specifics()
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						irc_set_casemapping (s, irc_tolower, irc_strxfrm);
 | 
					 | 
					 | 
					 | 
						irc_set_casemapping (s, irc_tolower, irc_strxfrm);
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -5021,14 +5071,17 @@ irc_initiate_disconnect (struct server *s, const char *reason)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static void
 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					initiate_quit (struct app_context *ctx, const char *message)
 | 
					 | 
					 | 
					 | 
					request_quit (struct app_context *ctx, const char *message)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (!ctx->quitting)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							log_global_status (ctx, "Shutting down");
 | 
					 | 
					 | 
					 | 
							log_global_status (ctx, "Shutting down");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							ctx->quitting = true;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// Hide the user interface
 | 
					 | 
					 | 
					 | 
							// Disable the user interface
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							CALL (ctx->input, hide);
 | 
					 | 
					 | 
					 | 
							CALL (ctx->input, hide);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// Initiate a connection close
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						struct str_map_iter iter = str_map_iter_make (&ctx->servers);
 | 
					 | 
					 | 
					 | 
						struct str_map_iter iter = str_map_iter_make (&ctx->servers);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						struct server *s;
 | 
					 | 
					 | 
					 | 
						struct server *s;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						while ((s = str_map_iter_next (&iter)))
 | 
					 | 
					 | 
					 | 
						while ((s = str_map_iter_next (&iter)))
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -5042,7 +5095,6 @@ initiate_quit (struct app_context *ctx, const char *message)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								irc_destroy_connector (s);
 | 
					 | 
					 | 
					 | 
								irc_destroy_connector (s);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						}
 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						ctx->quitting = true;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						try_finish_quit (ctx);
 | 
					 | 
					 | 
					 | 
						try_finish_quit (ctx);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -5689,9 +5741,9 @@ irc_register (struct server *s)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						const char *realname = get_config_string (s->config, "realname");
 | 
					 | 
					 | 
					 | 
						const char *realname = get_config_string (s->config, "realname");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						hard_assert (username && realname);
 | 
					 | 
					 | 
					 | 
						hard_assert (username && realname);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// Start IRCv3.1 capability negotiation;
 | 
					 | 
					 | 
					 | 
						// Start IRCv3 capability negotiation, with up to 3.2 features;
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// at worst the server will ignore this or send a harmless error message
 | 
					 | 
					 | 
					 | 
						// at worst the server will ignore this or send a harmless error message
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						irc_send (s, "CAP LS");
 | 
					 | 
					 | 
					 | 
						irc_send (s, "CAP LS 302");
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						const char *password = get_config_string (s->config, "password");
 | 
					 | 
					 | 
					 | 
						const char *password = get_config_string (s->config, "password");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (password)
 | 
					 | 
					 | 
					 | 
						if (password)
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -6398,11 +6450,11 @@ irc_handle_mode_user (struct server *s, char **params)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static void
 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					irc_handle_sent_cap (struct server *s, const struct irc_message *msg)
 | 
					 | 
					 | 
					 | 
					irc_handle_sent_cap (struct server *s, const struct irc_message *msg)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (msg->params.len < 2)
 | 
					 | 
					 | 
					 | 
						if (msg->params.len < 1)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							return;
 | 
					 | 
					 | 
					 | 
							return;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						const char *subcommand = msg->params.vector[1];
 | 
					 | 
					 | 
					 | 
						const char *subcommand = msg->params.vector[0];
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						const char *args = (msg->params.len > 2) ? msg->params.vector[2] : "";
 | 
					 | 
					 | 
					 | 
						const char *args = (msg->params.len > 1) ? msg->params.vector[1] : "";
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (!strcasecmp_ascii (subcommand, "REQ"))
 | 
					 | 
					 | 
					 | 
						if (!strcasecmp_ascii (subcommand, "REQ"))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							log_server_status (s, s->buffer,
 | 
					 | 
					 | 
					 | 
							log_server_status (s, s->buffer,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								"#s: #S", "Capabilities requested", args);
 | 
					 | 
					 | 
					 | 
								"#s: #S", "Capabilities requested", args);
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -6519,6 +6571,20 @@ irc_process_sent_message (const struct irc_message *msg, struct server *s)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					// --- Input handling ----------------------------------------------------------
 | 
					 | 
					 | 
					 | 
					// --- Input handling ----------------------------------------------------------
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					irc_handle_authenticate (struct server *s, const struct irc_message *msg)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (msg->params.len < 1)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							return;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						// Empty challenge -> empty response for e.g. SASL EXTERNAL,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						// abort anything else as it doesn't make much sense to let the user do it
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (!strcmp (msg->params.vector[0], "+"))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							irc_send (s, "AUTHENTICATE +");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						else
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							irc_send (s, "AUTHENTICATE *");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static void
 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					irc_handle_away (struct server *s, const struct irc_message *msg)
 | 
					 | 
					 | 
					 | 
					irc_handle_away (struct server *s, const struct irc_message *msg)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -6534,6 +6600,58 @@ irc_handle_away (struct server *s, const struct irc_message *msg)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							user->away = !!msg->params.len;
 | 
					 | 
					 | 
					 | 
							user->away = !!msg->params.len;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					irc_process_cap_ls (struct server *s)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						log_server_status (s, s->buffer,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							"#s: #&S", "Capabilities supported", strv_join (&s->cap_ls_buf, " "));
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						struct strv chosen = strv_make ();
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						struct strv use    = strv_make ();
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						cstr_split (get_config_string (s->config, "capabilities"), ",", true, &use);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						// Filter server capabilities for ones we can make use of
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						for (size_t i = 0; i < s->cap_ls_buf.len; i++)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							const char *cap = s->cap_ls_buf.vector[i];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							size_t cap_name_len = strcspn (cap, "=");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							for (size_t k = 0; k < use.len; k++)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								if (!strncasecmp_ascii (use.vector[k], cap, cap_name_len))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
									strv_append_owned (&chosen, xstrndup (cap, cap_name_len));
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						strv_reset (&s->cap_ls_buf);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						char *chosen_str = strv_join (&chosen, " ");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						strv_free (&chosen);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						strv_free (&use);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						// XXX: with IRCv3.2, this may end up being too long for one message,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						// and we need to be careful with CAP END.  One probably has to count
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						// the number of sent CAP REQ vs the number of received CAP ACK/NAK.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (s->state == IRC_CONNECTED)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							irc_send (s, "CAP REQ :%s", chosen_str);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						free (chosen_str);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					irc_toggle_cap (struct server *s, const char *cap, bool active)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (!strcasecmp_ascii (cap, "echo-message")) s->cap_echo_message = active;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (!strcasecmp_ascii (cap, "away-notify"))  s->cap_away_notify  = active;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (!strcasecmp_ascii (cap, "sasl"))         s->cap_sasl         = active;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					irc_try_finish_cap_negotiation (struct server *s)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						// It does not make sense to do this post-registration, although it would
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						// not hurt either, as the server must ignore it in that case
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (s->state == IRC_CONNECTED)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							irc_send (s, "CAP END");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static void
 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					irc_handle_cap (struct server *s, const struct irc_message *msg)
 | 
					 | 
					 | 
					 | 
					irc_handle_cap (struct server *s, const struct irc_message *msg)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -6559,50 +6677,89 @@ irc_handle_cap (struct server *s, const struct irc_message *msg)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
									active = false;
 | 
					 | 
					 | 
					 | 
									active = false;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
									cap++;
 | 
					 | 
					 | 
					 | 
									cap++;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								}
 | 
					 | 
					 | 
					 | 
								}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								if (!strcasecmp_ascii (cap, "echo-message"))
 | 
					 | 
					 | 
					 | 
								irc_toggle_cap (s, cap, active);
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
									s->cap_echo_message = active;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								if (!strcasecmp_ascii (cap, "away-notify"))
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
									s->cap_away_notify = active;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							}
 | 
					 | 
					 | 
					 | 
							}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							irc_send (s, "CAP END");
 | 
					 | 
					 | 
					 | 
							if (s->cap_sasl && s->transport == &g_transport_tls)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								irc_send (s, "AUTHENTICATE EXTERNAL");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							else
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								irc_try_finish_cap_negotiation (s);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						}
 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						else if (!strcasecmp_ascii (subcommand, "NAK"))
 | 
					 | 
					 | 
					 | 
						else if (!strcasecmp_ascii (subcommand, "NAK"))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							log_server_error (s, s->buffer,
 | 
					 | 
					 | 
					 | 
							log_server_error (s, s->buffer,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								"#s: #S", "Capabilities not acknowledged", args);
 | 
					 | 
					 | 
					 | 
								"#s: #S", "Capabilities not acknowledged", args);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							irc_send (s, "CAP END");
 | 
					 | 
					 | 
					 | 
							irc_try_finish_cap_negotiation (s);
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						else if (!strcasecmp_ascii (subcommand, "DEL"))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							log_server_error (s, s->buffer,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								"#s: #S", "Capabilities deleted", args);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							for (size_t i = 0; i < v.len; i++)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								irc_toggle_cap (s, v.vector[i], false);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						}
 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						else if (!strcasecmp_ascii (subcommand, "LS"))
 | 
					 | 
					 | 
					 | 
						else if (!strcasecmp_ascii (subcommand, "LS"))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							log_server_status (s, s->buffer,
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								"#s: #S", "Capabilities supported", args);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							struct strv chosen = strv_make ();
 | 
					 | 
					 | 
					 | 
							if (msg->params.len > 3 && !strcmp (args, "*"))
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							struct strv use    = strv_make ();
 | 
					 | 
					 | 
					 | 
								cstr_split (msg->params.vector[3], " ", true, &s->cap_ls_buf);
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
							else
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							cstr_split (get_config_string (s->config, "capabilities"),
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								",", true, &use);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							// Filter server capabilities for ones we can make use of
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							for (size_t i = 0; i < v.len; i++)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							{
 | 
					 | 
					 | 
					 | 
							{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								const char *cap = v.vector[i];
 | 
					 | 
					 | 
					 | 
								strv_append_vector (&s->cap_ls_buf, v.vector);
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								for (size_t k = 0; k < use.len; k++)
 | 
					 | 
					 | 
					 | 
								irc_process_cap_ls (s);
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
									if (!strcasecmp_ascii (use.vector[k], cap))
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
										strv_append (&chosen, cap);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							}
 | 
					 | 
					 | 
					 | 
							}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							char *chosen_str = strv_join (&chosen, " ");
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							strv_free (&chosen);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							strv_free (&use);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							irc_send (s, "CAP REQ :%s", chosen_str);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							free (chosen_str);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						}
 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						strv_free (&v);
 | 
					 | 
					 | 
					 | 
						strv_free (&v);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					irc_handle_chghost (struct server *s, const struct irc_message *msg)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (!msg->prefix || msg->params.len < 2)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							return;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						char *nickname = irc_cut_nickname (msg->prefix);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						struct user *user = str_map_find (&s->irc_users, nickname);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						free (nickname);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (!user)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							return;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						char *new_prefix = xstrdup_printf ("%s!%s@%s", user->nickname,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							msg->params.vector[0], msg->params.vector[1]);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (irc_is_this_us (s, msg->prefix))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							cstr_set (&s->irc_user_host, xstrdup_printf ("%s@%s",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								msg->params.vector[0], msg->params.vector[1]));
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							log_chghost_self (s, s->buffer, new_prefix);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							// Log a message in all open buffers on this server
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							struct str_map_iter iter = str_map_iter_make (&s->irc_buffer_map);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							struct buffer *buffer;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							while ((buffer = str_map_iter_next (&iter)))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								log_chghost_self (s, buffer, new_prefix);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						else
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							// Log a message in any PM buffer
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							struct buffer *buffer =
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								str_map_find (&s->irc_buffer_map, user->nickname);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							if (buffer)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								log_chghost (s, buffer, msg->prefix, new_prefix);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							// Log a message in all channels the user is in
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							LIST_FOR_EACH (struct user_channel, iter, user->channels)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								buffer = str_map_find (&s->irc_buffer_map, iter->channel->name);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								hard_assert (buffer != NULL);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								log_chghost (s, buffer, msg->prefix, new_prefix);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						free (new_prefix);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static void
 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					irc_handle_error (struct server *s, const struct irc_message *msg)
 | 
					 | 
					 | 
					 | 
					irc_handle_error (struct server *s, const struct irc_message *msg)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -6663,8 +6820,13 @@ irc_handle_join (struct server *s, const struct irc_message *msg)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							str_map_set (&s->irc_buffer_map, channel->name, buffer);
 | 
					 | 
					 | 
					 | 
							str_map_set (&s->irc_buffer_map, channel->name, buffer);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							buffer_add (s->ctx, buffer);
 | 
					 | 
					 | 
					 | 
							buffer_add (s->ctx, buffer);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							// XXX: this is annoying, consider only doing it a while after /join
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							char *input = CALL (s->ctx->input, get_line);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							if (!*input)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								buffer_activate (s->ctx, buffer);
 | 
					 | 
					 | 
					 | 
								buffer_activate (s->ctx, buffer);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							else
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								buffer->highlighted = true;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							free (input);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						}
 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (irc_is_this_us (s, msg->prefix))
 | 
					 | 
					 | 
					 | 
						if (irc_is_this_us (s, msg->prefix))
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -7236,8 +7398,10 @@ irc_handle_topic (struct server *s, const struct irc_message *msg)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static struct irc_handler g_irc_handlers[] =
 | 
					 | 
					 | 
					 | 
					static struct irc_handler g_irc_handlers[] =
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// This list needs to stay sorted
 | 
					 | 
					 | 
					 | 
						// This list needs to stay sorted
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						{ "AUTHENTICATE", irc_handle_authenticate },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "AWAY",         irc_handle_away         },
 | 
					 | 
					 | 
					 | 
						{ "AWAY",         irc_handle_away         },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "CAP",          irc_handle_cap          },
 | 
					 | 
					 | 
					 | 
						{ "CAP",          irc_handle_cap          },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						{ "CHGHOST",      irc_handle_chghost      },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "ERROR",        irc_handle_error        },
 | 
					 | 
					 | 
					 | 
						{ "ERROR",        irc_handle_error        },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "INVITE",       irc_handle_invite       },
 | 
					 | 
					 | 
					 | 
						{ "INVITE",       irc_handle_invite       },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "JOIN",         irc_handle_join         },
 | 
					 | 
					 | 
					 | 
						{ "JOIN",         irc_handle_join         },
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -7784,6 +7948,16 @@ irc_handle_isupport_statusmsg (struct server *s, char *value)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						cstr_set (&s->irc_statusmsg, xstrdup (value));
 | 
					 | 
					 | 
					 | 
						cstr_set (&s->irc_statusmsg, xstrdup (value));
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					irc_handle_isupport_extban (struct server *s, char *value)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						s->irc_extban_prefix = 0;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (*value && *value != ',')
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							s->irc_extban_prefix = *value++;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						cstr_set (&s->irc_extban_types, xstrdup (*value == ',' ? ++value : ""));
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static void
 | 
					 | 
					 | 
					 | 
					static void
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					irc_handle_isupport_chanmodes (struct server *s, char *value)
 | 
					 | 
					 | 
					 | 
					irc_handle_isupport_chanmodes (struct server *s, char *value)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -7840,6 +8014,7 @@ dispatch_isupport (struct server *s, const char *name, char *value)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						MATCH ("CHANTYPES",   irc_handle_isupport_chantypes);
 | 
					 | 
					 | 
					 | 
						MATCH ("CHANTYPES",   irc_handle_isupport_chantypes);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						MATCH ("IDCHAN",      irc_handle_isupport_idchan);
 | 
					 | 
					 | 
					 | 
						MATCH ("IDCHAN",      irc_handle_isupport_idchan);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						MATCH ("STATUSMSG",   irc_handle_isupport_statusmsg);
 | 
					 | 
					 | 
					 | 
						MATCH ("STATUSMSG",   irc_handle_isupport_statusmsg);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						MATCH ("EXTBAN",      irc_handle_isupport_extban);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						MATCH ("CHANMODES",   irc_handle_isupport_chanmodes);
 | 
					 | 
					 | 
					 | 
						MATCH ("CHANMODES",   irc_handle_isupport_chanmodes);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						MATCH ("MODES",       irc_handle_isupport_modes);
 | 
					 | 
					 | 
					 | 
						MATCH ("MODES",       irc_handle_isupport_modes);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -7932,6 +8107,15 @@ irc_process_numeric (struct server *s,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							if (irc_handle_rpl_endofwho (s, msg))  buffer = NULL;
 | 
					 | 
					 | 
					 | 
							if (irc_handle_rpl_endofwho (s, msg))  buffer = NULL;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							break;
 | 
					 | 
					 | 
					 | 
							break;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						case IRC_ERR_NICKLOCKED:
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						case IRC_RPL_SASLSUCCESS:
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						case IRC_ERR_SASLFAIL:
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						case IRC_ERR_SASLTOOLONG:
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						case IRC_ERR_SASLABORTED:
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						case IRC_ERR_SASLALREADY:
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							irc_try_finish_cap_negotiation (s);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							break;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						case IRC_RPL_LIST:
 | 
					 | 
					 | 
					 | 
						case IRC_RPL_LIST:
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						case IRC_ERR_UNKNOWNCOMMAND:
 | 
					 | 
					 | 
					 | 
						case IRC_ERR_UNKNOWNCOMMAND:
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -7988,8 +8172,8 @@ irc_process_message (const struct irc_message *msg, struct server *s)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							irc_sanitize_cut_off_utf8 (&msg->params.vector[msg->params.len - 1]);
 | 
					 | 
					 | 
					 | 
							irc_sanitize_cut_off_utf8 (&msg->params.vector[msg->params.len - 1]);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// TODO: make use of IRCv3.2 server-time (with fallback to unixtime_msec())
 | 
					 | 
					 | 
					 | 
						// TODO: make use of IRCv3.2 server-time (with fallback to unixtime_msec())
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						//   -> change all calls to log_{server,nick,outcoming,ctcp}*() to take
 | 
					 | 
					 | 
					 | 
						//   -> change all calls to log_{server,nick,chghost,outcoming,ctcp}*()
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						//   an extra argument specifying time
 | 
					 | 
					 | 
					 | 
						//   to take an extra numeric argument specifying time
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						struct irc_handler key = { .name = msg->command };
 | 
					 | 
					 | 
					 | 
						struct irc_handler key = { .name = msg->command };
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						struct irc_handler *handler = bsearch (&key, g_irc_handlers,
 | 
					 | 
					 | 
					 | 
						struct irc_handler *handler = bsearch (&key, g_irc_handlers,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							N_ELEMENTS (g_irc_handlers), sizeof key, irc_handler_cmp_by_name);
 | 
					 | 
					 | 
					 | 
							N_ELEMENTS (g_irc_handlers), sizeof key, irc_handler_cmp_by_name);
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -10916,45 +11100,38 @@ handle_command_buffer (struct handler_args *a)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						return result;
 | 
					 | 
					 | 
					 | 
						return result;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static bool
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					replace_string_array
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						(struct config_item *item, struct strv *array, struct error **e)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						char *changed = strv_join (array, ",");
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						struct str tmp = { .str = changed, .len = strlen (changed) };
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						bool result = config_item_set_from (item,
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							config_item_string_array (&tmp), e);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						free (changed);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						return result;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static bool
 | 
					 | 
					 | 
					 | 
					static bool
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					handle_command_set_add
 | 
					 | 
					 | 
					 | 
					handle_command_set_add
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						(struct config_item *item, const char *value, struct error **e)
 | 
					 | 
					 | 
					 | 
						(struct strv *items, const struct strv *values, struct error **e)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						struct strv items = strv_make ();
 | 
					 | 
					 | 
					 | 
						for (size_t i = 0; i < values->len; i++)
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (item->type != CONFIG_ITEM_NULL)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							cstr_split (item->value.string.str, ",", false, &items);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (items.len == 1 && !*items.vector[0])
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							strv_reset (&items);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// FIXME: handle multiple items properly
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						bool result = false;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (strv_find (&items, value) != -1)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							error_set (e, "already present in the array: %s", value);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						else
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							strv_append (&items, value);
 | 
					 | 
					 | 
					 | 
							const char *value = values->vector[i];
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							result = replace_string_array (item, &items, e);
 | 
					 | 
					 | 
					 | 
							if (strv_find (items, values->vector[i]) != -1)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								return error_set (e, "already present in the array: %s", value);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							strv_append (items, value);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						}
 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
						return true;
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						strv_free (&items);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						return result;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static bool
 | 
					 | 
					 | 
					 | 
					static bool
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					handle_command_set_remove
 | 
					 | 
					 | 
					 | 
					handle_command_set_remove
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						(struct config_item *item, const char *value, struct error **e)
 | 
					 | 
					 | 
					 | 
						(struct strv *items, const struct strv *values, struct error **e)
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						for (size_t i = 0; i < values->len; i++)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							const char *value = values->vector[i];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							ssize_t i = strv_find (items, value);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							if (i == -1)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								return error_set (e, "not present in the array: %s", value);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							strv_remove (items, i);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						return true;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					static bool
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					handle_command_set_modify
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						(struct config_item *item, const char *value, bool add, struct error **e)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						struct strv items = strv_make ();
 | 
					 | 
					 | 
					 | 
						struct strv items = strv_make ();
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (item->type != CONFIG_ITEM_NULL)
 | 
					 | 
					 | 
					 | 
						if (item->type != CONFIG_ITEM_NULL)
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -10962,18 +11139,23 @@ handle_command_set_remove
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (items.len == 1 && !*items.vector[0])
 | 
					 | 
					 | 
					 | 
						if (items.len == 1 && !*items.vector[0])
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							strv_reset (&items);
 | 
					 | 
					 | 
					 | 
							strv_reset (&items);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// FIXME: handle multiple items properly
 | 
					 | 
					 | 
					 | 
						struct strv values = strv_make ();
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						bool result = false;
 | 
					 | 
					 | 
					 | 
						cstr_split (value, ",", false, &values);
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						ssize_t i = strv_find (&items, value);
 | 
					 | 
					 | 
					 | 
						bool result = add
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (i == -1)
 | 
					 | 
					 | 
					 | 
							? handle_command_set_add (&items, &values, e)
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							error_set (e, "not present in the array: %s", value);
 | 
					 | 
					 | 
					 | 
							: handle_command_set_remove (&items, &values, e);
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						else
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (result)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							strv_remove (&items, i);
 | 
					 | 
					 | 
					 | 
							char *changed = strv_join (&items, ",");
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							result = replace_string_array (item, &items, e);
 | 
					 | 
					 | 
					 | 
							struct str tmp = { .str = changed, .len = strlen (changed) };
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							result = config_item_set_from (item,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
								config_item_string_array (&tmp), e);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							free (changed);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						}
 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						strv_free (&items);
 | 
					 | 
					 | 
					 | 
						strv_free (&items);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						strv_free (&values);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						return result;
 | 
					 | 
					 | 
					 | 
						return result;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -10992,10 +11174,8 @@ handle_command_set_assign_item (struct app_context *ctx,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							config_item_set_from (item, config_item_clone (new_), &e);
 | 
					 | 
					 | 
					 | 
							config_item_set_from (item, config_item_clone (new_), &e);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						else if (item->schema->type != CONFIG_ITEM_STRING_ARRAY)
 | 
					 | 
					 | 
					 | 
						else if (item->schema->type != CONFIG_ITEM_STRING_ARRAY)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							error_set (&e, "not a string array");
 | 
					 | 
					 | 
					 | 
							error_set (&e, "not a string array");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						else if (add)
 | 
					 | 
					 | 
					 | 
						else
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							handle_command_set_add (item, new_->value.string.str, &e);
 | 
					 | 
					 | 
					 | 
							handle_command_set_modify (item, new_->value.string.str, add, &e);
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						else if (remove)
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							handle_command_set_remove (item, new_->value.string.str, &e);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (e)
 | 
					 | 
					 | 
					 | 
						if (e)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -11241,6 +11421,20 @@ handle_command_notice (struct handler_args *a)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						return true;
 | 
					 | 
					 | 
					 | 
						return true;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					static bool
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					handle_command_squery (struct handler_args *a)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (!*a->arguments)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							return false;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						char *target = cut_word (&a->arguments);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (!*a->arguments)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							log_server_error (a->s, a->s->buffer, "No text to send");
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						else
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							irc_send (a->s, "SQUERY %s :%s", target, a->arguments);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						return true;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static bool
 | 
					 | 
					 | 
					 | 
					static bool
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					handle_command_ctcp (struct handler_args *a)
 | 
					 | 
					 | 
					 | 
					handle_command_ctcp (struct handler_args *a)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -11280,7 +11474,7 @@ handle_command_me (struct handler_args *a)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static bool
 | 
					 | 
					 | 
					 | 
					static bool
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					handle_command_quit (struct handler_args *a)
 | 
					 | 
					 | 
					 | 
					handle_command_quit (struct handler_args *a)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						initiate_quit (a->ctx, *a->arguments ? a->arguments : NULL);
 | 
					 | 
					 | 
					 | 
						request_quit (a->ctx, *a->arguments ? a->arguments : NULL);
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						return true;
 | 
					 | 
					 | 
					 | 
						return true;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -11405,7 +11599,8 @@ handle_command_topic (struct handler_args *a)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (*a->arguments)
 | 
					 | 
					 | 
					 | 
						if (*a->arguments)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							// FIXME: there's no way to start the topic with whitespace
 | 
					 | 
					 | 
					 | 
							// FIXME: there's no way to start the topic with whitespace
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							// FIXME: there's no way to unset the topic;
 | 
					 | 
					 | 
					 | 
							// FIXME: there's no way to unset the topic;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							//   we could adopt the Tcl style of "-switches" with "--" sentinels
 | 
					 | 
					 | 
					 | 
							//   we could adopt the Tcl style of "-switches" with "--" sentinels,
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							//   or we could accept "strings" in the config format
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							irc_send (a->s, "TOPIC %s :%s", a->channel_name, a->arguments);
 | 
					 | 
					 | 
					 | 
							irc_send (a->s, "TOPIC %s :%s", a->channel_name, a->arguments);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						else
 | 
					 | 
					 | 
					 | 
						else
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							irc_send (a->s, "TOPIC %s", a->channel_name);
 | 
					 | 
					 | 
					 | 
							irc_send (a->s, "TOPIC %s", a->channel_name);
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -11484,8 +11679,7 @@ mass_channel_mode_mask_list
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						for (size_t i = 0; i < v.len; i++)
 | 
					 | 
					 | 
					 | 
						for (size_t i = 0; i < v.len; i++)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							char *target = v.vector[i];
 | 
					 | 
					 | 
					 | 
							char *target = v.vector[i];
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							// TODO: support EXTBAN and leave those alone, too
 | 
					 | 
					 | 
					 | 
							if (strpbrk (target, "!@*?") || irc_is_extban (a->s, target))
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							if (strpbrk (target, "!@*?"))
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								continue;
 | 
					 | 
					 | 
					 | 
								continue;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							v.vector[i] = xstrdup_printf ("%s!*@*", target);
 | 
					 | 
					 | 
					 | 
							v.vector[i] = xstrdup_printf ("%s!*@*", target);
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -11769,11 +11963,17 @@ static bool
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					handle_command_channel_mode
 | 
					 | 
					 | 
					 | 
					handle_command_channel_mode
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						(struct handler_args *a, bool adding, char mode_char)
 | 
					 | 
					 | 
					 | 
						(struct handler_args *a, bool adding, char mode_char)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					{
 | 
					 | 
					 | 
					 | 
					{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (!*a->arguments)
 | 
					 | 
					 | 
					 | 
						const char *targets = a->arguments;
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						if (!*targets)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							if (adding)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								return false;
 | 
					 | 
					 | 
					 | 
								return false;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							targets = a->s->irc_user->nickname;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						struct strv v = strv_make ();
 | 
					 | 
					 | 
					 | 
						struct strv v = strv_make ();
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						cstr_split (a->arguments, " ", true, &v);
 | 
					 | 
					 | 
					 | 
						cstr_split (targets, " ", true, &v);
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						mass_channel_mode (a->s, a->channel_name, adding, mode_char, &v);
 | 
					 | 
					 | 
					 | 
						mass_channel_mode (a->s, a->channel_name, adding, mode_char, &v);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						strv_free (&v);
 | 
					 | 
					 | 
					 | 
						strv_free (&v);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						return true;
 | 
					 | 
					 | 
					 | 
						return true;
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -11856,6 +12056,9 @@ g_command_handlers[] =
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "notice",     "Send notice to a nick or channel",
 | 
					 | 
					 | 
					 | 
						{ "notice",     "Send notice to a nick or channel",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  "<target> <message>",
 | 
					 | 
					 | 
					 | 
						  "<target> <message>",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  handle_command_notice,     HANDLER_SERVER | HANDLER_NEEDS_REG },
 | 
					 | 
					 | 
					 | 
						  handle_command_notice,     HANDLER_SERVER | HANDLER_NEEDS_REG },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						{ "squery",     "Send a message to a service",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						  "<service> <message>",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						  handle_command_squery,     HANDLER_SERVER | HANDLER_NEEDS_REG },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "ctcp",       "Send a CTCP query",
 | 
					 | 
					 | 
					 | 
						{ "ctcp",       "Send a CTCP query",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  "<target> <tag>",
 | 
					 | 
					 | 
					 | 
						  "<target> <tag>",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  handle_command_ctcp,       HANDLER_SERVER | HANDLER_NEEDS_REG },
 | 
					 | 
					 | 
					 | 
						  handle_command_ctcp,       HANDLER_SERVER | HANDLER_NEEDS_REG },
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -11877,13 +12080,13 @@ g_command_handlers[] =
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  "<nick>...",
 | 
					 | 
					 | 
					 | 
						  "<nick>...",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  handle_command_op,         HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
 | 
					 | 
					 | 
					 | 
						  handle_command_op,         HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "deop",       "Remove channel operator status",
 | 
					 | 
					 | 
					 | 
						{ "deop",       "Remove channel operator status",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  "<nick>...",
 | 
					 | 
					 | 
					 | 
						  "[<nick>...]",
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  handle_command_deop,       HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
 | 
					 | 
					 | 
					 | 
						  handle_command_deop,       HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "voice",      "Give voice",
 | 
					 | 
					 | 
					 | 
						{ "voice",      "Give voice",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  "<nick>...",
 | 
					 | 
					 | 
					 | 
						  "<nick>...",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  handle_command_voice,      HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
 | 
					 | 
					 | 
					 | 
						  handle_command_voice,      HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "devoice",    "Remove voice",
 | 
					 | 
					 | 
					 | 
						{ "devoice",    "Remove voice",
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  "<nick>...",
 | 
					 | 
					 | 
					 | 
						  "[<nick>...]",
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						  handle_command_devoice,    HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
 | 
					 | 
					 | 
					 | 
						  handle_command_devoice,    HANDLER_SERVER | HANDLER_CHANNEL_FIRST },
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{ "mode",       "Change mode",
 | 
					 | 
					 | 
					 | 
						{ "mode",       "Change mode",
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -12025,8 +12228,9 @@ handle_command_help (struct handler_args *a)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (!*a->arguments)
 | 
					 | 
					 | 
					 | 
						if (!*a->arguments)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							return show_command_list (ctx);
 | 
					 | 
					 | 
					 | 
							return show_command_list (ctx);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// TODO: we should probably also accept commands names with a leading slash
 | 
					 | 
					 | 
					 | 
						const char *word = cut_word (&a->arguments);
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						char *command = cut_word (&a->arguments);
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						const char *command = word + (*word == '/');
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						for (size_t i = 0; i < N_ELEMENTS (g_command_handlers); i++)
 | 
					 | 
					 | 
					 | 
						for (size_t i = 0; i < N_ELEMENTS (g_command_handlers); i++)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							struct command_handler *handler = &g_command_handlers[i];
 | 
					 | 
					 | 
					 | 
							struct command_handler *handler = &g_command_handlers[i];
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -12034,13 +12238,13 @@ handle_command_help (struct handler_args *a)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
								return show_command_help (ctx, handler);
 | 
					 | 
					 | 
					 | 
								return show_command_help (ctx, handler);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						}
 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (try_handle_command_help_option (ctx, command))
 | 
					 | 
					 | 
					 | 
						if (try_handle_command_help_option (ctx, word))
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							return true;
 | 
					 | 
					 | 
					 | 
							return true;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (str_map_find (get_aliases_config (ctx), command))
 | 
					 | 
					 | 
					 | 
						if (str_map_find (get_aliases_config (ctx), command))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							log_global_status (ctx, "/#s is an alias", command);
 | 
					 | 
					 | 
					 | 
							log_global_status (ctx, "/#s is an alias", command);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						else
 | 
					 | 
					 | 
					 | 
						else
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							log_global_error (ctx, "#s: #s", "No such command or option", command);
 | 
					 | 
					 | 
					 | 
							log_global_error (ctx, "#s: #s", "No such command or option", word);
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						return true;
 | 
					 | 
					 | 
					 | 
						return true;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -12115,6 +12319,12 @@ expand_alias_escape (const char *p, const char *arguments, struct str *output)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						cstr_split (arguments, " ", true, &words);
 | 
					 | 
					 | 
					 | 
						cstr_split (arguments, " ", true, &words);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						// TODO: eventually also add support for argument ranges
 | 
					 | 
					 | 
					 | 
						// TODO: eventually also add support for argument ranges
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//  - Can use ${0}, ${0:}, ${:0}, ${1:-1} with strtol, dispose of $1 syntax
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//    (default aliases don't use numeric arguments).
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//  - Start numbering from zero, since we'd have to figure out what to do
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//    in case we encounter a zero if we keep the current approach.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//  - Ignore the sequence altogether if no closing '}' can be found,
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						//    or if the internal format doesn't fit the above syntax.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (*p >= '1' && *p <= '9')
 | 
					 | 
					 | 
					 | 
						if (*p >= '1' && *p <= '9')
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							size_t offset = *p - '1';
 | 
					 | 
					 | 
					 | 
							size_t offset = *p - '1';
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					@@ -13722,12 +13932,15 @@ on_signal_pipe_readable (const struct pollfd *fd, struct app_context *ctx)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						while (try_reap_child (ctx))
 | 
					 | 
					 | 
					 | 
						while (try_reap_child (ctx))
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							;
 | 
					 | 
					 | 
					 | 
							;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (g_termination_requested && !ctx->quitting)
 | 
					 | 
					 | 
					 | 
						if (g_termination_requested)
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							initiate_quit (ctx, NULL);
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							g_termination_requested = false;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							request_quit (ctx, NULL);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						if (g_winch_received)
 | 
					 | 
					 | 
					 | 
						if (g_winch_received)
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						{
 | 
					 | 
					 | 
					 | 
						{
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							redraw_screen (ctx);
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
							g_winch_received = false;
 | 
					 | 
					 | 
					 | 
							g_winch_received = false;
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
							redraw_screen (ctx);
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
						}
 | 
					 | 
					 | 
					 | 
						}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					}
 | 
					 | 
					 | 
					 | 
					}
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					
					| 
						
					 | 
					 | 
					 
 |