2020-06-22 16:58:10 +00:00
/*
* This file is part of uIRC . ( https : //git.redxen.eu/caskd/uIRC)
* Copyright ( c ) 2019 , 2020 Alex - David Denes
*
* uIRC 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
* any later version .
*
* uIRC 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 uIRC . If not , see < https : //www.gnu.org/licenses/>.
*/
# include "uirc.h"
2020-07-04 10:29:08 +00:00
const char * const uirc_ircmd [ ] = {
2020-06-22 16:58:10 +00:00
[ PASS ] = " PASS " ,
[ NICK ] = " NICK " ,
[ USER ] = " USER " ,
[ SERVER ] = " SERVER " ,
[ OPER ] = " OPER " ,
2020-07-03 20:41:52 +00:00
[ SERVICE ] = " SERVICE " ,
2020-06-22 16:58:10 +00:00
[ QUIT ] = " QUIT " ,
[ SQUIT ] = " SQUIT " ,
[ JOIN ] = " JOIN " ,
[ PART ] = " PART " ,
[ MODE ] = " MODE " ,
[ TOPIC ] = " TOPIC " ,
[ NAMES ] = " NAMES " ,
[ LIST ] = " LIST " ,
[ INVITE ] = " INVITE " ,
[ KICK ] = " KICK " ,
[ VERSION ] = " VERSION " ,
[ STATS ] = " STATS " ,
[ LINKS ] = " LINKS " ,
[ TIME ] = " TIME " ,
[ CONNECT ] = " CONNECT " ,
[ TRACE ] = " TRACE " ,
[ ADMIN ] = " ADMIN " ,
[ INFO ] = " INFO " ,
2020-07-04 10:29:08 +00:00
[ SERVLIST ] = " SERVLIST " ,
[ SQUERY ] = " SQUERY " ,
2020-06-22 16:58:10 +00:00
[ PRIVMSG ] = " PRIVMSG " ,
[ NOTICE ] = " NOTICE " ,
2020-07-04 10:29:08 +00:00
[ MOTD ] = " MOTD " ,
[ LUSERS ] = " LUSERS " ,
2020-06-22 16:58:10 +00:00
[ WHO ] = " WHO " ,
[ WHOIS ] = " WHOIS " ,
[ WHOWAS ] = " WHOWAS " ,
[ KILL ] = " KILL " ,
[ PING ] = " PING " ,
[ PONG ] = " PONG " ,
[ ERROR ] = " ERROR " ,
[ AWAY ] = " AWAY " ,
[ REHASH ] = " REHASH " ,
2020-07-04 10:29:08 +00:00
[ DIE ] = " DIE " ,
2020-06-22 16:58:10 +00:00
[ RESTART ] = " RESTART " ,
[ SUMMON ] = " SUMMON " ,
[ USERS ] = " USERS " ,
[ WALLOPS ] = " WALLOPS " ,
[ USERHOST ] = " USERHOST " ,
[ ISON ] = " ISON " } ;
2020-06-25 19:06:53 +00:00
2020-06-27 12:30:15 +00:00
struct tagmapping {
2020-07-02 16:32:25 +00:00
const char * name ;
2020-06-27 12:30:15 +00:00
IRC_Tag * assg ;
} ;
2020-06-27 19:07:59 +00:00
int uirc_tokenize_message ( IRC_Message * irc_msg , char * * line )
2020-06-22 16:58:10 +00:00
{
2020-07-02 16:32:25 +00:00
if ( * line = = NULL | | irc_msg = = NULL )
return ERR_UIRC_NULL_ARGS ;
int ret ;
2020-06-27 19:07:59 +00:00
char * progr = * line , * command ;
2020-06-22 16:59:23 +00:00
if ( * progr = = ' @ ' ) {
char * tags ;
2020-06-30 21:39:56 +00:00
if ( ( tags = strtok_r ( progr , " " , & progr ) ) ! = NULL ) {
2020-07-02 16:32:25 +00:00
if ( ( ret = tok_tags ( tags , irc_msg ) ) < 0 )
return ret ;
2020-06-22 16:59:23 +00:00
} else
2020-07-02 16:32:25 +00:00
return ERR_UIRC_INVALID_FORMAT ;
2020-06-22 16:59:23 +00:00
}
2020-06-22 16:58:10 +00:00
if ( * progr = = ' : ' ) {
char * prefix ;
2020-06-30 21:39:56 +00:00
if ( ( prefix = strtok_r ( progr , " " , & progr ) ) ! = NULL ) {
2020-07-02 16:32:25 +00:00
if ( ( ret = tok_prefix ( prefix , & irc_msg - > name ) ) < 0 )
return ret ;
2020-06-22 16:59:23 +00:00
} else
2020-07-02 16:32:25 +00:00
return ERR_UIRC_INVALID_FORMAT ;
2020-06-22 16:58:10 +00:00
}
2020-06-30 21:39:56 +00:00
if ( ( command = strtok_r ( NULL , " " , & progr ) ) ! = NULL ) {
if ( isalpha ( * command ) ) {
if ( ! ( irc_msg - > cmd = uirc_ircmd_stoi ( command ) ) )
2020-07-02 16:32:25 +00:00
return ERR_UIRC_UNKNOWN_TOKEN ;
2020-06-30 21:39:56 +00:00
} else if ( isdigit ( * command ) ) {
if ( ( irc_msg - > cmd = atoi ( command ) ) = = 0 )
2020-07-02 16:32:25 +00:00
return ERR_UIRC_UNKNOWN_TOKEN ;
2020-06-30 21:39:56 +00:00
} else {
2020-07-02 16:32:25 +00:00
return ERR_UIRC_UNKNOWN_TOKEN ;
2020-06-30 21:39:56 +00:00
}
2020-06-22 16:58:10 +00:00
} else {
2020-07-02 16:32:25 +00:00
return ERR_UIRC_INVALID_FORMAT ;
2020-06-22 16:58:10 +00:00
}
int i ;
for ( i = 0 ; * progr ! = ' : ' & & i < 14 ; i + + ) {
2020-06-30 21:39:56 +00:00
if ( ( irc_msg - > args [ i ] = strtok_r ( NULL , " " , & progr ) ) = = NULL )
2020-07-02 16:32:25 +00:00
return ERR_UIRC_INVALID_FORMAT ;
2020-06-22 16:58:10 +00:00
}
2020-06-25 19:06:53 +00:00
irc_msg - > args [ i ] = NULL ;
2020-06-22 16:58:10 +00:00
if ( * progr = = ' : ' )
+ + progr ;
2020-06-27 11:33:35 +00:00
irc_msg - > trailing = progr ;
2020-06-22 16:58:10 +00:00
return 1 ;
}
unsigned int uirc_ircmd_stoi ( char * str )
{
2020-07-03 20:41:52 +00:00
if ( str = = NULL )
return ERR_UIRC_NULL_ARGS ;
2020-06-22 16:58:10 +00:00
for ( unsigned short i = PASS ; i < = ISON ; i + + ) {
2020-07-03 20:41:52 +00:00
if ( ( uirc_ircmd [ i ] ! = NULL ) & & ( strcmp ( uirc_ircmd [ i ] , str ) = = 0 ) )
2020-06-22 16:58:10 +00:00
return i ;
}
2020-07-02 16:32:25 +00:00
return ERR_UIRC_UNKNOWN_TOKEN ;
2020-06-22 16:58:10 +00:00
}
2020-06-25 19:06:53 +00:00
int uirc_assm_mesg ( char * buf , IRC_Message * mesg )
2020-06-22 16:58:10 +00:00
{
2020-07-01 19:52:45 +00:00
if ( buf = = NULL | | mesg = = NULL )
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-06-25 19:06:53 +00:00
char * pos = buf ;
2020-07-02 16:32:25 +00:00
int cnt , ret ;
2020-06-27 13:30:04 +00:00
2020-07-02 16:32:25 +00:00
if ( ( ret = assm_tags ( & pos , mesg ) ) < 0 )
return ret ;
if ( mesg - > name . nick ! = NULL | | mesg - > name . host ! = NULL ) {
if ( ( ret = assm_prefix ( & pos , & mesg - > name ) ) < 0 )
return ret ;
2020-06-27 19:07:59 +00:00
}
2020-06-25 19:06:53 +00:00
if ( ( uirc_ircmd [ mesg - > cmd ] ! = NULL ) & & ( ( cnt = sprintf ( pos , " %s " , uirc_ircmd [ mesg - > cmd ] ) ) > 0 ) )
pos + = cnt ;
else
2020-07-02 16:32:25 +00:00
return ERR_UIRC_GENERIC ;
2020-06-25 19:06:53 +00:00
for ( unsigned int i = 0 ; i < 14 & & mesg - > args [ i ] ! = NULL ; i + + ) {
if ( ( cnt = sprintf ( pos , " %s " , mesg - > args [ i ] ) ) > 0 )
pos + = cnt ;
else
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-06-25 19:06:53 +00:00
}
2020-07-06 20:57:21 +00:00
if ( mesg - > trailing ! = NULL ) {
if ( ( cnt = sprintf ( pos , " :%s " , mesg - > trailing ) ) > 0 )
pos + = cnt ;
else
return ERR_UIRC_GENERIC ;
}
2020-06-25 19:06:53 +00:00
if ( ( cnt = sprintf ( pos , " \r \n " ) ) ! = 2 )
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-06-25 19:06:53 +00:00
return 1 ;
2020-06-22 16:58:10 +00:00
}
2020-07-01 19:52:45 +00:00
int assm_tags ( char * * pos , IRC_Message * mesg )
2020-06-27 13:30:04 +00:00
{
2020-07-02 16:32:25 +00:00
if ( * pos = = NULL | | mesg = = NULL )
return ERR_UIRC_NULL_ARGS ;
2020-06-28 15:19:38 +00:00
const char * origpos = * pos ;
2020-07-01 19:52:45 +00:00
char * cpos = * pos ;
2020-06-27 13:30:04 +00:00
int cnt ;
2020-06-28 15:19:38 +00:00
const struct tagmapping tags [ ] = {
2020-06-27 13:30:04 +00:00
{ . name = " time " , . assg = & mesg - > tags . time } ,
{ . name = " account " , . assg = & mesg - > tags . account } ,
{ . name = " batch " , . assg = & mesg - > tags . batch } ,
{ . name = " label " , . assg = & mesg - > tags . label } ,
{ . name = " msgid " , . assg = & mesg - > tags . msgid } ,
{ . name = " multiline-concat " , . assg = & mesg - > tags . multiline_concat } ,
{ . name = " typing " , . assg = & mesg - > tags . typing } ,
{ . name = " react " , . assg = & mesg - > tags . react } ,
{ . name = " reply " , . assg = & mesg - > tags . reply } } ;
2020-07-01 19:52:45 +00:00
for ( unsigned int i = 0 ; i < sizeof ( tags ) / sizeof ( struct tagmapping ) ; i + + ) {
if ( ( * tags [ i ] . assg ) . present ) {
if ( cpos = = origpos ) {
if ( sprintf ( cpos + + , " @ " ) ! = 1 )
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-07-01 19:52:45 +00:00
} else {
if ( sprintf ( cpos + + , " ; " ) ! = 1 )
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-06-27 13:30:04 +00:00
}
2020-07-01 19:52:45 +00:00
if ( ( * tags [ i ] . assg ) . clientbound ) {
if ( sprintf ( cpos + + , " + " ) ! = 1 )
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-06-30 21:39:56 +00:00
}
2020-07-01 19:52:45 +00:00
if ( ( * tags [ i ] . assg ) . value ! = NULL )
cnt = sprintf ( cpos , " %s=%s " , tags [ i ] . name , ( * tags [ i ] . assg ) . value ) ;
else
cnt = sprintf ( cpos , " %s " , tags [ i ] . name ) ;
if ( cnt > 0 )
cpos + = cnt ;
else
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-06-30 21:39:56 +00:00
}
2020-07-01 19:52:45 +00:00
}
if ( * origpos = = ' @ ' ) {
if ( sprintf ( cpos + + , " " ) ! = 1 )
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-07-01 19:52:45 +00:00
}
* pos = cpos ;
2020-07-02 16:32:25 +00:00
return 1 ;
2020-07-01 19:52:45 +00:00
}
int tok_tags ( char * msg , IRC_Message * mesg )
{
2020-07-02 16:32:25 +00:00
if ( msg = = NULL | | mesg = = NULL )
return ERR_UIRC_NULL_ARGS ;
2020-07-01 19:52:45 +00:00
char * cval , * cpos = msg , * ctag = NULL ;
bool clientbound ;
const struct tagmapping tags [ ] = {
{ . name = " time " , . assg = & mesg - > tags . time } ,
{ . name = " account " , . assg = & mesg - > tags . account } ,
{ . name = " batch " , . assg = & mesg - > tags . batch } ,
{ . name = " label " , . assg = & mesg - > tags . label } ,
{ . name = " msgid " , . assg = & mesg - > tags . msgid } ,
{ . name = " multiline-concat " , . assg = & mesg - > tags . multiline_concat } ,
{ . name = " typing " , . assg = & mesg - > tags . typing } ,
{ . name = " react " , . assg = & mesg - > tags . react } ,
{ . name = " reply " , . assg = & mesg - > tags . reply } } ;
if ( * cpos = = ' @ ' )
cpos + + ;
while ( ( ctag = strtok_r ( NULL , " ; " , & cpos ) ) ! = NULL ) {
clientbound = false ;
if ( * ctag = = ' + ' ) {
ctag + + ;
clientbound = true ;
}
2020-07-02 16:32:25 +00:00
if ( ( cval = strchr ( ctag , ' = ' ) ) ! = NULL )
2020-07-01 19:52:45 +00:00
* ( cval + + ) = ' \0 ' ;
2020-06-27 13:30:04 +00:00
for ( unsigned int i = 0 ; i < sizeof ( tags ) / sizeof ( struct tagmapping ) ; i + + ) {
2020-07-01 19:52:45 +00:00
if ( ! strcmp ( ctag , tags [ i ] . name ) ) {
if ( cval ! = NULL )
( * tags [ i ] . assg ) . value = cval ;
( * tags [ i ] . assg ) . clientbound = clientbound ;
( * tags [ i ] . assg ) . present = true ;
2020-06-27 13:30:04 +00:00
}
}
}
2020-07-02 16:32:25 +00:00
return 1 ;
2020-06-27 13:30:04 +00:00
}
2020-07-01 19:52:45 +00:00
int assm_prefix ( char * * str , IRC_User * usr )
2020-06-28 15:19:38 +00:00
{
2020-07-02 16:32:25 +00:00
if ( * str = = NULL | | usr = = NULL )
return ERR_UIRC_NULL_ARGS ;
2020-06-28 15:19:38 +00:00
int cnt ;
char * pos = * str ;
* ( pos + + ) = ' : ' ;
2020-07-02 16:32:25 +00:00
if ( usr - > nick = = NULL & & usr - > user = = NULL & & usr - > host ! = NULL ) {
2020-06-28 15:19:38 +00:00
if ( ( cnt = sprintf ( pos , " %s " , usr - > host ) ) > 0 )
pos + = cnt ;
else
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
} else if ( usr - > nick ! = NULL ) {
2020-06-28 15:19:38 +00:00
if ( ( cnt = sprintf ( pos , " %s " , usr - > nick ) ) > 0 )
pos + = cnt ;
else
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-06-30 21:39:56 +00:00
if ( usr - > user ! = NULL ) {
2020-06-28 15:19:38 +00:00
if ( ( cnt = sprintf ( pos , " !%s " , usr - > user ) ) > 0 )
pos + = cnt ;
else
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-06-28 15:19:38 +00:00
}
2020-06-30 21:39:56 +00:00
if ( usr - > host ! = NULL ) {
2020-06-28 15:19:38 +00:00
if ( ( cnt = sprintf ( pos , " @%s " , usr - > host ) ) > 0 )
pos + = cnt ;
else
2020-07-02 16:32:25 +00:00
return ERR_UIRC_BUFFER_ERR ;
2020-06-28 15:19:38 +00:00
}
2020-07-02 16:32:25 +00:00
} else
return ERR_UIRC_NULL_ARGS ;
2020-06-28 15:19:38 +00:00
* ( pos + + ) = ' ' ;
* str = pos ;
2020-07-02 16:32:25 +00:00
return 1 ;
2020-06-28 15:19:38 +00:00
}
2020-07-01 19:52:45 +00:00
int tok_prefix ( char * prefix , IRC_User * user )
2020-06-28 15:19:38 +00:00
{
char * pos = ( * prefix = = ' : ' ) ? prefix + 1 : prefix ;
2020-06-30 21:39:56 +00:00
if ( ( user - > host = strchr ( pos , ' @ ' ) ) ! = NULL )
2020-07-03 20:41:52 +00:00
* ( ( char * ) user - > host + + ) = ' \0 ' ;
2020-06-30 21:39:56 +00:00
if ( ( user - > user = strchr ( pos , ' ! ' ) ) ! = NULL ) /* RFC2812 says this cannot be here without the host but RFC1459 says it can, we accept both options */
2020-07-03 20:41:52 +00:00
* ( ( char * ) user - > user + + ) = ' \0 ' ;
2020-06-30 21:39:56 +00:00
if ( * ( user - > nick = pos ) = = ' \0 ' ) /* NOTE: This may be the server instead of a nick according to RFC2812, it is left to the library user to decide how to interpret this based on the context. */
2020-07-02 16:32:25 +00:00
return ERR_UIRC_INVALID_FORMAT ;
return 1 ;
2020-06-28 15:19:38 +00:00
}