2022-02-26 20:51:42 +00:00
/* Copyright (C) 2020 June McEnroe <june@causal.agency>
2020-02-01 06:18:01 +00:00
*
* 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 3 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 , see < https : //www.gnu.org/licenses/>.
2020-06-08 21:48:07 +00:00
*
* Additional permission under GNU GPL version 3 section 7 :
*
* If you modify this Program , or any covered work , by linking or
2020-08-04 16:19:14 +00:00
* combining it with OpenSSL ( or a modified version of that library ) ,
2020-06-08 21:48:07 +00:00
* containing parts covered by the terms of the OpenSSL License and the
* original SSLeay license , the licensors of this Program grant you
* additional permission to convey the resulting work . Corresponding
* Source for a non - source form of such a combination shall include the
2020-08-04 16:19:14 +00:00
* source code for the parts of OpenSSL used as well as that of the
2020-06-08 21:48:07 +00:00
* covered work .
2020-02-01 06:18:01 +00:00
*/
2020-02-08 05:25:09 +00:00
# include <assert.h>
2020-02-06 09:18:15 +00:00
# include <ctype.h>
2020-02-01 06:18:01 +00:00
# include <err.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sysexits.h>
2020-11-08 17:42:01 +00:00
# include <wchar.h>
2020-02-01 06:18:01 +00:00
# include "chat.h"
2020-12-30 21:49:55 +00:00
uint replies [ ReplyCap ] ;
2020-02-08 08:15:17 +00:00
2020-02-01 06:18:01 +00:00
static const char * CapNames [ ] = {
2020-02-01 07:19:55 +00:00
# define X(name, id) [id##Bit] = name,
2020-02-01 06:18:01 +00:00
ENUM_CAP
# undef X
} ;
static enum Cap capParse ( const char * list ) {
enum Cap caps = 0 ;
while ( * list ) {
enum Cap cap = 0 ;
size_t len = strcspn ( list , " " ) ;
for ( size_t i = 0 ; i < ARRAY_LEN ( CapNames ) ; + + i ) {
if ( len ! = strlen ( CapNames [ i ] ) ) continue ;
if ( strncmp ( list , CapNames [ i ] , len ) ) continue ;
cap = 1 < < i ;
break ;
}
caps | = cap ;
list + = len ;
if ( * list ) list + + ;
}
return caps ;
}
2021-06-07 04:09:58 +00:00
static void capList ( char * buf , size_t cap , enum Cap caps ) {
* buf = ' \0 ' ;
char * ptr = buf , * end = & buf [ cap ] ;
2020-02-01 06:18:01 +00:00
for ( size_t i = 0 ; i < ARRAY_LEN ( CapNames ) ; + + i ) {
if ( caps & ( 1 < < i ) ) {
2021-06-07 04:09:58 +00:00
ptr = seprintf (
ptr , end , " %s%s " , ( ptr > buf ? " " : " " ) , CapNames [ i ]
) ;
2020-02-01 06:18:01 +00:00
}
}
}
2020-02-16 03:19:55 +00:00
static void require ( struct Message * msg , bool origin , uint len ) {
2020-02-02 08:13:50 +00:00
if ( origin ) {
2020-09-18 22:13:09 +00:00
if ( ! msg - > nick ) msg - > nick = " *.* " ;
2020-02-02 08:13:50 +00:00
if ( ! msg - > user ) msg - > user = msg - > nick ;
if ( ! msg - > host ) msg - > host = msg - > user ;
2020-02-01 06:18:01 +00:00
}
2020-02-16 03:19:55 +00:00
for ( uint i = 0 ; i < len ; + + i ) {
2020-02-01 06:18:01 +00:00
if ( msg - > params [ i ] ) continue ;
2020-02-16 03:19:55 +00:00
errx ( EX_PROTOCOL , " %s missing parameter %u " , msg - > cmd , 1 + i ) ;
2020-02-01 06:18:01 +00:00
}
}
2020-02-03 23:41:52 +00:00
static const time_t * tagTime ( const struct Message * msg ) {
static time_t time ;
struct tm tm ;
2020-02-02 06:58:03 +00:00
if ( ! msg - > tags [ TagTime ] ) return NULL ;
2021-10-26 08:14:17 +00:00
if ( ! strptime ( msg - > tags [ TagTime ] , " %Y-%m-%dT%T " , & tm ) ) return NULL ;
2020-02-03 23:41:52 +00:00
time = timegm ( & tm ) ;
return & time ;
2020-02-02 06:58:03 +00:00
}
typedef void Handler ( struct Message * msg ) ;
2020-12-30 04:10:15 +00:00
static void handleStandardReply ( struct Message * msg ) {
require ( msg , false , 3 ) ;
for ( uint i = 2 ; i < ParamCap - 1 ; + + i ) {
if ( msg - > params [ i + 1 ] ) continue ;
uiFormat (
Network , Warm , tagTime ( msg ) ,
" %s " , msg - > params [ i ]
) ;
break ;
}
}
2020-02-20 06:38:31 +00:00
static void handleErrorGeneric ( struct Message * msg ) {
require ( msg , false , 2 ) ;
if ( msg - > params [ 2 ] ) {
size_t len = strlen ( msg - > params [ 2 ] ) ;
if ( msg - > params [ 2 ] [ len - 1 ] = = ' . ' ) msg - > params [ 2 ] [ len - 1 ] = ' \0 ' ;
uiFormat (
Network , Warm , tagTime ( msg ) ,
" %s: %s " , msg - > params [ 2 ] , msg - > params [ 1 ]
) ;
} else {
uiFormat (
Network , Warm , tagTime ( msg ) ,
" %s " , msg - > params [ 1 ]
) ;
}
}
2022-05-07 00:25:16 +00:00
static void handleReplyGeneric ( struct Message * msg ) {
2022-05-08 21:49:20 +00:00
uint first = 1 ;
uint id = Network ;
if ( msg - > params [ 1 ] & & strchr ( network . chanTypes , msg - > params [ 1 ] [ 0 ] ) ) {
id = idFor ( msg - > params [ 1 ] ) ;
first + + ;
}
2022-05-07 00:25:16 +00:00
char buf [ 1024 ] ;
char * ptr = buf , * end = & buf [ sizeof ( buf ) ] ;
2022-05-29 19:24:06 +00:00
ptr = seprintf ( ptr , end , " \3 %d(%s) \3 \t " , Gray , msg - > cmd ) ;
2022-05-08 21:49:20 +00:00
for ( uint i = first ; i < ParamCap & & msg - > params [ i ] ; + + i ) {
ptr = seprintf (
ptr , end , " %s%s " , ( i > first ? " " : " " ) , msg - > params [ i ]
) ;
2022-05-07 00:25:16 +00:00
}
2022-05-08 21:49:20 +00:00
uiWrite ( id , Ice , tagTime ( msg ) , buf ) ;
2022-05-07 00:25:16 +00:00
}
2020-02-02 07:30:35 +00:00
static void handleErrorNicknameInUse ( struct Message * msg ) {
require ( msg , false , 2 ) ;
2020-02-20 06:38:31 +00:00
if ( ! strcmp ( self . nick , " * " ) ) {
2022-05-29 22:09:52 +00:00
static uint i = 1 ;
if ( i < ARRAY_LEN ( self . nicks ) & & self . nicks [ i ] ) {
ircFormat ( " NICK %s \r \n " , self . nicks [ i + + ] ) ;
} else {
ircFormat ( " NICK %s_ \r \n " , msg - > params [ 1 ] ) ;
}
2020-02-20 06:38:31 +00:00
} else {
handleErrorGeneric ( msg ) ;
}
2020-02-02 07:30:35 +00:00
}
static void handleErrorErroneousNickname ( struct Message * msg ) {
require ( msg , false , 3 ) ;
2020-02-13 00:30:07 +00:00
if ( ! strcmp ( self . nick , " * " ) ) {
errx ( EX_CONFIG , " %s: %s " , msg - > params [ 1 ] , msg - > params [ 2 ] ) ;
} else {
2020-02-20 06:38:31 +00:00
handleErrorGeneric ( msg ) ;
2020-02-13 00:30:07 +00:00
}
2020-02-02 07:30:35 +00:00
}
2020-02-01 06:18:01 +00:00
static void handleCap ( struct Message * msg ) {
require ( msg , false , 3 ) ;
enum Cap caps = capParse ( msg - > params [ 2 ] ) ;
if ( ! strcmp ( msg - > params [ 1 ] , " LS " ) ) {
caps & = ~ CapSASL ;
2020-02-29 06:03:46 +00:00
if ( caps & CapConsumer & & self . pos ) {
ircFormat ( " CAP REQ %s=%zu \r \n " , CapNames [ CapConsumerBit ] , self . pos ) ;
caps & = ~ CapConsumer ;
}
2020-02-06 08:44:49 +00:00
if ( caps ) {
2021-06-07 04:09:58 +00:00
char buf [ 512 ] ;
capList ( buf , sizeof ( buf ) , caps ) ;
ircFormat ( " CAP REQ :%s \r \n " , buf ) ;
2020-02-06 08:44:49 +00:00
} else {
if ( ! ( self . caps & CapSASL ) ) ircFormat ( " CAP END \r \n " ) ;
}
2020-02-01 06:18:01 +00:00
} else if ( ! strcmp ( msg - > params [ 1 ] , " ACK " ) ) {
self . caps | = caps ;
if ( caps & CapSASL ) {
2022-02-12 18:26:38 +00:00
ircFormat (
" AUTHENTICATE %s \r \n " , ( self . plainUser ? " PLAIN " : " EXTERNAL " )
) ;
2020-02-01 06:18:01 +00:00
}
if ( ! ( self . caps & CapSASL ) ) ircFormat ( " CAP END \r \n " ) ;
} else if ( ! strcmp ( msg - > params [ 1 ] , " NAK " ) ) {
errx ( EX_CONFIG , " server does not support %s " , msg - > params [ 2 ] ) ;
}
}
2020-02-11 22:40:08 +00:00
# define BASE64_SIZE(len) (1 + ((len) + 2) / 3 * 4)
static void base64 ( char * dst , const byte * src , size_t len ) {
static const char Base64 [ 64 ] = {
" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ "
} ;
size_t i = 0 ;
while ( len > 2 ) {
dst [ i + + ] = Base64 [ 0x3F & ( src [ 0 ] > > 2 ) ] ;
dst [ i + + ] = Base64 [ 0x3F & ( src [ 0 ] < < 4 | src [ 1 ] > > 4 ) ] ;
dst [ i + + ] = Base64 [ 0x3F & ( src [ 1 ] < < 2 | src [ 2 ] > > 6 ) ] ;
dst [ i + + ] = Base64 [ 0x3F & src [ 2 ] ] ;
src + = 3 ;
len - = 3 ;
}
if ( len ) {
dst [ i + + ] = Base64 [ 0x3F & ( src [ 0 ] > > 2 ) ] ;
if ( len > 1 ) {
dst [ i + + ] = Base64 [ 0x3F & ( src [ 0 ] < < 4 | src [ 1 ] > > 4 ) ] ;
dst [ i + + ] = Base64 [ 0x3F & ( src [ 1 ] < < 2 ) ] ;
} else {
dst [ i + + ] = Base64 [ 0x3F & ( src [ 0 ] < < 4 ) ] ;
dst [ i + + ] = ' = ' ;
}
dst [ i + + ] = ' = ' ;
}
dst [ i ] = ' \0 ' ;
}
2020-02-01 06:18:01 +00:00
static void handleAuthenticate ( struct Message * msg ) {
( void ) msg ;
2022-02-12 18:26:38 +00:00
if ( ! self . plainUser ) {
2020-02-01 06:18:01 +00:00
ircFormat ( " AUTHENTICATE + \r \n " ) ;
return ;
}
2021-09-09 16:44:01 +00:00
byte buf [ 299 ] = { 0 } ;
2022-02-12 18:26:38 +00:00
size_t userLen = strlen ( self . plainUser ) ;
size_t passLen = strlen ( self . plainPass ) ;
size_t len = 1 + userLen + 1 + passLen ;
2021-09-09 16:44:01 +00:00
if ( sizeof ( buf ) < len ) errx ( EX_USAGE , " SASL PLAIN is too long " ) ;
2022-02-12 18:26:38 +00:00
memcpy ( & buf [ 1 ] , self . plainUser , userLen ) ;
memcpy ( & buf [ 1 + userLen + 1 ] , self . plainPass , passLen ) ;
2020-02-01 06:18:01 +00:00
char b64 [ BASE64_SIZE ( sizeof ( buf ) ) ] ;
base64 ( b64 , buf , len ) ;
ircFormat ( " AUTHENTICATE " ) ;
2022-01-16 19:17:46 +00:00
ircSend ( b64 , BASE64_SIZE ( len ) - 1 ) ;
2020-02-01 06:18:01 +00:00
ircFormat ( " \r \n " ) ;
explicit_bzero ( b64 , sizeof ( b64 ) ) ;
explicit_bzero ( buf , sizeof ( buf ) ) ;
2022-02-12 18:26:38 +00:00
explicit_bzero ( self . plainPass , strlen ( self . plainPass ) ) ;
2020-02-01 06:18:01 +00:00
}
static void handleReplyLoggedIn ( struct Message * msg ) {
( void ) msg ;
ircFormat ( " CAP END \r \n " ) ;
2022-05-07 00:25:16 +00:00
handleReplyGeneric ( msg ) ;
2020-02-01 06:18:01 +00:00
}
static void handleErrorSASLFail ( struct Message * msg ) {
require ( msg , false , 2 ) ;
errx ( EX_CONFIG , " %s " , msg - > params [ 1 ] ) ;
}
static void handleReplyWelcome ( struct Message * msg ) {
require ( msg , false , 1 ) ;
set ( & self . nick , msg - > params [ 0 ] ) ;
2022-07-30 22:47:26 +00:00
cacheInsert ( true , Network , self . nick ) ;
2021-06-18 16:28:09 +00:00
if ( self . mode ) ircFormat ( " MODE %s %s \r \n " , self . nick , self . mode ) ;
2020-02-08 08:15:17 +00:00
if ( self . join ) {
2021-02-21 19:23:17 +00:00
uint count = 1 ;
for ( const char * ch = self . join ; * ch & & * ch ! = ' ' ; + + ch ) {
if ( * ch = = ' , ' ) count + + ;
2021-02-05 02:25:59 +00:00
}
2021-02-21 19:23:17 +00:00
ircFormat ( " JOIN %s \r \n " , self . join ) ;
if ( count = = 1 ) replies [ ReplyJoin ] + + ;
replies [ ReplyTopicAuto ] + = count ;
replies [ ReplyNamesAuto ] + = count ;
2020-02-08 08:15:17 +00:00
}
2022-07-30 22:47:26 +00:00
commandCache ( ) ;
2022-05-07 00:25:16 +00:00
handleReplyGeneric ( msg ) ;
2020-02-01 06:18:01 +00:00
}
2020-02-02 00:37:48 +00:00
static void handleReplyISupport ( struct Message * msg ) {
2022-05-07 00:25:16 +00:00
handleReplyGeneric ( msg ) ;
2020-02-16 03:19:55 +00:00
for ( uint i = 1 ; i < ParamCap ; + + i ) {
2020-02-02 00:37:48 +00:00
if ( ! msg - > params [ i ] ) break ;
char * key = strsep ( & msg - > params [ i ] , " = " ) ;
if ( ! strcmp ( key , " NETWORK " ) ) {
2020-02-25 07:24:29 +00:00
if ( ! msg - > params [ i ] ) continue ;
2020-02-15 09:54:53 +00:00
set ( & network . name , msg - > params [ i ] ) ;
2022-05-08 20:39:31 +00:00
static bool arrived ;
if ( ! arrived ) {
uiFormat (
Network , Cold , tagTime ( msg ) ,
" You arrive in %s " , msg - > params [ i ]
) ;
arrived = true ;
}
2020-03-23 17:13:43 +00:00
} else if ( ! strcmp ( key , " USERLEN " ) ) {
if ( ! msg - > params [ i ] ) continue ;
network . userLen = strtoul ( msg - > params [ i ] , NULL , 10 ) ;
} else if ( ! strcmp ( key , " HOSTLEN " ) ) {
if ( ! msg - > params [ i ] ) continue ;
network . hostLen = strtoul ( msg - > params [ i ] , NULL , 10 ) ;
2020-02-02 22:37:36 +00:00
} else if ( ! strcmp ( key , " CHANTYPES " ) ) {
2020-02-25 07:24:29 +00:00
if ( ! msg - > params [ i ] ) continue ;
2020-02-15 09:54:53 +00:00
set ( & network . chanTypes , msg - > params [ i ] ) ;
2021-04-02 21:03:10 +00:00
} else if ( ! strcmp ( key , " STATUSMSG " ) ) {
if ( ! msg - > params [ i ] ) continue ;
set ( & network . statusmsg , msg - > params [ i ] ) ;
2020-02-02 22:37:36 +00:00
} else if ( ! strcmp ( key , " PREFIX " ) ) {
2020-02-15 10:29:54 +00:00
strsep ( & msg - > params [ i ] , " ( " ) ;
2020-04-05 17:11:19 +00:00
char * modes = strsep ( & msg - > params [ i ] , " ) " ) ;
char * prefixes = msg - > params [ i ] ;
if ( ! modes | | ! prefixes | | strlen ( modes ) ! = strlen ( prefixes ) ) {
errx ( EX_PROTOCOL , " invalid PREFIX value " ) ;
}
set ( & network . prefixModes , modes ) ;
set ( & network . prefixes , prefixes ) ;
2020-02-15 10:29:54 +00:00
} else if ( ! strcmp ( key , " CHANMODES " ) ) {
2020-04-05 17:11:19 +00:00
char * list = strsep ( & msg - > params [ i ] , " , " ) ;
char * param = strsep ( & msg - > params [ i ] , " , " ) ;
char * setParam = strsep ( & msg - > params [ i ] , " , " ) ;
char * channel = strsep ( & msg - > params [ i ] , " , " ) ;
if ( ! list | | ! param | | ! setParam | | ! channel ) {
errx ( EX_PROTOCOL , " invalid CHANMODES value " ) ;
}
set ( & network . listModes , list ) ;
set ( & network . paramModes , param ) ;
set ( & network . setParamModes , setParam ) ;
set ( & network . channelModes , channel ) ;
2020-02-16 23:31:50 +00:00
} else if ( ! strcmp ( key , " EXCEPTS " ) ) {
2020-04-03 21:10:52 +00:00
network . excepts = ( msg - > params [ i ] ? : " e " ) [ 0 ] ;
2020-02-16 23:31:50 +00:00
} else if ( ! strcmp ( key , " INVEX " ) ) {
2020-04-03 21:10:52 +00:00
network . invex = ( msg - > params [ i ] ? : " I " ) [ 0 ] ;
2020-02-02 00:37:48 +00:00
}
}
}
static void handleReplyMOTD ( struct Message * msg ) {
require ( msg , false , 2 ) ;
char * line = msg - > params [ 1 ] ;
2021-01-11 22:59:12 +00:00
urlScan ( Network , NULL , line ) ;
2020-02-05 05:24:54 +00:00
if ( ! strncmp ( line , " - " , 2 ) ) {
uiFormat ( Network , Cold , tagTime ( msg ) , " \3 %d- \3 \t %s " , Gray , & line [ 2 ] ) ;
} else {
uiFormat ( Network , Cold , tagTime ( msg ) , " %s " , line ) ;
}
2020-02-02 06:58:03 +00:00
}
2020-03-09 07:08:59 +00:00
static void handleErrorNoMOTD ( struct Message * msg ) {
( void ) msg ;
}
2020-10-02 23:38:37 +00:00
static void handleReplyHelp ( struct Message * msg ) {
require ( msg , false , 3 ) ;
2021-01-11 22:59:12 +00:00
urlScan ( Network , NULL , msg - > params [ 2 ] ) ;
2020-10-02 23:38:37 +00:00
uiWrite ( Network , Warm , tagTime ( msg ) , msg - > params [ 2 ] ) ;
}
2020-02-02 08:13:50 +00:00
static void handleJoin ( struct Message * msg ) {
require ( msg , true , 1 ) ;
2020-02-16 03:19:55 +00:00
uint id = idFor ( msg - > params [ 0 ] ) ;
2020-02-11 22:52:55 +00:00
if ( ! strcmp ( msg - > nick , self . nick ) ) {
2020-03-22 18:32:20 +00:00
if ( ! self . user | | strcmp ( self . user , msg - > user ) ) {
2020-02-05 05:40:24 +00:00
set ( & self . user , msg - > user ) ;
2023-01-22 08:51:12 +00:00
self . color = hash ( msg - > nick ) ;
2020-02-05 05:40:24 +00:00
}
2020-03-22 18:32:20 +00:00
if ( ! self . host | | strcmp ( self . host , msg - > host ) ) {
set ( & self . host , msg - > host ) ;
}
2020-02-02 08:27:50 +00:00
idColors [ id ] = hash ( msg - > params [ 0 ] ) ;
2022-07-31 20:28:08 +00:00
cacheInsert ( true , None , msg - > params [ 0 ] ) - > color = idColors [ id ] ;
2020-12-30 21:49:55 +00:00
if ( replies [ ReplyJoin ] ) {
2022-02-19 23:28:45 +00:00
windowShow ( windowFor ( id ) ) ;
2020-12-30 21:49:55 +00:00
replies [ ReplyJoin ] - - ;
2020-02-11 01:24:07 +00:00
}
2020-02-02 08:13:50 +00:00
}
2023-01-22 08:51:12 +00:00
cacheInsert ( true , id , msg - > nick ) - > color = hash ( msg - > nick ) ;
2020-02-09 05:53:55 +00:00
if ( msg - > params [ 2 ] & & ! strcasecmp ( msg - > params [ 2 ] , msg - > nick ) ) {
msg - > params [ 2 ] = NULL ;
}
2020-02-02 08:13:50 +00:00
uiFormat (
2021-01-16 18:30:59 +00:00
id , filterCheck ( Cold , id , msg ) , tagTime ( msg ) ,
2020-02-09 05:53:55 +00:00
" \3 %02d%s \3 \t %s%s%sarrives in \3 %02d%s \3 " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick ,
2020-02-09 05:53:55 +00:00
( msg - > params [ 2 ] ? " ( " : " " ) ,
2020-04-03 21:10:52 +00:00
( msg - > params [ 2 ] ? : " " ) ,
2021-04-28 01:06:16 +00:00
( msg - > params [ 2 ] ? " \17 ) " : " " ) ,
2020-02-09 05:53:55 +00:00
hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ]
2020-02-02 08:13:50 +00:00
) ;
2020-03-25 22:58:48 +00:00
logFormat ( id , tagTime ( msg ) , " %s arrives in %s " , msg - > nick , msg - > params [ 0 ] ) ;
2020-02-02 08:13:50 +00:00
}
2020-03-22 18:32:20 +00:00
static void handleChghost ( struct Message * msg ) {
require ( msg , true , 2 ) ;
if ( strcmp ( msg - > nick , self . nick ) ) return ;
if ( ! self . user | | strcmp ( self . user , msg - > params [ 0 ] ) ) {
set ( & self . user , msg - > params [ 0 ] ) ;
self . color = hash ( msg - > params [ 0 ] ) ;
}
if ( ! self . host | | strcmp ( self . host , msg - > params [ 1 ] ) ) {
set ( & self . host , msg - > params [ 1 ] ) ;
}
}
2020-02-06 06:16:35 +00:00
static void handlePart ( struct Message * msg ) {
require ( msg , true , 1 ) ;
2020-02-16 03:19:55 +00:00
uint id = idFor ( msg - > params [ 0 ] ) ;
2020-02-11 22:52:55 +00:00
if ( ! strcmp ( msg - > nick , self . nick ) ) {
2022-07-30 22:47:26 +00:00
cacheClear ( id ) ;
2020-02-08 04:44:03 +00:00
}
2022-07-30 22:47:26 +00:00
cacheRemove ( id , msg - > nick ) ;
2021-01-16 18:30:59 +00:00
enum Heat heat = filterCheck ( Cold , id , msg ) ;
2020-07-10 02:43:35 +00:00
if ( heat > Ice ) urlScan ( id , msg - > nick , msg - > params [ 1 ] ) ;
2020-02-06 06:16:35 +00:00
uiFormat (
2020-07-10 02:43:35 +00:00
id , heat , tagTime ( msg ) ,
2020-02-06 06:16:35 +00:00
" \3 %02d%s \3 \t leaves \3 %02d%s \3 %s%s " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ] ,
2020-04-03 21:10:52 +00:00
( msg - > params [ 1 ] ? " : " : " " ) , ( msg - > params [ 1 ] ? : " " )
2020-02-06 06:16:35 +00:00
) ;
2020-03-25 22:58:48 +00:00
logFormat (
id , tagTime ( msg ) , " %s leaves %s%s%s " ,
msg - > nick , msg - > params [ 0 ] ,
2020-04-03 21:10:52 +00:00
( msg - > params [ 1 ] ? " : " : " " ) , ( msg - > params [ 1 ] ? : " " )
2020-03-25 22:58:48 +00:00
) ;
2020-02-06 06:16:35 +00:00
}
2020-02-08 05:58:17 +00:00
static void handleKick ( struct Message * msg ) {
require ( msg , true , 2 ) ;
2020-02-16 03:19:55 +00:00
uint id = idFor ( msg - > params [ 0 ] ) ;
2020-02-11 22:52:55 +00:00
bool kicked = ! strcmp ( msg - > params [ 1 ] , self . nick ) ;
2023-01-22 08:51:12 +00:00
cacheInsert ( true , id , msg - > nick ) - > color = hash ( msg - > nick ) ;
2020-02-08 23:29:01 +00:00
urlScan ( id , msg - > nick , msg - > params [ 2 ] ) ;
2020-02-08 05:58:17 +00:00
uiFormat (
id , ( kicked ? Hot : Cold ) , tagTime ( msg ) ,
" %s \3 %02d%s \17 \t kicks \3 %02d%s \3 out of \3 %02d%s \3 %s%s " ,
( kicked ? " \26 " : " " ) ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick ,
2022-07-31 20:28:08 +00:00
cacheGet ( id , msg - > params [ 1 ] ) - > color , msg - > params [ 1 ] ,
2020-02-08 05:58:17 +00:00
hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ] ,
2020-04-03 21:10:52 +00:00
( msg - > params [ 2 ] ? " : " : " " ) , ( msg - > params [ 2 ] ? : " " )
2020-02-08 05:58:17 +00:00
) ;
2020-03-25 22:58:48 +00:00
logFormat (
id , tagTime ( msg ) , " %s kicks %s out of %s%s%s " ,
msg - > nick , msg - > params [ 1 ] , msg - > params [ 0 ] ,
2020-04-03 21:10:52 +00:00
( msg - > params [ 2 ] ? " : " : " " ) , ( msg - > params [ 2 ] ? : " " )
2020-03-25 22:58:48 +00:00
) ;
2022-07-30 22:47:26 +00:00
cacheRemove ( id , msg - > params [ 1 ] ) ;
if ( kicked ) cacheClear ( id ) ;
2020-02-08 05:58:17 +00:00
}
2020-02-08 05:36:23 +00:00
static void handleNick ( struct Message * msg ) {
require ( msg , true , 1 ) ;
2020-02-11 22:52:55 +00:00
if ( ! strcmp ( msg - > nick , self . nick ) ) {
2020-02-08 05:36:23 +00:00
set ( & self . nick , msg - > params [ 0 ] ) ;
2022-02-20 01:20:19 +00:00
inputUpdate ( ) ;
2020-02-08 05:36:23 +00:00
}
2022-07-30 22:47:26 +00:00
struct Cursor curs = { 0 } ;
2022-09-11 21:28:19 +00:00
for ( uint id ; ( id = cacheNextID ( & curs , msg - > nick ) ) ; ) {
2020-02-11 23:23:04 +00:00
if ( ! strcmp ( idNames [ id ] , msg - > nick ) ) {
set ( & idNames [ id ] , msg - > params [ 0 ] ) ;
}
2020-02-08 05:36:23 +00:00
uiFormat (
2021-01-16 18:30:59 +00:00
id , filterCheck ( Cold , id , msg ) , tagTime ( msg ) ,
2020-02-08 05:36:23 +00:00
" \3 %02d%s \3 \t is now known as \3 %02d%s \3 " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , hash ( msg - > nick ) , msg - > params [ 0 ]
2020-02-08 05:36:23 +00:00
) ;
2020-03-25 22:58:48 +00:00
if ( id = = Network ) continue ;
logFormat (
id , tagTime ( msg ) , " %s is now known as %s " ,
msg - > nick , msg - > params [ 0 ]
) ;
2020-02-08 05:36:23 +00:00
}
2022-07-30 22:47:26 +00:00
cacheReplace ( true , msg - > nick , msg - > params [ 0 ] ) ;
2020-02-08 05:36:23 +00:00
}
2020-12-30 05:15:11 +00:00
static void handleSetname ( struct Message * msg ) {
require ( msg , true , 1 ) ;
2022-07-30 22:47:26 +00:00
struct Cursor curs = { 0 } ;
2022-09-11 21:28:19 +00:00
for ( uint id ; ( id = cacheNextID ( & curs , msg - > nick ) ) ; ) {
2020-12-30 05:15:11 +00:00
uiFormat (
2021-01-16 18:30:59 +00:00
id , filterCheck ( Cold , id , msg ) , tagTime ( msg ) ,
2021-06-12 14:35:36 +00:00
" \3 %02d%s \3 \t is now known as \3 %02d%s \3 (%s \17 ) " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , hash ( msg - > nick ) , msg - > nick ,
2020-12-30 05:15:11 +00:00
msg - > params [ 0 ]
) ;
}
}
2020-02-08 05:36:23 +00:00
static void handleQuit ( struct Message * msg ) {
require ( msg , true , 0 ) ;
2022-07-30 22:47:26 +00:00
struct Cursor curs = { 0 } ;
2022-09-11 21:28:19 +00:00
for ( uint id ; ( id = cacheNextID ( & curs , msg - > nick ) ) ; ) {
2021-01-16 18:30:59 +00:00
enum Heat heat = filterCheck ( Cold , id , msg ) ;
2020-07-10 02:43:35 +00:00
if ( heat > Ice ) urlScan ( id , msg - > nick , msg - > params [ 0 ] ) ;
2020-02-08 05:36:23 +00:00
uiFormat (
2020-07-10 02:43:35 +00:00
id , heat , tagTime ( msg ) ,
2020-02-08 05:36:23 +00:00
" \3 %02d%s \3 \t leaves%s%s " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick ,
2020-04-03 21:10:52 +00:00
( msg - > params [ 0 ] ? " : " : " " ) , ( msg - > params [ 0 ] ? : " " )
2020-02-08 05:36:23 +00:00
) ;
2020-03-25 22:58:48 +00:00
if ( id = = Network ) continue ;
logFormat (
id , tagTime ( msg ) , " %s leaves%s%s " ,
msg - > nick ,
2020-04-03 21:10:52 +00:00
( msg - > params [ 0 ] ? " : " : " " ) , ( msg - > params [ 0 ] ? : " " )
2020-03-25 22:58:48 +00:00
) ;
2020-02-08 05:36:23 +00:00
}
2022-07-30 22:47:26 +00:00
cacheRemove ( None , msg - > nick ) ;
2020-02-08 05:36:23 +00:00
}
2020-02-20 08:18:48 +00:00
static void handleInvite ( struct Message * msg ) {
require ( msg , true , 2 ) ;
if ( ! strcmp ( msg - > params [ 0 ] , self . nick ) ) {
2021-07-02 19:24:11 +00:00
set ( & self . invited , msg - > params [ 1 ] ) ;
2020-02-20 08:18:48 +00:00
uiFormat (
2021-01-16 18:30:59 +00:00
Network , filterCheck ( Hot , Network , msg ) , tagTime ( msg ) ,
2020-02-20 08:18:48 +00:00
" \3 %02d%s \3 \t invites you to \3 %02d%s \3 " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , hash ( msg - > params [ 1 ] ) , msg - > params [ 1 ]
2020-02-20 08:18:48 +00:00
) ;
} else {
2020-03-25 22:58:48 +00:00
uint id = idFor ( msg - > params [ 1 ] ) ;
2020-02-20 08:18:48 +00:00
uiFormat (
2020-03-25 22:58:48 +00:00
id , Cold , tagTime ( msg ) ,
2020-02-20 08:18:48 +00:00
" \3 %02d%s \3 \t invites %s to \3 %02d%s \3 " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick ,
2020-02-20 08:18:48 +00:00
msg - > params [ 0 ] ,
hash ( msg - > params [ 1 ] ) , msg - > params [ 1 ]
) ;
2020-03-25 22:58:48 +00:00
logFormat (
id , tagTime ( msg ) , " %s invites %s to %s " ,
msg - > nick , msg - > params [ 0 ] , msg - > params [ 1 ]
) ;
2020-02-20 08:18:48 +00:00
}
2020-02-20 06:52:58 +00:00
}
2020-02-20 08:18:48 +00:00
static void handleReplyInviting ( struct Message * msg ) {
require ( msg , false , 3 ) ;
struct Message invite = {
. nick = self . nick ,
. user = self . user ,
. cmd = " INVITE " ,
. params [ 0 ] = msg - > params [ 1 ] ,
. params [ 1 ] = msg - > params [ 2 ] ,
} ;
handleInvite ( & invite ) ;
}
static void handleErrorUserOnChannel ( struct Message * msg ) {
2021-05-20 20:36:39 +00:00
require ( msg , false , 3 ) ;
2020-02-20 08:18:48 +00:00
uint id = idFor ( msg - > params [ 2 ] ) ;
2020-02-20 07:13:23 +00:00
uiFormat (
2021-01-26 22:24:05 +00:00
id , Warm , tagTime ( msg ) ,
2020-02-20 08:18:48 +00:00
" \3 %02d%s \3 is already in \3 %02d%s \3 " ,
2022-07-31 20:28:08 +00:00
cacheGet ( id , msg - > params [ 1 ] ) - > color , msg - > params [ 1 ] ,
2020-02-20 08:18:48 +00:00
hash ( msg - > params [ 2 ] ) , msg - > params [ 2 ]
2020-02-20 07:13:23 +00:00
) ;
}
2020-02-08 05:25:09 +00:00
static void handleReplyNames ( struct Message * msg ) {
require ( msg , false , 4 ) ;
2020-02-16 03:19:55 +00:00
uint id = idFor ( msg - > params [ 2 ] ) ;
2021-06-07 04:09:58 +00:00
char buf [ 1024 ] ;
char * ptr = buf , * end = & buf [ sizeof ( buf ) ] ;
2020-02-08 05:25:09 +00:00
while ( msg - > params [ 3 ] ) {
char * name = strsep ( & msg - > params [ 3 ] , " " ) ;
2020-02-16 03:19:05 +00:00
char * prefixes = strsep ( & name , " ! " ) ;
char * nick = & prefixes [ strspn ( prefixes , network . prefixes ) ] ;
2020-02-08 05:25:09 +00:00
char * user = strsep ( & name , " @ " ) ;
2023-01-22 08:51:12 +00:00
enum Color color = ( user ? hash ( nick ) : Default ) ; /* NOTE(bx): changed this from user to nick, bc it was getting gur wrong hash and color for people using bouncers + bots */
2022-08-03 00:46:25 +00:00
uint bits = 0 ;
for ( char * p = prefixes ; p < nick ; + + p ) {
bits | = prefixBit ( * p ) ;
}
2022-07-31 20:28:08 +00:00
struct Entry * entry = cacheInsert ( false , id , nick ) ;
if ( user ) entry - > color = color ;
2022-08-03 00:46:25 +00:00
entry - > prefixBits = bits ;
2021-02-21 19:23:17 +00:00
if ( ! replies [ ReplyNames ] & & ! replies [ ReplyNamesAuto ] ) continue ;
2021-06-07 04:09:58 +00:00
ptr = seprintf (
ptr , end , " %s \3 %02d%s \3 " , ( ptr > buf ? " , " : " " ) , color , prefixes
) ;
2020-02-08 05:25:09 +00:00
}
2021-06-07 04:09:58 +00:00
if ( ptr = = buf ) return ;
2020-02-08 05:25:09 +00:00
uiFormat (
2021-02-21 19:23:17 +00:00
id , ( replies [ ReplyNamesAuto ] ? Cold : Warm ) , tagTime ( msg ) ,
2020-09-30 23:21:34 +00:00
" In \3 %02d%s \3 are %s " ,
2020-02-08 05:25:09 +00:00
hash ( msg - > params [ 2 ] ) , msg - > params [ 2 ] , buf
) ;
}
2021-02-21 19:23:17 +00:00
static void handleReplyEndOfNames ( struct Message * msg ) {
( void ) msg ;
if ( replies [ ReplyNamesAuto ] ) {
replies [ ReplyNamesAuto ] - - ;
} else if ( replies [ ReplyNames ] ) {
replies [ ReplyNames ] - - ;
}
}
2020-02-06 07:07:39 +00:00
static void handleReplyNoTopic ( struct Message * msg ) {
require ( msg , false , 2 ) ;
uiFormat (
2021-01-26 22:24:05 +00:00
idFor ( msg - > params [ 1 ] ) , Warm , tagTime ( msg ) ,
2020-02-06 07:07:39 +00:00
" There is no sign in \3 %02d%s \3 " ,
hash ( msg - > params [ 1 ] ) , msg - > params [ 1 ]
) ;
}
2022-07-30 22:47:26 +00:00
static void topicCache ( uint id , const char * topic ) {
2020-07-13 14:22:33 +00:00
char buf [ 512 ] ;
2022-07-30 22:47:26 +00:00
struct Cursor curs = { 0 } ;
2022-07-30 22:55:09 +00:00
const char * prev = cacheComplete ( & curs , id , " /topic " ) ;
2020-07-13 14:22:33 +00:00
if ( prev ) {
snprintf ( buf , sizeof ( buf ) , " %s " , prev ) ;
2022-07-30 22:47:26 +00:00
cacheRemove ( id , buf ) ;
2020-07-13 14:22:33 +00:00
}
if ( topic ) {
snprintf ( buf , sizeof ( buf ) , " /topic %s " , topic ) ;
2022-07-30 22:47:26 +00:00
cacheInsert ( false , id , buf ) ;
2020-07-13 14:22:33 +00:00
}
}
2020-02-06 07:07:39 +00:00
static void handleReplyTopic ( struct Message * msg ) {
require ( msg , false , 3 ) ;
2020-07-13 14:22:33 +00:00
uint id = idFor ( msg - > params [ 1 ] ) ;
2022-07-30 22:47:26 +00:00
topicCache ( id , msg - > params [ 2 ] ) ;
2021-02-21 19:23:17 +00:00
if ( ! replies [ ReplyTopic ] & & ! replies [ ReplyTopicAuto ] ) return ;
2020-02-08 23:29:01 +00:00
urlScan ( id , NULL , msg - > params [ 2 ] ) ;
2020-02-06 07:07:39 +00:00
uiFormat (
2021-02-21 19:23:17 +00:00
id , ( replies [ ReplyTopicAuto ] ? Cold : Warm ) , tagTime ( msg ) ,
2020-02-06 07:07:39 +00:00
" The sign in \3 %02d%s \3 reads: %s " ,
hash ( msg - > params [ 1 ] ) , msg - > params [ 1 ] , msg - > params [ 2 ]
) ;
2020-03-25 22:58:48 +00:00
logFormat (
id , tagTime ( msg ) , " The sign in %s reads: %s " ,
msg - > params [ 1 ] , msg - > params [ 2 ]
) ;
2021-02-21 19:23:17 +00:00
if ( replies [ ReplyTopicAuto ] ) {
replies [ ReplyTopicAuto ] - - ;
} else {
replies [ ReplyTopic ] - - ;
}
2020-02-06 07:07:39 +00:00
}
2020-11-08 17:42:01 +00:00
static void swap ( wchar_t * a , wchar_t * b ) {
wchar_t x = * a ;
* a = * b ;
* b = x ;
}
2022-06-24 18:29:44 +00:00
static char * highlightMiddle (
char * ptr , char * end , enum Color color ,
wchar_t * str , size_t pre , size_t suf
) {
wchar_t nul = L ' \0 ' ;
swap ( & str [ pre ] , & nul ) ;
ptr = seprintf ( ptr , end , " %ls " , str ) ;
swap ( & str [ pre ] , & nul ) ;
swap ( & str [ suf ] , & nul ) ;
if ( hashBound ) {
ptr = seprintf (
ptr , end , " \3 %02d,%02d%ls \3 %02d,%02d " ,
Default , color , & str [ pre ] , Default , Default
) ;
} else {
ptr = seprintf ( ptr , end , " \26 %ls \26 " , & str [ pre ] ) ;
}
swap ( & str [ suf ] , & nul ) ;
ptr = seprintf ( ptr , end , " %ls " , & str [ suf ] ) ;
return ptr ;
}
2020-02-06 07:07:39 +00:00
static void handleTopic ( struct Message * msg ) {
require ( msg , true , 2 ) ;
2020-02-16 03:19:55 +00:00
uint id = idFor ( msg - > params [ 0 ] ) ;
2020-11-08 17:42:01 +00:00
if ( ! msg - > params [ 1 ] [ 0 ] ) {
2022-07-30 22:47:26 +00:00
topicCache ( id , NULL ) ;
2020-02-06 07:07:39 +00:00
uiFormat (
2020-02-08 23:29:01 +00:00
id , Warm , tagTime ( msg ) ,
2020-02-06 07:07:39 +00:00
" \3 %02d%s \3 \t removes the sign in \3 %02d%s \3 " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ]
2020-02-06 07:07:39 +00:00
) ;
2020-03-25 22:58:48 +00:00
logFormat (
id , tagTime ( msg ) , " %s removes the sign in %s " ,
msg - > nick , msg - > params [ 0 ]
) ;
2020-11-08 17:42:01 +00:00
return ;
}
2022-07-30 22:47:26 +00:00
struct Cursor curs = { 0 } ;
2022-07-30 22:55:09 +00:00
const char * prev = cacheComplete ( & curs , id , " /topic " ) ;
2020-11-08 17:42:01 +00:00
if ( prev ) {
prev + = 7 ;
} else {
goto plain ;
}
wchar_t old [ 512 ] ;
wchar_t new [ 512 ] ;
if ( swprintf ( old , ARRAY_LEN ( old ) , L " %s " , prev ) < 0 ) goto plain ;
if ( swprintf ( new , ARRAY_LEN ( new ) , L " %s " , msg - > params [ 1 ] ) < 0 ) goto plain ;
size_t pre ;
for ( pre = 0 ; old [ pre ] & & new [ pre ] & & old [ pre ] = = new [ pre ] ; + + pre ) ;
2022-06-24 18:29:44 +00:00
size_t osuf = wcslen ( old ) ;
size_t nsuf = wcslen ( new ) ;
while ( osuf > pre & & nsuf > pre & & old [ osuf - 1 ] = = new [ nsuf - 1 ] ) {
2020-11-08 17:42:01 +00:00
osuf - - ;
nsuf - - ;
2020-02-06 07:07:39 +00:00
}
2020-11-08 17:42:01 +00:00
2022-06-24 18:29:44 +00:00
char buf [ 1024 ] ;
char * ptr = buf , * end = & buf [ sizeof ( buf ) ] ;
ptr = seprintf (
ptr , end , " \3 %02d%s \3 \t takes down the sign in \3 %02d%s \3 : " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ]
2022-06-24 18:29:44 +00:00
) ;
ptr = highlightMiddle ( ptr , end , Brown , old , pre , osuf ) ;
2022-06-26 20:38:19 +00:00
if ( osuf ! = pre ) uiWrite ( id , Cold , tagTime ( msg ) , buf ) ;
2022-06-24 18:29:44 +00:00
ptr = buf ;
ptr = seprintf (
ptr , end , " \3 %02d%s \3 \t places a new sign in \3 %02d%s \3 : " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ]
2022-06-24 18:29:44 +00:00
) ;
ptr = highlightMiddle ( ptr , end , Green , new , pre , nsuf ) ;
uiWrite ( id , Warm , tagTime ( msg ) , buf ) ;
goto log ;
2020-11-08 17:42:01 +00:00
plain :
uiFormat (
id , Warm , tagTime ( msg ) ,
" \3 %02d%s \3 \t places a new sign in \3 %02d%s \3 : %s " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ] ,
2022-06-24 18:29:44 +00:00
msg - > params [ 1 ]
2020-11-08 17:42:01 +00:00
) ;
2022-06-24 18:29:44 +00:00
log :
2020-11-08 17:42:01 +00:00
logFormat (
id , tagTime ( msg ) , " %s places a new sign in %s: %s " ,
msg - > nick , msg - > params [ 0 ] , msg - > params [ 1 ]
) ;
2022-07-30 22:47:26 +00:00
topicCache ( id , msg - > params [ 1 ] ) ;
2022-06-24 18:29:44 +00:00
urlScan ( id , msg - > nick , msg - > params [ 1 ] ) ;
2020-02-06 07:07:39 +00:00
}
2020-03-18 12:33:42 +00:00
static const char * UserModes [ 256 ] = {
[ ' O ' ] = " local oper " ,
[ ' i ' ] = " invisible " ,
[ ' o ' ] = " oper " ,
[ ' r ' ] = " registered " ,
[ ' w ' ] = " wallops " ,
} ;
static void handleReplyUserModeIs ( struct Message * msg ) {
require ( msg , false , 2 ) ;
2021-06-07 04:09:58 +00:00
char buf [ 1024 ] ;
char * ptr = buf , * end = & buf [ sizeof ( buf ) ] ;
2020-03-18 12:33:42 +00:00
for ( char * ch = msg - > params [ 1 ] ; * ch ; + + ch ) {
if ( * ch = = ' + ' ) continue ;
2020-03-28 16:08:10 +00:00
const char * name = UserModes [ ( byte ) * ch ] ;
2021-06-07 04:09:58 +00:00
ptr = seprintf (
ptr , end , " , +%c%s%s " , * ch , ( name ? " " : " " ) , ( name ? : " " )
) ;
2020-03-18 12:33:42 +00:00
}
uiFormat (
Network , Warm , tagTime ( msg ) ,
2020-03-22 15:40:58 +00:00
" \3 %02d%s \3 \t is %s " ,
2021-06-07 04:09:58 +00:00
self . color , self . nick , ( ptr > buf ? & buf [ 2 ] : " modeless " )
2020-03-18 12:33:42 +00:00
) ;
}
2020-03-22 15:18:21 +00:00
static const char * ChanModes [ 256 ] = {
[ ' a ' ] = " protected " ,
[ ' h ' ] = " halfop " ,
2020-02-25 07:54:15 +00:00
[ ' i ' ] = " invite-only " ,
[ ' k ' ] = " key " ,
[ ' l ' ] = " client limit " ,
[ ' m ' ] = " moderated " ,
[ ' n ' ] = " no external messages " ,
[ ' o ' ] = " operator " ,
2020-03-22 15:18:21 +00:00
[ ' q ' ] = " founder " ,
2020-02-25 07:54:15 +00:00
[ ' s ' ] = " secret " ,
[ ' t ' ] = " protected topic " ,
[ ' v ' ] = " voice " ,
} ;
2020-03-22 15:40:58 +00:00
static void handleReplyChannelModeIs ( struct Message * msg ) {
require ( msg , false , 3 ) ;
uint param = 3 ;
2021-06-07 04:09:58 +00:00
char buf [ 1024 ] ;
char * ptr = buf , * end = & buf [ sizeof ( buf ) ] ;
2020-03-22 15:40:58 +00:00
for ( char * ch = msg - > params [ 2 ] ; * ch ; + + ch ) {
if ( * ch = = ' + ' ) continue ;
const char * name = ChanModes [ ( byte ) * ch ] ;
if (
strchr ( network . paramModes , * ch ) | |
strchr ( network . setParamModes , * ch )
) {
assert ( param < ParamCap ) ;
2021-06-07 04:09:58 +00:00
ptr = seprintf (
ptr , end , " , +%c%s%s %s " ,
2020-04-03 21:10:52 +00:00
* ch , ( name ? " " : " " ) , ( name ? : " " ) ,
2020-03-28 16:08:10 +00:00
msg - > params [ param + + ]
2020-03-22 15:40:58 +00:00
) ;
} else {
2021-06-07 04:09:58 +00:00
ptr = seprintf (
ptr , end , " , +%c%s%s " ,
2020-04-03 21:10:52 +00:00
* ch , ( name ? " " : " " ) , ( name ? : " " )
2020-03-28 16:08:10 +00:00
) ;
2020-03-22 15:40:58 +00:00
}
}
uiFormat (
2021-01-26 22:24:05 +00:00
idFor ( msg - > params [ 1 ] ) , Warm , tagTime ( msg ) ,
2020-03-28 16:08:10 +00:00
" \3 %02d%s \3 \t is %s " ,
2020-03-22 15:40:58 +00:00
hash ( msg - > params [ 1 ] ) , msg - > params [ 1 ] ,
2021-06-07 04:09:58 +00:00
( ptr > buf ? & buf [ 2 ] : " modeless " )
2020-03-22 15:40:58 +00:00
) ;
}
2020-02-20 09:18:25 +00:00
static void handleMode ( struct Message * msg ) {
require ( msg , true , 2 ) ;
2020-03-22 15:26:16 +00:00
2020-02-20 09:18:25 +00:00
if ( ! strchr ( network . chanTypes , msg - > params [ 0 ] [ 0 ] ) ) {
2020-03-22 15:26:16 +00:00
bool set = true ;
for ( char * ch = msg - > params [ 1 ] ; * ch ; + + ch ) {
if ( * ch = = ' + ' ) { set = true ; continue ; }
if ( * ch = = ' - ' ) { set = false ; continue ; }
const char * name = UserModes [ ( byte ) * ch ] ;
2020-03-28 16:08:10 +00:00
uiFormat (
Network , Warm , tagTime ( msg ) ,
" \3 %02d%s \3 \t %ssets \3 %02d%s \3 %c%c%s%s " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick ,
2020-03-28 16:08:10 +00:00
( set ? " " : " un " ) ,
self . color , msg - > params [ 0 ] ,
2020-04-03 21:10:52 +00:00
set [ " -+ " ] , * ch , ( name ? " " : " " ) , ( name ? : " " )
2020-03-22 15:26:16 +00:00
) ;
}
2020-02-20 09:18:25 +00:00
return ;
}
2020-02-20 09:53:06 +00:00
2020-02-20 09:18:25 +00:00
uint id = idFor ( msg - > params [ 0 ] ) ;
2020-03-22 15:18:21 +00:00
bool set = true ;
2020-03-28 16:08:10 +00:00
uint i = 2 ;
2020-02-20 09:18:25 +00:00
for ( char * ch = msg - > params [ 1 ] ; * ch ; + + ch ) {
2020-03-22 15:18:21 +00:00
if ( * ch = = ' + ' ) { set = true ; continue ; }
if ( * ch = = ' - ' ) { set = false ; continue ; }
2020-03-28 16:08:10 +00:00
const char * verb = ( set ? " sets " : " unsets " ) ;
2020-03-22 15:18:21 +00:00
const char * name = ChanModes [ ( byte ) * ch ] ;
if ( * ch = = network . excepts ) name = " except " ;
if ( * ch = = network . invex ) name = " invite " ;
2020-03-28 16:08:10 +00:00
const char * mode = ( const char [ ] ) {
set [ " -+ " ] , * ch , ( name ? ' ' : ' \0 ' ) , ' \0 '
} ;
if ( ! name ) name = " " ;
2020-03-22 15:18:21 +00:00
if ( strchr ( network . prefixModes , * ch ) ) {
2020-04-05 17:17:38 +00:00
if ( i > = ParamCap | | ! msg - > params [ i ] ) {
errx ( EX_PROTOCOL , " MODE missing %s parameter " , mode ) ;
}
2020-03-28 16:08:10 +00:00
char * nick = msg - > params [ i + + ] ;
2020-03-22 15:18:21 +00:00
char prefix = network . prefixes [
strchr ( network . prefixModes , * ch ) - network . prefixModes
] ;
2022-08-03 00:46:25 +00:00
if ( set ) {
cacheInsert ( false , id , nick ) - > prefixBits | = prefixBit ( prefix ) ;
} else {
cacheInsert ( false , id , nick ) - > prefixBits & = ~ prefixBit ( prefix ) ;
}
2020-03-28 16:08:10 +00:00
uiFormat (
id , Cold , tagTime ( msg ) ,
" \3 %02d%s \3 \t %s \3 %02d%c%s \3 %s%s in \3 %02d%s \3 " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , verb ,
2022-07-31 20:28:08 +00:00
cacheGet ( id , nick ) - > color , prefix , nick ,
2020-03-28 16:08:10 +00:00
mode , name , hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ]
) ;
logFormat (
id , tagTime ( msg ) , " %s %s %c%s %s%s in %s " ,
msg - > nick , verb , prefix , nick , mode , name , msg - > params [ 0 ]
2020-02-20 09:18:25 +00:00
) ;
2020-03-22 15:18:21 +00:00
}
2020-02-20 09:53:06 +00:00
2020-03-22 15:18:21 +00:00
if ( strchr ( network . listModes , * ch ) ) {
2020-04-05 17:17:38 +00:00
if ( i > = ParamCap | | ! msg - > params [ i ] ) {
errx ( EX_PROTOCOL , " MODE missing %s parameter " , mode ) ;
}
2020-03-28 16:08:10 +00:00
char * mask = msg - > params [ i + + ] ;
2020-02-20 09:53:06 +00:00
if ( * ch = = ' b ' ) {
2020-03-28 16:08:10 +00:00
verb = ( set ? " bans " : " unbans " ) ;
uiFormat (
id , Cold , tagTime ( msg ) ,
" \3 %02d%s \3 \t %s %c%c %s from \3 %02d%s \3 " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , verb , set [ " -+ " ] , * ch , mask ,
2020-02-20 09:53:06 +00:00
hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ]
) ;
2020-03-28 16:08:10 +00:00
logFormat (
id , tagTime ( msg ) , " %s %s %c%c %s from %s " ,
msg - > nick , verb , set [ " -+ " ] , * ch , mask , msg - > params [ 0 ]
) ;
2020-03-22 15:18:21 +00:00
} else {
2020-03-28 16:08:10 +00:00
verb = ( set ? " adds " : " removes " ) ;
const char * to = ( set ? " to " : " from " ) ;
uiFormat (
id , Cold , tagTime ( msg ) ,
" \3 %02d%s \3 \t %s %s %s the \3 %02d%s \3 %s%s list " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , verb , mask , to ,
2020-03-28 16:08:10 +00:00
hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ] , mode , name
) ;
logFormat (
id , tagTime ( msg ) , " %s %s %s %s the %s %s%s list " ,
msg - > nick , verb , mask , to , msg - > params [ 0 ] , mode , name
2020-03-22 15:18:21 +00:00
) ;
2020-02-20 09:53:06 +00:00
}
2020-03-22 15:18:21 +00:00
}
2020-02-20 09:53:06 +00:00
2020-03-22 15:18:21 +00:00
if ( strchr ( network . paramModes , * ch ) ) {
2020-04-05 17:17:38 +00:00
if ( i > = ParamCap | | ! msg - > params [ i ] ) {
errx ( EX_PROTOCOL , " MODE missing %s parameter " , mode ) ;
}
2020-03-28 16:08:10 +00:00
char * param = msg - > params [ i + + ] ;
uiFormat (
id , Cold , tagTime ( msg ) ,
" \3 %02d%s \3 \t %s \3 %02d%s \3 %s%s %s " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , verb ,
2020-03-28 16:08:10 +00:00
hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ] , mode , name , param
) ;
logFormat (
id , tagTime ( msg ) , " %s %s %s %s%s %s " ,
msg - > nick , verb , msg - > params [ 0 ] , mode , name , param
2020-02-25 07:54:15 +00:00
) ;
2020-03-22 15:18:21 +00:00
}
2020-02-25 07:54:15 +00:00
2020-03-22 15:18:21 +00:00
if ( strchr ( network . setParamModes , * ch ) & & set ) {
2020-04-05 17:17:38 +00:00
if ( i > = ParamCap | | ! msg - > params [ i ] ) {
errx ( EX_PROTOCOL , " MODE missing %s parameter " , mode ) ;
}
2020-03-28 16:08:10 +00:00
char * param = msg - > params [ i + + ] ;
uiFormat (
id , Cold , tagTime ( msg ) ,
" \3 %02d%s \3 \t %s \3 %02d%s \3 %s%s %s " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , verb ,
2020-03-28 16:08:10 +00:00
hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ] , mode , name , param
) ;
logFormat (
id , tagTime ( msg ) , " %s %s %s %s%s %s " ,
msg - > nick , verb , msg - > params [ 0 ] , mode , name , param
2020-03-22 15:18:21 +00:00
) ;
2020-02-20 09:18:25 +00:00
} else if ( strchr ( network . setParamModes , * ch ) ) {
2020-03-28 16:08:10 +00:00
uiFormat (
id , Cold , tagTime ( msg ) ,
" \3 %02d%s \3 \t %s \3 %02d%s \3 %s%s " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , verb ,
2020-03-28 16:08:10 +00:00
hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ] , mode , name
) ;
logFormat (
id , tagTime ( msg ) , " %s %s %s %s%s " ,
msg - > nick , verb , msg - > params [ 0 ] , mode , name
2020-03-22 15:18:21 +00:00
) ;
}
2020-02-25 07:54:15 +00:00
2020-03-22 15:18:21 +00:00
if ( strchr ( network . channelModes , * ch ) ) {
2020-03-28 16:08:10 +00:00
uiFormat (
id , Cold , tagTime ( msg ) ,
" \3 %02d%s \3 \t %s \3 %02d%s \3 %s%s " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , verb ,
2020-03-28 16:08:10 +00:00
hash ( msg - > params [ 0 ] ) , msg - > params [ 0 ] , mode , name
) ;
logFormat (
id , tagTime ( msg ) , " %s %s %s %s%s " ,
msg - > nick , verb , msg - > params [ 0 ] , mode , name
2020-02-25 07:54:15 +00:00
) ;
2020-02-20 09:18:25 +00:00
}
}
}
2020-02-20 10:04:58 +00:00
static void handleErrorChanopPrivsNeeded ( struct Message * msg ) {
require ( msg , false , 3 ) ;
uiFormat (
2021-01-26 22:24:05 +00:00
idFor ( msg - > params [ 1 ] ) , Warm , tagTime ( msg ) ,
2020-02-20 10:04:58 +00:00
" %s " , msg - > params [ 2 ]
) ;
}
2020-02-20 08:18:48 +00:00
static void handleErrorUserNotInChannel ( struct Message * msg ) {
require ( msg , false , 4 ) ;
uiFormat (
2021-01-26 22:24:05 +00:00
idFor ( msg - > params [ 2 ] ) , Warm , tagTime ( msg ) ,
2020-02-20 08:18:48 +00:00
" %s \t is not in \3 %02d%s \3 " ,
msg - > params [ 1 ] , hash ( msg - > params [ 2 ] ) , msg - > params [ 2 ]
) ;
}
static void handleErrorBanListFull ( struct Message * msg ) {
require ( msg , false , 4 ) ;
uiFormat (
2021-01-26 22:24:05 +00:00
idFor ( msg - > params [ 1 ] ) , Warm , tagTime ( msg ) ,
2020-04-03 21:10:52 +00:00
" %s " , ( msg - > params [ 4 ] ? : msg - > params [ 3 ] )
2020-02-20 08:18:48 +00:00
) ;
}
2020-02-20 02:47:16 +00:00
static void handleReplyBanList ( struct Message * msg ) {
require ( msg , false , 3 ) ;
uint id = idFor ( msg - > params [ 1 ] ) ;
if ( msg - > params [ 3 ] & & msg - > params [ 4 ] ) {
char since [ sizeof ( " 0000-00-00 00:00:00 " ) ] ;
time_t time = strtol ( msg - > params [ 4 ] , NULL , 10 ) ;
strftime ( since , sizeof ( since ) , " %F %T " , localtime ( & time ) ) ;
uiFormat (
2021-01-26 22:24:05 +00:00
id , Warm , tagTime ( msg ) ,
2020-02-20 08:44:57 +00:00
" Banned from \3 %02d%s \3 since %s by \3 %02d%s \3 : %s " ,
2020-02-20 02:47:16 +00:00
hash ( msg - > params [ 1 ] ) , msg - > params [ 1 ] ,
2022-07-31 20:28:08 +00:00
since , cacheGet ( id , msg - > params [ 3 ] ) - > color , msg - > params [ 3 ] ,
2020-02-20 02:47:16 +00:00
msg - > params [ 2 ]
) ;
} else {
uiFormat (
2021-01-26 22:24:05 +00:00
id , Warm , tagTime ( msg ) ,
2020-02-20 08:44:57 +00:00
" Banned from \3 %02d%s \3 : %s " ,
2020-02-20 02:47:16 +00:00
hash ( msg - > params [ 1 ] ) , msg - > params [ 1 ] , msg - > params [ 2 ]
) ;
}
}
2020-02-25 07:12:35 +00:00
static void onList ( const char * list , struct Message * msg ) {
2020-12-30 21:49:55 +00:00
require ( msg , false , 3 ) ;
2020-02-25 07:12:35 +00:00
uint id = idFor ( msg - > params [ 1 ] ) ;
if ( msg - > params [ 3 ] & & msg - > params [ 4 ] ) {
char since [ sizeof ( " 0000-00-00 00:00:00 " ) ] ;
time_t time = strtol ( msg - > params [ 4 ] , NULL , 10 ) ;
strftime ( since , sizeof ( since ) , " %F %T " , localtime ( & time ) ) ;
uiFormat (
2021-01-26 22:24:05 +00:00
id , Warm , tagTime ( msg ) ,
2020-02-25 07:12:35 +00:00
" On the \3 %02d%s \3 %s list since %s by \3 %02d%s \3 : %s " ,
hash ( msg - > params [ 1 ] ) , msg - > params [ 1 ] , list ,
2022-07-31 20:28:08 +00:00
since , cacheGet ( id , msg - > params [ 3 ] ) - > color , msg - > params [ 3 ] ,
2020-02-25 07:12:35 +00:00
msg - > params [ 2 ]
) ;
} else {
uiFormat (
2021-01-26 22:24:05 +00:00
id , Warm , tagTime ( msg ) ,
2020-02-25 07:12:35 +00:00
" On the \3 %02d%s \3 %s list: %s " ,
hash ( msg - > params [ 1 ] ) , msg - > params [ 1 ] , list , msg - > params [ 2 ]
) ;
}
}
static void handleReplyExceptList ( struct Message * msg ) {
onList ( " except " , msg ) ;
}
static void handleReplyInviteList ( struct Message * msg ) {
onList ( " invite " , msg ) ;
}
2020-02-12 07:39:23 +00:00
static void handleReplyList ( struct Message * msg ) {
2022-09-23 14:55:53 +00:00
require ( msg , false , 3 ) ;
2020-02-12 07:39:23 +00:00
uiFormat (
Network , Warm , tagTime ( msg ) ,
" In \3 %02d%s \3 are %ld under the banner: %s " ,
hash ( msg - > params [ 1 ] ) , msg - > params [ 1 ] ,
strtol ( msg - > params [ 2 ] , NULL , 10 ) ,
2022-09-23 14:55:53 +00:00
( msg - > params [ 3 ] ? : " " )
2020-02-12 07:39:23 +00:00
) ;
}
2020-02-09 21:45:49 +00:00
static void handleReplyWhoisUser ( struct Message * msg ) {
require ( msg , false , 6 ) ;
2022-07-31 20:28:08 +00:00
cacheInsert ( true , Network , msg - > params [ 1 ] ) - > color = hash ( msg - > params [ 2 ] ) ;
2020-02-09 21:45:49 +00:00
uiFormat (
Network , Warm , tagTime ( msg ) ,
2021-04-28 01:06:16 +00:00
" \3 %02d%s \3 \t is %s!%s@%s (%s \17 ) " ,
2020-02-09 21:45:49 +00:00
hash ( msg - > params [ 2 ] ) , msg - > params [ 1 ] ,
msg - > params [ 1 ] , msg - > params [ 2 ] , msg - > params [ 3 ] , msg - > params [ 5 ]
) ;
}
static void handleReplyWhoisServer ( struct Message * msg ) {
2020-12-30 21:49:55 +00:00
if ( ! replies [ ReplyWhois ] & & ! replies [ ReplyWhowas ] ) return ;
2020-02-09 21:45:49 +00:00
require ( msg , false , 4 ) ;
uiFormat (
Network , Warm , tagTime ( msg ) ,
2020-12-30 19:29:32 +00:00
" \3 %02d%s \3 \t %s connected to %s (%s) " ,
2022-07-31 20:28:08 +00:00
cacheGet ( Network , msg - > params [ 1 ] ) - > color , msg - > params [ 1 ] ,
2020-12-30 21:49:55 +00:00
( replies [ ReplyWhowas ] ? " was " : " is " ) , msg - > params [ 2 ] , msg - > params [ 3 ]
2020-02-09 21:45:49 +00:00
) ;
}
static void handleReplyWhoisIdle ( struct Message * msg ) {
require ( msg , false , 3 ) ;
unsigned long idle = strtoul ( msg - > params [ 2 ] , NULL , 10 ) ;
const char * unit = " second " ;
2020-02-11 09:00:25 +00:00
if ( idle / 60 ) {
idle / = 60 ; unit = " minute " ;
if ( idle / 60 ) {
idle / = 60 ; unit = " hour " ;
if ( idle / 24 ) {
idle / = 24 ; unit = " day " ;
}
}
}
2020-02-20 02:29:51 +00:00
char signon [ sizeof ( " 0000-00-00 00:00:00 " ) ] ;
2020-04-03 21:10:52 +00:00
time_t time = strtol ( ( msg - > params [ 3 ] ? : " " ) , NULL , 10 ) ;
2020-02-20 02:29:51 +00:00
strftime ( signon , sizeof ( signon ) , " %F %T " , localtime ( & time ) ) ;
2020-02-09 21:45:49 +00:00
uiFormat (
Network , Warm , tagTime ( msg ) ,
2020-02-20 02:29:51 +00:00
" \3 %02d%s \3 \t is idle for %lu %s%s%s%s " ,
2022-07-31 20:28:08 +00:00
cacheGet ( Network , msg - > params [ 1 ] ) - > color , msg - > params [ 1 ] ,
2020-02-09 21:45:49 +00:00
idle , unit , ( idle ! = 1 ? " s " : " " ) ,
2020-04-03 21:10:52 +00:00
( msg - > params [ 3 ] ? " , signed on " : " " ) , ( msg - > params [ 3 ] ? signon : " " )
2020-02-09 21:45:49 +00:00
) ;
}
static void handleReplyWhoisChannels ( struct Message * msg ) {
require ( msg , false , 3 ) ;
2021-06-07 04:09:58 +00:00
char buf [ 1024 ] ;
char * ptr = buf , * end = & buf [ sizeof ( buf ) ] ;
2020-02-09 21:45:49 +00:00
while ( msg - > params [ 2 ] ) {
char * channel = strsep ( & msg - > params [ 2 ] , " " ) ;
2021-06-10 23:38:12 +00:00
if ( ! channel [ 0 ] ) break ;
2020-02-15 10:04:43 +00:00
char * name = & channel [ strspn ( channel , network . prefixes ) ] ;
2021-06-07 04:09:58 +00:00
ptr = seprintf (
ptr , end , " %s \3 %02d%s \3 " ,
( ptr > buf ? " , " : " " ) , hash ( name ) , channel
) ;
2020-02-09 21:45:49 +00:00
}
uiFormat (
Network , Warm , tagTime ( msg ) ,
" \3 %02d%s \3 \t is in %s " ,
2022-07-31 20:28:08 +00:00
cacheGet ( Network , msg - > params [ 1 ] ) - > color , msg - > params [ 1 ] , buf
2020-02-09 21:45:49 +00:00
) ;
}
static void handleReplyWhoisGeneric ( struct Message * msg ) {
require ( msg , false , 3 ) ;
if ( msg - > params [ 3 ] ) {
msg - > params [ 0 ] = msg - > params [ 2 ] ;
msg - > params [ 2 ] = msg - > params [ 3 ] ;
msg - > params [ 3 ] = msg - > params [ 0 ] ;
}
uiFormat (
Network , Warm , tagTime ( msg ) ,
" \3 %02d%s \3 \t %s%s%s " ,
2022-07-31 20:28:08 +00:00
cacheGet ( Network , msg - > params [ 1 ] ) - > color , msg - > params [ 1 ] ,
2020-04-03 21:10:52 +00:00
msg - > params [ 2 ] , ( msg - > params [ 3 ] ? " " : " " ) , ( msg - > params [ 3 ] ? : " " )
2020-02-09 21:45:49 +00:00
) ;
}
static void handleReplyEndOfWhois ( struct Message * msg ) {
require ( msg , false , 2 ) ;
2020-02-11 22:52:55 +00:00
if ( strcmp ( msg - > params [ 1 ] , self . nick ) ) {
2022-07-30 22:47:26 +00:00
cacheRemove ( Network , msg - > params [ 1 ] ) ;
2020-02-09 21:45:49 +00:00
}
}
2020-12-30 19:29:32 +00:00
static void handleReplyWhowasUser ( struct Message * msg ) {
require ( msg , false , 6 ) ;
2022-07-31 20:28:08 +00:00
cacheInsert ( true , Network , msg - > params [ 1 ] ) - > color = hash ( msg - > params [ 2 ] ) ;
2020-12-30 19:29:32 +00:00
uiFormat (
Network , Warm , tagTime ( msg ) ,
" \3 %02d%s \3 \t was %s!%s@%s (%s) " ,
hash ( msg - > params [ 2 ] ) , msg - > params [ 1 ] ,
msg - > params [ 1 ] , msg - > params [ 2 ] , msg - > params [ 3 ] , msg - > params [ 5 ]
) ;
}
static void handleReplyEndOfWhowas ( struct Message * msg ) {
require ( msg , false , 2 ) ;
if ( strcmp ( msg - > params [ 1 ] , self . nick ) ) {
2022-07-30 22:47:26 +00:00
cacheRemove ( Network , msg - > params [ 1 ] ) ;
2020-12-30 19:29:32 +00:00
}
}
2020-02-12 05:17:28 +00:00
static void handleReplyAway ( struct Message * msg ) {
require ( msg , false , 3 ) ;
// Might be part of a WHOIS response.
2022-08-06 19:38:51 +00:00
uint id = ( replies [ ReplyWhois ] ? Network : idFor ( msg - > params [ 1 ] ) ) ;
2020-02-12 05:17:28 +00:00
uiFormat (
2022-01-16 19:17:03 +00:00
id , ( id = = Network ? Warm : Cold ) , tagTime ( msg ) ,
2020-02-12 05:17:28 +00:00
" \3 %02d%s \3 \t is away: %s " ,
2022-07-31 20:28:08 +00:00
cacheGet ( id , msg - > params [ 1 ] ) - > color , msg - > params [ 1 ] , msg - > params [ 2 ]
2020-02-12 05:17:28 +00:00
) ;
2020-03-25 22:58:48 +00:00
logFormat (
id , tagTime ( msg ) , " %s is away: %s " ,
msg - > params [ 1 ] , msg - > params [ 2 ]
) ;
2020-02-12 05:17:28 +00:00
}
2020-02-15 02:10:40 +00:00
static void handleReplyNowAway ( struct Message * msg ) {
require ( msg , false , 2 ) ;
uiFormat ( Network , Warm , tagTime ( msg ) , " %s " , msg - > params [ 1 ] ) ;
}
2020-02-02 22:26:20 +00:00
static bool isAction ( struct Message * msg ) {
2021-06-20 23:22:20 +00:00
if ( strncmp ( msg - > params [ 1 ] , " \1 ACTION " , 7 ) ) return false ;
if ( msg - > params [ 1 ] [ 7 ] = = ' ' ) {
msg - > params [ 1 ] + = 8 ;
} else if ( msg - > params [ 1 ] [ 7 ] = = ' \1 ' ) {
msg - > params [ 1 ] + = 7 ;
} else {
return false ;
}
2020-02-02 22:26:20 +00:00
size_t len = strlen ( msg - > params [ 1 ] ) ;
2021-06-20 23:22:20 +00:00
if ( msg - > params [ 1 ] [ len - 1 ] = = ' \1 ' ) {
msg - > params [ 1 ] [ len - 1 ] = ' \0 ' ;
}
2020-02-02 22:26:20 +00:00
return true ;
}
2022-05-29 22:09:52 +00:00
static bool matchWord ( const char * str , const char * word ) {
size_t len = strlen ( word ) ;
const char * match = str ;
while ( NULL ! = ( match = strstr ( match , word ) ) ) {
char a = ( match > str ? match [ - 1 ] : ' ' ) ;
2020-04-03 21:10:52 +00:00
char b = ( match [ len ] ? : ' ' ) ;
2020-02-06 09:18:15 +00:00
if ( ( isspace ( a ) | | ispunct ( a ) ) & & ( isspace ( b ) | | ispunct ( b ) ) ) {
return true ;
}
match = & match [ len ] ;
}
return false ;
}
2022-05-29 22:09:52 +00:00
static bool isMention ( const struct Message * msg ) {
if ( matchWord ( msg - > params [ 1 ] , self . nick ) ) return true ;
for ( uint i = 0 ; i < ARRAY_LEN ( self . nicks ) & & self . nicks [ i ] ; + + i ) {
if ( matchWord ( msg - > params [ 1 ] , self . nicks [ i ] ) ) return true ;
}
return false ;
}
2022-07-30 19:25:25 +00:00
static char * colorMentions ( char * ptr , char * end , uint id , const char * msg ) {
// Consider words before a colon, or only the first two.
const char * split = strstr ( msg , " : " ) ;
2020-02-11 23:08:05 +00:00
if ( ! split ) {
2022-07-30 19:25:25 +00:00
split = strchr ( msg , ' ' ) ;
2020-02-11 23:08:05 +00:00
if ( split ) split = strchr ( & split [ 1 ] , ' ' ) ;
}
2022-07-30 19:25:25 +00:00
if ( ! split ) split = & msg [ strlen ( msg ) ] ;
// Bail if there is existing formatting.
for ( const char * ch = msg ; ch < split ; + + ch ) {
if ( iscntrl ( * ch ) ) goto rest ;
2020-02-09 03:51:13 +00:00
}
2022-07-30 19:25:25 +00:00
while ( msg < split ) {
size_t skip = strspn ( msg , " ,:<> " ) ;
ptr = seprintf ( ptr , end , " %.*s " , ( int ) skip , msg ) ;
msg + = skip ;
size_t len = strcspn ( msg , " ,:<> " ) ;
char * p = seprintf ( ptr , end , " %.*s " , ( int ) len , msg ) ;
2022-07-31 20:28:08 +00:00
enum Color color = cacheGet ( id , ptr ) - > color ;
2020-02-11 23:15:25 +00:00
if ( color ! = Default ) {
2022-07-30 19:25:25 +00:00
ptr = seprintf ( ptr , end , " \3 %02d%.*s \3 " , color , ( int ) len , msg ) ;
2020-02-11 23:15:25 +00:00
} else {
2022-07-30 19:25:25 +00:00
ptr = p ;
2020-02-11 23:15:25 +00:00
}
2022-07-30 19:25:25 +00:00
msg + = len ;
2020-02-09 03:51:13 +00:00
}
2022-07-30 19:25:25 +00:00
rest :
return seprintf ( ptr , end , " %s " , msg ) ;
2020-02-09 03:51:13 +00:00
}
2020-02-02 08:43:18 +00:00
static void handlePrivmsg ( struct Message * msg ) {
require ( msg , true , 2 ) ;
2022-07-30 19:43:11 +00:00
char statusmsg = ' \0 ' ;
if ( network . statusmsg & & strchr ( network . statusmsg , msg - > params [ 0 ] [ 0 ] ) ) {
statusmsg = msg - > params [ 0 ] [ 0 ] ;
msg - > params [ 0 ] + + ;
2021-04-02 21:03:10 +00:00
}
2020-02-15 09:54:53 +00:00
bool query = ! strchr ( network . chanTypes , msg - > params [ 0 ] [ 0 ] ) ;
2020-09-18 22:13:09 +00:00
bool server = strchr ( msg - > nick , ' . ' ) ;
2020-02-11 22:52:55 +00:00
bool mine = ! strcmp ( msg - > nick , self . nick ) ;
2020-02-16 03:19:55 +00:00
uint id ;
2020-02-15 09:54:53 +00:00
if ( query & & server ) {
2020-02-06 09:01:11 +00:00
id = Network ;
} else if ( query & & ! mine ) {
id = idFor ( msg - > nick ) ;
2023-01-22 08:51:12 +00:00
idColors [ id ] = hash ( msg - > nick ) ;
2020-02-06 09:01:11 +00:00
} else {
id = idFor ( msg - > params [ 0 ] ) ;
}
2020-02-02 22:26:20 +00:00
bool notice = ( msg - > cmd [ 0 ] = = ' N ' ) ;
2021-06-20 22:17:38 +00:00
bool action = ! notice & & isAction ( msg ) ;
2021-01-16 19:04:53 +00:00
bool highlight = ! mine & & isMention ( msg ) ;
2022-07-30 19:25:25 +00:00
enum Heat heat = ( ! notice & & ( highlight | | query ) ? Hot : Warm ) ;
heat = filterCheck ( heat , id , msg ) ;
2021-01-16 19:04:53 +00:00
if ( heat > Warm & & ! mine & & ! query ) highlight = true ;
2020-09-02 21:51:51 +00:00
if ( ! notice & & ! mine & & heat > Ice ) {
2023-01-22 08:51:12 +00:00
cacheInsert ( true , id , msg - > nick ) - > color = hash ( msg - > nick ) ;
2020-09-02 21:51:51 +00:00
}
2020-07-10 02:43:35 +00:00
if ( heat > Ice ) urlScan ( id , msg - > nick , msg - > params [ 1 ] ) ;
2020-09-02 21:51:51 +00:00
2023-01-22 08:51:12 +00:00
static char last_nick [ 128 ] = " " ;
int reset_last_nick = 1 ;
2021-06-07 04:09:58 +00:00
char buf [ 1024 ] ;
2022-07-30 19:25:25 +00:00
char * ptr = buf , * end = & buf [ sizeof ( buf ) ] ;
2022-07-30 19:43:11 +00:00
if ( statusmsg ) {
ptr = seprintf (
ptr , end , " \3 %d[%c] \3 " , hash ( msg - > params [ 0 ] ) , statusmsg
) ;
}
2020-02-07 03:59:49 +00:00
if ( notice ) {
2020-03-25 22:58:48 +00:00
if ( id ! = Network ) {
logFormat ( id , tagTime ( msg ) , " -%s- %s " , msg - > nick , msg - > params [ 1 ] ) ;
}
2022-07-30 19:25:25 +00:00
ptr = seprintf (
ptr , end , " \3 %d-%s- \3 %d \t " ,
2023-01-22 08:51:12 +00:00
hash ( msg - > nick ) , msg - > nick , LightGray
2020-02-07 03:59:49 +00:00
) ;
} else if ( action ) {
2020-03-25 22:58:48 +00:00
logFormat ( id , tagTime ( msg ) , " * %s %s " , msg - > nick , msg - > params [ 1 ] ) ;
2022-07-30 19:25:25 +00:00
ptr = seprintf (
2023-01-22 08:51:12 +00:00
ptr , end , " %s \35 \3 %d * %s \17 \35 \t " ,
( highlight ? " \26 " : " " ) , hash ( msg - > nick ) , msg - > nick
2020-02-07 03:59:49 +00:00
) ;
} else {
2020-03-25 22:58:48 +00:00
logFormat ( id , tagTime ( msg ) , " <%s> %s " , msg - > nick , msg - > params [ 1 ] ) ;
2022-07-30 19:25:25 +00:00
ptr = seprintf (
2023-01-22 08:51:12 +00:00
ptr , end , " %s \3 %d %12.12s \17 \t " , /* NOTE(bx): i changed this cause i like things to line up */
( highlight ? " \26 " : " " ) , hash ( msg - > nick ) ,
( strncmp ( msg - > nick , last_nick , sizeof ( last_nick ) - 1 ) ! = 0 ) ? msg - > nick : " "
2020-02-07 03:59:49 +00:00
) ;
2023-01-22 08:51:12 +00:00
reset_last_nick = 0 ;
2020-02-07 03:59:49 +00:00
}
2022-07-30 19:25:25 +00:00
if ( notice ) {
ptr = seprintf ( ptr , end , " %s " , msg - > params [ 1 ] ) ;
} else {
ptr = colorMentions ( ptr , end , id , msg - > params [ 1 ] ) ;
}
uiWrite ( id , heat , tagTime ( msg ) , buf ) ;
2023-01-22 08:51:12 +00:00
if ( reset_last_nick ) {
strncpy ( last_nick , " " , sizeof ( last_nick ) - 1 ) ;
} else {
strncpy ( last_nick , msg - > nick , sizeof ( last_nick ) - 1 ) ;
}
2020-02-02 08:43:18 +00:00
}
2020-02-02 06:58:03 +00:00
static void handlePing ( struct Message * msg ) {
require ( msg , false , 1 ) ;
ircFormat ( " PONG :%s \r \n " , msg - > params [ 0 ] ) ;
2020-02-02 00:37:48 +00:00
}
2020-02-06 04:27:43 +00:00
static void handleError ( struct Message * msg ) {
require ( msg , false , 1 ) ;
errx ( EX_UNAVAILABLE , " %s " , msg - > params [ 0 ] ) ;
}
2020-02-01 06:18:01 +00:00
static const struct Handler {
const char * cmd ;
2020-12-30 21:49:55 +00:00
int reply ;
2020-02-01 06:18:01 +00:00
Handler * fn ;
} Handlers [ ] = {
2020-12-30 21:49:55 +00:00
{ " 001 " , 0 , handleReplyWelcome } ,
{ " 005 " , 0 , handleReplyISupport } ,
{ " 221 " , - ReplyMode , handleReplyUserModeIs } ,
{ " 276 " , + ReplyWhois , handleReplyWhoisGeneric } ,
{ " 301 " , 0 , handleReplyAway } ,
{ " 305 " , - ReplyAway , handleReplyNowAway } ,
{ " 306 " , - ReplyAway , handleReplyNowAway } ,
{ " 307 " , + ReplyWhois , handleReplyWhoisGeneric } ,
{ " 311 " , + ReplyWhois , handleReplyWhoisUser } ,
{ " 312 " , 0 , handleReplyWhoisServer } ,
{ " 313 " , + ReplyWhois , handleReplyWhoisGeneric } ,
{ " 314 " , + ReplyWhowas , handleReplyWhowasUser } ,
{ " 317 " , + ReplyWhois , handleReplyWhoisIdle } ,
{ " 318 " , - ReplyWhois , handleReplyEndOfWhois } ,
{ " 319 " , + ReplyWhois , handleReplyWhoisChannels } ,
2021-11-11 01:31:27 +00:00
{ " 320 " , + ReplyWhois , handleReplyWhoisGeneric } ,
2020-12-30 21:49:55 +00:00
{ " 322 " , + ReplyList , handleReplyList } ,
{ " 323 " , - ReplyList , NULL } ,
{ " 324 " , - ReplyMode , handleReplyChannelModeIs } ,
{ " 330 " , + ReplyWhois , handleReplyWhoisGeneric } ,
{ " 331 " , - ReplyTopic , handleReplyNoTopic } ,
{ " 332 " , 0 , handleReplyTopic } ,
2021-01-28 20:31:00 +00:00
{ " 335 " , + ReplyWhois , handleReplyWhoisGeneric } ,
2021-06-18 00:18:37 +00:00
{ " 338 " , + ReplyWhois , handleReplyWhoisGeneric } ,
2020-12-30 21:49:55 +00:00
{ " 341 " , 0 , handleReplyInviting } ,
{ " 346 " , + ReplyInvex , handleReplyInviteList } ,
{ " 347 " , - ReplyInvex , NULL } ,
{ " 348 " , + ReplyExcepts , handleReplyExceptList } ,
{ " 349 " , - ReplyExcepts , NULL } ,
{ " 353 " , 0 , handleReplyNames } ,
2021-02-21 19:23:17 +00:00
{ " 366 " , 0 , handleReplyEndOfNames } ,
2020-12-30 21:49:55 +00:00
{ " 367 " , + ReplyBan , handleReplyBanList } ,
{ " 368 " , - ReplyBan , NULL } ,
{ " 369 " , - ReplyWhowas , handleReplyEndOfWhowas } ,
{ " 372 " , 0 , handleReplyMOTD } ,
{ " 378 " , + ReplyWhois , handleReplyWhoisGeneric } ,
{ " 379 " , + ReplyWhois , handleReplyWhoisGeneric } ,
{ " 422 " , 0 , handleErrorNoMOTD } ,
{ " 432 " , 0 , handleErrorErroneousNickname } ,
{ " 433 " , 0 , handleErrorNicknameInUse } ,
{ " 437 " , 0 , handleErrorNicknameInUse } ,
{ " 441 " , 0 , handleErrorUserNotInChannel } ,
{ " 443 " , 0 , handleErrorUserOnChannel } ,
{ " 478 " , 0 , handleErrorBanListFull } ,
{ " 482 " , 0 , handleErrorChanopPrivsNeeded } ,
{ " 671 " , + ReplyWhois , handleReplyWhoisGeneric } ,
{ " 704 " , + ReplyHelp , handleReplyHelp } ,
{ " 705 " , + ReplyHelp , handleReplyHelp } ,
{ " 706 " , - ReplyHelp , NULL } ,
{ " 900 " , 0 , handleReplyLoggedIn } ,
{ " 904 " , 0 , handleErrorSASLFail } ,
{ " 905 " , 0 , handleErrorSASLFail } ,
{ " 906 " , 0 , handleErrorSASLFail } ,
{ " AUTHENTICATE " , 0 , handleAuthenticate } ,
{ " CAP " , 0 , handleCap } ,
{ " CHGHOST " , 0 , handleChghost } ,
{ " ERROR " , 0 , handleError } ,
{ " FAIL " , 0 , handleStandardReply } ,
{ " INVITE " , 0 , handleInvite } ,
{ " JOIN " , 0 , handleJoin } ,
{ " KICK " , 0 , handleKick } ,
{ " MODE " , 0 , handleMode } ,
{ " NICK " , 0 , handleNick } ,
{ " NOTE " , 0 , handleStandardReply } ,
{ " NOTICE " , 0 , handlePrivmsg } ,
{ " PART " , 0 , handlePart } ,
{ " PING " , 0 , handlePing } ,
{ " PRIVMSG " , 0 , handlePrivmsg } ,
{ " QUIT " , 0 , handleQuit } ,
{ " SETNAME " , 0 , handleSetname } ,
{ " TOPIC " , 0 , handleTopic } ,
{ " WARN " , 0 , handleStandardReply } ,
2020-02-01 06:18:01 +00:00
} ;
static int compar ( const void * cmd , const void * _handler ) {
const struct Handler * handler = _handler ;
return strcmp ( cmd , handler - > cmd ) ;
}
2020-06-24 17:36:24 +00:00
void handle ( struct Message * msg ) {
if ( ! msg - > cmd ) return ;
if ( msg - > tags [ TagPos ] ) {
self . pos = strtoull ( msg - > tags [ TagPos ] , NULL , 10 ) ;
2020-03-09 03:15:56 +00:00
}
2020-02-01 06:18:01 +00:00
const struct Handler * handler = bsearch (
2020-06-24 17:36:24 +00:00
msg - > cmd , Handlers , ARRAY_LEN ( Handlers ) , sizeof ( * handler ) , compar
2020-02-01 06:18:01 +00:00
) ;
2020-02-20 06:38:31 +00:00
if ( handler ) {
2020-12-30 21:49:55 +00:00
if ( handler - > reply & & ! replies [ abs ( handler - > reply ) ] ) return ;
if ( handler - > fn ) handler - > fn ( msg ) ;
if ( handler - > reply < 0 ) replies [ abs ( handler - > reply ) ] - - ;
2020-06-24 17:36:24 +00:00
} else if ( strcmp ( msg - > cmd , " 400 " ) > = 0 & & strcmp ( msg - > cmd , " 599 " ) < = 0 ) {
handleErrorGeneric ( msg ) ;
2022-05-07 00:25:16 +00:00
} else if ( isdigit ( msg - > cmd [ 0 ] ) ) {
handleReplyGeneric ( msg ) ;
2020-02-20 06:38:31 +00:00
}
2020-02-01 06:18:01 +00:00
}