123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- /*\
- |*| Copyright 2015 bill-auger <https://github.com/bill-auger/av-caster/issues>
- |*|
- |*| This file is part of the AvCaster program.
- |*|
- |*| AvCaster is free software: you can redistribute it and/or modify
- |*| it under the terms of the GNU Lesser General Public License version 3
- |*| as published by the Free Software Foundation.
- |*|
- |*| AvCaster 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 Lesser General Public License for more details.
- |*|
- |*| You should have received a copy of the GNU Lesser General Public License
- |*| along with AvCaster. If not, see <http://www.gnu.org/licenses/>.
- \*/
- #include "IrcClient.h"
- #ifndef DISABLE_CHAT
- #include <libircclient/libirc_rfcnumeric.h>
- #include "AvCaster.h"
- #include "IrcClient.h"
- #include "Trace/TraceIrcClient.h"
- String bitlbeehost = "localhost" ; // TODO: GUI support
- // String debianhost = "irc.debian.org" ; // TODO: GUI support
- String my_nick = "bill-auger" ; // TODO: GUI support
- String xmpp_channel = "#mychat" ; // TODO: GUI support
- String greeting = "" ; // TODO: GUI support
- /* IrcClient class methods */
- IrcClient::IrcClient(ValueTree servers_store) : Thread(APP::IRC_THREAD_NAME) ,
- serversStore(servers_store)
- {
- // create per server session instances and login
- int n_servers = this->serversStore.getNumChildren() ;
- for (int server_n = 0 ; server_n < n_servers ; ++server_n)
- {
- ValueTree server_store = this->serversStore.getChild(server_n) ;
- IrcServerInfo server_info = createSession(server_store) ;
- if (IsValidServerInfo(&server_info))
- this->servers.add(server_info) ; login(&server_info) ;
- }
- }
- IrcClient::~IrcClient()
- {
- IrcServerInfo* server = this->servers.begin() ;
- while (server != this->servers.end())
- {
- irc_cmd_quit (server->session , IRC::LOGOOUT_MSG) ;
- irc_disconnect (server->session) ;
- irc_destroy_session(server->session) ;
- ++server ;
- }
- }
- IrcServerInfo IrcClient::createSession(ValueTree server_store)
- {
- // assign shared server callbacks
- irc_callbacks_t callbacks ;
- memset(&callbacks , 0 , sizeof(callbacks)) ;
- callbacks.event_connect = OnConnect ;
- callbacks.event_channel = OnChannelMsg ;
- callbacks.event_join = OnJoin ;
- callbacks.event_part = OnPart ;
- callbacks.event_nick = OnNickChange ;
- callbacks.event_numeric = OnNumeric ;
- // create session
- String host = STRING(server_store[CONFIG::HOST_ID]) ;
- unsigned short port = int (server_store[CONFIG::PORT_ID]) ;
- IrcServerInfo server_info = { NULL , host , port , my_nick } ;
- server_info.session = irc_create_session(&callbacks) ;
- DEBUG_TRACE_CREATE_SESSION
- //irc_option_set(a_server.session , LIBIRC_OPTION_STRIPNICKS) ;
- //See LIBIRC_OPTION_SSL_NO_VERIFY for servers which use self-signed SSL certificates
- return server_info ;
- }
- void IrcClient::OnConnect(irc_session_t* session , const char* event , const char* origin ,
- const char** params , unsigned int count )
- {
- String host = origin ;
- String message = params[1] ;
- bool is_bitlbee = host == bitlbeehost &&
- message == IRC::BITLBEE_WELCOME_MSG + my_nick ;
- DEBUG_TRACE_CONNECTED
- // String OFTC_TLD = "oftc.net" ;
- // if (host.endsWith(IRC::OFTC_TLD)) AvCaster::RenameServer(debianhost , host) ;
- if (host.endsWith(IRC::OFTC_TLD)) AvCaster::UpdateIrcHost(IRC::OFTC_ALIAS_URIS , host) ;
- // display connected message
- AddClientChat(IRC::LOGGED_IN_MSG + host) ; AddServerChat(message) ;
- if (is_bitlbee)
- {
- // identify with bitlbee
- String my_pass = "123" ; // TODO: GUI and model support
- String identify_cmd = IRC::IDENTIFY_CMD + my_pass ;
- irc_cmd_msg(session , IRC::ROOT_CHANNEL , CHARSTAR(identify_cmd)) ;
- }
- else irc_cmd_join(session , CHARSTAR(xmpp_channel) , NULL) ;
- }
- /* handle nickserv
- static void event_notice (irc_session_t * session, const char * event,
- const char * origin, const char ** params, unsigned int count)
- {
- char buf[256];
- if ( !origin )
- return;
- if ( strcasecmp (origin, "nickserv") )
- return;
- if ( strstr (params[1], "This nick is not registered") == params[1] )
- {
- sprintf (buf, "REGISTER %s NOMAIL", gCfg.irc_nickserv_password);
- irc_cmd_msg(session, "nickserv", buf);
- }
- else if ( strstr (params[1], "This nickname is registered and protected") == params[1] )
- {
- sprintf (buf, "IDENTIFY %s", gCfg.irc_nickserv_password);
- irc_cmd_msg(session, "nickserv", buf);
- }
- else if ( strstr (params[1], "Password accepted - you are now recognized") == params[1] )
- printf ("Nickserv authentication succeed.");
- }
- */
- void IrcClient::OnChannelMsg(irc_session_t* session , const char* event , const char* origin ,
- const char** params , unsigned int count )
- {
- DEBUG_TRACE_CHAT_MSG_VB
- if (!origin || count != 2) return ;
- char nickbuf[128] ; irc_target_get_nick(origin , nickbuf , sizeof(nickbuf)) ;
- String user_id = String(origin) ;
- String nick = nickbuf ;
- String channel = CharPointer_UTF8(params[0]) ;
- String filtered_message = ProcessTextMeta(params[1]) ;
- StringArray processed_message = ProcessTimestamp(filtered_message) ;
- String timestamp = processed_message[0] ;
- String message = processed_message[1] ;
- bool is_root_user = user_id.startsWith(IRC::ROOT_USER + bitlbeehost) ;
- bool is_root_channel = channel == String (IRC::ROOT_CHANNEL ) ;
- bool is_xmpp_channel = channel == String (xmpp_channel ) ;
- bool is_login_blocked = message.endsWith (IRC::LOGIN_BLOCKED_MSG ) ;
- bool is_logged_in = message.endsWith (IRC::CONNECTED_MSG ) ||
- message.endsWith (IRC::KICKED_SELF_MSG ) ;
- DEBUG_TRACE_CHAT_MSG
- // handle login message from the bee
- if (is_root_user && is_root_channel)
- if (is_login_blocked) AddClientChat(IRC::SESSION_BLOCKED_MSG) ;
- else if (is_logged_in ) irc_cmd_join(session , CHARSTAR(xmpp_channel) , NULL) ;
- if (is_root_user || is_root_channel) return ;
- // handle peer messages
- AddUserChat(timestamp , nick , message) ;
- }
- void IrcClient::OnJoin(irc_session_t* session , const char* event , const char* origin ,
- const char** params , unsigned int count )
- {
- char nickbuf[128] ; irc_target_get_nick(origin , nickbuf , sizeof(nickbuf)) ;
- String nick = nickbuf ;
- String channel_name = params[0] ;
- irc_cmd_user_mode(session , "+i") ;
- DEBUG_TRACE_ONJOIN
- if (channel_name == IRC::ROOT_CHANNEL) return ;
- #ifndef HIDE_JOIN_PART_MESSAGES // TODO: GUI support
- AddClientChat(nick + " just joined channel " + channel_name) ;
- #endif // HIDE_JOIN_PART_MESSAGES
- if (nick == my_nick) irc_cmd_msg(session , params[0] , IRC::LOGIN_MSG ) ;
- else if (greeting.isNotEmpty()) irc_cmd_msg(session , params[0] , CHARSTAR(greeting)) ;
- // TODO: this maybe redundant
- irc_cmd_names(session , CHARSTAR(channel_name)) ;
- }
- void IrcClient::OnPart(irc_session_t* session , const char* event , const char* origin ,
- const char** params , unsigned int count )
- {
- char nickbuf[128] ; irc_target_get_nick(origin , nickbuf , sizeof(nickbuf)) ;
- String nick = nickbuf ;
- String channel_name = params[0] ;
- DEBUG_TRACE_ONPART
- #ifndef HIDE_JOIN_PART_MESSAGES
- AddClientChat(nick + " just parted channel " + channel_name) ;
- #endif // HIDE_JOIN_PART_MESSAGES
- // TODO: this maybe redundant
- irc_cmd_names(session , CHARSTAR(channel_name)) ;
- }
- void IrcClient::OnNickChange(irc_session_t* session , const char* event , const char* origin ,
- const char** params , unsigned int count )
- {
- String from_nick = origin ;
- String to_nick = params[0] ;
- // String channel_name = session. ; // TODO: may need to add session_ctx
- DEBUG_TRACE_NICK_CHANGE
- // DUMP_SERVER_PARAMS
- // irc_cmd_names(session , CHARSTAR(channel_name)) ;
- }
- void IrcClient::OnNumeric(irc_session_t* session , unsigned int event , const char* origin ,
- const char** params , unsigned int count )
- {
- DEBUG_TRACE_SERVER_MSG
- if (event == LIBIRC_RFC_RPL_NAMREPLY)
- {
- String host = origin ;
- String channel = params[2] ;
- StringArray nicks = StringArray::fromTokens(params[3] , false) ;
- HandleNicks(host , channel , nicks) ;
- }
- }
- bool IrcClient::IsValidServerInfo(IrcServerInfo* a_server_info)
- {
- DEBUG_TRACE_SERVER_INFO
- return a_server_info->session != 0 &&
- a_server_info->host.isNotEmpty() &&
- a_server_info->port > 0 &&
- a_server_info->nick.isNotEmpty() ;
- }
- bool IrcClient::IsSufficientVersion()
- {
- unsigned int major_version , minor_version ;
- irc_get_version(&major_version , &minor_version) ;
- return major_version >= IRC::MIN_MAJOR_VERSION &&
- minor_version >= IRC::MIN_MINOR_VERSION ;
- }
- void IrcClient::HandleNicks(String host , String channel , StringArray nicks)
- {
- if (host == bitlbeehost && channel == IRC::ROOT_CHANNEL) return ;
- nicks.removeEmptyStrings() ; nicks.removeString(IRC::ROOT_NICK) ;
- DEBUG_TRACE_NICKS
- #ifdef PREFIX_CHAT_NICKS
- AvCaster::UpdateChatNicks(host , channel , nicks) ;
- #else // PREFIX_CHAT_NICKS
- AvCaster::UpdateChatNicks(host , nicks) ;
- #endif // PREFIX_CHAT_NICKS
- }
- String IrcClient::ProcessTextMeta(const char* message)
- {
- // TODO: for now we just strip these - see docs for irc_color_convert_to_mirc()
- char* msg_cstr = irc_color_convert_from_mirc(message) ;
- String converted_msg = CharPointer_UTF8(msg_cstr) ; delete msg_cstr ;
- String filtered_msg = converted_msg.replace("[B]" , "").replace("[/B]" , "") // bold
- .replace("[I]" , "").replace("[/I]" , "") // italic
- .replace("[U]" , "").replace("[/U]" , "") // underline
- .replace("[/COLOR]" , "") ;
- while (filtered_msg.contains("[COLOR="))
- filtered_msg.replace(filtered_msg.fromFirstOccurrenceOf("[COLOR=" , true , false)
- .upToFirstOccurrenceOf("]" , true , false) , "") ;
- return filtered_msg ;
- }
- StringArray IrcClient::ProcessTimestamp(String message)
- {
- String timestamp = message.fromFirstOccurrenceOf("[" , true , false)
- .upToFirstOccurrenceOf("]" , true , false) ;
- String processed_msg = message.replace(timestamp , "") ;
- bool is_timestamp = timestamp.replaceCharacters("[:]" , "000")
- .containsOnly(APP::DIGITS) ;
- return (is_timestamp) ? StringArray::fromLines(timestamp + " \n" + processed_msg) :
- StringArray::fromLines("\n" + message) ;
- }
- void IrcClient::AddServerChat(String message)
- {
- AvCaster::AddChatLine(String::empty , GUI::SERVER_NICK , message) ;
- }
- void IrcClient::AddClientChat(String message)
- {
- AvCaster::AddChatLine(String::empty , GUI::CLIENT_NICK , message) ;
- }
- void IrcClient::AddUserChat(String prefix , String nick , String message)
- {
- AvCaster::AddChatLine(prefix , nick , message) ;
- }
- /* IrcClient instance methods */
- bool IrcClient::login(IrcServerInfo* a_server_info)
- {
- // NOTE: prefix 'server' with # for SSL
- irc_session_t* session = a_server_info->session ;
- const char* host = CHARSTAR(a_server_info->host ) ;
- unsigned short port = a_server_info->port ;
- const char* nick = CHARSTAR(a_server_info->nick ) ;
- // display login message
- AddClientChat(IRC::LOGGING_IN_MSG + a_server_info->host) ;
- bool is_err = irc_connect(session , host , port , 0 , nick , 0 , 0) ;
- DEBUG_TRACE_LOGIN
- return !is_err ;
- }
- void IrcClient::run()
- {
- #ifdef RUN_NETWORK_AS_PROC
- DEBUG_TRACE_THREAD_RUN_IN
- bool is_err = irc_run(this->session) ;
- DEBUG_TRACE_THREAD_RUN_OUT
- return ;
- #endif // RUN_NETWORK_AS_PROC
- if (threadShouldExit()) return ;
- for (IrcServerInfo* server = this->servers.begin() ; server != this->servers.end() ; ++server)
- if (!irc_is_connected(server->session)) { login(server) ; continue ; }
- struct timeval timeout ;
- timeout.tv_usec = 250000 ;
- timeout.tv_sec = 0 ;
- int maxfd = 0 ;
- fd_set in_set , out_set ;
- FD_ZERO(&in_set) ; FD_ZERO(&out_set) ;
- for (IrcServerInfo* server = this->servers.begin() ; server != this->servers.end() ; ++server)
- irc_add_select_descriptors(server->session , &in_set , &out_set , &maxfd) ;
- if (select(maxfd + 1 , &in_set , &out_set , 0 , &timeout) < 0) return ;
- for (IrcServerInfo* server = this->servers.begin() ; server != this->servers.end() ; ++server)
- if (irc_process_select_descriptors(server->session , &in_set , &out_set)) continue ;
- }
- void IrcClient::sendChat(String chat_msg)
- {
- IrcServerInfo* server = this->servers.begin() ;
- while (server != this->servers.end())
- irc_cmd_msg((server++)->session , CHARSTAR(xmpp_channel) , CHARSTAR(String(chat_msg))) ;
- AddUserChat(String::empty , my_nick , chat_msg) ;
- }
- #endif // DISABLE_CHAT
|