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 11:36:54 -08: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
2026-01-05 23:12:38 -07:00
#include "zoitechat.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"
2026-01-05 23:12:38 -07:00
#include "zoitechatc.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-27 20:02:27 -03:00
#ifdef USE_LIBCANBERRA
#include <canberra.h>
#endif
2015-01-31 00:52:31 -08: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 10:41:52 -04:00
#ifdef USE_LIBCANBERRA
static ca_context * ca_con ;
#endif
2011-02-24 04:14:30 +01:00
2016-01-31 15:13:18 -05:00
#define SCROLLBACK_MAX 32000
2012-11-03 10:24:25 -07: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 10:24:25 -07:00
scrollback_get_filename ( session * sess )
2011-02-24 04:14:30 +01:00
{
2016-01-31 15:13:18 -05: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 ;
2016-09-11 15:49:06 -04:00
net = log_create_filename ( net );
2012-11-03 10:24:25 -07: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 10:24:25 -07:00
g_free ( buf );
2011-02-24 04:14:30 +01:00
chan = log_create_filename ( sess -> channel );
2014-03-24 12:46:02 -07: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 06:37:25 -05:00
g_free ( chan );
2016-09-11 15:49:06 -04:00
g_free ( net );
2011-02-24 04:14:30 +01:00
2016-01-31 15:13:18 -05: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 15:13:18 -05:00
return ret ;
2011-02-24 04:14:30 +01:00
}
void
scrollback_close ( session * sess )
{
2016-01-31 15:13:18 -05: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 15:13:18 -05:00
char * buf , * p ;
2013-01-19 12:33:16 -08:00
gsize len ;
2016-01-31 15:13:18 -05: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 15:13:18 -05: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 15:13:18 -05:00
offset = lines - max_lines ;
2011-02-24 04:14:30 +01:00
2016-01-31 15:13:18 -05: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 15:13:18 -05:00
lines = 0 ;
2011-02-24 04:14:30 +01:00
while ( p != buf + len )
{
if ( * p == '\n' )
{
2016-01-31 15:13:18 -05:00
lines ++ ;
if ( lines == offset )
2011-02-24 04:14:30 +01:00
{
p ++ ;
break ;
}
}
p ++ ;
}
2016-01-31 15:13:18 -05: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-08 21:24:59 -08: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 15:13:18 -05:00
GOutputStream * ostream ;
2012-11-03 10:24:25 -07:00
char * buf ;
2011-02-24 04:14:30 +01:00
2014-03-24 12:46:02 -07: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 15:13:18 -05:00
if ( ! sess -> scrollfile )
2011-02-24 04:14:30 +01:00
{
2012-11-03 10:24:25 -07:00
if (( buf = scrollback_get_filename ( sess )) == NULL )
2011-02-24 04:14:30 +01:00
return ;
2016-01-31 15:13:18 -05:00
sess -> scrollfile = g_file_new_for_path ( buf );
2012-11-03 10:24:25 -07:00
g_free ( buf );
2011-02-24 04:14:30 +01:00
}
2016-01-31 15:13:18 -05: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 09:11:41 -08:00
buf = g_strdup_printf ( "T %d " , ( int ) stamp );
2011-02-24 04:14:30 +01:00
else
2012-11-03 10:24:25 -07:00
buf = g_strdup_printf ( "T %" G_GINT64_FORMAT " " , ( gint64 ) stamp );
2011-02-24 04:14:30 +01:00
2016-01-31 15:13:18 -05: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 15:13:18 -05: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 15:13:18 -05:00
GInputStream * stream ;
GDataInputStream * istream ;
gchar * buf , * text ;
gint lines = 0 ;
2016-04-26 12:05:57 -04: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 15:13:18 -05:00
if ( ! sess -> scrollfile )
{
if (( buf = scrollback_get_filename ( sess )) == NULL )
return ;
2011-02-24 04:14:30 +01:00
2016-01-31 15:13:18 -05: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 15:13:18 -05: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 10:24:25 -07:00
while ( 1 )
2011-12-11 17:34:02 +01:00
{
2016-01-31 15:13:18 -05:00
GError * err = NULL ;
2012-11-03 21:32:52 -07:00
gsize n_bytes ;
2011-12-11 17:34:02 +01:00
2016-01-31 15:13:18 -05: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 15:13:18 -05:00
if ( ! err && buf )
{
2014-03-29 14:27:23 -07: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 12:05:57 -04:00
if ( buf [ 0 ] == 'T' && buf [ 1 ] == ' ' )
2011-02-24 04:14:30 +01:00
{
2012-11-03 10:24:25 -07:00
if ( sizeof ( time_t ) == 4 )
2016-04-26 12:05:57 -04:00
stamp = strtoul ( buf + 2 , NULL , 10 );
2012-11-03 10:24:25 -07:00
else
2014-12-17 18:49:59 -05:00
stamp = g_ascii_strtoull ( buf + 2 , NULL , 10 ); /* in case time_t is 64 bits */
2016-04-26 12:05:57 -04:00
if ( G_UNLIKELY ( stamp == 0 ))
{
g_warning ( "Invalid timestamp in scrollback file" );
continue ;
}
2012-11-03 10:24:25 -07:00
text = strchr ( buf + 3 , ' ' );
2014-03-29 14:27:23 -07:00
if ( text && text [ 1 ])
2012-07-19 09:14:34 +02:00
{
2012-11-03 10:24:25 -07: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-08 23:59:36 +01: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 14:27:23 -07:00
else
{
fe_print_text ( sess , " " , stamp , TRUE );
}
2011-02-24 04:14:30 +01:00
}
2014-03-29 14:27:23 -07:00
else
{
if ( strlen ( buf ))
fe_print_text ( sess , buf , 0 , TRUE );
else
fe_print_text ( sess , " " , 0 , TRUE );
}
lines ++ ;
2012-11-03 10:24:25 -07:00
g_free ( buf );
2011-02-24 04:14:30 +01:00
}
2016-01-31 15:13:18 -05: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 12:05:57 -04:00
g_warning ( "Invalid utf8 in scrollback file" );
2016-01-31 15:13:18 -05:00
g_clear_error ( & err );
continue ;
}
2012-11-03 10:24:25 -07:00
2016-01-31 15:13:18 -05:00
/* For general errors just give up */
g_clear_error ( & err );
2012-11-03 10:24:25 -07:00
break ;
2016-01-31 15:13:18 -05:00
}
else /* No new line */
{
break ;
}
2011-02-24 04:14:30 +01:00
}
2016-01-31 15:13:18 -05:00
g_object_unref ( istream );
2012-11-03 10:24:25 -07:00
2011-02-24 04:14:30 +01:00
sess -> scrollwritten = lines ;
if ( lines )
{
text = ctime ( & stamp );
2016-04-26 12:05:57 -04:00
buf = g_strdup_printf ( " \n * \t %s %s \n " , _ ( "Loaded log from" ), text );
2013-10-08 23:59:36 +01:00
fe_print_text ( sess , buf , 0 , TRUE );
2012-11-03 10:24:25 -07: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-17 18:49:59 -05: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 15:13:18 -05: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 10:24:25 -07:00
mkdir_p ( char * filename )
2011-02-24 04:14:30 +01:00
{
2016-01-31 15:13:18 -05: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 15:13:18 -05:00
g_mkdir_with_parents ( dirname_fs , 0700 );
2011-02-24 04:14:30 +01:00
2012-11-03 10:24:25 -07:00
g_free ( dirname );
2016-01-31 15:13:18 -05: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 06:37:25 -05: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 06:37:25 -05:00
netname = g_strdup ( "NETWORK" );
2013-09-15 07:40:36 -04: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 06:37:25 -05: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 16:22:04 -05: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 06:37:25 -05:00
g_free ( channame );
g_free ( netname );
2016-01-28 16:22:04 -05:00
g_free ( servname );
2011-02-24 04:14:30 +01:00
/* insert time/date */
now = time ( NULL );
2014-04-21 01:39:28 -07:00
strftime_utf8 ( fnametime , sizeof ( fnametime ), fname , now );
2011-02-24 04:14:30 +01:00
2015-02-28 07:23:11 -05: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-17 18:49:59 -05:00
g_snprintf ( fname , sizeof ( fname ), "%s" , fnametime );
2012-09-22 22:03:42 +02:00
}
else /* relative path */
{
2014-12-17 18:49:59 -05: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 10:24:25 -07:00
mkdir_p ( fname );
2011-02-24 04:14:30 +01:00
2014-12-08 21:24:59 -08: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 06:42:20 -05: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-17 18:49:59 -05: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-08 21:25:00 -08: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-08 21:25:00 -08:00
g_free ( filename );
2012-10-03 23:18:10 +02:00
fe_message ( message , FE_MSG_WAIT | FE_MSG_ERROR );
2012-11-03 10:24:25 -07:00
g_free ( message );
2014-12-08 21:25:00 -08: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 02:10:04 -08:00
gsize len_locale ;
gsize len_utf8 ;
2011-02-24 04:14:30 +01:00
2015-01-18 02:10:04 -08: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 02:10:04 -08: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 02:10:04 -08:00
if ( len_locale == 0 )
2011-02-24 04:14:30 +01:00
{
2015-01-18 02:10:04 -08:00
return 0 ;
2011-02-24 04:14:30 +01:00
}
2015-01-18 02:10:04 -08: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 02:10:04 -08:00
return len_utf8 ;
2011-02-24 04:14:30 +01:00
}
static void
2014-05-06 17:10:34 -07: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-14 16:29:03 -08:00
{
2011-02-24 04:14:30 +01:00
log_open ( sess );
2015-01-14 16:29:03 -08:00
}
2011-02-24 04:14:30 +01:00
/* change to a different log file? */
2014-12-08 21:24:59 -08: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 09:27:15 -08:00
if ( g_access ( file , F_OK ) != 0 )
2011-02-24 04:14:30 +01:00
{
2015-01-14 16:29:03 -08:00
if ( sess -> logfd != - 1 )
{
close ( sess -> logfd );
}
2014-12-08 21:24:59 -08: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-14 16:29:03 -08:00
2011-02-24 04:14:30 +01:00
g_free ( file );
}
2015-01-14 16:29:03 -08: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-06 17:10:34 -07: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-14 16:29:03 -08: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 02:10:04 -08:00
/**
2015-01-31 00:52:31 -08: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 02:10:04 -08: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 00:52:31 -08: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 02:10:04 -08: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 02:10:04 -08:00
{
2011-02-24 04:14:30 +01:00
len = strlen ( text );
2015-01-18 02:10:04 -08:00
}
2011-02-24 04:14:30 +01:00
2015-01-18 02:10:04 -08:00
end = text + len ;
2011-02-24 04:14:30 +01:00
2015-01-18 02:10:04 -08:00
/* Find the first position of an invalid sequence. */
2015-01-31 00:52:31 -08:00
result_part = g_convert_with_iconv ( text , len , converter , & invalid_start_pos , & result_part_len , NULL );
2016-06-24 12:09:35 -07:00
g_iconv ( converter , NULL , NULL , NULL , NULL );
2015-01-18 02:10:04 -08:00
if ( result_part != NULL )
2011-02-24 04:14:30 +01:00
{
2015-01-18 02:10:04 -08: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 02:10:04 -08:00
* len_out = result_part_len ;
2011-02-24 04:14:30 +01:00
}
2015-01-18 02:10:04 -08: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 12:09:35 -07: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.
*
2026-01-06 01:23:57 -07:00
* See https://github.com/hexchat/hexchat/issues/1758
2016-06-24 12:09:35 -07:00
*/
2015-01-31 00:52:31 -08:00
result_part = g_convert_with_iconv ( current_start , invalid_start_pos , converter , & invalid_start_pos , & result_part_len , NULL );
2016-06-24 12:09:35 -07:00
g_iconv ( converter , NULL , NULL , NULL , NULL );
2015-01-18 02:10:04 -08: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 00:52:31 -08:00
result_part = g_convert_with_iconv ( current_start , end - current_start , converter , & invalid_start_pos , & result_part_len , NULL );
2016-06-24 12:09:35 -07:00
g_iconv ( converter , NULL , NULL , NULL , NULL );
2015-01-18 02:10:04 -08:00
if ( result_part != NULL )
2011-02-24 04:14:30 +01:00
{
2015-01-18 02:10:04 -08: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 02:10:04 -08:00
* len_out = result -> len ;
2011-02-24 04:14:30 +01:00
}
2015-01-18 02:10:04 -08:00
return g_string_free ( result , FALSE );
2011-02-24 04:14:30 +01:00
}
2015-01-18 02:10:04 -08: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 00:52:31 -08:00
/**
* Replaces any invalid UTF-8 in the given text with the unicode replacement character.
*/
2015-01-18 02:10:04 -08:00
gchar *
2015-01-31 00:52:31 -08:00
text_fixup_invalid_utf8 ( const gchar * text , gssize len , gsize * len_out )
2011-02-24 04:14:30 +01:00
{
2017-10-02 15:11:42 -04:00
#if GLIB_CHECK_VERSION (2, 52, 0)
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gchar * result = g_utf8_make_valid ( text , len );
G_GNUC_END_IGNORE_DEPRECATIONS
if ( len_out )
{
* len_out = strlen ( result );
}
return result ;
#else
2015-01-31 00:52:31 -08: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 00:52:31 -08:00
return text_convert_invalid ( text , len , utf8_fixup_converter , unicode_fallback_string , len_out );
2017-10-02 15:11:42 -04:00
#endif
2011-02-24 04:14:30 +01:00
}
2013-06-23 19:53:41 +01:00
void
2013-06-22 00:13:36 +01: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 02:10:04 -08:00
if ( text [ 0 ] == '\0' )
2011-02-24 04:14:30 +01:00
{
2015-01-18 02:10:04 -08:00
text = g_strdup ( " \n " );
2014-12-08 21:24:59 -08:00
}
else
2011-02-24 04:14:30 +01:00
{
2015-01-31 00:52:31 -08:00
text = text_fixup_invalid_utf8 ( text , - 1 , NULL );
2011-02-24 04:14:30 +01:00
}
2014-05-06 17:10:34 -07:00
log_write ( sess , text , timestamp );
2015-03-02 19:58:27 +01:00
scrollback_save ( sess , text , timestamp );
2013-10-08 23:59:36 +01:00
fe_print_text ( sess , text , timestamp , FALSE );
2015-01-18 02:10:04 -08:00
g_free ( text );
2011-02-24 04:14:30 +01:00
}
2013-06-22 00:13:36 +01:00
void
PrintText ( session * sess , char * text )
{
PrintTextTimeStamp ( sess , text , 0 );
}
2011-02-24 04:14:30 +01:00
void
2014-07-18 11:25:41 +10: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-22 23:48:17 +01:00
void
2014-07-18 11:25:41 +10:00
PrintTextTimeStampf ( session * sess , time_t timestamp , const char * format , ...)
2013-06-22 23:48:17 +01: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-17 18:49:59 -05: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 15:22:51 -04: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 16:02:04 -05: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 17:49:30 -04: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 04:29:39 -04: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 04:29:39 -04: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 04:29:39 -04: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 04:29:39 -04: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 17:49:30 -04:00
static char * const pevt_chanunquiet_help [] = {
2015-04-04 04:29:39 -04:00
N_ ( "The nick of the person who did the unquiet'ing" ),
2013-04-07 17:49:30 -04: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 14:24:37 -04: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" ),
};
2021-05-24 01:32:00 +01:00
static char * const pevt_invitedother_help [] = {
N_ ( "Channel Name" ),
N_ ( "Nick of person who sent the invite" ),
N_ ( "Nick of person who was invited" ),
N_ ( "Server Name" ),
};
2011-02-24 04:14:30 +01:00
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 06:14:30 -04: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-07 20:41:29 -04: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" ),
};
2021-06-21 00:29:36 +01:00
static char * const pevt_stdrpl_help [] = {
N_ ( "Error Code" ),
N_ ( "Error Message" ),
};
static char * const pevt_stdrplcmd_help [] = {
N_ ( "Command" ),
N_ ( "Error Code" ),
N_ ( "Error Message" ),
};
2011-02-24 04:14:30 +01:00
#include "textevents.h"
static void
2016-02-18 08:57:22 -05: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 06:37:25 -05: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 06:37:25 -05:00
pntevts_text [ i ] = g_strdup ( te [ i ]. def );
2011-02-24 04:14:30 +01:00
else
2014-12-28 06:37:25 -05:00
pntevts_text [ i ] = g_strdup ( _ ( te [ i ]. def ));
2011-02-24 04:14:30 +01:00
}
}
void
2016-02-18 08:57:22 -05:00
pevent_make_pntevts ( void )
2011-02-24 04:14:30 +01:00
{
int i , m ;
for ( i = 0 ; i < NUM_XP ; i ++ )
{
2014-12-28 06:37:25 -05:00
g_free ( pntevts [ i ]);
2011-02-24 04:14:30 +01:00
if ( pevt_build_string ( pntevts_text [ i ], & ( pntevts [ i ]), & m ) != 0 )
{
/* make-te.c sets this 128 flag (DON'T call gettext() flag) */
2016-10-10 14:08:47 -04:00
const gboolean translate = ! ( te [ i ]. num_args & 128 );
g_warning ( "Error parsing event %s \n Loading default." , te [ i ]. name );
g_free ( pntevts_text [ i ]);
if ( translate )
pntevts_text [ i ] = g_strdup ( _ ( te [ i ]. def ));
else
2014-12-28 06:37:25 -05:00
pntevts_text [ i ] = g_strdup ( te [ i ]. def );
2016-10-10 14:08:47 -04:00
if ( pevt_build_string ( pntevts_text [ i ], & ( pntevts [ i ]), & m ) != 0 && ! translate )
{
2026-01-05 23:12:38 -07:00
g_error ( "ZoiteChat CRITICAL *** default event text failed to build!" );
2016-10-10 14:08:47 -04:00
}
2011-02-24 04:14:30 +01:00
else
{
2016-10-10 14:08:47 -04:00
g_warning ( "Error parsing translated event %s \n Loading untranslated." , te [ i ]. name );
g_free ( pntevts_text [ i ]);
pntevts_text [ i ] = g_strdup ( te [ i ]. def );
if ( pevt_build_string ( pntevts_text [ i ], & ( pntevts [ i ]), & m ) != 0 )
{
2026-01-05 23:12:38 -07:00
g_error ( "ZoiteChat CRITICAL *** default event text failed to build!" );
2016-10-10 14:08:47 -04:00
}
2011-02-24 04:14:30 +01:00
}
}
}
}
/* 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 06:37:25 -05: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 06:37:25 -05:00
g_free ( pntevts_text [ penum ]);
pntevts_text [ penum ] = g_strdup ( text );
2011-02-24 04:14:30 +01:00
}
2014-12-28 06:37:25 -05: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 )
2026-01-05 23:12:38 -07:00
fd = zoitechat_open_file ( "pevents.conf" , O_RDONLY , 0 , 0 );
2011-02-24 04:14:30 +01:00
else
2026-01-05 23:12:38 -07:00
fd = zoitechat_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 10:28:53 -05:00
{
close ( fd );
2011-02-24 04:14:30 +01:00
return 1 ;
2014-02-12 10:28:53 -05:00
}
2014-12-28 06:37:25 -05: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 06:37:25 -05:00
g_free ( text );
text = g_strdup ( ofs );
2011-02-24 04:14:30 +01:00
continue ;
2014-12-28 06:37:25 -05:00
}
2011-02-24 04:14:30 +01:00
continue ;
}
pevent_trigger_load ( & penum , & text , & snd );
2014-12-28 06:37:25 -05:00
g_free ( ibuf );
2011-02-24 04:14:30 +01:00
return 0 ;
}
2026-07-07 09:08:00 -06:00
static gboolean
text_event_uses_color ( const char * text , int color )
{
const char * p ;
if ( ! text || color < 0 || color > 31 )
return FALSE ;
for ( p = text ; * p ; p ++ )
{
int value ;
int digits ;
if (( unsigned char ) * p == 3 )
p ++ ;
else if ( * p == '%' && p [ 1 ] == 'C' )
p += 2 ;
else
continue ;
if ( ! g_ascii_isdigit ( * p ))
{
p -- ;
continue ;
}
value = * p - '0' ;
digits = 1 ;
if ( g_ascii_isdigit ( p [ 1 ]))
{
value = value * 10 + p [ 1 ] - '0' ;
digits = 2 ;
}
if ( value == color )
return TRUE ;
p += digits - 1 ;
if ( * p == ',' && g_ascii_isdigit ( p [ 1 ]))
{
p ++ ;
value = * p - '0' ;
if ( g_ascii_isdigit ( p [ 1 ]))
{
value = value * 10 + p [ 1 ] - '0' ;
p ++ ;
}
if ( value == color )
return TRUE ;
}
}
return FALSE ;
}
2026-07-08 00:58:34 -06:00
char **
text_color_event_names ( int color , int * count )
2026-07-07 09:08:00 -06:00
{
2026-07-08 00:58:34 -06:00
GPtrArray * names ;
int found = 0 ;
2026-07-07 09:08:00 -06:00
int i ;
2026-07-08 00:58:34 -06:00
if ( count )
* count = 0 ;
names = g_ptr_array_new ();
2026-07-07 09:08:00 -06:00
for ( i = 0 ; i < NUM_XP ; i ++ )
{
const char * text = pntevts_text [ i ] ? pntevts_text [ i ] : te [ i ]. def ;
if ( ! text_event_uses_color ( text , color ))
continue ;
2026-07-08 00:58:34 -06:00
g_ptr_array_add ( names , g_strdup ( _ ( te [ i ]. name )));
found ++ ;
2026-07-07 09:08:00 -06:00
}
2026-07-08 00:58:34 -06:00
if ( ! found )
2026-07-07 09:08:00 -06:00
{
2026-07-08 00:58:34 -06:00
g_ptr_array_free ( names , TRUE );
return NULL ;
2026-07-07 09:08:00 -06:00
}
2026-07-08 00:58:34 -06:00
g_ptr_array_add ( names , NULL );
if ( count )
* count = found ;
return ( char ** ) g_ptr_array_free ( names , FALSE );
2026-07-07 09:08:00 -06:00
}
2011-02-24 04:14:30 +01:00
static void
2016-02-18 08:57:22 -05: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);
2026-01-05 23:12:38 -07:00
g_snprintf(out, sizeof(out), "The data for event %s failed to load. Reverting to defaults.\nThis may be because a new version of ZoiteChat is loading an old config file.\n\nCheck 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 06:37:25 -05:00
pntevts_text [ i ] = g_strdup ( te [ i ]. def );
2011-02-24 04:14:30 +01:00
else
2014-12-28 06:37:25 -05: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 10:25:28 -08: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 10:25:28 -08: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 ,
2026-01-05 23:12:38 -07:00
"ZoiteChat 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 13:08:49 -07: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 10:25:28 -08: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 ;
}
2026-06-10 15:05:00 -06:00
static char *
text_event_without_hostmask_format ( const char * format , int host_arg )
{
char token [ 3 ];
const char * arg ;
const char * open ;
const char * close ;
const char * start ;
char * out ;
gsize prefix_len ;
g_snprintf ( token , sizeof ( token ), "$%d" , host_arg );
arg = strstr ( format , token );
if ( ! arg )
return NULL ;
open = arg ;
while ( open > format && * open != '(' && * open != '\n' )
open -- ;
close = arg + strlen ( token );
while ( * close && * close != ')' && * close != '\n' )
close ++ ;
if ( * open != '(' || * close != ')' )
return NULL ;
start = open ;
if ( start > format && start [ - 1 ] == ' ' )
start -- ;
prefix_len = start - format ;
out = g_malloc ( prefix_len + strlen ( close + 1 ) + 1 );
memcpy ( out , format , prefix_len );
strcpy ( out + prefix_len , close + 1 );
return out ;
}
static void
display_event_string ( session * sess , int event , char ** args , char * format ,
unsigned int stripcolor_args , time_t timestamp )
{
char * compiled ;
char * saved ;
char o [ 4096 ];
int max_arg ;
if ( pevt_build_string ( format , & compiled , & max_arg ) != 0 )
return ;
saved = pntevts [ event ];
pntevts [ event ] = compiled ;
format_event ( sess , event , args , o , sizeof ( o ), stripcolor_args );
pntevts [ event ] = saved ;
g_free ( compiled );
if ( o [ 0 ])
PrintTextTimeStamp ( sess , o , timestamp );
}
2011-02-24 04:14:30 +01:00
static void
2013-06-22 00:13:36 +01:00
display_event ( session * sess , int event , char ** args ,
2013-06-23 19:53:41 +01:00
unsigned int stripcolor_args , time_t timestamp )
2011-02-24 04:14:30 +01:00
{
char o [ 4096 ];
2026-06-10 15:05:00 -06:00
char * format ;
char * host ;
int host_arg ;
if ( prefs . hex_irc_hide_join_part_hostmask &&
( event == XP_TE_JOIN || event == XP_TE_PART || event == XP_TE_PARTREASON ))
{
host_arg = event == XP_TE_JOIN ? 3 : 2 ;
format = text_event_without_hostmask_format ( pntevts_text [ event ], host_arg );
if ( format )
{
display_event_string ( sess , event , args , format , stripcolor_args , timestamp );
g_free ( format );
return ;
}
host = args [ host_arg ];
args [ host_arg ] = "" ;
format_event ( sess , event , args , o , sizeof ( o ), stripcolor_args );
args [ host_arg ] = host ;
}
else
{
format_event ( sess , event , args , o , sizeof ( o ), stripcolor_args );
}
2011-02-24 04:14:30 +01:00
if ( o [ 0 ])
2013-06-22 00:13:36 +01: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 06:37:25 -05: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 06:37:25 -05: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 06:37:25 -05: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 06:37:25 -05:00
goto err ;
2011-02-24 04:14:30 +01:00
}
d = i [ ii ++ ];
if ( d == 'a' )
2014-12-28 06:37:25 -05: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 06:37:25 -05:00
a_len_error :
2011-02-24 04:14:30 +01:00
fe_message ( "String ends in $a" , FE_MSG_WARN );
2014-12-28 06:37:25 -05: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 06:37:25 -05: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 06:37:25 -05: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 06:37:25 -05: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-17 18:49:59 -05: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 06:37:25 -05:00
goto err ;
2011-02-24 04:14:30 +01:00
}
d -= '0' ;
if ( max < d )
max = d ;
2014-12-28 06:37:25 -05: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 06:37:25 -05: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 06:37:25 -05: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 06:37:25 -05: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 06:37:25 -05: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 06:37:25 -05: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 06:37:25 -05: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 06:37:25 -05:00
g_free ( s -> data );
g_free ( s );
2011-02-24 04:14:30 +01:00
s = next ;
}
2014-12-28 06:37:25 -05: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 12:13:57 -05:00
else
2014-12-28 06:37:25 -05:00
g_free ( obuf );
2011-02-24 04:14:30 +01:00
return 0 ;
2014-12-28 06:37:25 -05: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 17:57:06 -05: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 00:13:36 +01:00
text_emit ( int index , session * sess , char * a , char * b , char * c , char * d ,
2013-06-23 19:53:41 +01:00
time_t timestamp )
2011-02-24 04:14:30 +01:00
{
char * word [ PDIWORDS ];
int i ;
2016-11-11 19:25:46 -05:00
tab_state_flags current_state = sess -> tab_state ;
2016-11-11 20:02:24 -05:00
tab_state_flags plugin_state = sess -> last_tab_state ;
2013-06-06 17:47:01 -04: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 ];
2017-06-18 06:31:34 -04:00
if ( a != NULL && prefs . hex_text_color_nicks && ( index == XP_TE_CHANACTION || index == XP_TE_CHANMSG ))
2011-02-24 04:14:30 +01:00
{
2014-12-17 18:49:59 -05: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 " ;
2016-11-11 19:25:46 -05:00
/* We want to ignore the tab state if the plugin emits new events
* and restore it if it doesn't eat the current one */
2016-11-11 20:02:24 -05:00
sess -> tab_state = plugin_state ;
2013-11-28 00:59:31 +00:00
if ( plugin_emit_print ( sess , word , timestamp ))
2011-02-24 04:14:30 +01:00
return ;
2016-11-11 20:02:24 -05:00
/* The plugin may have changed the state which we should respect.
* If the state is NEW_DATA we don't actually know if that was on
* purpose though as print() sets it, so for now we ignore that. FIXME */
if ( sess -> tab_state == plugin_state || sess -> tab_state == TAB_STATE_NEW_DATA )
sess -> tab_state = current_state ;
2011-02-24 04:14:30 +01:00
/* 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
}
2017-06-10 16:28:48 -04:00
if ( ! prefs . hex_away_omit_alerts || ! sess -> server -> is_away )
sound_play_event ( index );
2013-06-22 00:13:36 +01:00
display_event ( sess , index , word , stripcolor_args , timestamp );
2026-10-02 14:14:24 -06:00
plugin_emit_print_after ( sess , word , 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 01:33:35 +01: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 01:33:35 +01: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 )
2026-01-05 23:12:38 -07:00
fd = zoitechat_open_file ( "pevents.conf" , O_CREAT | O_TRUNC | O_WRONLY ,
2011-02-24 04:14:30 +01:00
0x180 , XOF_DOMODE );
else
2026-01-05 23:12:38 -07:00
fd = zoitechat_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-17 18:49:59 -05: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-17 18:49:59 -05: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 14:13:34 -04:00
if ( ! prefs . hex_gui_focus_omitalerts || fe_gui_info ( sess , 0 ) != 1 )
2013-04-23 00:45:37 -04: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 10:24:25 -07: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 07:23:11 -05:00
if ( g_path_is_absolute ( file ))
2011-02-24 04:14:30 +01:00
{
2012-11-03 10:24:25 -07:00
wavfile = g_strdup ( file );
}
else
2011-02-24 04:14:30 +01:00
{
2026-01-25 16:13:47 -07:00
wavfile = g_build_filename ( get_xdir (), ZOITECHAT_SOUND_DIR , file , NULL );
2011-02-24 04:14:30 +01:00
}
2012-11-03 10:24:25 -07: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 00:23:44 -07: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-27 20:02:27 -03:00
#ifdef USE_LIBCANBERRA
2013-06-12 10:41:52 -04:00
if ( ca_con == NULL )
{
ca_context_create ( & ca_con );
ca_context_change_props ( ca_con ,
2026-01-05 23:12:38 -07:00
CA_PROP_APPLICATION_ID , "zoitechat" ,
CA_PROP_APPLICATION_NAME , "ZoiteChat" ,
CA_PROP_APPLICATION_ICON_NAME , "zoitechat" , NULL );
2013-06-12 10:41:52 -04:00
}
if ( ca_context_play ( ca_con , 0 , CA_PROP_MEDIA_FILENAME , wavfile , NULL ) != 0 )
2013-04-27 20:02:27 -03:00
#endif
2011-02-24 04:14:30 +01:00
{
2013-04-27 20:29:28 -03:00
cmd = g_find_program_in_path ( "play" );
if ( cmd )
{
buf = g_strdup_printf ( "%s \" %s \" " , cmd , wavfile );
2026-01-05 23:12:38 -07:00
zoitechat_exec ( buf );
2013-04-27 20:29:28 -03:00
g_free ( buf );
g_free ( cmd );
}
2012-11-03 10:24:25 -07:00
}
2011-02-24 04:14:30 +01:00
#endif
2012-11-03 10:24:25 -07:00
}
else
2011-02-24 04:14:30 +01:00
{
if ( ! quiet )
{
2012-11-03 10:24:25 -07: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 10:24:25 -07:00
g_free ( buf );
2011-02-24 04:14:30 +01:00
}
}
2012-11-03 10:24:25 -07: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 06:37:25 -05: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 ));
2026-01-05 23:12:38 -07:00
fd = zoitechat_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 ];
2026-01-05 23:12:38 -07:00
fd = zoitechat_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-17 18:49:59 -05: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-17 18:49:59 -05: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 );
}