// This->Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
# include <stdexcept>
# include <optional>
# include <strophe.h>
# include <time.h>
# include <stdlib.h>
# include <stdint.h>
# include <stdio.h>
# include <string.h>
# include <sys/utsname.h>
# include <fmt/core.h>
# include <fmt/chrono.h>
# include <libxml/uri.h>
# include <utility>
# include <weechat/weechat-plugin.h>
# include "plugin.hh"
# include "xmpp/node.hh"
# include "xmpp/stanza.hh"
# include "config.hh"
# include "account.hh"
# include "user.hh"
# include "channel.hh"
# include "connection.hh"
# include "omemo.hh"
# include "pgp.hh"
# include "util.hh"
extern " C " {
# include "diff/diff.h"
}
void weechat : : connection : : init ( )
{
srand ( time ( NULL ) ) ;
libstrophe : : initialize ( ) ;
}
bool weechat : : connection : : version_handler ( xmpp_stanza_t * stanza )
{
const char * weechat_name = " weechat " ;
std : : unique_ptr < char > weechat_version ( weechat_info_get ( " version " , NULL ) ) ;
weechat_printf ( NULL , " Received version request from %s " , xmpp_stanza_get_from ( stanza ) ) ;
auto reply = libstrophe : : stanza : : reply ( stanza )
. set_type ( " result " ) ;
auto query = libstrophe : : stanza ( account . context )
. set_name ( " query " ) ;
if ( const char * ns = xmpp_stanza_get_ns ( xmpp_stanza_get_children ( stanza ) ) ; ns ) {
query . set_ns ( ns ) ;
}
query . add_child ( libstrophe : : stanza ( account . context )
. set_name ( " name " )
. add_child ( libstrophe : : stanza ( account . context )
. set_text ( weechat_name ) ) ) ;
query . add_child ( libstrophe : : stanza ( account . context )
. set_name ( " version " )
. add_child ( libstrophe : : stanza ( account . context )
. set_text ( weechat_version . get ( ) ) ) ) ;
reply . add_child ( query ) ;
account . connection . send ( reply ) ;
return true ;
}
bool weechat : : connection : : presence_handler ( xmpp_stanza_t * stanza )
{
weechat : : user * user ;
weechat : : channel * channel ;
auto binding = xml : : presence ( account . context , stanza ) ;
if ( ! binding . from )
return 1 ;
std : : string clientid ;
if ( auto caps = binding . capabilities ( ) )
{
auto node = caps - > node ;
auto ver = caps - > verification ;
clientid = fmt : : format ( " {}#{} " , node , ver ) ;
account . connection . send ( stanza : : iq ( )
. from ( binding . to ? binding . to - > full : " " )
. to ( binding . from
. transform ( [ ] ( auto & x ) { return x . full ; } )
. value_or ( std : : string ( ) ) )
. type ( " get " )
. id ( stanza : : uuid ( account . context ) )
. xep0030 ( )
. query ( )
. build ( account . context )
. get ( ) ) ;
}
channel = account . channels . contains ( binding . from - > bare . data ( ) )
? & account . channels . find ( binding . from - > bare . data ( ) ) - > second : nullptr ;
if ( ! ( binding . type & & * binding . type = = " unavailable " ) & & ! binding . muc_user ( ) & & ! channel )
{
const char * jid = binding . from - > bare . data ( ) ;
channel = & account . channels . emplace (
std : : make_pair ( jid , weechat : : channel {
account , weechat : : channel : : chat_type : : MUC , jid , jid
} ) ) . first - > second ;
}
if ( binding . type & & * binding . type = = " error " & & channel )
{
if ( auto error = binding . error ( ) )
{
weechat_printf ( channel - > buffer , " [!] \t %s%sError: %s " ,
weechat_color ( " gray " ) ,
binding . muc ( ) ? " MUC " : " " , error - > reason ( ) ) ;
}
return 1 ;
}
if ( auto x = binding . muc_user ( ) )
{
for ( int & status : x - > statuses )
{
switch ( status )
{
case 100 : // Non-Anonymous: [message | Entering a room]: Inform user that any occupant is allowed to see the user's full JID
if ( channel )
weechat_buffer_set ( channel - > buffer , " notify " , " 2 " ) ;
break ;
case 101 : // : [message (out of band) | Affiliation change]: Inform user that his or her affiliation changed while not in the room
break ;
case 102 : // : [message | Configuration change]: Inform occupants that room now shows unavailable members
break ;
case 103 : // : [message | Configuration change]: Inform occupants that room now does not show unavailable members
break ;
case 104 : // : [message | Configuration change]: Inform occupants that a non-privacy-related room configuration change has occurred
break ;
case 110 : // Self-Presence: [presence | Any room presence]: Inform user that presence refers to one of its own room occupants
break ;
case 170 : // Logging Active: [message or initial presence | Configuration change]: Inform occupants that room logging is now enabled
break ;
case 171 : // : [message | Configuration change]: Inform occupants that room logging is now disabled
break ;
case 172 : // : [message | Configuration change]: Inform occupants that the room is now non-anonymous
break ;
case 173 : // : [message | Configuration change]: Inform occupants that the room is now semi-anonymous
break ;
case 174 : // : [message | Configuration change]: Inform occupants that the room is now fully-anonymous
break ;
case 201 : // : [presence | Entering a room]: Inform user that a new room has been created
break ;
case 210 : // Nick Modified: [presence | Entering a room]: Inform user that the service has assigned or modified the occupant's roomnick
break ;
case 301 : // : [presence | Removal from room]: Inform user that he or she has been banned from the room
weechat_printf ( channel - > buffer , " [!] \t %sBanned from Room " , weechat_color ( " gray " ) ) ;
break ;
case 303 : // : [presence | Exiting a room]: Inform all occupants of new room nickname
break ;
case 307 : // : [presence | Removal from room]: Inform user that he or she has been kicked from the room
weechat_printf ( channel - > buffer , " [!] \t %sKicked from room " , weechat_color ( " gray " ) ) ;
break ;
case 321 : // : [presence | Removal from room]: Inform user that he or she is being removed from the room because of an affiliation change
weechat_printf ( channel - > buffer , " [!] \t %sRoom Affiliation changed, kicked " , weechat_color ( " gray " ) ) ;
break ;
case 322 : // : [presence | Removal from room]: Inform user that he or she is being removed from the room because the room has been changed to members-only and the user is not a member
weechat_printf ( channel - > buffer , " [!] \t %sRoom now members-only, kicked " , weechat_color ( " gray " ) ) ;
break ;
case 332 : // : [presence | Removal from room]: Inform user that he or she is being removed from the room because of a system shutdown
weechat_printf ( channel - > buffer , " [!] \t %sRoom Shutdown " , weechat_color ( " gray " ) ) ;
break ;
default :
break ;
}
}
for ( auto & item : x - > items )
{
using xml : : xep0045 ;
std : : string role ( item . role ? xep0045 : : format_role ( * item . role ) : " " ) ;
std : : string affiliation ( item . affiliation ? xep0045 : : format_affiliation ( * item . affiliation ) : " " ) ;
std : : string jid = item . target ? item . target - > full : clientid ;
user = weechat : : user : : search ( & account , binding . from - > full . data ( ) ) ;
if ( ! user )
{
auto name = binding . from - > full . data ( ) ;
user = & account . users . emplace ( std : : piecewise_construct ,
std : : forward_as_tuple ( name ) ,
std : : forward_as_tuple ( & account , channel , name ,
channel & & binding . from - > bare . data ( ) = = channel - > id
? ( binding . from - > resource . size ( ) ? binding . from - > resource . data ( ) : " " )
: binding . from - > full . data ( ) ) ) . first - > second ;
}
auto status = binding . status ( ) ;
auto show = binding . show ( ) ;
auto idle = binding . idle_since ( ) ;
user - > profile . status_text = status ? strdup ( status - > data ( ) ) : NULL ;
user - > profile . status = show ? strdup ( show - > data ( ) ) : NULL ;
user - > profile . idle = idle ? fmt : : format ( " {} " , * idle ) : std : : string ( ) ;
user - > is_away = show ? * show = = " away " : false ;
user - > profile . role = role . size ( ) ? strdup ( role . data ( ) ) : NULL ;
user - > profile . affiliation = affiliation . size ( ) & & affiliation = = " none "
? strdup ( affiliation . data ( ) ) : NULL ;
if ( channel )
{
if ( auto signature = binding . signature ( ) )
{
user - > profile . pgp_id = account . pgp . verify ( channel - > buffer , signature - > data ( ) ) ;
if ( channel - > type ! = weechat : : channel : : chat_type : : MUC )
channel - > pgp . ids . emplace ( user - > profile . pgp_id ) ;
}
if ( weechat_strcasecmp ( role . data ( ) , " none " ) = = 0 )
channel - > remove_member ( binding . from - > full . data ( ) , status ? status - > data ( ) : nullptr ) ;
else
channel - > add_member ( binding . from - > full . data ( ) , jid . data ( ) ) ;
}
}
}
else
{
user = user : : search ( & account , binding . from - > full . data ( ) ) ;
if ( ! user )
{
auto name = binding . from - > full . data ( ) ;
user = & account . users . emplace ( std : : piecewise_construct ,
std : : forward_as_tuple ( name ) ,
std : : forward_as_tuple ( & account , channel , name ,
channel & & binding . from - > bare . data ( ) = = channel - > id
? ( binding . from - > resource . size ( ) ? binding . from - > resource . data ( ) : " " )
: binding . from - > full . data ( ) ) ) . first - > second ;
}
auto status = binding . status ( ) ;
auto show = binding . show ( ) ;
auto idle = binding . idle_since ( ) ;
user - > profile . status_text = status ? strdup ( status - > data ( ) ) : NULL ;
user - > profile . status = show ? strdup ( show - > data ( ) ) : NULL ;
user - > profile . idle = idle ? fmt : : format ( " {} " , * idle ) : std : : string ( ) ;
user - > is_away = show ? * show = = " away " : false ;
user - > profile . role = NULL ;
user - > profile . affiliation = NULL ;
if ( channel )
{
if ( auto signature = binding . signature ( ) ; signature )
{
user - > profile . pgp_id = account . pgp . verify ( channel - > buffer , signature - > data ( ) ) ;
if ( channel - > type ! = weechat : : channel : : chat_type : : MUC )
channel - > pgp . ids . emplace ( user - > profile . pgp_id ) ;
}
if ( user - > profile . role )
channel - > remove_member ( binding . from - > full . data ( ) , status ? status - > data ( ) : nullptr ) ;
else
channel - > add_member ( binding . from - > full . data ( ) , clientid . data ( ) ) ;
}
}
return true ;
}
bool weechat : : connection : : message_handler ( xmpp_stanza_t * stanza )
{
weechat : : channel * channel , * parent_channel ;
xmpp_stanza_t * x , * body , * delay , * topic , * replace , * request , * markable , * composing , * sent , * received , * result , * forwarded , * event , * items , * item , * list , * device , * encrypted ;
const char * type , * from , * nick , * from_bare , * to , * to_bare , * id , * thread , * replace_id , * timestamp ;
char * text , * intext , * difftext = NULL , * cleartext = NULL ;
struct tm time = { 0 } ;
time_t date = 0 ;
auto binding = xml : : message ( account . context , stanza ) ;
body = xmpp_stanza_get_child_by_name ( stanza , " body " ) ;
if ( body = = NULL )
{
topic = xmpp_stanza_get_child_by_name ( stanza , " subject " ) ;
if ( topic ! = NULL )
{
intext = xmpp_stanza_get_text ( topic ) ;
type = xmpp_stanza_get_type ( stanza ) ;
if ( type ! = NULL & & strcmp ( type , " error " ) = = 0 )
return 1 ;
from = xmpp_stanza_get_from ( stanza ) ;
if ( from = = NULL )
return 1 ;
from_bare = xmpp_jid_bare ( account . context , from ) ;
from = xmpp_jid_resource ( account . context , from ) ;
channel = account . channels . contains ( from_bare )
? & account . channels . find ( from_bare ) - > second : nullptr ;
if ( ! channel )
{
if ( weechat_strcasecmp ( type , " groupchat " ) = = 0 )
channel = new weechat : : channel ( account , weechat : : channel : : chat_type : : MUC , from_bare , from_bare ) ;
else
channel = new weechat : : channel ( account , weechat : : channel : : chat_type : : PM , from_bare , from_bare ) ;
}
channel - > update_topic ( intext ? intext : " " , from , 0 ) ;
if ( intext ! = NULL )
xmpp_free ( account . context , intext ) ;
}
composing = xmpp_stanza_get_child_by_name_and_ns (
stanza , " composing " , " http://jabber.org/protocol/chatstates " ) ;
if ( composing ! = NULL )
{
from = xmpp_stanza_get_from ( stanza ) ;
if ( from = = NULL )
return 1 ;
from_bare = xmpp_jid_bare ( account . context , from ) ;
nick = xmpp_jid_resource ( account . context , from ) ;
channel = account . channels . contains ( from_bare )
? & account . channels . find ( from_bare ) - > second : nullptr ;
if ( ! channel )
return 1 ;
auto user = user : : search ( & account , from ) ;
if ( ! user )
{
auto name = from ;
user = & account . users . emplace ( std : : piecewise_construct ,
std : : forward_as_tuple ( name ) ,
std : : forward_as_tuple ( & account , channel , name ,
weechat_strcasecmp ( from_bare , channel - > id . data ( ) ) = = 0
? nick : from ) ) . first - > second ;
}
channel - > add_typing ( user ) ;
weechat_printf ( channel - > buffer , " ... \t %s%s typing " ,
weechat_color ( " gray " ) ,
channel - > type = = weechat : : channel : : chat_type : : MUC ? nick : from ) ;
}
sent = xmpp_stanza_get_child_by_name_and_ns (
stanza , " sent " , " urn:xmpp:carbons:2 " ) ;
if ( sent )
forwarded = xmpp_stanza_get_child_by_name_and_ns (
sent , " forwarded " , " urn:xmpp:forward:0 " ) ;
received = xmpp_stanza_get_child_by_name_and_ns (
stanza , " received " , " urn:xmpp:carbons:2 " ) ;
if ( received )
forwarded = xmpp_stanza_get_child_by_name_and_ns (
received , " forwarded " , " urn:xmpp:forward:0 " ) ;
if ( ( sent | | received ) & & forwarded ! = NULL )
{
xmpp_stanza_t * message = xmpp_stanza_get_children ( forwarded ) ;
return message_handler ( message ) ;
}
result = xmpp_stanza_get_child_by_name_and_ns (
stanza , " result " , " urn:xmpp:mam:2 " ) ;
if ( result )
{
forwarded = xmpp_stanza_get_child_by_name_and_ns (
result , " forwarded " , " urn:xmpp:forward:0 " ) ;
if ( forwarded ! = NULL )
{
xmpp_stanza_t * message = xmpp_stanza_get_child_by_name ( forwarded , " message " ) ;
if ( message )
{
message = xmpp_stanza_copy ( message ) ;
delay = xmpp_stanza_get_child_by_name_and_ns (
forwarded , " delay " , " urn:xmpp:delay " ) ;
if ( delay ! = NULL )
xmpp_stanza_add_child_ex ( message , xmpp_stanza_copy ( delay ) , 0 ) ;
int ret = message_handler ( message ) ;
xmpp_stanza_release ( message ) ;
return ret ;
}
}
}
event = xmpp_stanza_get_child_by_name_and_ns (
stanza , " event " , " http://jabber.org/protocol/pubsub#event " ) ;
if ( event )
{
items = xmpp_stanza_get_child_by_name ( event , " items " ) ;
if ( items )
{
const char * items_node = xmpp_stanza_get_attribute ( items , " node " ) ;
from = xmpp_stanza_get_from ( stanza ) ;
to = xmpp_stanza_get_to ( stanza ) ;
if ( items_node
& & weechat_strcasecmp ( items_node ,
" eu.siacs.conversations.axolotl.devicelist " ) = = 0 )
{
item = xmpp_stanza_get_child_by_name ( items , " item " ) ;
if ( item )
{
list = xmpp_stanza_get_child_by_name_and_ns (
item , " list " , " eu.siacs.conversations.axolotl " ) ;
if ( list )
{
if ( account . omemo )
{
account . omemo . handle_devicelist (
from ? from : account . jid ( ) . data ( ) , items ) ;
}
auto children = std : : unique_ptr < xmpp_stanza_t * [ ] > ( new xmpp_stanza_t * [ 3 + 1 ] ) ;
for ( device = xmpp_stanza_get_children ( list ) ;
device ; device = xmpp_stanza_get_next ( device ) )
{
const char * name = xmpp_stanza_get_name ( device ) ;
if ( weechat_strcasecmp ( name , " device " ) ! = 0 )
continue ;
const char * device_id = xmpp_stanza_get_id ( device ) ;
char bundle_node [ 128 ] = { 0 } ;
snprintf ( bundle_node , sizeof ( bundle_node ) ,
" eu.siacs.conversations.axolotl.bundles:%s " ,
device_id ) ;
children [ 1 ] = NULL ;
children [ 0 ] =
stanza__iq_pubsub_items ( account . context , NULL ,
strdup ( bundle_node ) ) ;
children [ 0 ] =
stanza__iq_pubsub ( account . context , NULL , children . get ( ) ,
with_noop ( " http://jabber.org/protocol/pubsub " ) ) ;
char * uuid = xmpp_uuid_gen ( account . context ) ;
children [ 0 ] =
stanza__iq ( account . context , NULL , children . get ( ) , NULL , uuid ,
strdup ( to ) , strdup ( from ) , strdup ( " get " ) ) ;
xmpp_free ( account . context , uuid ) ;
account . connection . send ( children [ 0 ] ) ;
xmpp_stanza_release ( children [ 0 ] ) ;
}
}
}
}
}
}
return 1 ;
}
type = xmpp_stanza_get_type ( stanza ) ;
if ( type ! = NULL & & strcmp ( type , " error " ) = = 0 )
return 1 ;
from = xmpp_stanza_get_from ( stanza ) ;
if ( from = = NULL )
return 1 ;
from_bare = xmpp_jid_bare ( account . context , from ) ;
to = xmpp_stanza_get_to ( stanza ) ;
if ( to = = NULL )
to = account . jid ( ) . data ( ) ;
to_bare = to ? xmpp_jid_bare ( account . context , to ) : NULL ;
id = xmpp_stanza_get_id ( stanza ) ;
thread = xmpp_stanza_get_attribute ( stanza , " thread " ) ;
replace = xmpp_stanza_get_child_by_name_and_ns ( stanza , " replace " ,
" urn:xmpp:message-correct:0 " ) ;
replace_id = replace ? xmpp_stanza_get_id ( replace ) : NULL ;
request = xmpp_stanza_get_child_by_name_and_ns ( stanza , " request " ,
" urn:xmpp:receipts " ) ;
markable = xmpp_stanza_get_child_by_name_and_ns ( stanza , " markable " ,
" urn:xmpp:chat-markers:0 " ) ;
const char * channel_id = account . jid ( ) = = from_bare ? to_bare : from_bare ;
parent_channel = account . channels . contains ( channel_id )
? & account . channels . find ( channel_id ) - > second : nullptr ;
const char * pm_id = account . jid ( ) = = from_bare ? to : from ;
channel = parent_channel ;
if ( ! channel )
channel = new weechat : : channel ( account ,
weechat_strcasecmp ( type , " groupchat " ) = = 0
? weechat : : channel : : chat_type : : MUC : weechat : : channel : : chat_type : : PM ,
channel_id , channel_id ) ;
if ( channel & & channel - > type = = weechat : : channel : : chat_type : : MUC
& & weechat_strcasecmp ( type , " chat " ) = = 0 )
channel = new weechat : : channel ( account , weechat : : channel : : chat_type : : PM ,
pm_id , pm_id ) ;
if ( id & & ( markable | | request ) )
{
auto unread = new weechat : : channel : : unread ( ) ;
unread - > id = strdup ( id ) ;
unread - > thread = thread ? strdup ( thread ) : NULL ;
xmpp_stanza_t * message = xmpp_message_new ( account . context , NULL ,
channel - > id . data ( ) , NULL ) ;
if ( request )
{
xmpp_stanza_t * message__received = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_name ( message__received , " received " ) ;
xmpp_stanza_set_ns ( message__received , " urn:xmpp:receipts " ) ;
xmpp_stanza_set_id ( message__received , unread - > id ) ;
xmpp_stanza_add_child ( message , message__received ) ;
xmpp_stanza_release ( message__received ) ;
}
if ( markable )
{
xmpp_stanza_t * message__received = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_name ( message__received , " received " ) ;
xmpp_stanza_set_ns ( message__received , " urn:xmpp:chat-markers:0 " ) ;
xmpp_stanza_set_id ( message__received , unread - > id ) ;
xmpp_stanza_add_child ( message , message__received ) ;
xmpp_stanza_release ( message__received ) ;
}
if ( unread - > thread )
{
xmpp_stanza_t * message__thread = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_name ( message__thread , " thread " ) ;
xmpp_stanza_t * message__thread__text = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_text ( message__thread__text , unread - > thread ) ;
xmpp_stanza_add_child ( message__thread , message__thread__text ) ;
xmpp_stanza_release ( message__thread__text ) ;
xmpp_stanza_add_child ( message , message__thread ) ;
xmpp_stanza_release ( message__thread ) ;
}
xmpp_send ( account . connection , message ) ;
xmpp_stanza_release ( message ) ;
channel - > unreads . push_back ( * unread ) ;
}
encrypted = xmpp_stanza_get_child_by_name_and_ns ( stanza , " encrypted " ,
" eu.siacs.conversations.axolotl " ) ;
x = xmpp_stanza_get_child_by_name_and_ns ( stanza , " x " , " jabber:x:encrypted " ) ;
intext = xmpp_stanza_get_text ( body ) ;
if ( encrypted & & account . omemo )
{
cleartext = account . omemo . decode ( & account , from_bare , encrypted ) ;
if ( ! cleartext )
{
weechat_printf_date_tags ( channel - > buffer , 0 , " notify_none " , " %s%s (%s) " ,
weechat_prefix ( " error " ) , " OMEMO Decryption Error " , from ) ;
return 1 ;
}
}
if ( x )
{
char * ciphertext = xmpp_stanza_get_text ( x ) ;
cleartext = account . pgp . decrypt ( channel - > buffer , ciphertext ) ;
xmpp_free ( account . context , ciphertext ) ;
}
text = cleartext ? cleartext : intext ;
if ( replace )
{
const char * orig = NULL ;
void * lines = weechat_hdata_pointer ( weechat_hdata_get ( " buffer " ) ,
channel - > buffer , " lines " ) ;
if ( lines )
{
void * last_line = weechat_hdata_pointer ( weechat_hdata_get ( " lines " ) ,
lines , " last_line " ) ;
while ( last_line & & ! orig )
{
void * line_data = weechat_hdata_pointer ( weechat_hdata_get ( " line " ) ,
last_line , " data " ) ;
if ( line_data )
{
int tags_count = weechat_hdata_integer ( weechat_hdata_get ( " line_data " ) ,
line_data , " tags_count " ) ;
char str_tag [ 24 ] = { 0 } ;
for ( int n_tag = 0 ; n_tag < tags_count ; n_tag + + )
{
snprintf ( str_tag , sizeof ( str_tag ) , " %d|tags_array " , n_tag ) ;
const char * tag = weechat_hdata_string ( weechat_hdata_get ( " line_data " ) ,
line_data , str_tag ) ;
if ( strlen ( tag ) > strlen ( " id_ " ) & &
weechat_strcasecmp ( tag + strlen ( " id_ " ) , replace_id ) = = 0 )
{
struct t_arraylist * orig_lines = weechat_arraylist_new (
0 , 0 , 0 , NULL , NULL , NULL , NULL ) ;
char * msg = ( char * ) weechat_hdata_string ( weechat_hdata_get ( " line_data " ) ,
line_data , " message " ) ;
weechat_arraylist_insert ( orig_lines , 0 , msg ) ;
while ( msg )
{
last_line = weechat_hdata_pointer ( weechat_hdata_get ( " line " ) ,
last_line , " prev_line " ) ;
if ( last_line )
line_data = weechat_hdata_pointer ( weechat_hdata_get ( " line " ) ,
last_line , " data " ) ;
else
line_data = NULL ;
msg = NULL ;
if ( line_data )
{
tags_count = weechat_hdata_integer ( weechat_hdata_get ( " line_data " ) ,
line_data , " tags_count " ) ;
for ( n_tag = 0 ; n_tag < tags_count ; n_tag + + )
{
snprintf ( str_tag , sizeof ( str_tag ) , " %d|tags_array " , n_tag ) ;
tag = weechat_hdata_string ( weechat_hdata_get ( " line_data " ) ,
line_data , str_tag ) ;
if ( strlen ( tag ) > strlen ( " id_ " ) & &
weechat_strcasecmp ( tag + strlen ( " id_ " ) , replace_id ) = = 0 )
{
msg = ( char * ) weechat_hdata_string ( weechat_hdata_get ( " line_data " ) ,
line_data , " message " ) ;
break ;
}
}
}
if ( msg )
weechat_arraylist_insert ( orig_lines , 0 , msg ) ;
}
char * * orig_message = weechat_string_dyn_alloc ( 256 ) ;
for ( int i = 0 ; i < weechat_arraylist_size ( orig_lines ) ; i + + )
weechat_string_dyn_concat ( orig_message ,
( const char * ) weechat_arraylist_get ( orig_lines , i ) ,
- 1 ) ;
orig = * orig_message ;
weechat_string_dyn_free ( orig_message , 0 ) ;
break ;
}
}
}
last_line = weechat_hdata_pointer ( weechat_hdata_get ( " line " ) ,
last_line , " prev_line " ) ;
}
}
if ( orig )
{
struct diff result ;
if ( diff ( & result , char_cmp , 1 , orig , strlen ( orig ) , text , strlen ( text ) ) > 0 )
{
char * * visual = weechat_string_dyn_alloc ( 256 ) ;
char ch [ 2 ] = { 0 } ;
int retention = 0 ;
int modification = 0 ;
for ( size_t i = 0 ; i < result . sessz ; i + + )
switch ( result . ses [ i ] . type )
{
case DIFF_ADD :
weechat_string_dyn_concat ( visual , weechat_color ( " green " ) , - 1 ) ;
* ch = * ( const char * ) result . ses [ i ] . e ;
weechat_string_dyn_concat ( visual , ch , - 1 ) ;
modification + + ;
break ;
case DIFF_DELETE :
weechat_string_dyn_concat ( visual , weechat_color ( " red " ) , - 1 ) ;
* ch = * ( const char * ) result . ses [ i ] . e ;
weechat_string_dyn_concat ( visual , ch , - 1 ) ;
modification + + ;
break ;
case DIFF_COMMON :
default :
weechat_string_dyn_concat ( visual , weechat_color ( " resetcolor " ) , - 1 ) ;
* ch = * ( const char * ) result . ses [ i ] . e ;
weechat_string_dyn_concat ( visual , ch , - 1 ) ;
retention + + ;
break ;
}
free ( result . ses ) ;
free ( result . lcs ) ;
if ( ( modification > 20 ) & & ( modification > retention ) ) {
weechat_string_dyn_free ( visual , 1 ) ;
visual = weechat_string_dyn_alloc ( 256 ) ;
weechat_string_dyn_concat ( visual , weechat_color ( " red " ) , - 1 ) ;
if ( strlen ( orig ) > = 16 ) {
weechat_string_dyn_concat ( visual , orig , 16 ) ;
weechat_string_dyn_concat ( visual , " ... " , - 1 ) ;
} else
weechat_string_dyn_concat ( visual , orig , - 1 ) ;
weechat_string_dyn_concat ( visual , weechat_color ( " green " ) , - 1 ) ;
weechat_string_dyn_concat ( visual , text , - 1 ) ;
}
difftext = strdup ( * visual ) ;
weechat_string_dyn_free ( visual , 1 ) ;
}
}
}
nick = from ;
if ( weechat_strcasecmp ( type , " groupchat " ) = = 0 )
{
nick = channel - > name = = xmpp_jid_bare ( account . context , from )
? xmpp_jid_resource ( account . context , from )
: from ;
}
else if ( parent_channel & & parent_channel - > type = = weechat : : channel : : chat_type : : MUC )
{
nick = channel - > name = = from
? xmpp_jid_resource ( account . context , from )
: from ;
}
delay = xmpp_stanza_get_child_by_name_and_ns ( stanza , " delay " , " urn:xmpp:delay " ) ;
timestamp = delay ? xmpp_stanza_get_attribute ( delay , " stamp " ) : NULL ;
if ( timestamp )
{
strptime ( timestamp , " %FT%T " , & time ) ;
date = mktime ( & time ) ;
}
char * * dyn_tags = weechat_string_dyn_alloc ( 1 ) ;
weechat_string_dyn_concat ( dyn_tags , " xmpp_message,message " , - 1 ) ;
{
weechat_string_dyn_concat ( dyn_tags , " ,nick_ " , - 1 ) ;
weechat_string_dyn_concat ( dyn_tags , nick , - 1 ) ;
}
{
weechat_string_dyn_concat ( dyn_tags , " ,host_ " , - 1 ) ;
weechat_string_dyn_concat ( dyn_tags , from , - 1 ) ;
}
if ( id )
{
weechat_string_dyn_concat ( dyn_tags , " ,id_ " , - 1 ) ;
weechat_string_dyn_concat ( dyn_tags , id , - 1 ) ;
}
if ( channel - > type = = weechat : : channel : : chat_type : : PM )
weechat_string_dyn_concat ( dyn_tags , " ,private " , - 1 ) ;
if ( weechat_string_match ( text , " /me * " , 0 ) )
weechat_string_dyn_concat ( dyn_tags , " ,xmpp_action " , - 1 ) ;
if ( replace )
{
weechat_string_dyn_concat ( dyn_tags , " ,edit " , - 1 ) ;
weechat_string_dyn_concat ( dyn_tags , " ,replace_ " , - 1 ) ;
weechat_string_dyn_concat ( dyn_tags , replace_id , - 1 ) ;
}
if ( date ! = 0 | | encrypted )
weechat_string_dyn_concat ( dyn_tags , " ,notify_none " , - 1 ) ;
else if ( channel - > type = = weechat : : channel : : chat_type : : PM
& & from_bare ! = account . jid ( ) )
weechat_string_dyn_concat ( dyn_tags , " ,notify_private " , - 1 ) ;
else
weechat_string_dyn_concat ( dyn_tags , " ,notify_message,log1 " , - 1 ) ;
const char * edit = replace ? " * " : " " ; // Losing which message was edited, sadly
if ( x & & text = = cleartext & & channel - > transport ! = weechat : : channel : : transport : : PGP )
{
channel - > transport = weechat : : channel : : transport : : PGP ;
weechat_printf_date_tags ( channel - > buffer , date , NULL , " %s%sTransport: %s " ,
weechat_prefix ( " network " ) , weechat_color ( " gray " ) ,
channel : : transport_name ( channel - > transport ) ) ;
}
else if ( ! x & & text = = intext & & channel - > transport ! = weechat : : channel : : transport : : PLAIN )
{
channel - > transport = weechat : : channel : : transport : : PLAIN ;
weechat_printf_date_tags ( channel - > buffer , date , NULL , " %s%sTransport: %s " ,
weechat_prefix ( " network " ) , weechat_color ( " gray " ) ,
channel : : transport_name ( channel - > transport ) ) ;
}
if ( channel_id = = from_bare & & to = = channel - > id )
weechat_printf_date_tags ( channel - > buffer , date , * dyn_tags , " %s%s \t [to %s]: %s " ,
edit , user : : as_prefix_raw ( & account , from ) . data ( ) ,
to , difftext ? difftext : text ? text : " " ) ;
else if ( weechat_string_match ( text , " /me * " , 0 ) )
weechat_printf_date_tags ( channel - > buffer , date , * dyn_tags , " %s%s \t %s %s " ,
edit , weechat_prefix ( " action " ) , user : : as_prefix_raw ( & account , from ) . data ( ) ,
difftext ? difftext + 4 : text ? text + 4 : " " ) ;
else
weechat_printf_date_tags ( channel - > buffer , date , * dyn_tags , " %s%s \t %s " ,
edit , user : : as_prefix_raw ( & account , from ) . data ( ) ,
difftext ? difftext : text ? text : " " ) ;
weechat_string_dyn_free ( dyn_tags , 1 ) ;
if ( intext )
xmpp_free ( account . context , intext ) ;
if ( difftext )
free ( difftext ) ;
if ( cleartext )
free ( cleartext ) ;
return true ;
}
xmpp_stanza_t * weechat : : connection : : get_caps ( xmpp_stanza_t * reply , char * * hash )
{
xmpp_stanza_t * query = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_name ( query , " query " ) ;
xmpp_stanza_set_ns ( query , " http://jabber.org/protocol/disco#info " ) ;
char * client_name = weechat_string_eval_expression (
" weechat ${info:version} " , NULL , NULL , NULL ) ;
char * * serial = weechat_string_dyn_alloc ( 256 ) ;
weechat_string_dyn_concat ( serial , " client/pc// " , - 1 ) ;
weechat_string_dyn_concat ( serial , client_name , - 1 ) ;
weechat_string_dyn_concat ( serial , " < " , - 1 ) ;
xmpp_stanza_t * identity = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_name ( identity , " identity " ) ;
xmpp_stanza_set_attribute ( identity , " category " , " client " ) ;
xmpp_stanza_set_attribute ( identity , " name " , client_name ) ;
free ( client_name ) ;
xmpp_stanza_set_attribute ( identity , " type " , " pc " ) ;
xmpp_stanza_add_child ( query , identity ) ;
xmpp_stanza_release ( identity ) ;
xmpp_stanza_t * feature = NULL ;
# define FEATURE(NS) \
feature = xmpp_stanza_new ( account . context ) ; \
xmpp_stanza_set_name ( feature , " feature " ) ; \
xmpp_stanza_set_attribute ( feature , " var " , NS ) ; \
xmpp_stanza_add_child ( query , feature ) ; \
xmpp_stanza_release ( feature ) ; \
weechat_string_dyn_concat ( serial , NS , - 1 ) ; \
weechat_string_dyn_concat ( serial , " < " , - 1 ) ;
FEATURE ( " eu.siacs.conversations.axolotl.devicelist+notify " ) ;
FEATURE ( " http://jabber.org/protocol/caps " ) ;
FEATURE ( " http://jabber.org/protocol/chatstates " ) ;
FEATURE ( " http://jabber.org/protocol/disco#info " ) ;
FEATURE ( " http://jabber.org/protocol/disco#items " ) ;
FEATURE ( " http://jabber.org/protocol/muc " ) ;
FEATURE ( " http://jabber.org/protocol/nick+notify " ) ;
FEATURE ( " jabber:iq:version " ) ;
FEATURE ( " jabber:x:conference " ) ;
FEATURE ( " jabber:x:oob " ) ;
FEATURE ( " storage:bookmarks+notify " ) ;
FEATURE ( " urn:xmpp:avatar:metadata+notify " ) ;
FEATURE ( " urn:xmpp:chat-markers:0 " ) ;
FEATURE ( " urn:xmpp:idle:1 " ) ;
//FEATURE("urn:xmpp:jingle-message:0");
//FEATURE("urn:xmpp:jingle:1");
//FEATURE("urn:xmpp:jingle:apps:dtls:0");
//FEATURE("urn:xmpp:jingle:apps:file-transfer:3");
//FEATURE("urn:xmpp:jingle:apps:file-transfer:4");
//FEATURE("urn:xmpp:jingle:apps:file-transfer:5");
//FEATURE("urn:xmpp:jingle:apps:rtp:1");
//FEATURE("urn:xmpp:jingle:apps:rtp:audio");
//FEATURE("urn:xmpp:jingle:apps:rtp:video");
//FEATURE("urn:xmpp:jingle:jet-omemo:0");
//FEATURE("urn:xmpp:jingle:jet:0");
//FEATURE("urn:xmpp:jingle:transports:ibb:1");
//FEATURE("urn:xmpp:jingle:transports:ice-udp:1");
//FEATURE("urn:xmpp:jingle:transports:s5b:1");
FEATURE ( " urn:xmpp:message-correct:0 " ) ;
FEATURE ( " urn:xmpp:ping " ) ;
FEATURE ( " urn:xmpp:receipts " ) ;
FEATURE ( " urn:xmpp:time " ) ;
# undef FEATURE
xmpp_stanza_t * x = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_name ( x , " x " ) ;
xmpp_stanza_set_ns ( x , " jabber:x:data " ) ;
xmpp_stanza_set_attribute ( x , " type " , " result " ) ;
static struct utsname osinfo ;
if ( uname ( & osinfo ) < 0 )
{
* osinfo . sysname = 0 ;
* osinfo . release = 0 ;
}
xmpp_stanza_t * field , * value , * text ;
// This is utter bullshit, TODO: anything but this
# define FEATURE1(VAR, TYPE, VALUE) \
field = xmpp_stanza_new ( account . context ) ; \
xmpp_stanza_set_name ( field , " field " ) ; \
xmpp_stanza_set_attribute ( field , " var " , VAR ) ; \
if ( TYPE ) xmpp_stanza_set_attribute ( field , " type " , TYPE ) ; \
value = xmpp_stanza_new ( account . context ) ; \
xmpp_stanza_set_name ( value , " value " ) ; \
text = xmpp_stanza_new ( account . context ) ; \
xmpp_stanza_set_text ( text , VALUE ) ; \
xmpp_stanza_add_child ( value , text ) ; \
xmpp_stanza_release ( text ) ; \
xmpp_stanza_add_child ( field , value ) ; \
xmpp_stanza_release ( value ) ; \
xmpp_stanza_add_child ( x , field ) ; \
xmpp_stanza_release ( field ) ; \
if ( strcmp ( VAR , " FORM_TYPE " ) = = 0 ) { \
weechat_string_dyn_concat ( serial , VAR , - 1 ) ; \
weechat_string_dyn_concat ( serial , " < " , - 1 ) ; \
} \
weechat_string_dyn_concat ( serial , VALUE , - 1 ) ; \
weechat_string_dyn_concat ( serial , " < " , - 1 ) ;
# define FEATURE2(VAR, TYPE, VALUE1, VALUE2) \
field = xmpp_stanza_new ( account . context ) ; \
xmpp_stanza_set_name ( field , " field " ) ; \
xmpp_stanza_set_attribute ( field , " var " , VAR ) ; \
xmpp_stanza_set_attribute ( field , " type " , TYPE ) ; \
value = xmpp_stanza_new ( account . context ) ; \
xmpp_stanza_set_name ( value , " value " ) ; \
text = xmpp_stanza_new ( account . context ) ; \
xmpp_stanza_set_text ( text , VALUE1 ) ; \
xmpp_stanza_add_child ( value , text ) ; \
xmpp_stanza_release ( text ) ; \
xmpp_stanza_add_child ( field , value ) ; \
xmpp_stanza_release ( value ) ; \
value = xmpp_stanza_new ( account . context ) ; \
xmpp_stanza_set_name ( value , " value " ) ; \
text = xmpp_stanza_new ( account . context ) ; \
xmpp_stanza_set_text ( text , VALUE2 ) ; \
xmpp_stanza_add_child ( value , text ) ; \
xmpp_stanza_release ( text ) ; \
xmpp_stanza_add_child ( field , value ) ; \
xmpp_stanza_release ( value ) ; \
xmpp_stanza_add_child ( x , field ) ; \
xmpp_stanza_release ( field ) ; \
weechat_string_dyn_concat ( serial , VAR , - 1 ) ; \
weechat_string_dyn_concat ( serial , " < " , - 1 ) ; \
weechat_string_dyn_concat ( serial , VALUE1 , - 1 ) ; \
weechat_string_dyn_concat ( serial , " < " , - 1 ) ; \
weechat_string_dyn_concat ( serial , VALUE2 , - 1 ) ; \
weechat_string_dyn_concat ( serial , " < " , - 1 ) ;
FEATURE1 ( " FORM_TYPE " , " hidden " , " urn:xmpp:dataforms:softwareinfo " ) ;
FEATURE2 ( " ip_version " , " text-multi " , " ipv4 " , " ipv6 " ) ;
FEATURE1 ( " os " , NULL , osinfo . sysname ) ;
FEATURE1 ( " os_version " , NULL , osinfo . release ) ;
FEATURE1 ( " software " , NULL , " weechat " ) ;
FEATURE1 ( " software_version " , NULL , weechat_info_get ( " version " , NULL ) ) ;
# undef FEATURE1
# undef FEATURE2
xmpp_stanza_add_child ( query , x ) ;
xmpp_stanza_release ( x ) ;
xmpp_stanza_set_type ( reply , " result " ) ;
xmpp_stanza_add_child ( reply , query ) ;
unsigned char digest [ 20 ] ;
xmpp_sha1_t * sha1 = xmpp_sha1_new ( account . context ) ;
xmpp_sha1_update ( sha1 , ( unsigned char * ) * serial , strlen ( * serial ) ) ;
xmpp_sha1_final ( sha1 ) ;
weechat_string_dyn_free ( serial , 1 ) ;
xmpp_sha1_to_digest ( sha1 , digest ) ;
xmpp_sha1_free ( sha1 ) ;
if ( hash )
{
char * cap_hash = xmpp_base64_encode ( account . context , digest , 20 ) ;
* hash = strdup ( cap_hash ) ;
xmpp_free ( account . context , cap_hash ) ;
}
return reply ;
}
bool weechat : : connection : : iq_handler ( xmpp_stanza_t * stanza )
{
xmpp_stanza_t * reply , * query , * text , * fin ;
xmpp_stanza_t * pubsub , * items , * item , * list , * bundle , * device ;
xmpp_stanza_t * storage , * conference , * nick ;
auto binding = xml : : iq ( account . context , stanza ) ;
const char * id = xmpp_stanza_get_id ( stanza ) ;
const char * from = xmpp_stanza_get_from ( stanza ) ;
const char * to = xmpp_stanza_get_to ( stanza ) ;
query = xmpp_stanza_get_child_by_name_and_ns (
stanza , " query " , " http://jabber.org/protocol/disco#info " ) ;
const char * type = xmpp_stanza_get_attribute ( stanza , " type " ) ;
if ( query & & type )
{
if ( weechat_strcasecmp ( type , " get " ) = = 0 )
{
reply = get_caps ( xmpp_stanza_reply ( stanza ) , NULL ) ;
account . connection . send ( reply ) ;
xmpp_stanza_release ( reply ) ;
}
if ( weechat_strcasecmp ( type , " result " ) = = 0 )
{
xmpp_stanza_t * identity = xmpp_stanza_get_child_by_name ( query , " identity " ) ;
if ( identity )
{
std : : string category ;
std : : string name ;
std : : string type ;
if ( const char * attr = xmpp_stanza_get_attribute ( identity , " category " ) )
category = attr ;
if ( const char * attr = xmpp_stanza_get_attribute ( identity , " name " ) )
name = unescape ( attr ) ;
if ( const char * attr = xmpp_stanza_get_attribute ( identity , " type " ) )
type = attr ;
if ( category = = " conference " )
{
auto ptr_channel = account . channels . find ( from ) ;
if ( ptr_channel ! = account . channels . end ( ) )
ptr_channel - > second . update_name ( name . data ( ) ) ;
}
else if ( category = = " conference " )
{
xmpp_stanza_t * children [ 2 ] = { NULL } ;
children [ 0 ] = stanza__iq_pubsub_items ( account . context , NULL ,
const_cast < char * > ( " eu.siacs.conversations.axolotl.devicelist " ) ) ;
children [ 0 ] = stanza__iq_pubsub ( account . context , NULL ,
children , with_noop ( " http://jabber.org/protocol/pubsub " ) ) ;
children [ 0 ] = stanza__iq ( account . context , NULL , children , NULL ,
strdup ( " fetch2 " ) , to ? strdup ( to ) : NULL ,
binding . from ? strdup ( binding . from - > bare . data ( ) ) : NULL , strdup ( " get " ) ) ;
account . connection . send ( children [ 0 ] ) ;
xmpp_stanza_release ( children [ 0 ] ) ;
}
}
}
}
query = xmpp_stanza_get_child_by_name_and_ns (
stanza , " query " , " jabber:iq:private " ) ;
if ( query & & type )
{
storage = xmpp_stanza_get_child_by_name_and_ns (
query , " storage " , " storage:bookmarks " ) ;
if ( storage )
{
for ( conference = xmpp_stanza_get_children ( storage ) ;
conference ; conference = xmpp_stanza_get_next ( conference ) )
{
const char * name = xmpp_stanza_get_name ( conference ) ;
if ( weechat_strcasecmp ( name , " conference " ) ! = 0 )
continue ;
const char * jid = xmpp_stanza_get_attribute ( conference , " jid " ) ;
const char * autojoin = xmpp_stanza_get_attribute ( conference , " autojoin " ) ;
name = xmpp_stanza_get_attribute ( conference , " name " ) ;
nick = xmpp_stanza_get_child_by_name ( conference , " nick " ) ;
char * intext ;
if ( nick )
{
text = xmpp_stanza_get_children ( nick ) ;
intext = xmpp_stanza_get_text ( text ) ;
}
account . connection . send ( stanza : : iq ( )
. from ( to )
. to ( jid )
. type ( " get " )
. id ( stanza : : uuid ( account . context ) )
. xep0030 ( )
. query ( )
. build ( account . context )
. get ( ) ) ;
if ( weechat_strcasecmp ( autojoin , " true " ) = = 0 )
{
char * * command = weechat_string_dyn_alloc ( 256 ) ;
weechat_string_dyn_concat ( command , " /enter " , - 1 ) ;
weechat_string_dyn_concat ( command , jid , - 1 ) ;
if ( nick )
{
weechat_string_dyn_concat ( command , " / " , - 1 ) ;
weechat_string_dyn_concat ( command , intext , - 1 ) ;
}
weechat_command ( account . buffer , * command ) ;
auto ptr_channel = account . channels . find ( jid ) ;
struct t_gui_buffer * ptr_buffer =
ptr_channel ! = account . channels . end ( )
? ptr_channel - > second . buffer : NULL ;
if ( ptr_buffer )
weechat_buffer_set ( ptr_buffer , " short_name " , name ) ;
weechat_string_dyn_free ( command , 1 ) ;
}
if ( nick )
free ( intext ) ;
}
}
}
pubsub = xmpp_stanza_get_child_by_name_and_ns (
stanza , " pubsub " , " http://jabber.org/protocol/pubsub " ) ;
if ( pubsub )
{
const char * items_node , * device_id ;
items = xmpp_stanza_get_child_by_name ( pubsub , " items " ) ;
if ( items )
{
items_node = xmpp_stanza_get_attribute ( items , " node " ) ;
if ( items_node
& & weechat_strcasecmp ( items_node ,
" eu.siacs.conversations.axolotl.devicelist " ) = = 0 )
{
item = xmpp_stanza_get_child_by_name ( items , " item " ) ;
if ( item )
{
const char * item_id = xmpp_stanza_get_id ( item ) ;
list = xmpp_stanza_get_child_by_name_and_ns (
item , " list " , " eu.siacs.conversations.axolotl " ) ;
if ( list & & account . omemo )
{
account . omemo . handle_devicelist (
from ? from : account . jid ( ) . data ( ) , items ) ;
xmpp_stanza_t * children [ 2 ] = { NULL } ;
for ( device = xmpp_stanza_get_children ( list ) ;
device ; device = xmpp_stanza_get_next ( device ) )
{
const char * name = xmpp_stanza_get_name ( device ) ;
if ( weechat_strcasecmp ( name , " device " ) ! = 0 )
continue ;
const char * device_id = xmpp_stanza_get_id ( device ) ;
char bundle_node [ 128 ] = { 0 } ;
snprintf ( bundle_node , sizeof ( bundle_node ) ,
" eu.siacs.conversations.axolotl.bundles:%s " ,
device_id ) ;
children [ 1 ] = NULL ;
children [ 0 ] =
stanza__iq_pubsub_items ( account . context , NULL ,
strdup ( bundle_node ) ) ;
children [ 0 ] =
stanza__iq_pubsub ( account . context , NULL , children ,
with_noop ( " http://jabber.org/protocol/pubsub " ) ) ;
char * uuid = xmpp_uuid_gen ( account . context ) ;
children [ 0 ] =
stanza__iq ( account . context , NULL , children , NULL , uuid ,
to ? strdup ( to ) : NULL , from ? strdup ( from ) : NULL ,
strdup ( " get " ) ) ;
xmpp_free ( account . context , uuid ) ;
account . connection . send ( children [ 0 ] ) ;
xmpp_stanza_release ( children [ 0 ] ) ;
}
if ( account . jid ( ) = = from )
{
weechat : : account : : device dev ;
char id [ 64 ] = { 0 } ;
account . devices . clear ( ) ;
dev . id = account . omemo . device_id ;
snprintf ( id , sizeof ( id ) , " %d " , dev . id ) ;
dev . name = id ;
dev . label = " weechat " ;
account . devices . emplace ( dev . id , dev ) ;
for ( device = xmpp_stanza_get_children ( list ) ;
device ; device = xmpp_stanza_get_next ( device ) )
{
const char * name = xmpp_stanza_get_name ( device ) ;
if ( weechat_strcasecmp ( name , " device " ) ! = 0 )
continue ;
device_id = xmpp_stanza_get_id ( device ) ;
dev . id = atoi ( device_id ) ;
dev . name = device_id ;
dev . label = " " ;
account . devices . emplace ( dev . id , dev ) ;
}
reply = account . get_devicelist ( ) ;
char * uuid = xmpp_uuid_gen ( account . context ) ;
xmpp_stanza_set_id ( reply , uuid ) ;
xmpp_free ( account . context , uuid ) ;
xmpp_stanza_set_attribute ( reply , " to " , from ) ;
xmpp_stanza_set_attribute ( reply , " from " , to ) ;
account . connection . send ( reply ) ;
xmpp_stanza_release ( reply ) ;
}
}
}
}
if ( items_node
& & strncmp ( items_node ,
" eu.siacs.conversations.axolotl.bundles " ,
strnlen ( items_node ,
strlen ( " eu.siacs.conversations.axolotl.bundles " ) ) ) = = 0 )
{
item = xmpp_stanza_get_child_by_name ( items , " item " ) ;
if ( item )
{
bundle = xmpp_stanza_get_child_by_name_and_ns ( item , " bundle " , " eu.siacs.conversations.axolotl " ) ;
if ( bundle )
{
size_t node_prefix =
strlen ( " eu.siacs.conversations.axolotl.bundles: " ) ;
if ( account . omemo & & strlen ( items_node ) > node_prefix )
{
account . omemo . handle_bundle (
from ? from : account . jid ( ) . data ( ) ,
strtol ( items_node + node_prefix ,
NULL , 10 ) ,
items ) ;
}
}
}
}
}
}
fin = xmpp_stanza_get_child_by_name_and_ns (
stanza , " fin " , " urn:xmpp:mam:2 " ) ;
if ( fin )
{
xmpp_stanza_t * set , * set__last ;
char * set__last__text ;
weechat : : account : : mam_query mam_query ;
set = xmpp_stanza_get_child_by_name_and_ns (
fin , " set " , " http://jabber.org/protocol/rsm " ) ;
if ( set & & account . mam_query_search ( & mam_query , id ) )
{
auto channel = account . channels . find ( mam_query . with . data ( ) ) ;
set__last = xmpp_stanza_get_child_by_name ( set , " last " ) ;
set__last__text = set__last
? xmpp_stanza_get_text ( set__last ) : NULL ;
if ( channel ! = account . channels . end ( ) & & set__last__text )
{
channel - > second . fetch_mam ( id ,
mam_query . start . transform ( [ ] ( time_t & t ) { return & t ; } ) . value_or ( nullptr ) ,
mam_query . end . transform ( [ ] ( time_t & t ) { return & t ; } ) . value_or ( nullptr ) ,
set__last__text ) ;
}
else if ( ! set__last )
account . mam_query_remove ( mam_query . id ) ;
}
}
return true ;
}
bool weechat : : connection : : conn_handler ( event status , int error , xmpp_stream_error_t * stream_error )
{
( void ) error ;
( void ) stream_error ;
if ( status = = event : : connect )
{
account . disconnected = 0 ;
xmpp_stanza_t * pres__c , * pres__status , * pres__status__text ,
* pres__x , * pres__x__text ;
this - > handler_add < jabber : : iq : : version > (
" iq " , nullptr , [ ] ( xmpp_conn_t * conn , xmpp_stanza_t * stanza , void * userdata ) {
auto & connection = * reinterpret_cast < weechat : : connection * > ( userdata ) ;
if ( connection ! = conn ) throw std : : invalid_argument ( " connection != conn " ) ;
return connection . version_handler ( stanza ) ? 1 : 0 ;
} ) ;
this - > handler_add (
" presence " , nullptr , [ ] ( xmpp_conn_t * conn , xmpp_stanza_t * stanza , void * userdata ) {
auto & connection = * reinterpret_cast < weechat : : connection * > ( userdata ) ;
if ( connection ! = conn ) throw std : : invalid_argument ( " connection != conn " ) ;
return connection . presence_handler ( stanza ) ? 1 : 0 ;
} ) ;
this - > handler_add (
" message " , /*type*/ nullptr , [ ] ( xmpp_conn_t * conn , xmpp_stanza_t * stanza , void * userdata ) {
auto & connection = * reinterpret_cast < weechat : : connection * > ( userdata ) ;
if ( connection ! = conn ) throw std : : invalid_argument ( " connection != conn " ) ;
return connection . message_handler ( stanza ) ? 1 : 0 ;
} ) ;
this - > handler_add (
" iq " , nullptr , [ ] ( xmpp_conn_t * conn , xmpp_stanza_t * stanza , void * userdata ) {
auto & connection = * reinterpret_cast < weechat : : connection * > ( userdata ) ;
if ( connection ! = conn ) throw std : : invalid_argument ( " connection != conn " ) ;
return connection . iq_handler ( stanza ) ? 1 : 0 ;
} ) ;
/* Send initial <presence/> so that we appear online to contacts */
auto children = std : : unique_ptr < xmpp_stanza_t * [ ] > ( new xmpp_stanza_t * [ 3 + 1 ] ) ;
pres__c = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_name ( pres__c , " c " ) ;
xmpp_stanza_set_ns ( pres__c , " http://jabber.org/protocol/caps " ) ;
xmpp_stanza_set_attribute ( pres__c , " hash " , " sha-1 " ) ;
xmpp_stanza_set_attribute ( pres__c , " node " , " http://weechat.org " ) ;
xmpp_stanza_t * caps = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_name ( caps , " caps " ) ;
char * cap_hash ;
caps = this - > get_caps ( caps , & cap_hash ) ;
xmpp_stanza_release ( caps ) ;
xmpp_stanza_set_attribute ( pres__c , " ver " , cap_hash ) ;
free ( cap_hash ) ;
children [ 0 ] = pres__c ;
pres__status = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_name ( pres__status , " status " ) ;
pres__status__text = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_text ( pres__status__text , account . status ( ) . data ( ) ) ;
xmpp_stanza_add_child ( pres__status , pres__status__text ) ;
xmpp_stanza_release ( pres__status__text ) ;
children [ 1 ] = pres__status ;
children [ 2 ] = NULL ;
if ( true ) //account.pgp)
{
pres__x = xmpp_stanza_new ( account . context ) ;
xmpp_stanza_set_name ( pres__x , " x " ) ;
xmpp_stanza_set_ns ( pres__x , " jabber:x:signed " ) ;
pres__x__text = xmpp_stanza_new ( account . context ) ;
char * signature = account . pgp . sign ( account . buffer , account . pgp_keyid ( ) . data ( ) , account . status ( ) . data ( ) ) ;
xmpp_stanza_set_text ( pres__x__text , signature ? signature : " " ) ;
free ( signature ) ;
xmpp_stanza_add_child ( pres__x , pres__x__text ) ;
xmpp_stanza_release ( pres__x__text ) ;
children [ 2 ] = pres__x ;
children [ 3 ] = NULL ;
}
this - > send ( stanza : : presence ( )
. from ( account . jid ( ) )
. build ( account . context )
. get ( ) ) ;
this - > send ( stanza : : iq ( )
. from ( account . jid ( ) )
. type ( " set " )
. id ( stanza : : uuid ( account . context ) )
. xep0280 ( )
. enable ( )
. build ( account . context )
. get ( ) ) ;
this - > send ( stanza : : iq ( )
. from ( account . jid ( ) )
. to ( account . jid ( ) )
. type ( " get " )
. id ( stanza : : uuid ( account . context ) )
. rfc6121 ( )
. query ( stanza : : rfc6121 : : query ( ) )
. build ( account . context )
. get ( ) ) ;
this - > send ( stanza : : iq ( )
. from ( account . jid ( ) )
. to ( account . jid ( ) )
. type ( " get " )
. id ( stanza : : uuid ( account . context ) )
. xep0049 ( )
. query ( stanza : : xep0049 : : query ( ) . bookmarks ( ) )
. build ( account . context )
. get ( ) ) ;
children [ 1 ] = NULL ;
children [ 0 ] =
stanza__iq_pubsub_items ( account . context , NULL ,
strdup ( " eu.siacs.conversations.axolotl.devicelist " ) ) ;
children [ 0 ] =
stanza__iq_pubsub ( account . context , NULL , children . get ( ) ,
with_noop ( " http://jabber.org/protocol/pubsub " ) ) ;
char * uuid = xmpp_uuid_gen ( account . context ) ;
children [ 0 ] =
stanza__iq ( account . context , NULL , children . get ( ) , NULL , uuid ,
strdup ( account . jid ( ) . data ( ) ) , strdup ( account . jid ( ) . data ( ) ) ,
strdup ( " get " ) ) ;
xmpp_free ( account . context , uuid ) ;
this - > send ( children [ 0 ] ) ;
xmpp_stanza_release ( children [ 0 ] ) ;
account . omemo . init ( account . buffer , account . name . data ( ) ) ;
if ( account . omemo )
{
children [ 0 ] =
account . omemo . get_bundle ( account . context ,
strdup ( account . jid ( ) . data ( ) ) , NULL ) ;
this - > send ( children [ 0 ] ) ;
xmpp_stanza_release ( children [ 0 ] ) ;
}
( void ) weechat_hook_signal_send ( " xmpp_account_connected " ,
WEECHAT_HOOK_SIGNAL_STRING , account . name . data ( ) ) ;
}
else
{
account . disconnect ( 1 ) ;
//xmpp_stop(account.context); //keep context?
}
return true ;
}
char * rand_string ( int length )
{
char * string = new char [ length ] ;
for ( int i = 0 ; i < length ; + + i ) {
string [ i ] = ' 0 ' + rand ( ) % 72 ; // starting on '0', ending on '}'
if ( ! ( ( string [ i ] > = ' 0 ' & & string [ i ] < = ' 9 ' ) | |
( string [ i ] > = ' A ' & & string [ i ] < = ' Z ' ) | |
( string [ i ] > = ' a ' & & string [ i ] < = ' z ' ) ) )
i - - ; // reroll
}
string [ length ] = 0 ;
return string ;
}
int weechat : : connection : : connect ( std : : string jid , std : : string password , weechat : : tls_policy tls )
{
static const unsigned ka_timeout_sec = 60 ;
static const unsigned ka_timeout_ivl = 1 ;
m_conn . set_keepalive ( ka_timeout_sec , ka_timeout_ivl ) ;
const char * resource = account . resource ( ) . data ( ) ;
if ( ! ( resource & & strlen ( resource ) ) )
{
char * const rand = rand_string ( 8 ) ;
char ident [ 64 ] = { 0 } ;
snprintf ( ident , sizeof ( ident ) , " weechat.%s " , rand ) ;
delete [ ] rand ;
account . resource ( ident ) ;
resource = account . resource ( ) . data ( ) ;
}
m_conn . set_jid ( xmpp_jid_new ( account . context ,
xmpp_jid_node ( account . context , jid . data ( ) ) ,
xmpp_jid_domain ( account . context , jid . data ( ) ) ,
resource ) ) ;
m_conn . set_pass ( password . data ( ) ) ;
int flags = m_conn . get_flags ( ) ;
switch ( tls )
{
case weechat : : tls_policy : : disable :
flags | = XMPP_CONN_FLAG_DISABLE_TLS ;
break ;
case weechat : : tls_policy : : normal :
flags & = ~ XMPP_CONN_FLAG_DISABLE_TLS ;
flags & = ~ XMPP_CONN_FLAG_TRUST_TLS ;
break ;
case weechat : : tls_policy : : trust :
flags | = XMPP_CONN_FLAG_TRUST_TLS ;
break ;
default :
break ;
}
m_conn . set_flags ( flags ) ;
if ( ! connect_client (
nullptr , 0 , [ ] ( xmpp_conn_t * conn , xmpp_conn_event_t status ,
int error , xmpp_stream_error_t * stream_error ,
void * userdata ) {
auto & connection = * reinterpret_cast < weechat : : connection * > ( userdata ) ;
if ( connection ! = conn ) throw std : : invalid_argument ( " connection != conn " ) ;
connection . conn_handler ( static_cast < event > ( status ) , error , stream_error ) ;
} ) )
{
weechat_printf (
nullptr ,
_ ( " %s%s: error connecting to %s " ) ,
weechat_prefix ( " error " ) , WEECHAT_XMPP_PLUGIN_NAME ,
jid . data ( ) ) ;
return false ;
}
return true ;
}
void weechat : : connection : : process ( xmpp_ctx_t * context , const unsigned long timeout )
{
xmpp_run_once ( context ? context : this - > context ( ) , timeout ) ;
}