2020-03-29 02:16:53 -07:00
/*
* modules / chanmodes / history - Channel History
* ( C ) Copyright 2009 - 2019 Bram Matthys ( Syzop ) and the UnrealIRCd team
2022-11-19 20:12:40 -08:00
* License : GPLv2 or later
2020-03-29 02:16:53 -07:00
*/
# include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
" chanmodes/history " ,
" 1.0 " ,
" Channel Mode +H " ,
" UnrealIRCd Team " ,
2022-01-14 21:16:34 -08:00
" unrealircd-6 " ,
2020-03-29 02:16:53 -07:00
} ;
typedef struct ConfigHistoryExt ConfigHistoryExt ;
struct ConfigHistoryExt {
int lines ; /**< number of lines */
long time ; /**< seconds */
} ;
2021-06-19 08:52:51 -07:00
typedef struct cfgstruct cfgstruct ;
struct cfgstruct {
2020-03-29 02:16:53 -07:00
ConfigHistoryExt playback_on_join ; /**< Maximum number of lines & time to playback on-join */
2021-06-19 08:52:51 -07:00
ConfigHistoryExt max_storage_per_channel_registered ; /**< Maximum number of lines & time to record for +r channels*/
ConfigHistoryExt max_storage_per_channel_unregistered ; /**< Maximum number of lines & time to record for -r channels */
} ;
2020-03-29 02:16:53 -07:00
typedef struct HistoryChanMode HistoryChanMode ;
struct HistoryChanMode {
unsigned int max_lines ; /**< Maximum number of messages to record */
unsigned long max_time ; /**< Maximum number of time (in seconds) to record */
} ;
2021-06-19 08:52:51 -07:00
/* Global variables */
2020-03-29 02:16:53 -07:00
Cmode_t EXTMODE_HISTORY = 0L ;
2021-06-19 08:52:51 -07:00
static cfgstruct cfg ;
static cfgstruct test ;
2022-01-14 21:16:34 -08:00
# define HistoryEnabled(channel) (channel->mode.mode & EXTMODE_HISTORY)
2020-03-29 02:16:53 -07:00
/* Forward declarations */
2021-06-19 08:52:51 -07:00
static void init_config ( cfgstruct * cfg ) ;
2020-03-29 02:16:53 -07:00
int history_config_test ( ConfigFile * , ConfigEntry * , int , int * ) ;
2021-06-19 08:52:51 -07:00
int history_config_posttest ( int * ) ;
2020-03-29 02:16:53 -07:00
int history_config_run ( ConfigFile * , ConfigEntry * , int ) ;
2022-01-14 21:16:34 -08:00
int history_chanmode_change ( Client * client , Channel * channel , MessageTag * mtags , const char * modebuf , const char * parabuf , time_t sendts , int samode , int * destroy_channel ) ;
2020-03-29 02:16:53 -07:00
static int compare_history_modes ( HistoryChanMode * a , HistoryChanMode * b ) ;
2022-01-14 21:16:34 -08:00
int history_chanmode_is_ok ( Client * client , Channel * channel , char mode , const char * para , int type , int what ) ;
void * history_chanmode_put_param ( void * r_in , const char * param ) ;
const char * history_chanmode_get_param ( void * r_in ) ;
const char * history_chanmode_conv_param ( const char * param , Client * client , Channel * channel ) ;
2023-05-05 15:12:01 -07:00
int history_chanmode_free_param ( void * r , int soft ) ;
2020-03-29 02:16:53 -07:00
void * history_chanmode_dup_struct ( void * r_in ) ;
int history_chanmode_sjoin_check ( Channel * channel , void * ourx , void * theirx ) ;
int history_channel_destroy ( Channel * channel , int * should_destroy ) ;
2022-01-14 21:16:34 -08:00
int history_chanmsg ( Client * client , Channel * channel , int sendflags , const char * prefix , const char * target , MessageTag * mtags , const char * text , SendType sendtype ) ;
int history_join ( Client * client , Channel * channel , MessageTag * mtags ) ;
2021-06-19 08:52:51 -07:00
CMD_OVERRIDE_FUNC ( override_mode ) ;
2020-03-29 02:16:53 -07:00
MOD_TEST ( )
{
2021-06-19 08:52:51 -07:00
init_config ( & test ) ;
2020-03-29 02:16:53 -07:00
HookAdd ( modinfo - > handle , HOOKTYPE_CONFIGTEST , 0 , history_config_test ) ;
2021-06-19 08:52:51 -07:00
HookAdd ( modinfo - > handle , HOOKTYPE_CONFIGPOSTTEST , 0 , history_config_posttest ) ;
2020-03-29 02:16:53 -07:00
return MOD_SUCCESS ;
}
MOD_INIT ( )
{
CmodeInfo creq ;
ModDataInfo mreq ;
MARK_AS_OFFICIAL_MODULE ( modinfo ) ;
memset ( & creq , 0 , sizeof ( creq ) ) ;
creq . paracount = 1 ;
creq . is_ok = history_chanmode_is_ok ;
2022-01-14 21:16:34 -08:00
creq . letter = ' H ' ;
2020-03-29 02:16:53 -07:00
creq . put_param = history_chanmode_put_param ;
creq . get_param = history_chanmode_get_param ;
creq . conv_param = history_chanmode_conv_param ;
creq . free_param = history_chanmode_free_param ;
creq . dup_struct = history_chanmode_dup_struct ;
creq . sjoin_check = history_chanmode_sjoin_check ;
CmodeAdd ( modinfo - > handle , creq , & EXTMODE_HISTORY ) ;
2021-06-19 08:52:51 -07:00
init_config ( & cfg ) ;
2020-03-29 02:16:53 -07:00
HookAdd ( modinfo - > handle , HOOKTYPE_CONFIGRUN , 0 , history_config_run ) ;
HookAdd ( modinfo - > handle , HOOKTYPE_LOCAL_CHANMODE , 0 , history_chanmode_change ) ;
HookAdd ( modinfo - > handle , HOOKTYPE_REMOTE_CHANMODE , 0 , history_chanmode_change ) ;
HookAdd ( modinfo - > handle , HOOKTYPE_LOCAL_JOIN , 0 , history_join ) ;
HookAdd ( modinfo - > handle , HOOKTYPE_CHANMSG , 0 , history_chanmsg ) ;
HookAdd ( modinfo - > handle , HOOKTYPE_CHANNEL_DESTROY , 1000000 , history_channel_destroy ) ;
return MOD_SUCCESS ;
}
MOD_LOAD ( )
{
2022-01-14 21:16:34 -08:00
CommandOverrideAdd ( modinfo - > handle , " MODE " , 0 , override_mode ) ;
CommandOverrideAdd ( modinfo - > handle , " SVSMODE " , 0 , override_mode ) ;
CommandOverrideAdd ( modinfo - > handle , " SVS2MODE " , 0 , override_mode ) ;
CommandOverrideAdd ( modinfo - > handle , " SAMODE " , 0 , override_mode ) ;
CommandOverrideAdd ( modinfo - > handle , " SJOIN " , 0 , override_mode ) ;
2020-03-29 02:16:53 -07:00
return MOD_SUCCESS ;
}
MOD_UNLOAD ( )
{
return MOD_SUCCESS ;
}
2021-06-19 08:52:51 -07:00
static void init_config ( cfgstruct * cfg )
2020-03-29 02:16:53 -07:00
{
/* Set default values */
2021-06-19 08:52:51 -07:00
memset ( cfg , 0 , sizeof ( cfgstruct ) ) ;
cfg - > playback_on_join . lines = 15 ;
cfg - > playback_on_join . time = 86400 ;
cfg - > max_storage_per_channel_unregistered . lines = 200 ;
cfg - > max_storage_per_channel_unregistered . time = 86400 * 31 ;
cfg - > max_storage_per_channel_registered . lines = 5000 ;
cfg - > max_storage_per_channel_registered . time = 86400 * 31 ;
2020-03-29 02:16:53 -07:00
}
int history_config_test ( ConfigFile * cf , ConfigEntry * ce , int type , int * errs )
{
int errors = 0 ;
2021-06-19 08:52:51 -07:00
ConfigEntry * cep , * cepp , * cep4 , * cep5 ;
int on_join_lines = 0 , maximum_storage_lines_registered = 0 , maximum_storage_lines_unregistered = 0 ;
long on_join_time = 0L , maximum_storage_time_registered = 0L , maximum_storage_time_unregistered = 0L ;
2020-03-29 02:16:53 -07:00
/* We only care about set::history */
2022-01-14 21:16:34 -08:00
if ( ( type ! = CONFIG_SET ) | | strcmp ( ce - > name , " history " ) )
2020-03-29 02:16:53 -07:00
return 0 ;
2022-01-14 21:16:34 -08:00
for ( cep = ce - > items ; cep ; cep = cep - > next )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep - > name , " channel " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
for ( cepp = cep - > items ; cepp ; cepp = cepp - > next )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cepp - > name , " playback-on-join " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
for ( cep4 = cepp - > items ; cep4 ; cep4 = cep4 - > next )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep4 - > name , " lines " ) )
2020-03-29 02:16:53 -07:00
{
int v ;
CheckNull ( cep4 ) ;
2022-01-14 21:16:34 -08:00
v = atoi ( cep4 - > value ) ;
2021-06-19 08:52:51 -07:00
if ( ( v < 0 ) | | ( v > 1000 ) )
2020-03-29 02:16:53 -07:00
{
2021-06-19 08:52:51 -07:00
config_error ( " %s:%i: set::history::channel::playback-on-join::lines must be between 0 and 1000. "
2020-03-29 02:16:53 -07:00
" Recommended values are 10-50. Got: %d. " ,
2022-01-14 21:16:34 -08:00
cep4 - > file - > filename , cep4 - > line_number , v ) ;
2020-03-29 02:16:53 -07:00
errors + + ;
continue ;
}
2021-06-19 08:52:51 -07:00
test . playback_on_join . lines = v ;
2020-03-29 02:16:53 -07:00
} else
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep4 - > name , " time " ) )
2020-03-29 02:16:53 -07:00
{
long v ;
CheckNull ( cep4 ) ;
2022-01-14 21:16:34 -08:00
v = config_checkval ( cep4 - > value , CFG_TIME ) ;
2021-06-19 08:52:51 -07:00
if ( v < 0 )
2020-03-29 02:16:53 -07:00
{
2021-06-19 08:52:51 -07:00
config_error ( " %s:%i: set::history::channel::playback-on-join::time must be zero or more. " ,
2022-01-14 21:16:34 -08:00
cep4 - > file - > filename , cep4 - > line_number ) ;
2020-03-29 02:16:53 -07:00
errors + + ;
continue ;
}
2021-06-19 08:52:51 -07:00
test . playback_on_join . time = v ;
2020-03-29 02:16:53 -07:00
} else
{
2022-01-14 21:16:34 -08:00
config_error_unknown ( cep4 - > file - > filename ,
cep4 - > line_number , " set::history::channel::playback-on-join " , cep4 - > name ) ;
2020-03-29 02:16:53 -07:00
errors + + ;
}
}
} else
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cepp - > name , " max-storage-per-channel " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
for ( cep4 = cepp - > items ; cep4 ; cep4 = cep4 - > next )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep4 - > name , " registered " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
for ( cep5 = cep4 - > items ; cep5 ; cep5 = cep5 - > next )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep5 - > name , " lines " ) )
2021-06-19 08:52:51 -07:00
{
int v ;
CheckNull ( cep5 ) ;
2022-01-14 21:16:34 -08:00
v = atoi ( cep5 - > value ) ;
2021-06-19 08:52:51 -07:00
if ( v < 1 )
{
config_error ( " %s:%i: set::history::channel::max-storage-per-channel::registered::lines must be a positive number. " ,
2022-01-14 21:16:34 -08:00
cep5 - > file - > filename , cep5 - > line_number ) ;
2021-06-19 08:52:51 -07:00
errors + + ;
continue ;
}
test . max_storage_per_channel_registered . lines = v ;
} else
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep5 - > name , " time " ) )
2021-06-19 08:52:51 -07:00
{
long v ;
CheckNull ( cep5 ) ;
2022-01-14 21:16:34 -08:00
v = config_checkval ( cep5 - > value , CFG_TIME ) ;
2021-06-19 08:52:51 -07:00
if ( v < 1 )
{
config_error ( " %s:%i: set::history::channel::max-storage-per-channel::registered::time must be a positive number. " ,
2022-01-14 21:16:34 -08:00
cep5 - > file - > filename , cep5 - > line_number ) ;
2021-06-19 08:52:51 -07:00
errors + + ;
continue ;
}
test . max_storage_per_channel_registered . time = v ;
} else
{
2022-01-14 21:16:34 -08:00
config_error_unknown ( cep5 - > file - > filename ,
cep5 - > line_number , " set::history::channel::max-storage-per-channel::registered " , cep5 - > name ) ;
2021-06-19 08:52:51 -07:00
errors + + ;
}
2020-03-29 02:16:53 -07:00
}
} else
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep4 - > name , " unregistered " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
for ( cep5 = cep4 - > items ; cep5 ; cep5 = cep5 - > next )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep5 - > name , " lines " ) )
2021-06-19 08:52:51 -07:00
{
int v ;
CheckNull ( cep5 ) ;
2022-01-14 21:16:34 -08:00
v = atoi ( cep5 - > value ) ;
2021-06-19 08:52:51 -07:00
if ( v < 1 )
{
config_error ( " %s:%i: set::history::channel::max-storage-per-channel::unregistered::lines must be a positive number. " ,
2022-01-14 21:16:34 -08:00
cep5 - > file - > filename , cep5 - > line_number ) ;
2021-06-19 08:52:51 -07:00
errors + + ;
continue ;
}
test . max_storage_per_channel_unregistered . lines = v ;
} else
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep5 - > name , " time " ) )
2021-06-19 08:52:51 -07:00
{
long v ;
CheckNull ( cep5 ) ;
2022-01-14 21:16:34 -08:00
v = config_checkval ( cep5 - > value , CFG_TIME ) ;
2021-06-19 08:52:51 -07:00
if ( v < 1 )
{
config_error ( " %s:%i: set::history::channel::max-storage-per-channel::unregistered::time must be a positive number. " ,
2022-01-14 21:16:34 -08:00
cep5 - > file - > filename , cep5 - > line_number ) ;
2021-06-19 08:52:51 -07:00
errors + + ;
continue ;
}
test . max_storage_per_channel_unregistered . time = v ;
} else
{
2022-01-14 21:16:34 -08:00
config_error_unknown ( cep5 - > file - > filename ,
cep5 - > line_number , " set::history::channel::max-storage-per-channel::unregistered " , cep5 - > name ) ;
2021-06-19 08:52:51 -07:00
errors + + ;
}
2020-03-29 02:16:53 -07:00
}
} else
{
2022-01-14 21:16:34 -08:00
config_error_unknown ( cep - > file - > filename ,
cep - > line_number , " set::history::max-storage-per-channel " , cep - > name ) ;
2020-03-29 02:16:53 -07:00
errors + + ;
}
}
} else
{
2021-06-19 08:52:51 -07:00
/* hmm.. I don't like this method. but I just quickly copied it from CONFIG_ALLOW for now... */
int used = 0 ;
Hook * h ;
for ( h = Hooks [ HOOKTYPE_CONFIGTEST ] ; h ; h = h - > next )
{
int value , errs = 0 ;
if ( h - > owner & & ! ( h - > owner - > flags & MODFLAG_TESTING )
& & ! ( h - > owner - > options & MOD_OPT_PERM ) )
continue ;
value = ( * ( h - > func . intfunc ) ) ( cf , cepp , CONFIG_SET_HISTORY_CHANNEL , & errs ) ;
if ( value = = 2 )
used = 1 ;
if ( value = = 1 )
{
used = 1 ;
break ;
}
if ( value = = - 1 )
{
used = 1 ;
errors + = errs ;
break ;
}
if ( value = = - 2 )
{
used = 1 ;
errors + = errs ;
}
}
if ( ! used )
{
2022-01-14 21:16:34 -08:00
config_error_unknown ( cepp - > file - > filename ,
cepp - > line_number , " set::history::channel " , cepp - > name ) ;
2021-06-19 08:52:51 -07:00
errors + + ;
}
2020-03-29 02:16:53 -07:00
}
}
} else {
2022-01-14 21:16:34 -08:00
config_error_unknown ( cep - > file - > filename ,
cep - > line_number , " set::history " , cep - > name ) ;
2020-03-29 02:16:53 -07:00
errors + + ;
}
}
2021-06-19 08:52:51 -07:00
* errs = errors ;
return errors ? - 1 : 1 ;
}
int history_config_posttest ( int * errs )
{
int errors = 0 ;
/* We could check here for on join lines / on join time being bigger than max storage but..
* not really important .
*/
2020-03-29 02:16:53 -07:00
* errs = errors ;
return errors ? - 1 : 1 ;
}
int history_config_run ( ConfigFile * cf , ConfigEntry * ce , int type )
{
2021-06-19 08:52:51 -07:00
ConfigEntry * cep , * cepp , * cep4 , * cep5 ;
2020-03-29 02:16:53 -07:00
2022-01-14 21:16:34 -08:00
if ( ( type ! = CONFIG_SET ) | | strcmp ( ce - > name , " history " ) )
2020-03-29 02:16:53 -07:00
return 0 ;
2022-01-14 21:16:34 -08:00
for ( cep = ce - > items ; cep ; cep = cep - > next )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep - > name , " channel " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
for ( cepp = cep - > items ; cepp ; cepp = cepp - > next )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cepp - > name , " playback-on-join " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
for ( cep4 = cepp - > items ; cep4 ; cep4 = cep4 - > next )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep4 - > name , " lines " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
cfg . playback_on_join . lines = atoi ( cep4 - > value ) ;
2020-03-29 02:16:53 -07:00
} else
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep4 - > name , " time " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
cfg . playback_on_join . time = config_checkval ( cep4 - > value , CFG_TIME ) ;
2020-03-29 02:16:53 -07:00
}
}
} else
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cepp - > name , " max-storage-per-channel " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
for ( cep4 = cepp - > items ; cep4 ; cep4 = cep4 - > next )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep4 - > name , " registered " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
for ( cep5 = cep4 - > items ; cep5 ; cep5 = cep5 - > next )
2021-06-19 08:52:51 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep5 - > name , " lines " ) )
2021-06-19 08:52:51 -07:00
{
2022-01-14 21:16:34 -08:00
cfg . max_storage_per_channel_registered . lines = atoi ( cep5 - > value ) ;
2021-06-19 08:52:51 -07:00
} else
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep5 - > name , " time " ) )
2021-06-19 08:52:51 -07:00
{
2022-01-14 21:16:34 -08:00
cfg . max_storage_per_channel_registered . time = config_checkval ( cep5 - > value , CFG_TIME ) ;
2021-06-19 08:52:51 -07:00
}
}
2020-03-29 02:16:53 -07:00
} else
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep4 - > name , " unregistered " ) )
2020-03-29 02:16:53 -07:00
{
2022-01-14 21:16:34 -08:00
for ( cep5 = cep4 - > items ; cep5 ; cep5 = cep5 - > next )
2021-06-19 08:52:51 -07:00
{
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep5 - > name , " lines " ) )
2021-06-19 08:52:51 -07:00
{
2022-01-14 21:16:34 -08:00
cfg . max_storage_per_channel_unregistered . lines = atoi ( cep5 - > value ) ;
2021-06-19 08:52:51 -07:00
} else
2022-01-14 21:16:34 -08:00
if ( ! strcmp ( cep5 - > name , " time " ) )
2021-06-19 08:52:51 -07:00
{
2022-01-14 21:16:34 -08:00
cfg . max_storage_per_channel_unregistered . time = config_checkval ( cep5 - > value , CFG_TIME ) ;
2021-06-19 08:52:51 -07:00
}
}
2020-03-29 02:16:53 -07:00
}
}
2021-06-19 08:52:51 -07:00
} else
{
Hook * h ;
for ( h = Hooks [ HOOKTYPE_CONFIGRUN ] ; h ; h = h - > next )
{
int value = ( * ( h - > func . intfunc ) ) ( cf , cepp , CONFIG_SET_HISTORY_CHANNEL ) ;
if ( value = = 1 )
break ;
}
2020-03-29 02:16:53 -07:00
}
}
}
}
return 0 ; /* Retval 0 = trick so other modules can see the same configuration */
}
/** Helper function for .is_ok(), .conv_param() and .put_param().
* @ param param : The mode parameter .
* @ param lines : The number of lines ( the X in + H X : Y )
* @ param t : The time value ( the Y in + H X : Y )
*/
2022-01-14 21:16:34 -08:00
int history_parse_chanmode ( Channel * channel , const char * param , int * lines , long * t )
2020-03-29 02:16:53 -07:00
{
char buf [ 64 ] , * p , * q ;
char contains_non_digit = 0 ;
/* Work on a copy */
strlcpy ( buf , param , sizeof ( buf ) ) ;
/* Initialize, to be safe */
* lines = 0 ;
* t = 0 ;
p = strchr ( buf , ' : ' ) ;
if ( ! p )
return 0 ;
/* Parse lines */
* p + + = ' \0 ' ;
* lines = atoi ( buf ) ;
/* Parse time value */
/* If it is all digits then it is in minutes */
for ( q = p ; * q ; q + + )
{
if ( ! isdigit ( * q ) )
{
contains_non_digit = 1 ;
break ;
}
}
if ( contains_non_digit )
* t = config_checkval ( p , CFG_TIME ) ;
else
* t = atoi ( p ) * 60 ;
/* Sanity checking... */
if ( * lines < 1 )
return 0 ;
if ( * t < 60 )
return 0 ;
/* Check imposed configuration limits... */
2021-06-19 08:52:51 -07:00
if ( ! channel | | has_channel_mode ( channel , ' r ' ) )
{
if ( * lines > cfg . max_storage_per_channel_registered . lines )
* lines = cfg . max_storage_per_channel_registered . lines ;
2020-03-29 02:16:53 -07:00
2021-06-19 08:52:51 -07:00
if ( * t > cfg . max_storage_per_channel_registered . time )
* t = cfg . max_storage_per_channel_registered . time ;
} else {
if ( * lines > cfg . max_storage_per_channel_unregistered . lines )
* lines = cfg . max_storage_per_channel_unregistered . lines ;
2020-03-29 02:16:53 -07:00
2021-06-19 08:52:51 -07:00
if ( * t > cfg . max_storage_per_channel_unregistered . time )
* t = cfg . max_storage_per_channel_unregistered . time ;
}
2020-03-29 02:16:53 -07:00
return 1 ;
}
/** Channel Mode +H check:
* Does the user have rights to add / remove this channel mode ?
* Is the supplied mode parameter ok ?
*/
2022-01-14 21:16:34 -08:00
int history_chanmode_is_ok ( Client * client , Channel * channel , char mode , const char * param , int type , int what )
2020-03-29 02:16:53 -07:00
{
if ( ( type = = EXCHK_ACCESS ) | | ( type = = EXCHK_ACCESS_ERR ) )
{
2022-01-14 21:16:34 -08:00
if ( IsUser ( client ) & & check_channel_access ( client , channel , " oaq " ) )
2020-03-29 02:16:53 -07:00
return EX_ALLOW ;
if ( type = = EXCHK_ACCESS_ERR ) /* can only be due to being halfop */
sendnumeric ( client , ERR_NOTFORHALFOPS , ' H ' ) ;
return EX_DENY ;
} else
if ( type = = EXCHK_PARAM )
{
int lines = 0 ;
long t = 0L ;
2021-06-19 08:52:51 -07:00
if ( ! history_parse_chanmode ( channel , param , & lines , & t ) )
2020-03-29 02:16:53 -07:00
{
sendnumeric ( client , ERR_CANNOTCHANGECHANMODE , ' H ' , " Invalid syntax for MODE +H. Use +H lines:period. The period must be in minutes (eg: 10) or a time value (eg: 1h). " ) ;
return EX_DENY ;
}
/* Don't bother about lines/t limits here, we will auto-convert in .conv_param */
return EX_ALLOW ;
}
/* fallthrough -- should not be used */
return EX_DENY ;
}
2021-06-19 08:52:51 -07:00
static void history_chanmode_helper ( char * buf , size_t bufsize , int lines , long t )
{
if ( ( t % 86400 ) = = 0 )
{
/* Can be represented in full days, eg "1d" */
snprintf ( buf , bufsize , " %d:%ldd " , lines , t / 86400 ) ;
} else
if ( ( t % 3600 ) = = 0 )
{
/* Can be represented in hours, eg "8h" */
snprintf ( buf , bufsize , " %d:%ldh " , lines , t / 3600 ) ;
} else
{
/* Otherwise, stick to minutes */
snprintf ( buf , bufsize , " %d:%ldm " , lines , t / 60 ) ;
}
}
2020-03-29 02:16:53 -07:00
/** Convert channel parameter to something proper.
* NOTE : client may be NULL if called for e . g . set : : modes - playback - on - join
*/
2022-01-14 21:16:34 -08:00
const char * history_chanmode_conv_param ( const char * param , Client * client , Channel * channel )
2020-03-29 02:16:53 -07:00
{
static char buf [ 64 ] ;
int lines = 0 ;
long t = 0L ;
2021-06-19 08:52:51 -07:00
if ( ! history_parse_chanmode ( channel , param , & lines , & t ) )
2020-03-29 02:16:53 -07:00
return NULL ;
2021-06-19 08:52:51 -07:00
history_chanmode_helper ( buf , sizeof ( buf ) , lines , t ) ;
2020-03-29 02:16:53 -07:00
return buf ;
}
/** Store the +H x:y channel mode */
2022-01-14 21:16:34 -08:00
void * history_chanmode_put_param ( void * mode_in , const char * param )
2020-03-29 02:16:53 -07:00
{
HistoryChanMode * h = ( HistoryChanMode * ) mode_in ;
int lines = 0 ;
long t = 0L ;
2021-06-19 08:52:51 -07:00
if ( ! history_parse_chanmode ( NULL , param , & lines , & t ) )
2020-03-29 02:16:53 -07:00
return NULL ;
if ( ! h )
{
/* Need to create one */
h = safe_alloc ( sizeof ( HistoryChanMode ) ) ;
}
h - > max_lines = lines ;
h - > max_time = t ;
return ( void * ) h ;
}
/** Retrieve the +H settings (the X:Y string) */
2022-01-14 21:16:34 -08:00
const char * history_chanmode_get_param ( void * h_in )
2020-03-29 02:16:53 -07:00
{
HistoryChanMode * h = ( HistoryChanMode * ) h_in ;
static char buf [ 64 ] ;
if ( ! h_in )
return NULL ;
2021-06-19 08:52:51 -07:00
history_chanmode_helper ( buf , sizeof ( buf ) , h - > max_lines , h - > max_time ) ;
2020-03-29 02:16:53 -07:00
return buf ;
}
/** Free channel mode */
2023-05-05 15:12:01 -07:00
int history_chanmode_free_param ( void * r , int soft )
2020-03-29 02:16:53 -07:00
{
safe_free ( r ) ;
2023-05-05 15:12:01 -07:00
return 0 ;
2020-03-29 02:16:53 -07:00
}
/** Duplicate the channel mode +H settings */
void * history_chanmode_dup_struct ( void * r_in )
{
HistoryChanMode * r = ( HistoryChanMode * ) r_in ;
HistoryChanMode * w = safe_alloc ( sizeof ( HistoryChanMode ) ) ;
memcpy ( w , r , sizeof ( HistoryChanMode ) ) ;
return ( void * ) w ;
}
/** If two servers with an identical creation time stamp connect,
* we have to deal with merging the settings on different sides
* ( if they differ at all ) . That ' s what we do here .
*/
int history_chanmode_sjoin_check ( Channel * channel , void * ourx , void * theirx )
{
HistoryChanMode * our = ( HistoryChanMode * ) ourx ;
HistoryChanMode * their = ( HistoryChanMode * ) theirx ;
if ( ( our - > max_lines = = their - > max_lines ) & & ( our - > max_time = = their - > max_time ) )
return EXSJ_SAME ;
our - > max_lines = MAX ( our - > max_lines , their - > max_lines ) ;
our - > max_time = MAX ( our - > max_time , their - > max_time ) ;
return EXSJ_MERGE ;
}
/** On channel mode change, communicate the +H limits to the history backend layer */
2022-01-14 21:16:34 -08:00
int history_chanmode_change ( Client * client , Channel * channel , MessageTag * mtags , const char * modebuf , const char * parabuf , time_t sendts , int samode , int * destroy_channel )
2020-03-29 02:16:53 -07:00
{
HistoryChanMode * settings ;
/* Did anything change, with regards to channel mode H ? */
if ( ! strchr ( modebuf , ' H ' ) )
return 0 ;
/* If so, grab the settings, and communicate them */
settings = ( HistoryChanMode * ) GETPARASTRUCT ( channel , ' H ' ) ;
if ( settings )
2022-01-14 21:16:34 -08:00
history_set_limit ( channel - > name , settings - > max_lines , settings - > max_time ) ;
2020-03-29 02:16:53 -07:00
else
2022-01-14 21:16:34 -08:00
history_destroy ( channel - > name ) ;
2020-03-29 02:16:53 -07:00
return 0 ;
}
/** Channel is destroyed (or is it?) */
int history_channel_destroy ( Channel * channel , int * should_destroy )
{
if ( * should_destroy = = 0 )
return 0 ; /* channel will not be destroyed */
2022-01-14 21:16:34 -08:00
history_destroy ( channel - > name ) ;
2020-03-29 02:16:53 -07:00
return 0 ;
}
2022-01-14 21:16:34 -08:00
int history_chanmsg ( Client * client , Channel * channel , int sendflags , const char * prefix , const char * target , MessageTag * mtags , const char * text , SendType sendtype )
2020-03-29 02:16:53 -07:00
{
char buf [ 512 ] ;
char source [ 64 ] ;
HistoryChanMode * settings ;
if ( ! HistoryEnabled ( channel ) )
return 0 ;
/* Filter out CTCP / CTCP REPLY */
if ( ( * text = = ' \001 ' ) & & strncmp ( text + 1 , " ACTION " , 6 ) )
return 0 ;
2020-05-28 19:06:50 -07:00
/* Filter out TAGMSG */
if ( sendtype = = SEND_TYPE_TAGMSG )
return 0 ;
2020-03-29 02:16:53 -07:00
/* Lazy: if any prefix is addressed (eg: @#channel) then don't record it.
* This so we don ' t have to check privileges during history playback etc .
*/
if ( prefix )
return 0 ;
if ( IsUser ( client ) )
snprintf ( source , sizeof ( source ) , " %s!%s@%s " , client - > name , client - > user - > username , GetHost ( client ) ) ;
else
strlcpy ( source , client - > name , sizeof ( source ) ) ;
snprintf ( buf , sizeof ( buf ) , " :%s %s %s :%s " ,
source ,
2020-05-28 19:06:50 -07:00
sendtype_to_cmd ( sendtype ) ,
2022-01-14 21:16:34 -08:00
channel - > name ,
2020-03-29 02:16:53 -07:00
text ) ;
2022-01-14 21:16:34 -08:00
history_add ( channel - > name , mtags , buf ) ;
2020-03-29 02:16:53 -07:00
return 0 ;
}
2022-01-14 21:16:34 -08:00
int history_join ( Client * client , Channel * channel , MessageTag * mtags )
2020-03-29 02:16:53 -07:00
{
2021-06-19 08:52:51 -07:00
/* Only for +H channels */
if ( ! HistoryEnabled ( channel ) | | ! cfg . playback_on_join . lines | | ! cfg . playback_on_join . time )
return 0 ;
/* No history-on-join for clients that implement CHATHISTORY,
* they will pull history themselves if they need it .
*/
2022-01-14 21:16:34 -08:00
if ( HasCapability ( client , " draft/chathistory " ) /*|| HasCapability(client, "chathistory")*/ )
2020-03-29 02:16:53 -07:00
return 0 ;
2021-06-19 08:52:51 -07:00
if ( MyUser ( client ) & & can_receive_history ( client ) )
2020-07-16 15:06:52 -07:00
{
HistoryFilter filter ;
2021-06-19 08:52:51 -07:00
HistoryResult * r ;
2020-07-16 15:06:52 -07:00
memset ( & filter , 0 , sizeof ( filter ) ) ;
2021-06-19 08:52:51 -07:00
filter . cmd = HFC_SIMPLE ;
2020-07-16 15:06:52 -07:00
filter . last_lines = cfg . playback_on_join . lines ;
filter . last_seconds = cfg . playback_on_join . time ;
2022-01-14 21:16:34 -08:00
r = history_request ( channel - > name , & filter ) ;
2021-06-19 08:52:51 -07:00
if ( r )
{
history_send_result ( client , r ) ;
free_history_result ( r ) ;
}
2020-07-16 15:06:52 -07:00
}
2020-03-29 02:16:53 -07:00
return 0 ;
}
2021-06-19 08:52:51 -07:00
/** Check if a channel went from +r to -r and adjust +H if needed.
* This does not only override " MODE " but also " SAMODE " , " SJOIN " and more .
*/
CMD_OVERRIDE_FUNC ( override_mode )
{
Channel * channel ;
int had_r = 0 ;
/* We only bother checking for this corner case if the -r
* comes from a server directly linked to us , this normally
* means : we are the server that services are linked to .
*/
if ( ( IsServer ( client ) & & client - > local ) | |
2022-01-14 21:16:34 -08:00
( IsUser ( client ) & & client - > uplink & & client - > uplink - > local ) )
2021-06-19 08:52:51 -07:00
{
/* Now check if the channel is currently +r */
2022-01-14 21:16:34 -08:00
if ( ( parc > = 2 ) & & ! BadPtr ( parv [ 1 ] ) & & ( ( channel = find_channel ( parv [ 1 ] ) ) ) & &
2021-06-19 08:52:51 -07:00
has_channel_mode ( channel , ' r ' ) )
{
had_r = 1 ;
}
}
2023-05-05 15:12:01 -07:00
CALL_NEXT_COMMAND_OVERRIDE ( ) ;
2021-06-19 08:52:51 -07:00
/* If..
* - channel was + r
* - re - lookup the channel and check that it still
* exists ( as it may have been destroyed )
* - and is now - r
* - and has + H set
* then . . .
*/
if ( had_r & &
2022-01-14 21:16:34 -08:00
( ( channel = find_channel ( parv [ 1 ] ) ) ) & &
2021-06-19 08:52:51 -07:00
! has_channel_mode ( channel , ' r ' ) & &
HistoryEnabled ( channel ) )
{
/* Check if limit is higher than allowed for unregistered channels */
HistoryChanMode * settings = ( HistoryChanMode * ) GETPARASTRUCT ( channel , ' H ' ) ;
int changed = 0 ;
if ( ! settings )
return ; /* Weird */
if ( settings - > max_lines > cfg . max_storage_per_channel_unregistered . lines )
{
settings - > max_lines = cfg . max_storage_per_channel_unregistered . lines ;
changed = 1 ;
}
if ( settings - > max_time > cfg . max_storage_per_channel_unregistered . time )
{
settings - > max_time = cfg . max_storage_per_channel_unregistered . time ;
changed = 1 ;
}
if ( changed )
{
MessageTag * mtags = NULL ;
2022-01-14 21:16:34 -08:00
const char * params = history_chanmode_get_param ( settings ) ;
char modebuf [ BUFSIZE ] , parabuf [ BUFSIZE ] ;
int destroy_channel = 0 ;
2021-06-19 08:52:51 -07:00
if ( ! params )
return ; /* Weird */
strlcpy ( modebuf , " +H " , sizeof ( modebuf ) ) ;
strlcpy ( parabuf , params , sizeof ( modebuf ) ) ;
new_message ( & me , NULL , & mtags ) ;
sendto_channel ( channel , & me , & me , 0 , 0 , SEND_LOCAL , mtags ,
" :%s MODE %s %s %s " ,
2022-01-14 21:16:34 -08:00
me . name , channel - > name , modebuf , parabuf ) ;
2021-06-19 08:52:51 -07:00
sendto_server ( NULL , 0 , 0 , mtags , " :%s MODE %s %s %s %lld " ,
2022-01-14 21:16:34 -08:00
me . id , channel - > name , modebuf , parabuf ,
2021-06-19 08:52:51 -07:00
( long long ) channel - > creationtime ) ;
/* Activate this hook just like cmd_mode.c */
2022-01-14 21:16:34 -08:00
RunHook ( HOOKTYPE_REMOTE_CHANMODE , & me , channel , mtags , modebuf , parabuf , 0 , 0 , & destroy_channel ) ;
2021-06-19 08:52:51 -07:00
free_message_tags ( mtags ) ;
* modebuf = * parabuf = ' \0 ' ;
}
}
}