2011-02-24 04:14:30 +01:00
/* X-Chat
* Copyright ( C ) 1998 Peter Zelezny .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
2012-12-23 20:36:54 +01:00
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 , USA
2011-02-24 04:14:30 +01:00
*/
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <ctype.h>
# include <time.h>
# include <sys/types.h>
# include <fcntl.h>
# include <sys/stat.h>
2012-07-21 14:26:19 +02:00
# ifdef WIN32
# include <io.h>
# else
2011-12-11 17:34:02 +01:00
# include <unistd.h>
# include <sys/mman.h>
# endif
2012-10-24 21:33:02 +02:00
# include "hexchat.h"
2011-02-24 04:14:30 +01:00
# include "cfgfiles.h"
# include "chanopt.h"
# include "plugin.h"
# include "fe.h"
# include "server.h"
# include "util.h"
# include "outbound.h"
2012-10-24 21:33:02 +02:00
# include "hexchatc.h"
2011-02-24 04:14:30 +01:00
# include "text.h"
2012-10-30 22:13:40 +01:00
# include "typedef.h"
2011-02-24 04:14:30 +01:00
# ifdef WIN32
# include <windows.h>
# endif
2013-04-28 01:02:27 +02:00
# ifdef USE_LIBCANBERRA
# include <canberra.h>
# endif
2015-01-31 09:52:31 +01:00
const gchar * unicode_fallback_string = " \357 \277 \275 " ; /* The Unicode replacement character 0xFFFD */
const gchar * arbitrary_encoding_fallback_string = " ? " ;
2011-02-24 04:14:30 +01:00
struct pevt_stage1
{
int len ;
char * data ;
struct pevt_stage1 * next ;
} ;
2013-06-12 16:41:52 +02:00
# ifdef USE_LIBCANBERRA
static ca_context * ca_con ;
# endif
2011-02-24 04:14:30 +01:00
2016-01-31 21:13:18 +01:00
# define SCROLLBACK_MAX 32000
2012-11-03 18:24:25 +01:00
static void mkdir_p ( char * filename ) ;
2011-02-24 04:14:30 +01:00
static char * log_create_filename ( char * channame ) ;
static char *
2012-11-03 18:24:25 +01:00
scrollback_get_filename ( session * sess )
2011-02-24 04:14:30 +01:00
{
2016-01-31 21:13:18 +01:00
char * net , * chan , * buf , * ret = NULL ;
2011-02-24 04:14:30 +01:00
net = server_get_network ( sess - > server , FALSE ) ;
if ( ! net )
return NULL ;
2012-11-03 18:24:25 +01:00
buf = g_strdup_printf ( " %s " G_DIR_SEPARATOR_S " scrollback " G_DIR_SEPARATOR_S " %s " G_DIR_SEPARATOR_S " %s.txt " , get_xdir ( ) , net , " " ) ;
2011-02-24 04:14:30 +01:00
mkdir_p ( buf ) ;
2012-11-03 18:24:25 +01:00
g_free ( buf ) ;
2011-02-24 04:14:30 +01:00
chan = log_create_filename ( sess - > channel ) ;
2014-03-24 20:46:02 +01:00
if ( chan [ 0 ] )
buf = g_strdup_printf ( " %s " G_DIR_SEPARATOR_S " scrollback " G_DIR_SEPARATOR_S " %s " G_DIR_SEPARATOR_S " %s.txt " , get_xdir ( ) , net , chan ) ;
else
buf = NULL ;
2014-12-28 12:37:25 +01:00
g_free ( chan ) ;
2011-02-24 04:14:30 +01:00
2016-01-31 21:13:18 +01:00
if ( buf )
{
ret = g_filename_from_utf8 ( buf , - 1 , NULL , NULL , NULL ) ;
g_free ( buf ) ;
}
2011-02-24 04:14:30 +01:00
2016-01-31 21:13:18 +01:00
return ret ;
2011-02-24 04:14:30 +01:00
}
void
scrollback_close ( session * sess )
{
2016-01-31 21:13:18 +01:00
g_clear_object ( & sess - > scrollfile ) ;
2011-02-24 04:14:30 +01:00
}
2012-10-22 15:55:43 +02:00
/* shrink the file to roughly prefs.hex_text_max_lines */
2011-02-24 04:14:30 +01:00
static void
scrollback_shrink ( session * sess )
{
2016-01-31 21:13:18 +01:00
char * buf , * p ;
2013-01-19 21:33:16 +01:00
gsize len ;
2016-01-31 21:13:18 +01:00
gint offset , lines = 0 ;
const gint max_lines = MIN ( prefs . hex_text_max_lines , SCROLLBACK_MAX ) ;
2011-02-24 04:14:30 +01:00
2016-01-31 21:13:18 +01:00
if ( ! g_file_load_contents ( sess - > scrollfile , NULL , & buf , & len , NULL , NULL ) )
2011-02-24 04:14:30 +01:00
return ;
/* count all lines */
p = buf ;
while ( p ! = buf + len )
{
if ( * p = = ' \n ' )
lines + + ;
p + + ;
}
2016-01-31 21:13:18 +01:00
offset = lines - max_lines ;
2011-02-24 04:14:30 +01:00
2016-01-31 21:13:18 +01:00
/* now just go back to where we want to start the file */
2011-02-24 04:14:30 +01:00
p = buf ;
2016-01-31 21:13:18 +01:00
lines = 0 ;
2011-02-24 04:14:30 +01:00
while ( p ! = buf + len )
{
if ( * p = = ' \n ' )
{
2016-01-31 21:13:18 +01:00
lines + + ;
if ( lines = = offset )
2011-02-24 04:14:30 +01:00
{
p + + ;
break ;
}
}
p + + ;
}
2016-01-31 21:13:18 +01:00
if ( g_file_replace_contents ( sess - > scrollfile , p , strlen ( p ) , NULL , FALSE ,
G_FILE_CREATE_PRIVATE , NULL , NULL , NULL ) )
sess - > scrollwritten = lines ;
2014-12-09 06:24:59 +01:00
g_free ( buf ) ;
2011-02-24 04:14:30 +01:00
}
static void
2015-03-02 19:58:27 +01:00
scrollback_save ( session * sess , char * text , time_t stamp )
2011-02-24 04:14:30 +01:00
{
2016-01-31 21:13:18 +01:00
GOutputStream * ostream ;
2012-11-03 18:24:25 +01:00
char * buf ;
2011-02-24 04:14:30 +01:00
2014-03-24 20:46:02 +01:00
if ( sess - > type = = SESS_SERVER & & prefs . hex_gui_tab_server = = 1 )
2011-02-24 04:14:30 +01:00
return ;
if ( sess - > text_scrollback = = SET_DEFAULT )
{
2012-10-22 15:55:43 +02:00
if ( ! prefs . hex_text_replay )
2011-02-24 04:14:30 +01:00
return ;
}
else
{
if ( sess - > text_scrollback ! = SET_ON )
return ;
}
2016-01-31 21:13:18 +01:00
if ( ! sess - > scrollfile )
2011-02-24 04:14:30 +01:00
{
2012-11-03 18:24:25 +01:00
if ( ( buf = scrollback_get_filename ( sess ) ) = = NULL )
2011-02-24 04:14:30 +01:00
return ;
2016-01-31 21:13:18 +01:00
sess - > scrollfile = g_file_new_for_path ( buf ) ;
2012-11-03 18:24:25 +01:00
g_free ( buf ) ;
2011-02-24 04:14:30 +01:00
}
2016-01-31 21:13:18 +01:00
else
{
/* Users can delete the folder after it's created... */
GFile * parent = g_file_get_parent ( sess - > scrollfile ) ;
g_file_make_directory_with_parents ( parent , NULL , NULL ) ;
g_object_unref ( parent ) ;
}
ostream = G_OUTPUT_STREAM ( g_file_append_to ( sess - > scrollfile , G_FILE_CREATE_PRIVATE , NULL , NULL ) ) ;
if ( ! ostream )
return ;
2011-02-24 04:14:30 +01:00
2015-03-02 19:58:27 +01:00
if ( ! stamp )
stamp = time ( 0 ) ;
2011-02-24 04:14:30 +01:00
if ( sizeof ( stamp ) = = 4 ) /* gcc will optimize one of these out */
2012-11-04 18:11:41 +01:00
buf = g_strdup_printf ( " T %d " , ( int ) stamp ) ;
2011-02-24 04:14:30 +01:00
else
2012-11-03 18:24:25 +01:00
buf = g_strdup_printf ( " T % " G_GINT64_FORMAT " " , ( gint64 ) stamp ) ;
2011-02-24 04:14:30 +01:00
2016-01-31 21:13:18 +01:00
g_output_stream_write ( ostream , buf , strlen ( buf ) , NULL , NULL ) ;
g_output_stream_write ( ostream , text , strlen ( text ) , NULL , NULL ) ;
if ( ! g_str_has_suffix ( text , " \n " ) )
g_output_stream_write ( ostream , " \n " , 1 , NULL , NULL ) ;
g_free ( buf ) ;
g_object_unref ( ostream ) ;
2011-02-24 04:14:30 +01:00
sess - > scrollwritten + + ;
2016-01-31 21:13:18 +01:00
if ( ( sess - > scrollwritten > prefs . hex_text_max_lines & & prefs . hex_text_max_lines > 0 ) | |
sess - > scrollwritten > SCROLLBACK_MAX )
2011-02-24 04:14:30 +01:00
scrollback_shrink ( sess ) ;
}
void
scrollback_load ( session * sess )
{
2016-01-31 21:13:18 +01:00
GInputStream * stream ;
GDataInputStream * istream ;
gchar * buf , * text ;
gint lines = 0 ;
2016-04-26 18:05:57 +02:00
time_t stamp = 0 ;
2011-02-24 04:14:30 +01:00
if ( sess - > text_scrollback = = SET_DEFAULT )
{
2012-10-22 15:55:43 +02:00
if ( ! prefs . hex_text_replay )
2011-02-24 04:14:30 +01:00
return ;
}
else
{
if ( sess - > text_scrollback ! = SET_ON )
return ;
}
2016-01-31 21:13:18 +01:00
if ( ! sess - > scrollfile )
{
if ( ( buf = scrollback_get_filename ( sess ) ) = = NULL )
return ;
2011-02-24 04:14:30 +01:00
2016-01-31 21:13:18 +01:00
sess - > scrollfile = g_file_new_for_path ( buf ) ;
g_free ( buf ) ;
}
stream = G_INPUT_STREAM ( g_file_read ( sess - > scrollfile , NULL , NULL ) ) ;
if ( ! stream )
2011-02-24 04:14:30 +01:00
return ;
2016-01-31 21:13:18 +01:00
istream = g_data_input_stream_new ( stream ) ;
/*
* This is to avoid any issues moving between windows / unix
* but the docs mention an invalid \ r without a following \ n
* can lock up the program . . . ( Our write ( ) always adds \ n )
*/
g_data_input_stream_set_newline_type ( istream , G_DATA_STREAM_NEWLINE_TYPE_ANY ) ;
g_object_unref ( stream ) ;
2012-11-03 18:24:25 +01:00
while ( 1 )
2011-12-11 17:34:02 +01:00
{
2016-01-31 21:13:18 +01:00
GError * err = NULL ;
2012-11-04 05:32:52 +01:00
gsize n_bytes ;
2011-12-11 17:34:02 +01:00
2016-01-31 21:13:18 +01:00
buf = g_data_input_stream_read_line_utf8 ( istream , & n_bytes , NULL , & err ) ;
2011-12-11 17:34:02 +01:00
2016-01-31 21:13:18 +01:00
if ( ! err & & buf )
{
2014-03-29 22:27:23 +01:00
/*
* Some scrollback lines have three blanks after the timestamp and a newline
* Some have only one blank and a newline
* Some don ' t even have a timestamp
* Some don ' t have any text at all
*/
2016-04-26 18:05:57 +02:00
if ( buf [ 0 ] = = ' T ' & & buf [ 1 ] = = ' ' )
2011-02-24 04:14:30 +01:00
{
2012-11-03 18:24:25 +01:00
if ( sizeof ( time_t ) = = 4 )
2016-04-26 18:05:57 +02:00
stamp = strtoul ( buf + 2 , NULL , 10 ) ;
2012-11-03 18:24:25 +01:00
else
2014-12-18 00:49:59 +01:00
stamp = g_ascii_strtoull ( buf + 2 , NULL , 10 ) ; /* in case time_t is 64 bits */
2016-04-26 18:05:57 +02:00
if ( G_UNLIKELY ( stamp = = 0 ) )
{
g_warning ( " Invalid timestamp in scrollback file " ) ;
continue ;
}
2012-11-03 18:24:25 +01:00
text = strchr ( buf + 3 , ' ' ) ;
2014-03-29 22:27:23 +01:00
if ( text & & text [ 1 ] )
2012-07-19 09:14:34 +02:00
{
2012-11-03 18:24:25 +01:00
if ( prefs . hex_text_stripcolor_replay )
{
text = strip_color ( text + 1 , - 1 , STRIP_COLOR ) ;
}
2013-03-15 22:00:55 +01:00
2013-10-09 00:59:36 +02:00
fe_print_text ( sess , text , stamp , TRUE ) ;
2013-03-15 22:00:55 +01:00
2012-10-22 15:55:43 +02:00
if ( prefs . hex_text_stripcolor_replay )
2012-07-20 18:45:12 +02:00
{
g_free ( text ) ;
}
2012-07-19 10:34:48 +02:00
}
2014-03-29 22:27:23 +01:00
else
{
fe_print_text ( sess , " " , stamp , TRUE ) ;
}
2011-02-24 04:14:30 +01:00
}
2014-03-29 22:27:23 +01:00
else
{
if ( strlen ( buf ) )
fe_print_text ( sess , buf , 0 , TRUE ) ;
else
fe_print_text ( sess , " " , 0 , TRUE ) ;
}
lines + + ;
2012-11-03 18:24:25 +01:00
g_free ( buf ) ;
2011-02-24 04:14:30 +01:00
}
2016-01-31 21:13:18 +01:00
else if ( err )
{
/* If its only an encoding error it may be specific to the line */
if ( g_error_matches ( err , G_CONVERT_ERROR , G_CONVERT_ERROR_ILLEGAL_SEQUENCE ) )
{
2016-04-26 18:05:57 +02:00
g_warning ( " Invalid utf8 in scrollback file " ) ;
2016-01-31 21:13:18 +01:00
g_clear_error ( & err ) ;
continue ;
}
2012-11-03 18:24:25 +01:00
2016-01-31 21:13:18 +01:00
/* For general errors just give up */
g_clear_error ( & err ) ;
2012-11-03 18:24:25 +01:00
break ;
2016-01-31 21:13:18 +01:00
}
else /* No new line */
{
break ;
}
2011-02-24 04:14:30 +01:00
}
2016-01-31 21:13:18 +01:00
g_object_unref ( istream ) ;
2012-11-03 18:24:25 +01:00
2011-02-24 04:14:30 +01:00
sess - > scrollwritten = lines ;
if ( lines )
{
text = ctime ( & stamp ) ;
2016-04-26 18:05:57 +02:00
buf = g_strdup_printf ( " \n * \t %s %s \n " , _ ( " Loaded log from " ) , text ) ;
2013-10-09 00:59:36 +02:00
fe_print_text ( sess , buf , 0 , TRUE ) ;
2012-11-03 18:24:25 +01:00
g_free ( buf ) ;
2011-02-24 04:14:30 +01:00
/*EMIT_SIGNAL (XP_TE_GENMSG, sess, "*", buf, NULL, NULL, NULL, 0);*/
}
}
void
log_close ( session * sess )
{
char obuf [ 512 ] ;
time_t currenttime ;
if ( sess - > logfd ! = - 1 )
{
currenttime = time ( NULL ) ;
write ( sess - > logfd , obuf ,
2014-12-18 00:49:59 +01:00
g_snprintf ( obuf , sizeof ( obuf ) - 1 , _ ( " **** ENDING LOGGING AT %s \n " ) ,
2011-02-24 04:14:30 +01:00
ctime ( & currenttime ) ) ) ;
close ( sess - > logfd ) ;
sess - > logfd = - 1 ;
}
}
2016-01-31 21:13:18 +01:00
/*
* filename should be in utf8 encoding and will be
* converted to filesystem encoding automatically .
*/
2011-02-24 04:14:30 +01:00
static void
2012-11-03 18:24:25 +01:00
mkdir_p ( char * filename )
2011-02-24 04:14:30 +01:00
{
2016-01-31 21:13:18 +01:00
char * dirname , * dirname_fs ;
GError * err = NULL ;
dirname = g_path_get_dirname ( filename ) ;
dirname_fs = g_filename_from_utf8 ( dirname , - 1 , NULL , NULL , & err ) ;
if ( ! dirname_fs )
{
g_warning ( " %s " , err - > message ) ;
g_error_free ( err ) ;
g_free ( dirname ) ;
return ;
}
2011-02-24 04:14:30 +01:00
2016-01-31 21:13:18 +01:00
g_mkdir_with_parents ( dirname_fs , 0700 ) ;
2011-02-24 04:14:30 +01:00
2012-11-03 18:24:25 +01:00
g_free ( dirname ) ;
2016-01-31 21:13:18 +01:00
g_free ( dirname_fs ) ;
2011-02-24 04:14:30 +01:00
}
static char *
log_create_filename ( char * channame )
{
char * tmp , * ret ;
int mbl ;
2014-12-28 12:37:25 +01:00
ret = tmp = g_strdup ( channame ) ;
2011-02-24 04:14:30 +01:00
while ( * tmp )
{
mbl = g_utf8_skip [ ( ( unsigned char * ) tmp ) [ 0 ] ] ;
if ( mbl = = 1 )
{
# ifndef WIN32
* tmp = rfc_tolower ( * tmp ) ;
if ( * tmp = = ' / ' )
# else
/* win32 can't handle filenames with \|/><:"*? characters */
if ( * tmp = = ' \\ ' | | * tmp = = ' | ' | | * tmp = = ' / ' | |
* tmp = = ' > ' | | * tmp = = ' < ' | | * tmp = = ' : ' | |
* tmp = = ' \" ' | | * tmp = = ' * ' | | * tmp = = ' ? ' )
# endif
* tmp = ' _ ' ;
}
tmp + = mbl ;
}
return ret ;
}
/* like strcpy, but % turns into %% */
static char *
log_escape_strcpy ( char * dest , char * src , char * end )
{
while ( * src )
{
* dest = * src ;
if ( dest + 1 = = end )
break ;
dest + + ;
src + + ;
if ( * src = = ' % ' )
{
if ( dest + 1 = = end )
break ;
dest [ 0 ] = ' % ' ;
dest + + ;
}
}
dest [ 0 ] = 0 ;
return dest - 1 ;
}
/* substitutes %c %n %s into buffer */
static void
log_insert_vars ( char * buf , int bufsize , char * fmt , char * c , char * n , char * s )
{
char * end = buf + bufsize ;
while ( 1 )
{
switch ( fmt [ 0 ] )
{
case 0 :
buf [ 0 ] = 0 ;
return ;
case ' % ' :
fmt + + ;
switch ( fmt [ 0 ] )
{
case ' c ' :
buf = log_escape_strcpy ( buf , c , end ) ;
break ;
case ' n ' :
buf = log_escape_strcpy ( buf , n , end ) ;
break ;
case ' s ' :
buf = log_escape_strcpy ( buf , s , end ) ;
break ;
default :
buf [ 0 ] = ' % ' ;
buf + + ;
buf [ 0 ] = fmt [ 0 ] ;
break ;
}
break ;
default :
buf [ 0 ] = fmt [ 0 ] ;
}
fmt + + ;
buf + + ;
/* doesn't fit? */
if ( buf = = end )
{
buf [ - 1 ] = 0 ;
return ;
}
}
}
static char *
log_create_pathname ( char * servname , char * channame , char * netname )
{
char fname [ 384 ] ;
char fnametime [ 384 ] ;
time_t now ;
if ( ! netname )
2012-10-03 23:18:10 +02:00
{
2014-12-28 12:37:25 +01:00
netname = g_strdup ( " NETWORK " ) ;
2013-09-15 13:40:36 +02:00
}
else
{
netname = log_create_filename ( netname ) ;
2012-10-03 23:18:10 +02:00
}
2011-02-24 04:14:30 +01:00
/* first, everything is in UTF-8 */
if ( ! rfc_casecmp ( channame , servname ) )
2012-10-03 23:18:10 +02:00
{
2014-12-28 12:37:25 +01:00
channame = g_strdup ( " server " ) ;
2012-10-03 23:18:10 +02:00
}
2011-02-24 04:14:30 +01:00
else
2012-10-03 23:18:10 +02:00
{
2011-02-24 04:14:30 +01:00
channame = log_create_filename ( channame ) ;
2012-10-03 23:18:10 +02:00
}
2016-01-28 22:22:04 +01:00
servname = log_create_filename ( servname ) ;
2012-10-22 14:50:36 +02:00
log_insert_vars ( fname , sizeof ( fname ) , prefs . hex_irc_logmask , channame , netname , servname ) ;
2014-12-28 12:37:25 +01:00
g_free ( channame ) ;
g_free ( netname ) ;
2016-01-28 22:22:04 +01:00
g_free ( servname ) ;
2011-02-24 04:14:30 +01:00
/* insert time/date */
now = time ( NULL ) ;
2014-04-21 10:39:28 +02:00
strftime_utf8 ( fnametime , sizeof ( fnametime ) , fname , now ) ;
2011-02-24 04:14:30 +01:00
2015-02-28 13:23:11 +01:00
/* If one uses log mask variables, such as "%c/...", %c will be empty upon
* connecting since there ' s no channel name yet , so we have to make sure
* we won ' t try to write to the FS root . */
if ( g_path_is_absolute ( prefs . hex_irc_logmask ) )
2012-09-22 22:03:42 +02:00
{
2014-12-18 00:49:59 +01:00
g_snprintf ( fname , sizeof ( fname ) , " %s " , fnametime ) ;
2012-09-22 22:03:42 +02:00
}
else /* relative path */
{
2014-12-18 00:49:59 +01:00
g_snprintf ( fname , sizeof ( fname ) , " %s " G_DIR_SEPARATOR_S " logs " G_DIR_SEPARATOR_S " %s " , get_xdir ( ) , fnametime ) ;
2012-09-22 22:03:42 +02:00
}
2011-02-24 04:14:30 +01:00
/* create all the subdirectories */
2012-11-03 18:24:25 +01:00
mkdir_p ( fname ) ;
2011-02-24 04:14:30 +01:00
2014-12-09 06:24:59 +01:00
return g_strdup ( fname ) ;
2011-02-24 04:14:30 +01:00
}
static int
log_open_file ( char * servname , char * channame , char * netname )
{
char buf [ 512 ] ;
int fd ;
char * file ;
time_t currenttime ;
file = log_create_pathname ( servname , channame , netname ) ;
if ( ! file )
return - 1 ;
2015-02-28 12:42:20 +01:00
fd = g_open ( file , O_CREAT | O_APPEND | O_WRONLY | OFLAGS , 0644 ) ;
2011-02-24 04:14:30 +01:00
g_free ( file ) ;
if ( fd = = - 1 )
return - 1 ;
currenttime = time ( NULL ) ;
write ( fd , buf ,
2014-12-18 00:49:59 +01:00
g_snprintf ( buf , sizeof ( buf ) , _ ( " **** BEGIN LOGGING AT %s \n " ) ,
2011-02-24 04:14:30 +01:00
ctime ( & currenttime ) ) ) ;
return fd ;
}
static void
log_open ( session * sess )
{
static gboolean log_error = FALSE ;
log_close ( sess ) ;
sess - > logfd = log_open_file ( sess - > server - > servername , sess - > channel ,
server_get_network ( sess - > server , FALSE ) ) ;
if ( ! log_error & & sess - > logfd = = - 1 )
{
2014-12-09 06:25:00 +01:00
char * filename = log_create_pathname ( sess - > server - > servername , sess - > channel , server_get_network ( sess - > server , FALSE ) ) ;
char * message = g_strdup_printf ( _ ( " * Can't open log file(s) for writing. Check the \n permissions on %s " ) , filename ) ;
2011-02-24 04:14:30 +01:00
2014-12-09 06:25:00 +01:00
g_free ( filename ) ;
2012-10-03 23:18:10 +02:00
fe_message ( message , FE_MSG_WAIT | FE_MSG_ERROR ) ;
2012-11-03 18:24:25 +01:00
g_free ( message ) ;
2014-12-09 06:25:00 +01:00
2011-02-24 04:14:30 +01:00
log_error = TRUE ;
}
}
void
log_open_or_close ( session * sess )
{
if ( sess - > text_logging = = SET_DEFAULT )
{
2012-10-22 14:50:36 +02:00
if ( prefs . hex_irc_logging )
2011-02-24 04:14:30 +01:00
log_open ( sess ) ;
else
log_close ( sess ) ;
}
else
{
if ( sess - > text_logging )
log_open ( sess ) ;
else
log_close ( sess ) ;
}
}
int
get_stamp_str ( char * fmt , time_t tim , char * * ret )
{
char dest [ 128 ] ;
2015-01-18 11:10:04 +01:00
gsize len_locale ;
gsize len_utf8 ;
2011-02-24 04:14:30 +01:00
2015-01-18 11:10:04 +01:00
/* strftime requires the format string to be in locale encoding. */
fmt = g_locale_from_utf8 ( fmt , - 1 , NULL , NULL , NULL ) ;
2011-02-24 04:14:30 +01:00
2015-01-18 11:10:04 +01:00
len_locale = strftime_validated ( dest , sizeof ( dest ) , fmt , localtime ( & tim ) ) ;
g_free ( fmt ) ;
2011-02-24 04:14:30 +01:00
2015-01-18 11:10:04 +01:00
if ( len_locale = = 0 )
2011-02-24 04:14:30 +01:00
{
2015-01-18 11:10:04 +01:00
return 0 ;
2011-02-24 04:14:30 +01:00
}
2015-01-18 11:10:04 +01:00
* ret = g_locale_to_utf8 ( dest , len_locale , NULL , & len_utf8 , NULL ) ;
if ( * ret = = NULL )
{
return 0 ;
}
2011-02-24 04:14:30 +01:00
2015-01-18 11:10:04 +01:00
return len_utf8 ;
2011-02-24 04:14:30 +01:00
}
static void
2014-05-07 02:10:34 +02:00
log_write ( session * sess , char * text , time_t ts )
2011-02-24 04:14:30 +01:00
{
char * temp ;
char * stamp ;
char * file ;
int len ;
if ( sess - > text_logging = = SET_DEFAULT )
{
2012-10-22 14:50:36 +02:00
if ( ! prefs . hex_irc_logging )
2011-02-24 04:14:30 +01:00
return ;
}
else
{
if ( sess - > text_logging ! = SET_ON )
return ;
}
if ( sess - > logfd = = - 1 )
2015-01-15 01:29:03 +01:00
{
2011-02-24 04:14:30 +01:00
log_open ( sess ) ;
2015-01-15 01:29:03 +01:00
}
2011-02-24 04:14:30 +01:00
/* change to a different log file? */
2014-12-09 06:24:59 +01:00
file = log_create_pathname ( sess - > server - > servername , sess - > channel , server_get_network ( sess - > server , FALSE ) ) ;
2011-02-24 04:14:30 +01:00
if ( file )
{
2012-11-04 18:27:15 +01:00
if ( g_access ( file , F_OK ) ! = 0 )
2011-02-24 04:14:30 +01:00
{
2015-01-15 01:29:03 +01:00
if ( sess - > logfd ! = - 1 )
{
close ( sess - > logfd ) ;
}
2014-12-09 06:24:59 +01:00
sess - > logfd = log_open_file ( sess - > server - > servername , sess - > channel , server_get_network ( sess - > server , FALSE ) ) ;
2011-02-24 04:14:30 +01:00
}
2015-01-15 01:29:03 +01:00
2011-02-24 04:14:30 +01:00
g_free ( file ) ;
}
2015-01-15 01:29:03 +01:00
if ( sess - > logfd = = - 1 )
{
return ;
}
2012-10-22 15:55:43 +02:00
if ( prefs . hex_stamp_log )
2011-02-24 04:14:30 +01:00
{
2014-05-07 02:10:34 +02:00
if ( ! ts ) ts = time ( 0 ) ;
len = get_stamp_str ( prefs . hex_stamp_log_format , ts , & stamp ) ;
2011-02-24 04:14:30 +01:00
if ( len )
{
write ( sess - > logfd , stamp , len ) ;
g_free ( stamp ) ;
}
}
2015-01-15 01:29:03 +01:00
2011-02-24 04:14:30 +01:00
temp = strip_color ( text , - 1 , STRIP_ALL ) ;
len = strlen ( temp ) ;
write ( sess - > logfd , temp , len ) ;
/* lots of scripts/plugins print without a \n at the end */
if ( temp [ len - 1 ] ! = ' \n ' )
write ( sess - > logfd , " \n " , 1 ) ; /* emulate what xtext would display */
g_free ( temp ) ;
}
2015-01-18 11:10:04 +01:00
/**
2015-01-31 09:52:31 +01:00
* Converts a given string using the given iconv converter . This is similar to g_convert_with_fallback , except that it is tolerant of sequences in
2015-01-18 11:10:04 +01:00
* the original input that are invalid even in from_encoding . g_convert_with_fallback fails for such text , whereas this function replaces such a
* sequence with the fallback string .
*
* If len is - 1 , strlen ( text ) is used to calculate the length . Do not pass - 1 if text is supposed to contain \ 0 bytes , such as if from_encoding is a
* multi - byte encoding like UTF - 16.
*/
2015-01-31 09:52:31 +01:00
gchar *
text_convert_invalid ( const gchar * text , gssize len , GIConv converter , const gchar * fallback , gsize * len_out )
2011-02-24 04:14:30 +01:00
{
2015-01-18 11:10:04 +01:00
gchar * result_part ;
gsize result_part_len ;
const gchar * end ;
gsize invalid_start_pos ;
GString * result ;
const gchar * current_start ;
2011-02-24 04:14:30 +01:00
if ( len = = - 1 )
2015-01-18 11:10:04 +01:00
{
2011-02-24 04:14:30 +01:00
len = strlen ( text ) ;
2015-01-18 11:10:04 +01:00
}
2011-02-24 04:14:30 +01:00
2015-01-18 11:10:04 +01:00
end = text + len ;
2011-02-24 04:14:30 +01:00
2015-01-18 11:10:04 +01:00
/* Find the first position of an invalid sequence. */
2015-01-31 09:52:31 +01:00
result_part = g_convert_with_iconv ( text , len , converter , & invalid_start_pos , & result_part_len , NULL ) ;
2016-06-24 21:09:35 +02:00
g_iconv ( converter , NULL , NULL , NULL , NULL ) ;
2015-01-18 11:10:04 +01:00
if ( result_part ! = NULL )
2011-02-24 04:14:30 +01:00
{
2015-01-18 11:10:04 +01:00
/* All text converted successfully on the first try. Return it. */
if ( len_out ! = NULL )
2011-02-24 04:14:30 +01:00
{
2015-01-18 11:10:04 +01:00
* len_out = result_part_len ;
2011-02-24 04:14:30 +01:00
}
2015-01-18 11:10:04 +01:00
return result_part ;
}
/* One or more invalid sequences exist that need to be replaced with the fallback. */
result = g_string_sized_new ( len ) ;
current_start = text ;
for ( ; ; )
{
g_assert ( current_start + invalid_start_pos < end ) ;
2016-06-24 21:09:35 +02:00
/* Convert everything before the position of the invalid sequence. It should be successful.
* But iconv may not convert everything till invalid_start_pos since the last few bytes may be part of a shift sequence .
* So get the new bytes_read and use it as the actual invalid_start_pos to handle this .
*
* See https : //github.com/hexchat/hexchat/issues/1758
*/
2015-01-31 09:52:31 +01:00
result_part = g_convert_with_iconv ( current_start , invalid_start_pos , converter , & invalid_start_pos , & result_part_len , NULL ) ;
2016-06-24 21:09:35 +02:00
g_iconv ( converter , NULL , NULL , NULL , NULL ) ;
2015-01-18 11:10:04 +01:00
g_assert ( result_part ! = NULL ) ;
g_string_append_len ( result , result_part , result_part_len ) ;
g_free ( result_part ) ;
/* Append the fallback */
g_string_append ( result , fallback ) ;
/* Now try converting everything after the invalid sequence. */
current_start + = invalid_start_pos + 1 ;
2015-01-31 09:52:31 +01:00
result_part = g_convert_with_iconv ( current_start , end - current_start , converter , & invalid_start_pos , & result_part_len , NULL ) ;
2016-06-24 21:09:35 +02:00
g_iconv ( converter , NULL , NULL , NULL , NULL ) ;
2015-01-18 11:10:04 +01:00
if ( result_part ! = NULL )
2011-02-24 04:14:30 +01:00
{
2015-01-18 11:10:04 +01:00
/* The rest of the text converted successfully. Append it and return the whole converted text. */
g_string_append_len ( result , result_part , result_part_len ) ;
g_free ( result_part ) ;
if ( len_out ! = NULL )
2011-02-24 04:14:30 +01:00
{
2015-01-18 11:10:04 +01:00
* len_out = result - > len ;
2011-02-24 04:14:30 +01:00
}
2015-01-18 11:10:04 +01:00
return g_string_free ( result , FALSE ) ;
2011-02-24 04:14:30 +01:00
}
2015-01-18 11:10:04 +01:00
/* The rest of the text didn't convert successfully. invalid_start_pos has the position of the next invalid sequence. */
}
2011-02-24 04:14:30 +01:00
}
2015-01-31 09:52:31 +01:00
/**
* Replaces any invalid UTF - 8 in the given text with the unicode replacement character .
*/
2015-01-18 11:10:04 +01:00
gchar *
2015-01-31 09:52:31 +01:00
text_fixup_invalid_utf8 ( const gchar * text , gssize len , gsize * len_out )
2011-02-24 04:14:30 +01:00
{
2015-01-31 09:52:31 +01:00
static GIConv utf8_fixup_converter = NULL ;
if ( utf8_fixup_converter = = NULL )
{
utf8_fixup_converter = g_iconv_open ( " UTF-8 " , " UTF-8 " ) ;
}
2011-02-24 04:14:30 +01:00
2015-01-31 09:52:31 +01:00
return text_convert_invalid ( text , len , utf8_fixup_converter , unicode_fallback_string , len_out ) ;
2011-02-24 04:14:30 +01:00
}
2013-06-23 20:53:41 +02:00
void
2013-06-22 01:13:36 +02:00
PrintTextTimeStamp ( session * sess , char * text , time_t timestamp )
2011-02-24 04:14:30 +01:00
{
if ( ! sess )
{
if ( ! sess_list )
return ;
sess = ( session * ) sess_list - > data ;
}
/* make sure it's valid utf8 */
2015-01-18 11:10:04 +01:00
if ( text [ 0 ] = = ' \0 ' )
2011-02-24 04:14:30 +01:00
{
2015-01-18 11:10:04 +01:00
text = g_strdup ( " \n " ) ;
2014-12-09 06:24:59 +01:00
}
else
2011-02-24 04:14:30 +01:00
{
2015-01-31 09:52:31 +01:00
text = text_fixup_invalid_utf8 ( text , - 1 , NULL ) ;
2011-02-24 04:14:30 +01:00
}
2014-05-07 02:10:34 +02:00
log_write ( sess , text , timestamp ) ;
2015-03-02 19:58:27 +01:00
scrollback_save ( sess , text , timestamp ) ;
2013-10-09 00:59:36 +02:00
fe_print_text ( sess , text , timestamp , FALSE ) ;
2015-01-18 11:10:04 +01:00
g_free ( text ) ;
2011-02-24 04:14:30 +01:00
}
2013-06-22 01:13:36 +02:00
void
PrintText ( session * sess , char * text )
{
PrintTextTimeStamp ( sess , text , 0 ) ;
}
2011-02-24 04:14:30 +01:00
void
2014-07-18 03:25:41 +02:00
PrintTextf ( session * sess , const char * format , . . . )
2011-02-24 04:14:30 +01:00
{
va_list args ;
char * buf ;
va_start ( args , format ) ;
buf = g_strdup_vprintf ( format , args ) ;
va_end ( args ) ;
PrintText ( sess , buf ) ;
g_free ( buf ) ;
}
2013-06-23 00:48:17 +02:00
void
2014-07-18 03:25:41 +02:00
PrintTextTimeStampf ( session * sess , time_t timestamp , const char * format , . . . )
2013-06-23 00:48:17 +02:00
{
va_list args ;
char * buf ;
va_start ( args , format ) ;
buf = g_strdup_vprintf ( format , args ) ;
va_end ( args ) ;
PrintTextTimeStamp ( sess , buf , timestamp ) ;
g_free ( buf ) ;
}
2011-02-24 04:14:30 +01:00
/* Print Events stuff here --AGL */
/* Consider the following a NOTES file:
The main upshot of this is :
* Plugins and Perl scripts ( when I get round to signaling perl . c ) can intercept text events and do what they like
* The default text engine can be config ' ed
By default it should appear * exactly * the same ( I ' m working hard not to change the default style ) but if you go into Settings - > Edit Event Texts you can change the text ' s . The format is thus :
The normal % Cx ( color ) and % B ( bold ) etc work
$ x is replaced with the data in var x ( e . g . $ 1 is often the nick )
$ axxx is replace with a single byte of value xxx ( in base 10 )
AGL ( 990507 )
*/
/* These lists are thus:
pntevts_text [ ] are the strings the user sees ( WITH % x etc )
pntevts [ ] are the data strings with \ 000 etc
*/
/* To add a new event:
Think up a name ( like " Join " )
Make up a pevt_name_help struct
Add an entry to textevents . in
Type : make textevents
*/
/* Internals:
On startup ~ / . xchat / printevents . conf is loaded if it doesn ' t exist the
defaults are loaded . Any missing events are filled from defaults .
Each event is parsed by pevt_build_string and a binary output is produced
which looks like :
( byte ) value : 0 = {
( int ) numbers of bytes
( char [ ] ) that number of byte to be memcpy ' ed into the buffer
}
1 =
( byte ) number of varable to insert
2 = end of buffer
Each XP_TE_ * signal is hard coded to call text_emit which calls
display_event which decodes the data
2014-12-18 00:49:59 +01:00
This means that this system * should be faster * than g_snprintf because
2011-02-24 04:14:30 +01:00
it always ' knows ' that format of the string ( basically is preparses much
of the work )
- - AGL
*/
char * pntevts_text [ NUM_XP ] ;
char * pntevts [ NUM_XP ] ;
# define pevt_generic_none_help NULL
static char * const pevt_genmsg_help [ ] = {
N_ ( " Left message " ) ,
N_ ( " Right message " ) ,
} ;
2012-11-03 21:04:07 +01:00
#if 0
2012-11-03 19:04:47 +01:00
static char * const pevt_identd_help [ ] = {
N_ ( " IP address " ) ,
N_ ( " Username " )
} ;
2012-11-03 21:04:07 +01:00
# endif
2012-11-03 19:04:47 +01:00
2011-02-24 04:14:30 +01:00
static char * const pevt_join_help [ ] = {
N_ ( " The nick of the joining person " ) ,
N_ ( " The channel being joined " ) ,
N_ ( " The host of the person " ) ,
2014-04-06 21:22:51 +02:00
N_ ( " The account of the person " ) ,
2011-02-24 04:14:30 +01:00
} ;
static char * const pevt_chanaction_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " The action " ) ,
N_ ( " Mode char " ) ,
N_ ( " Identified text " ) ,
} ;
static char * const pevt_chanmsg_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " The text " ) ,
N_ ( " Mode char " ) ,
N_ ( " Identified text " ) ,
} ;
static char * const pevt_privmsg_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " The message " ) ,
N_ ( " Identified text " )
} ;
2012-11-03 07:46:51 +01:00
static char * const pevt_capack_help [ ] = {
N_ ( " Server Name " ) ,
2012-11-03 18:29:08 +01:00
N_ ( " Acknowledged Capabilities " )
2012-11-03 07:46:51 +01:00
} ;
2016-01-28 22:02:04 +01:00
static char * const pevt_capdel_help [ ] = {
N_ ( " Server Name " ) ,
N_ ( " Removed Capabilities " )
} ;
2012-11-03 07:46:51 +01:00
static char * const pevt_caplist_help [ ] = {
N_ ( " Server Name " ) ,
N_ ( " Server Capabilities " )
} ;
static char * const pevt_capreq_help [ ] = {
2012-11-03 18:29:08 +01:00
N_ ( " Requested Capabilities " )
2012-11-03 07:46:51 +01:00
} ;
2011-02-24 04:14:30 +01:00
static char * const pevt_changenick_help [ ] = {
N_ ( " Old nickname " ) ,
N_ ( " New nickname " ) ,
} ;
static char * const pevt_newtopic_help [ ] = {
N_ ( " Nick of person who changed the topic " ) ,
N_ ( " Topic " ) ,
N_ ( " Channel " ) ,
} ;
static char * const pevt_topic_help [ ] = {
N_ ( " Channel " ) ,
N_ ( " Topic " ) ,
} ;
static char * const pevt_kick_help [ ] = {
N_ ( " The nickname of the kicker " ) ,
N_ ( " The person being kicked " ) ,
N_ ( " The channel " ) ,
N_ ( " The reason " ) ,
} ;
static char * const pevt_part_help [ ] = {
N_ ( " The nick of the person leaving " ) ,
N_ ( " The host of the person " ) ,
N_ ( " The channel " ) ,
} ;
static char * const pevt_chandate_help [ ] = {
N_ ( " The channel " ) ,
N_ ( " The time " ) ,
} ;
static char * const pevt_topicdate_help [ ] = {
N_ ( " The channel " ) ,
N_ ( " The creator " ) ,
N_ ( " The time " ) ,
} ;
static char * const pevt_quit_help [ ] = {
N_ ( " Nick " ) ,
N_ ( " Reason " ) ,
N_ ( " Host " ) ,
} ;
static char * const pevt_pingrep_help [ ] = {
N_ ( " Who it's from " ) ,
N_ ( " The time in x.x format (see below) " ) ,
} ;
static char * const pevt_notice_help [ ] = {
N_ ( " Who it's from " ) ,
N_ ( " The message " ) ,
} ;
static char * const pevt_channotice_help [ ] = {
N_ ( " Who it's from " ) ,
N_ ( " The Channel it's going to " ) ,
N_ ( " The message " ) ,
} ;
static char * const pevt_uchangenick_help [ ] = {
N_ ( " Old nickname " ) ,
N_ ( " New nickname " ) ,
} ;
static char * const pevt_ukick_help [ ] = {
N_ ( " The person being kicked " ) ,
N_ ( " The channel " ) ,
N_ ( " The nickname of the kicker " ) ,
N_ ( " The reason " ) ,
} ;
static char * const pevt_partreason_help [ ] = {
N_ ( " The nick of the person leaving " ) ,
N_ ( " The host of the person " ) ,
N_ ( " The channel " ) ,
N_ ( " The reason " ) ,
} ;
static char * const pevt_ctcpsnd_help [ ] = {
N_ ( " The sound " ) ,
N_ ( " The nick of the person " ) ,
N_ ( " The channel " ) ,
} ;
static char * const pevt_ctcpgen_help [ ] = {
N_ ( " The CTCP event " ) ,
N_ ( " The nick of the person " ) ,
} ;
static char * const pevt_ctcpgenc_help [ ] = {
N_ ( " The CTCP event " ) ,
N_ ( " The nick of the person " ) ,
N_ ( " The Channel it's going to " ) ,
} ;
static char * const pevt_chansetkey_help [ ] = {
N_ ( " The nick of the person who set the key " ) ,
N_ ( " The key " ) ,
} ;
static char * const pevt_chansetlimit_help [ ] = {
N_ ( " The nick of the person who set the limit " ) ,
N_ ( " The limit " ) ,
} ;
static char * const pevt_chanop_help [ ] = {
N_ ( " The nick of the person who did the op'ing " ) ,
N_ ( " The nick of the person who has been op'ed " ) ,
} ;
static char * const pevt_chanhop_help [ ] = {
N_ ( " The nick of the person who has been halfop'ed " ) ,
N_ ( " The nick of the person who did the halfop'ing " ) ,
} ;
static char * const pevt_chanvoice_help [ ] = {
N_ ( " The nick of the person who did the voice'ing " ) ,
N_ ( " The nick of the person who has been voice'ed " ) ,
} ;
static char * const pevt_chanban_help [ ] = {
N_ ( " The nick of the person who did the banning " ) ,
N_ ( " The ban mask " ) ,
} ;
2013-04-07 23:49:30 +02:00
static char * const pevt_chanquiet_help [ ] = {
N_ ( " The nick of the person who did the quieting " ) ,
N_ ( " The quiet mask " ) ,
} ;
2011-02-24 04:14:30 +01:00
static char * const pevt_chanrmkey_help [ ] = {
N_ ( " The nick who removed the key " ) ,
} ;
static char * const pevt_chanrmlimit_help [ ] = {
N_ ( " The nick who removed the limit " ) ,
} ;
static char * const pevt_chandeop_help [ ] = {
2015-04-04 10:29:39 +02:00
N_ ( " The nick of the person who did the deop'ing " ) ,
2011-02-24 04:14:30 +01:00
N_ ( " The nick of the person who has been deop'ed " ) ,
} ;
static char * const pevt_chandehop_help [ ] = {
2015-04-04 10:29:39 +02:00
N_ ( " The nick of the person who did the dehalfop'ing " ) ,
2011-02-24 04:14:30 +01:00
N_ ( " The nick of the person who has been dehalfop'ed " ) ,
} ;
static char * const pevt_chandevoice_help [ ] = {
2015-04-04 10:29:39 +02:00
N_ ( " The nick of the person who did the devoice'ing " ) ,
2011-02-24 04:14:30 +01:00
N_ ( " The nick of the person who has been devoice'ed " ) ,
} ;
static char * const pevt_chanunban_help [ ] = {
2015-04-04 10:29:39 +02:00
N_ ( " The nick of the person who did the unban'ing " ) ,
2011-02-24 04:14:30 +01:00
N_ ( " The ban mask " ) ,
} ;
2013-04-07 23:49:30 +02:00
static char * const pevt_chanunquiet_help [ ] = {
2015-04-04 10:29:39 +02:00
N_ ( " The nick of the person who did the unquiet'ing " ) ,
2013-04-07 23:49:30 +02:00
N_ ( " The quiet mask " ) ,
} ;
2011-02-24 04:14:30 +01:00
static char * const pevt_chanexempt_help [ ] = {
N_ ( " The nick of the person who did the exempt " ) ,
N_ ( " The exempt mask " ) ,
} ;
static char * const pevt_chanrmexempt_help [ ] = {
N_ ( " The nick of the person removed the exempt " ) ,
N_ ( " The exempt mask " ) ,
} ;
static char * const pevt_chaninvite_help [ ] = {
N_ ( " The nick of the person who did the invite " ) ,
N_ ( " The invite mask " ) ,
} ;
static char * const pevt_chanrminvite_help [ ] = {
N_ ( " The nick of the person removed the invite " ) ,
N_ ( " The invite mask " ) ,
} ;
static char * const pevt_chanmodegen_help [ ] = {
N_ ( " The nick of the person setting the mode " ) ,
N_ ( " The mode's sign (+/-) " ) ,
N_ ( " The mode letter " ) ,
N_ ( " The channel it's being set on " ) ,
} ;
static char * const pevt_whois1_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Username " ) ,
N_ ( " Host " ) ,
N_ ( " Full name " ) ,
} ;
static char * const pevt_whois2_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Channel Membership/ \" is an IRC operator \" " ) ,
} ;
static char * const pevt_whois3_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Server Information " ) ,
} ;
static char * const pevt_whois4_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Idle time " ) ,
} ;
static char * const pevt_whois4t_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Idle time " ) ,
N_ ( " Signon time " ) ,
} ;
static char * const pevt_whois5_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Away reason " ) ,
} ;
static char * const pevt_whois6_help [ ] = {
N_ ( " Nickname " ) ,
} ;
static char * const pevt_whoisid_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Message " ) ,
" Numeric "
} ;
static char * const pevt_whoisauth_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Message " ) ,
N_ ( " Account " ) ,
} ;
static char * const pevt_whoisrealhost_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Real user@host " ) ,
N_ ( " Real IP " ) ,
N_ ( " Message " ) ,
} ;
static char * const pevt_generic_channel_help [ ] = {
N_ ( " Channel Name " ) ,
} ;
2012-11-03 04:45:32 +01:00
static char * const pevt_saslauth_help [ ] = {
2013-09-02 20:24:37 +02:00
N_ ( " Username " ) ,
N_ ( " Mechanism " )
2012-11-03 04:45:32 +01:00
} ;
static char * const pevt_saslresponse_help [ ] = {
N_ ( " Server Name " ) ,
N_ ( " Raw Numeric or Identifier " ) ,
N_ ( " Username " ) ,
N_ ( " Message " )
} ;
2011-02-24 04:14:30 +01:00
static char * const pevt_servertext_help [ ] = {
N_ ( " Text " ) ,
N_ ( " Server Name " ) ,
N_ ( " Raw Numeric or Identifier " )
} ;
static char * const pevt_sslmessage_help [ ] = {
N_ ( " Text " ) ,
N_ ( " Server Name " )
} ;
static char * const pevt_invited_help [ ] = {
N_ ( " Channel Name " ) ,
N_ ( " Nick of person who invited you " ) ,
N_ ( " Server Name " ) ,
} ;
static char * const pevt_usersonchan_help [ ] = {
N_ ( " Channel Name " ) ,
N_ ( " Users " ) ,
} ;
static char * const pevt_nickclash_help [ ] = {
N_ ( " Nickname in use " ) ,
N_ ( " Nick being tried " ) ,
} ;
static char * const pevt_connfail_help [ ] = {
N_ ( " Error " ) ,
} ;
static char * const pevt_connect_help [ ] = {
N_ ( " Host " ) ,
N_ ( " IP " ) ,
N_ ( " Port " ) ,
} ;
static char * const pevt_sconnect_help [ ] = {
" PID "
} ;
static char * const pevt_generic_nick_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Server Name " ) ,
N_ ( " Network " )
} ;
static char * const pevt_chanmodes_help [ ] = {
N_ ( " Channel Name " ) ,
N_ ( " Modes string " ) ,
} ;
2013-08-07 12:14:30 +02:00
static char * const pevt_chanurl_help [ ] = {
N_ ( " Channel Name " ) ,
N_ ( " URL " ) ,
} ;
2011-02-24 04:14:30 +01:00
static char * const pevt_rawmodes_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Modes string " ) ,
} ;
static char * const pevt_kill_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Reason " ) ,
} ;
static char * const pevt_dccchaterr_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " IP address " ) ,
N_ ( " Port " ) ,
N_ ( " Error " ) ,
} ;
static char * const pevt_dccstall_help [ ] = {
N_ ( " DCC Type " ) ,
N_ ( " Filename " ) ,
N_ ( " Nickname " ) ,
} ;
static char * const pevt_generic_file_help [ ] = {
N_ ( " Filename " ) ,
N_ ( " Error " ) ,
} ;
static char * const pevt_dccrecverr_help [ ] = {
N_ ( " Filename " ) ,
N_ ( " Destination filename " ) ,
N_ ( " Nickname " ) ,
N_ ( " Error " ) ,
} ;
static char * const pevt_dccrecvcomp_help [ ] = {
N_ ( " Filename " ) ,
N_ ( " Destination filename " ) ,
N_ ( " Nickname " ) ,
N_ ( " CPS " ) ,
} ;
static char * const pevt_dccconfail_help [ ] = {
N_ ( " DCC Type " ) ,
N_ ( " Nickname " ) ,
N_ ( " Error " ) ,
} ;
static char * const pevt_dccchatcon_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " IP address " ) ,
} ;
static char * const pevt_dcccon_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " IP address " ) ,
N_ ( " Filename " ) ,
} ;
static char * const pevt_dccsendfail_help [ ] = {
N_ ( " Filename " ) ,
N_ ( " Nickname " ) ,
N_ ( " Error " ) ,
} ;
static char * const pevt_dccsendcomp_help [ ] = {
N_ ( " Filename " ) ,
N_ ( " Nickname " ) ,
N_ ( " CPS " ) ,
} ;
static char * const pevt_dccoffer_help [ ] = {
N_ ( " Filename " ) ,
N_ ( " Nickname " ) ,
N_ ( " Pathname " ) ,
} ;
static char * const pevt_dccfileabort_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Filename " )
} ;
static char * const pevt_dccchatabort_help [ ] = {
N_ ( " Nickname " ) ,
} ;
static char * const pevt_dccresumeoffer_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Filename " ) ,
N_ ( " Position " ) ,
} ;
static char * const pevt_dccsendoffer_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Filename " ) ,
N_ ( " Size " ) ,
N_ ( " IP address " ) ,
} ;
static char * const pevt_dccgenericoffer_help [ ] = {
N_ ( " DCC String " ) ,
N_ ( " Nickname " ) ,
} ;
2013-04-08 02:41:29 +02:00
static char * const pevt_notifyaway_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Away Reason " ) ,
} ;
2011-02-24 04:14:30 +01:00
static char * const pevt_notifynumber_help [ ] = {
N_ ( " Number of notify items " ) ,
} ;
static char * const pevt_serverlookup_help [ ] = {
N_ ( " Server Name " ) ,
} ;
static char * const pevt_servererror_help [ ] = {
N_ ( " Text " ) ,
} ;
static char * const pevt_foundip_help [ ] = {
N_ ( " IP " ) ,
} ;
static char * const pevt_dccrename_help [ ] = {
N_ ( " Old Filename " ) ,
N_ ( " New Filename " ) ,
} ;
static char * const pevt_ctcpsend_help [ ] = {
N_ ( " Receiver " ) ,
N_ ( " Message " ) ,
} ;
static char * const pevt_ignoreaddremove_help [ ] = {
N_ ( " Hostmask " ) ,
} ;
static char * const pevt_resolvinguser_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " Hostname " ) ,
} ;
static char * const pevt_malformed_help [ ] = {
N_ ( " Nickname " ) ,
N_ ( " The Packet " ) ,
} ;
static char * const pevt_pingtimeout_help [ ] = {
N_ ( " Seconds " ) ,
} ;
static char * const pevt_uinvite_help [ ] = {
N_ ( " Nick of person who have been invited " ) ,
N_ ( " Channel Name " ) ,
N_ ( " Server Name " ) ,
} ;
static char * const pevt_banlist_help [ ] = {
N_ ( " Channel " ) ,
N_ ( " Banmask " ) ,
N_ ( " Who set the ban " ) ,
N_ ( " Ban time " ) ,
} ;
static char * const pevt_discon_help [ ] = {
N_ ( " Error " ) ,
} ;
# include "textevents.h"
static void
2016-02-18 14:57:22 +01:00
pevent_load_defaults ( void )
2011-02-24 04:14:30 +01:00
{
int i ;
for ( i = 0 ; i < NUM_XP ; i + + )
{
2014-12-28 12:37:25 +01:00
g_free ( pntevts_text [ i ] ) ;
2011-02-24 04:14:30 +01:00
/* make-te.c sets this 128 flag (DON'T call gettext() flag) */
if ( te [ i ] . num_args & 128 )
2014-12-28 12:37:25 +01:00
pntevts_text [ i ] = g_strdup ( te [ i ] . def ) ;
2011-02-24 04:14:30 +01:00
else
2014-12-28 12:37:25 +01:00
pntevts_text [ i ] = g_strdup ( _ ( te [ i ] . def ) ) ;
2011-02-24 04:14:30 +01:00
}
}
void
2016-02-18 14:57:22 +01:00
pevent_make_pntevts ( void )
2011-02-24 04:14:30 +01:00
{
int i , m ;
char out [ 1024 ] ;
for ( i = 0 ; i < NUM_XP ; i + + )
{
2014-12-28 12:37:25 +01:00
g_free ( pntevts [ i ] ) ;
2011-02-24 04:14:30 +01:00
if ( pevt_build_string ( pntevts_text [ i ] , & ( pntevts [ i ] ) , & m ) ! = 0 )
{
2014-12-18 00:49:59 +01:00
g_snprintf ( out , sizeof ( out ) ,
2011-02-24 04:14:30 +01:00
_ ( " Error parsing event %s. \n Loading default. " ) , te [ i ] . name ) ;
fe_message ( out , FE_MSG_WARN ) ;
2014-12-28 12:37:25 +01:00
g_free ( pntevts_text [ i ] ) ;
2011-02-24 04:14:30 +01:00
/* make-te.c sets this 128 flag (DON'T call gettext() flag) */
if ( te [ i ] . num_args & 128 )
2014-12-28 12:37:25 +01:00
pntevts_text [ i ] = g_strdup ( te [ i ] . def ) ;
2011-02-24 04:14:30 +01:00
else
2014-12-28 12:37:25 +01:00
pntevts_text [ i ] = g_strdup ( _ ( te [ i ] . def ) ) ;
2011-02-24 04:14:30 +01:00
if ( pevt_build_string ( pntevts_text [ i ] , & ( pntevts [ i ] ) , & m ) ! = 0 )
{
fprintf ( stderr ,
2012-11-11 13:51:50 +01:00
" HexChat CRITICAL *** default event text failed to build! \n " ) ;
2011-02-24 04:14:30 +01:00
abort ( ) ;
}
}
}
}
/* Loading happens at 2 levels:
1 ) File is read into blocks
2 ) Pe block is parsed and loaded
- - AGL */
/* Better hope you pass good args.. --AGL */
static void
pevent_trigger_load ( int * i_penum , char * * i_text , char * * i_snd )
{
2014-12-28 12:37:25 +01:00
int penum = * i_penum ;
2011-02-24 04:14:30 +01:00
char * text = * i_text , * snd = * i_snd ;
if ( penum ! = - 1 & & text ! = NULL )
{
2014-12-28 12:37:25 +01:00
g_free ( pntevts_text [ penum ] ) ;
pntevts_text [ penum ] = g_strdup ( text ) ;
2011-02-24 04:14:30 +01:00
}
2014-12-28 12:37:25 +01:00
g_free ( text ) ;
g_free ( snd ) ;
2011-02-24 04:14:30 +01:00
* i_text = NULL ;
* i_snd = NULL ;
* i_penum = 0 ;
}
static int
pevent_find ( char * name , int * i_i )
{
int i = * i_i , j ;
j = i + 1 ;
while ( 1 )
{
if ( j = = NUM_XP )
j = 0 ;
if ( strcmp ( te [ j ] . name , name ) = = 0 )
{
* i_i = j ;
return j ;
}
if ( j = = i )
return - 1 ;
j + + ;
}
}
int
pevent_load ( char * filename )
{
/* AGL, I've changed this file and pevent_save, could you please take a look at
* the changes and possibly modify them to suit you
* //David H
*/
char * buf , * ibuf ;
int fd , i = 0 , pnt = 0 ;
struct stat st ;
char * text = NULL , * snd = NULL ;
int penum = 0 ;
char * ofs ;
if ( filename = = NULL )
2012-10-30 11:35:39 +01:00
fd = hexchat_open_file ( " pevents.conf " , O_RDONLY , 0 , 0 ) ;
2011-02-24 04:14:30 +01:00
else
2012-10-30 11:35:39 +01:00
fd = hexchat_open_file ( filename , O_RDONLY , 0 , XOF_FULLPATH ) ;
2011-02-24 04:14:30 +01:00
if ( fd = = - 1 )
return 1 ;
if ( fstat ( fd , & st ) ! = 0 )
2014-02-12 16:28:53 +01:00
{
close ( fd ) ;
2011-02-24 04:14:30 +01:00
return 1 ;
2014-02-12 16:28:53 +01:00
}
2014-12-28 12:37:25 +01:00
ibuf = g_malloc ( st . st_size ) ;
2011-02-24 04:14:30 +01:00
read ( fd , ibuf , st . st_size ) ;
close ( fd ) ;
while ( buf_get_line ( ibuf , & buf , & pnt , st . st_size ) )
{
if ( buf [ 0 ] = = ' # ' )
continue ;
if ( strlen ( buf ) = = 0 )
continue ;
ofs = strchr ( buf , ' = ' ) ;
if ( ! ofs )
continue ;
* ofs = 0 ;
ofs + + ;
if ( strcmp ( buf , " event_name " ) = = 0 )
{
if ( penum > = 0 )
pevent_trigger_load ( & penum , & text , & snd ) ;
penum = pevent_find ( ofs , & i ) ;
continue ;
} else if ( strcmp ( buf , " event_text " ) = = 0 )
{
2014-12-28 12:37:25 +01:00
g_free ( text ) ;
text = g_strdup ( ofs ) ;
2011-02-24 04:14:30 +01:00
continue ;
2014-12-28 12:37:25 +01:00
}
2011-02-24 04:14:30 +01:00
continue ;
}
pevent_trigger_load ( & penum , & text , & snd ) ;
2014-12-28 12:37:25 +01:00
g_free ( ibuf ) ;
2011-02-24 04:14:30 +01:00
return 0 ;
}
static void
2016-02-18 14:57:22 +01:00
pevent_check_all_loaded ( void )
2011-02-24 04:14:30 +01:00
{
int i ;
for ( i = 0 ; i < NUM_XP ; i + + )
{
if ( pntevts_text [ i ] = = NULL )
{
/*printf ("%s\n", te[i].name);
2014-12-18 00:49:59 +01:00
g_snprintf ( out , sizeof ( out ) , " The data for event %s failed to load. Reverting to defaults. \n This may be because a new version of HexChat is loading an old config file. \n \n Check all print event texts are correct " , evtnames [ i ] ) ;
2011-02-24 04:14:30 +01:00
gtkutil_simpledialog ( out ) ; */
/* make-te.c sets this 128 flag (DON'T call gettext() flag) */
if ( te [ i ] . num_args & 128 )
2014-12-28 12:37:25 +01:00
pntevts_text [ i ] = g_strdup ( te [ i ] . def ) ;
2011-02-24 04:14:30 +01:00
else
2014-12-28 12:37:25 +01:00
pntevts_text [ i ] = g_strdup ( _ ( te [ i ] . def ) ) ;
2011-02-24 04:14:30 +01:00
}
}
}
void
load_text_events ( )
{
memset ( & pntevts_text , 0 , sizeof ( char * ) * ( NUM_XP ) ) ;
memset ( & pntevts , 0 , sizeof ( char * ) * ( NUM_XP ) ) ;
if ( pevent_load ( NULL ) )
pevent_load_defaults ( ) ;
pevent_check_all_loaded ( ) ;
pevent_make_pntevts ( ) ;
}
/*
CL : format_event now handles filtering of arguments :
2012-10-22 15:55:43 +02:00
1 ) if prefs . hex_text_stripcolor_msg is set , filter all style control codes from arguments
2011-02-24 04:14:30 +01:00
2 ) always strip \ 010 ( ATTR_HIDDEN ) from arguments : it is only for use in the format string itself
*/
# define ARG_FLAG(argn) (1 << (argn))
void
2014-12-15 19:25:28 +01:00
format_event ( session * sess , int index , char * * args , char * o , gsize sizeofo , unsigned int stripcolor_args )
2011-02-24 04:14:30 +01:00
{
2014-12-15 19:25:28 +01:00
int len , ii , numargs ;
gsize oi ;
2011-02-24 04:14:30 +01:00
char * i , * ar , d , a , done_all = FALSE ;
i = pntevts [ index ] ;
numargs = te [ index ] . num_args & 0x7f ;
oi = ii = len = d = a = 0 ;
o [ 0 ] = 0 ;
if ( i = = NULL )
return ;
while ( done_all = = FALSE )
{
d = i [ ii + + ] ;
switch ( d )
{
case 0 :
memcpy ( & len , & ( i [ ii ] ) , sizeof ( int ) ) ;
ii + = sizeof ( int ) ;
if ( oi + len > sizeofo )
{
printf ( " Overflow in display_event (%s) \n " , i ) ;
o [ 0 ] = 0 ;
return ;
}
memcpy ( & ( o [ oi ] ) , & ( i [ ii ] ) , len ) ;
oi + = len ;
ii + = len ;
break ;
case 1 :
a = i [ ii + + ] ;
if ( a > numargs )
{
fprintf ( stderr ,
2012-11-11 13:51:50 +01:00
" HexChat DEBUG: display_event: arg > numargs (%d %d %s) \n " ,
2011-02-24 04:14:30 +01:00
a , numargs , i ) ;
break ;
}
ar = args [ ( int ) a + 1 ] ;
if ( ar = = NULL )
{
printf ( " arg[%d] is NULL in print event \n " , a + 1 ) ;
} else
{
2013-03-23 21:08:49 +01:00
if ( strlen ( ar ) > sizeofo - oi - 4 )
ar [ sizeofo - oi - 4 ] = 0 ; /* Avoid buffer overflow */
2011-02-24 04:14:30 +01:00
if ( stripcolor_args & ARG_FLAG ( a + 1 ) ) len = strip_color2 ( ar , - 1 , & o [ oi ] , STRIP_ALL ) ;
else len = strip_hidden_attribute ( ar , & o [ oi ] ) ;
oi + = len ;
}
break ;
case 2 :
o [ oi + + ] = ' \n ' ;
o [ oi + + ] = 0 ;
done_all = TRUE ;
continue ;
case 3 :
2014-12-15 19:25:28 +01:00
if ( prefs . hex_text_indent )
o [ oi + + ] = ' \t ' ;
else
o [ oi + + ] = ' ' ;
2011-02-24 04:14:30 +01:00
break ;
}
}
o [ oi ] = 0 ;
if ( * o = = ' \n ' )
o [ 0 ] = 0 ;
}
static void
2013-06-22 01:13:36 +02:00
display_event ( session * sess , int event , char * * args ,
2013-06-23 20:53:41 +02:00
unsigned int stripcolor_args , time_t timestamp )
2011-02-24 04:14:30 +01:00
{
char o [ 4096 ] ;
format_event ( sess , event , args , o , sizeof ( o ) , stripcolor_args ) ;
if ( o [ 0 ] )
2013-06-22 01:13:36 +02:00
PrintTextTimeStamp ( sess , o , timestamp ) ;
2011-02-24 04:14:30 +01:00
}
int
pevt_build_string ( const char * input , char * * output , int * max_arg )
{
struct pevt_stage1 * s = NULL , * base = NULL , * last = NULL , * next ;
int clen ;
char o [ 4096 ] , d , * obuf , * i ;
int oi , ii , max = - 1 , len , x ;
len = strlen ( input ) ;
2014-12-28 12:37:25 +01:00
i = g_malloc ( len + 1 ) ;
2011-02-24 04:14:30 +01:00
memcpy ( i , input , len + 1 ) ;
check_special_chars ( i , TRUE ) ;
len = strlen ( i ) ;
clen = oi = ii = 0 ;
for ( ; ; )
{
if ( ii = = len )
break ;
d = i [ ii + + ] ;
if ( d ! = ' $ ' )
{
o [ oi + + ] = d ;
continue ;
}
if ( i [ ii ] = = ' $ ' )
{
o [ oi + + ] = ' $ ' ;
continue ;
}
if ( oi > 0 )
{
2014-12-28 12:37:25 +01:00
s = g_new ( struct pevt_stage1 , 1 ) ;
2011-02-24 04:14:30 +01:00
if ( base = = NULL )
base = s ;
if ( last ! = NULL )
last - > next = s ;
last = s ;
s - > next = NULL ;
2014-12-28 12:37:25 +01:00
s - > data = g_malloc ( oi + sizeof ( int ) + 1 ) ;
2011-02-24 04:14:30 +01:00
s - > len = oi + sizeof ( int ) + 1 ;
clen + = oi + sizeof ( int ) + 1 ;
s - > data [ 0 ] = 0 ;
memcpy ( & ( s - > data [ 1 ] ) , & oi , sizeof ( int ) ) ;
memcpy ( & ( s - > data [ 1 + sizeof ( int ) ] ) , o , oi ) ;
oi = 0 ;
}
if ( ii = = len )
{
fe_message ( " String ends with a $ " , FE_MSG_WARN ) ;
2014-12-28 12:37:25 +01:00
goto err ;
2011-02-24 04:14:30 +01:00
}
d = i [ ii + + ] ;
if ( d = = ' a ' )
2014-12-28 12:37:25 +01:00
{
/* Hex value */
2011-02-24 04:14:30 +01:00
if ( ii = = len )
goto a_len_error ;
d = i [ ii + + ] ;
d - = ' 0 ' ;
x = d * 100 ;
if ( ii = = len )
goto a_len_error ;
d = i [ ii + + ] ;
d - = ' 0 ' ;
x + = d * 10 ;
if ( ii = = len )
goto a_len_error ;
d = i [ ii + + ] ;
d - = ' 0 ' ;
x + = d ;
if ( x > 255 )
goto a_range_error ;
o [ oi + + ] = x ;
continue ;
2014-12-28 12:37:25 +01:00
a_len_error :
2011-02-24 04:14:30 +01:00
fe_message ( " String ends in $a " , FE_MSG_WARN ) ;
2014-12-28 12:37:25 +01:00
goto err ;
a_range_error :
2011-02-24 04:14:30 +01:00
fe_message ( " $a value is greater than 255 " , FE_MSG_WARN ) ;
2014-12-28 12:37:25 +01:00
goto err ;
2011-02-24 04:14:30 +01:00
}
if ( d = = ' t ' )
{
/* Tab - if tabnicks is set then write '\t' else ' ' */
2014-12-28 12:37:25 +01:00
s = g_new ( struct pevt_stage1 , 1 ) ;
2011-02-24 04:14:30 +01:00
if ( base = = NULL )
base = s ;
if ( last ! = NULL )
last - > next = s ;
last = s ;
s - > next = NULL ;
2014-12-28 12:37:25 +01:00
s - > data = g_malloc ( 1 ) ;
2011-02-24 04:14:30 +01:00
s - > len = 1 ;
clen + = 1 ;
s - > data [ 0 ] = 3 ;
continue ;
}
if ( d < ' 1 ' | | d > ' 9 ' )
{
2014-12-18 00:49:59 +01:00
g_snprintf ( o , sizeof ( o ) , " Error, invalid argument $%c \n " , d ) ;
2011-02-24 04:14:30 +01:00
fe_message ( o , FE_MSG_WARN ) ;
2014-12-28 12:37:25 +01:00
goto err ;
2011-02-24 04:14:30 +01:00
}
d - = ' 0 ' ;
if ( max < d )
max = d ;
2014-12-28 12:37:25 +01:00
s = g_new ( struct pevt_stage1 , 1 ) ;
2011-02-24 04:14:30 +01:00
if ( base = = NULL )
base = s ;
if ( last ! = NULL )
last - > next = s ;
last = s ;
s - > next = NULL ;
2014-12-28 12:37:25 +01:00
s - > data = g_malloc ( 2 ) ;
2011-02-24 04:14:30 +01:00
s - > len = 2 ;
clen + = 2 ;
s - > data [ 0 ] = 1 ;
s - > data [ 1 ] = d - 1 ;
}
if ( oi > 0 )
{
2014-12-28 12:37:25 +01:00
s = g_new ( struct pevt_stage1 , 1 ) ;
2011-02-24 04:14:30 +01:00
if ( base = = NULL )
base = s ;
if ( last ! = NULL )
last - > next = s ;
last = s ;
s - > next = NULL ;
2014-12-28 12:37:25 +01:00
s - > data = g_malloc ( oi + sizeof ( int ) + 1 ) ;
2011-02-24 04:14:30 +01:00
s - > len = oi + sizeof ( int ) + 1 ;
clen + = oi + sizeof ( int ) + 1 ;
s - > data [ 0 ] = 0 ;
memcpy ( & ( s - > data [ 1 ] ) , & oi , sizeof ( int ) ) ;
memcpy ( & ( s - > data [ 1 + sizeof ( int ) ] ) , o , oi ) ;
oi = 0 ;
}
2014-12-28 12:37:25 +01:00
s = g_new ( struct pevt_stage1 , 1 ) ;
2011-02-24 04:14:30 +01:00
if ( base = = NULL )
base = s ;
if ( last ! = NULL )
last - > next = s ;
s - > next = NULL ;
2014-12-28 12:37:25 +01:00
s - > data = g_malloc ( 1 ) ;
2011-02-24 04:14:30 +01:00
s - > len = 1 ;
clen + = 1 ;
s - > data [ 0 ] = 2 ;
oi = 0 ;
s = base ;
2014-12-28 12:37:25 +01:00
obuf = g_malloc ( clen ) ;
2011-02-24 04:14:30 +01:00
while ( s )
{
next = s - > next ;
memcpy ( & obuf [ oi ] , s - > data , s - > len ) ;
oi + = s - > len ;
2014-12-28 12:37:25 +01:00
g_free ( s - > data ) ;
g_free ( s ) ;
2011-02-24 04:14:30 +01:00
s = next ;
}
2014-12-28 12:37:25 +01:00
g_free ( i ) ;
2011-02-24 04:14:30 +01:00
if ( max_arg )
* max_arg = max ;
if ( output )
* output = obuf ;
2014-12-11 18:13:57 +01:00
else
2014-12-28 12:37:25 +01:00
g_free ( obuf ) ;
2011-02-24 04:14:30 +01:00
return 0 ;
2014-12-28 12:37:25 +01:00
err :
while ( s )
{
next = s - > next ;
g_free ( s - > data ) ;
g_free ( s ) ;
s = next ;
}
g_free ( i ) ;
return 1 ;
2011-02-24 04:14:30 +01:00
}
/* black n white(0/1) are bad colors for nicks, and we'll use color 2 for us */
/* also light/dark gray (14/15) */
2013-04-28 00:56:11 +02:00
/* 5,7,8 are all shades of yellow which happen to look damn near the same */
2011-02-24 04:14:30 +01:00
static char rcolors [ ] = { 19 , 20 , 22 , 24 , 25 , 26 , 27 , 28 , 29 } ;
2013-03-09 23:57:06 +01:00
int
text_color_of ( char * name )
2011-02-24 04:14:30 +01:00
{
int i = 0 , sum = 0 ;
while ( name [ i ] )
sum + = name [ i + + ] ;
sum % = sizeof ( rcolors ) / sizeof ( char ) ;
return rcolors [ sum ] ;
}
/* called by EMIT_SIGNAL macro */
void
2013-06-22 01:13:36 +02:00
text_emit ( int index , session * sess , char * a , char * b , char * c , char * d ,
2013-06-23 20:53:41 +02:00
time_t timestamp )
2011-02-24 04:14:30 +01:00
{
char * word [ PDIWORDS ] ;
int i ;
2013-06-06 23:47:01 +02:00
unsigned int stripcolor_args = ( chanopt_is_set ( prefs . hex_text_stripcolor_msg , sess - > text_strip ) ? 0xFFFFFFFF : 0 ) ;
2011-02-24 04:14:30 +01:00
char tbuf [ NICKLEN + 4 ] ;
2012-10-22 15:55:43 +02:00
if ( prefs . hex_text_color_nicks & & ( index = = XP_TE_CHANACTION | | index = = XP_TE_CHANMSG ) )
2011-02-24 04:14:30 +01:00
{
2014-12-18 00:49:59 +01:00
g_snprintf ( tbuf , sizeof ( tbuf ) , " \003 %d%s " , text_color_of ( a ) , a ) ;
2011-02-24 04:14:30 +01:00
a = tbuf ;
stripcolor_args & = ~ ARG_FLAG ( 1 ) ; /* don't strip color from this argument */
}
word [ 0 ] = te [ index ] . name ;
word [ 1 ] = ( a ? a : " \000 " ) ;
word [ 2 ] = ( b ? b : " \000 " ) ;
word [ 3 ] = ( c ? c : " \000 " ) ;
word [ 4 ] = ( d ? d : " \000 " ) ;
for ( i = 5 ; i < PDIWORDS ; i + + )
word [ i ] = " \000 " ;
2013-11-28 01:59:31 +01:00
if ( plugin_emit_print ( sess , word , timestamp ) )
2011-02-24 04:14:30 +01:00
return ;
/* If a plugin's callback executes "/close", 'sess' may be invalid */
if ( ! is_session ( sess ) )
return ;
switch ( index )
{
case XP_TE_JOIN :
case XP_TE_PART :
case XP_TE_PARTREASON :
case XP_TE_QUIT :
/* implement ConfMode / Hide Join and Part Messages */
2012-10-22 14:50:36 +02:00
if ( chanopt_is_set ( prefs . hex_irc_conf_mode , sess - > text_hidejoinpart ) )
2011-02-24 04:14:30 +01:00
return ;
break ;
/* ===Private message=== */
case XP_TE_PRIVMSG :
case XP_TE_DPRIVMSG :
case XP_TE_PRIVACTION :
case XP_TE_DPRIVACTION :
2013-04-08 19:19:55 +02:00
if ( chanopt_is_set ( prefs . hex_input_beep_priv , sess - > alert_beep ) & & ( ! prefs . hex_away_omit_alerts | | ! sess - > server - > is_away ) )
2011-02-24 04:14:30 +01:00
sound_beep ( sess ) ;
2013-04-08 19:19:55 +02:00
if ( chanopt_is_set ( prefs . hex_input_flash_priv , sess - > alert_taskbar ) & & ( ! prefs . hex_away_omit_alerts | | ! sess - > server - > is_away ) )
2011-02-24 04:14:30 +01:00
fe_flash_window ( sess ) ;
/* why is this one different? because of plugin-tray.c's hooks! ugly */
if ( sess - > alert_tray = = SET_ON )
fe_tray_set_icon ( FE_ICON_MESSAGE ) ;
break ;
/* ===Highlighted message=== */
case XP_TE_HCHANACTION :
case XP_TE_HCHANMSG :
2013-04-08 19:19:55 +02:00
if ( chanopt_is_set ( prefs . hex_input_beep_hilight , sess - > alert_beep ) & & ( ! prefs . hex_away_omit_alerts | | ! sess - > server - > is_away ) )
2011-02-24 04:14:30 +01:00
sound_beep ( sess ) ;
2013-04-08 19:19:55 +02:00
if ( chanopt_is_set ( prefs . hex_input_flash_hilight , sess - > alert_taskbar ) & & ( ! prefs . hex_away_omit_alerts | | ! sess - > server - > is_away ) )
2011-02-24 04:14:30 +01:00
fe_flash_window ( sess ) ;
if ( sess - > alert_tray = = SET_ON )
fe_tray_set_icon ( FE_ICON_MESSAGE ) ;
break ;
/* ===Channel message=== */
case XP_TE_CHANACTION :
case XP_TE_CHANMSG :
2013-04-08 19:19:55 +02:00
if ( chanopt_is_set ( prefs . hex_input_beep_chans , sess - > alert_beep ) & & ( ! prefs . hex_away_omit_alerts | | ! sess - > server - > is_away ) )
2011-02-24 04:14:30 +01:00
sound_beep ( sess ) ;
2013-04-08 19:19:55 +02:00
if ( chanopt_is_set ( prefs . hex_input_flash_chans , sess - > alert_taskbar ) & & ( ! prefs . hex_away_omit_alerts | | ! sess - > server - > is_away ) )
2011-02-24 04:14:30 +01:00
fe_flash_window ( sess ) ;
if ( sess - > alert_tray = = SET_ON )
fe_tray_set_icon ( FE_ICON_MESSAGE ) ;
break ;
2014-05-06 19:22:09 +02:00
/* ===Nick change message=== */
case XP_TE_CHANGENICK :
if ( prefs . hex_irc_hide_nickchange )
return ;
break ;
2011-02-24 04:14:30 +01:00
}
sound_play_event ( index ) ;
2013-06-22 01:13:36 +02:00
display_event ( sess , index , word , stripcolor_args , timestamp ) ;
2011-02-24 04:14:30 +01:00
}
char *
text_find_format_string ( char * name )
{
int i = 0 ;
i = pevent_find ( name , & i ) ;
if ( i > = 0 )
return pntevts_text [ i ] ;
return NULL ;
}
int
2013-07-12 02:33:35 +02:00
text_emit_by_name ( char * name , session * sess , time_t timestamp ,
char * a , char * b , char * c , char * d )
2011-02-24 04:14:30 +01:00
{
int i = 0 ;
i = pevent_find ( name , & i ) ;
if ( i > = 0 )
{
2013-07-12 02:33:35 +02:00
text_emit ( i , sess , a , b , c , d , timestamp ) ;
2011-02-24 04:14:30 +01:00
return 1 ;
}
return 0 ;
}
void
pevent_save ( char * fn )
{
int fd , i ;
char buf [ 1024 ] ;
if ( ! fn )
2012-10-30 11:35:39 +01:00
fd = hexchat_open_file ( " pevents.conf " , O_CREAT | O_TRUNC | O_WRONLY ,
2011-02-24 04:14:30 +01:00
0x180 , XOF_DOMODE ) ;
else
2012-10-30 11:35:39 +01:00
fd = hexchat_open_file ( fn , O_CREAT | O_TRUNC | O_WRONLY , 0x180 ,
2011-02-24 04:14:30 +01:00
XOF_FULLPATH | XOF_DOMODE ) ;
if ( fd = = - 1 )
{
/*
fe_message ( " Error opening config file \n " , FALSE ) ;
If we get here when X - Chat is closing the fe - message causes a nice & hard crash
so we have to use perror which doesn ' t rely on GTK
*/
perror ( " Error opening config file \n " ) ;
return ;
}
for ( i = 0 ; i < NUM_XP ; i + + )
{
2014-12-18 00:49:59 +01:00
write ( fd , buf , g_snprintf ( buf , sizeof ( buf ) ,
2011-02-24 04:14:30 +01:00
" event_name=%s \n " , te [ i ] . name ) ) ;
2014-12-18 00:49:59 +01:00
write ( fd , buf , g_snprintf ( buf , sizeof ( buf ) ,
2011-02-24 04:14:30 +01:00
" event_text=%s \n \n " , pntevts_text [ i ] ) ) ;
}
close ( fd ) ;
}
/* =========================== */
/* ========== SOUND ========== */
/* =========================== */
char * sound_files [ NUM_XP ] ;
void
sound_beep ( session * sess )
{
2015-05-28 20:13:34 +02:00
if ( ! prefs . hex_gui_focus_omitalerts | | fe_gui_info ( sess , 0 ) ! = 1 )
2013-04-23 06:45:37 +02:00
{
if ( sound_files [ XP_TE_BEEP ] & & sound_files [ XP_TE_BEEP ] [ 0 ] )
/* user defined beep _file_ */
sound_play_event ( XP_TE_BEEP ) ;
else
/* system beep */
fe_beep ( sess ) ;
}
2011-02-24 04:14:30 +01:00
}
void
2013-04-27 23:00:18 +02:00
sound_play ( const char * file , gboolean quiet )
2011-02-24 04:14:30 +01:00
{
2013-04-28 03:06:02 +02:00
char * buf ;
2012-11-03 18:24:25 +01:00
char * wavfile ;
2013-04-27 23:28:24 +02:00
# ifndef WIN32
2011-02-24 04:14:30 +01:00
char * cmd ;
2013-04-27 23:28:24 +02:00
# endif
2011-02-24 04:14:30 +01:00
/* the pevents GUI editor triggers this after removing a soundfile */
if ( ! file [ 0 ] )
2013-04-27 21:07:05 +02:00
{
2011-02-24 04:14:30 +01:00
return ;
2013-04-27 21:07:05 +02:00
}
2011-02-24 04:14:30 +01:00
2013-04-27 23:00:18 +02:00
/* check for fullpath */
2015-02-28 13:23:11 +01:00
if ( g_path_is_absolute ( file ) )
2011-02-24 04:14:30 +01:00
{
2012-11-03 18:24:25 +01:00
wavfile = g_strdup ( file ) ;
}
else
2011-02-24 04:14:30 +01:00
{
2013-04-28 04:12:00 +02:00
wavfile = g_build_filename ( get_xdir ( ) , HEXCHAT_SOUND_DIR , file , NULL ) ;
2011-02-24 04:14:30 +01:00
}
2012-11-03 18:24:25 +01:00
if ( g_access ( wavfile , R_OK ) = = 0 )
2011-02-24 04:14:30 +01:00
{
2013-04-27 21:07:05 +02:00
# ifdef WIN32
2014-09-13 09:23:44 +02:00
gunichar2 * wavfile_utf16 = g_utf8_to_utf16 ( wavfile , - 1 , NULL , NULL , NULL ) ;
if ( wavfile_utf16 ! = NULL )
{
PlaySoundW ( wavfile_utf16 , NULL , SND_NODEFAULT | SND_FILENAME | SND_ASYNC ) ;
g_free ( wavfile_utf16 ) ;
}
2013-04-27 21:07:05 +02:00
# else
2013-04-28 01:02:27 +02:00
# ifdef USE_LIBCANBERRA
2013-06-12 16:41:52 +02:00
if ( ca_con = = NULL )
{
ca_context_create ( & ca_con ) ;
ca_context_change_props ( ca_con ,
CA_PROP_APPLICATION_ID , " hexchat " ,
CA_PROP_APPLICATION_NAME , " HexChat " ,
CA_PROP_APPLICATION_ICON_NAME , " hexchat " , NULL ) ;
}
if ( ca_context_play ( ca_con , 0 , CA_PROP_MEDIA_FILENAME , wavfile , NULL ) ! = 0 )
2013-04-28 01:02:27 +02:00
# endif
2011-02-24 04:14:30 +01:00
{
2013-04-28 01:29:28 +02:00
cmd = g_find_program_in_path ( " play " ) ;
if ( cmd )
{
buf = g_strdup_printf ( " %s \" %s \" " , cmd , wavfile ) ;
hexchat_exec ( buf ) ;
g_free ( buf ) ;
g_free ( cmd ) ;
}
2012-11-03 18:24:25 +01:00
}
2011-02-24 04:14:30 +01:00
# endif
2012-11-03 18:24:25 +01:00
}
else
2011-02-24 04:14:30 +01:00
{
if ( ! quiet )
{
2012-11-03 18:24:25 +01:00
buf = g_strdup_printf ( _ ( " Cannot read sound file: \n %s " ) , wavfile ) ;
2011-02-24 04:14:30 +01:00
fe_message ( buf , FE_MSG_ERROR ) ;
2012-11-03 18:24:25 +01:00
g_free ( buf ) ;
2011-02-24 04:14:30 +01:00
}
}
2012-11-03 18:24:25 +01:00
g_free ( wavfile ) ;
2011-02-24 04:14:30 +01:00
}
void
sound_play_event ( int i )
{
if ( sound_files [ i ] )
2013-04-27 23:00:18 +02:00
{
sound_play ( sound_files [ i ] , FALSE ) ;
}
2011-02-24 04:14:30 +01:00
}
static void
sound_load_event ( char * evt , char * file )
{
int i = 0 ;
if ( file [ 0 ] & & pevent_find ( evt , & i ) ! = - 1 )
{
2014-12-28 12:37:25 +01:00
g_free ( sound_files [ i ] ) ;
sound_files [ i ] = g_strdup ( file ) ;
2011-02-24 04:14:30 +01:00
}
}
void
sound_load ( )
{
int fd ;
char buf [ 512 ] ;
char evt [ 128 ] ;
memset ( & sound_files , 0 , sizeof ( char * ) * ( NUM_XP ) ) ;
2012-10-30 11:35:39 +01:00
fd = hexchat_open_file ( " sound.conf " , O_RDONLY , 0 , 0 ) ;
2011-02-24 04:14:30 +01:00
if ( fd = = - 1 )
return ;
evt [ 0 ] = 0 ;
while ( waitline ( fd , buf , sizeof buf , FALSE ) ! = - 1 )
{
if ( strncmp ( buf , " event= " , 6 ) = = 0 )
{
safe_strcpy ( evt , buf + 6 , sizeof ( evt ) ) ;
}
else if ( strncmp ( buf , " sound= " , 6 ) = = 0 )
{
if ( evt [ 0 ] ! = 0 )
{
sound_load_event ( evt , buf + 6 ) ;
evt [ 0 ] = 0 ;
}
}
}
close ( fd ) ;
}
void
sound_save ( )
{
int fd , i ;
char buf [ 512 ] ;
2012-10-30 11:35:39 +01:00
fd = hexchat_open_file ( " sound.conf " , O_CREAT | O_TRUNC | O_WRONLY , 0x180 ,
2011-02-24 04:14:30 +01:00
XOF_DOMODE ) ;
if ( fd = = - 1 )
return ;
for ( i = 0 ; i < NUM_XP ; i + + )
{
if ( sound_files [ i ] & & sound_files [ i ] [ 0 ] )
{
2014-12-18 00:49:59 +01:00
write ( fd , buf , g_snprintf ( buf , sizeof ( buf ) ,
2011-02-24 04:14:30 +01:00
" event=%s \n " , te [ i ] . name ) ) ;
2014-12-18 00:49:59 +01:00
write ( fd , buf , g_snprintf ( buf , sizeof ( buf ) ,
2011-02-24 04:14:30 +01:00
" sound=%s \n \n " , sound_files [ i ] ) ) ;
}
}
close ( fd ) ;
}