2022-10-26 10:44:57 +02:00
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
2013-12-01 16:09:54 +01:00
import sys
2022-10-24 00:11:12 +02:00
import os
2013-12-01 16:09:54 +01:00
import socket
import select
import re
2014-02-20 12:38:20 +01:00
import pickle
2022-10-24 00:11:12 +02:00
import logging
import ctypes
2022-11-02 09:18:52 +01:00
import traceback
2013-12-01 16:09:54 +01:00
from time import sleep
2014-04-06 02:16:18 +02:00
from threading import Thread
2022-10-24 00:11:12 +02:00
from random import shuffle
2022-11-03 03:51:14 +01:00
from errno import errorcode
2022-10-26 10:44:57 +02:00
from OpenSSL import SSL
2013-12-01 16:09:54 +01:00
2022-10-29 20:44:37 +02:00
import warnings
warnings . filterwarnings ( ' ignore ' )
2022-10-24 00:11:12 +02:00
import wrapper
2022-11-02 09:18:52 +01:00
import wrapper_tests
2022-10-24 00:11:12 +02:00
from wrapper . tox import Tox
from wrapper . toxav import ToxAV
import wrapper . toxcore_enums_and_consts as enums
from wrapper . toxcore_enums_and_consts import \
TOX_CONNECTION , TOX_USER_STATUS , TOX_MESSAGE_TYPE , \
TOX_SECRET_KEY_SIZE , TOX_FILE_CONTROL , TOX_ADDRESS_SIZE , \
TOX_GROUP_PRIVACY_STATE , TOX_GROUP_ROLE
2022-10-26 10:44:57 +02:00
from wrapper_tests import socks
2022-10-24 00:11:12 +02:00
try :
import support_testing as ts
except ImportError :
import wrapper_tests . support_testing as ts
2022-10-26 10:44:57 +02:00
import wrapper . toxencryptsave as tox_encrypt_save
2013-12-01 16:09:54 +01:00
2022-10-24 00:11:12 +02:00
global LOG
LOG = logging . getLogger ( ' app. ' + ' ts ' )
2022-11-03 03:51:14 +01:00
class SyniToxError ( BaseException ) : pass
2014-04-09 22:48:24 +02:00
2022-10-26 10:44:57 +02:00
NAME = ' SyniTox '
2022-11-06 04:57:13 +01:00
sMSG = ' MSG '
2022-11-03 03:51:14 +01:00
SSL_TOR_RANGE = ' 172. '
2022-10-26 10:44:57 +02:00
# possible CA locations picks the first one
2022-11-03 03:51:14 +01:00
lCAs = [ # debian and gentoo
' /etc/ssl/certs/ ' ,
]
lCAfs = SSL . _CERTIFICATE_FILE_LOCATIONS
# openssl ciphers -s -v|grep 1.3 > /tmp/v1.3
lOPENSSL_13_CIPHERS = [ ' TLS_AES_256_GCM_SHA384 ' ,
' TLS_CHACHA20_POLY1305_SHA256 ' ,
' TLS_AES_128_GCM_SHA256 ' ]
lOPENSSL_12_CIPHERS = [ ' ECDHE-ECDSA-AES256-GCM-SHA384 ' ,
' ECDHE-RSA-AES256-GCM-SHA384 ' ,
' DHE-RSA-AES256-GCM-SHA384 ' ,
' ECDHE-ECDSA-CHACHA20-POLY1305 ' ,
' ECDHE-RSA-CHACHA20-POLY1305 ' ,
' DHE-RSA-CHACHA20-POLY1305 ' ,
' ECDHE-ECDSA-AES128-GCM-SHA256 ' ,
' ECDHE-RSA-AES128-GCM-SHA256 ' ,
' DHE-RSA-AES128-GCM-SHA256 ' ,
' ECDHE-ECDSA-AES256-SHA384 ' ,
' ECDHE-RSA-AES256-SHA384 ' ,
' DHE-RSA-AES256-SHA256 ' ,
' ECDHE-ECDSA-AES128-SHA256 ' ,
' ECDHE-RSA-AES128-SHA256 ' ,
' DHE-RSA-AES128-SHA256 ' ,
' AES256-GCM-SHA384 ' ,
' AES128-GCM-SHA256 ' ,
' AES256-SHA256 ' ,
' AES128-SHA256 '
]
2022-10-24 00:11:12 +02:00
bot_toxname = ' SyniTox '
2022-11-03 03:51:14 +01:00
iSocks5ErrorMax = 5
iSocks5Error = 0
2022-10-24 00:11:12 +02:00
2022-10-26 10:44:57 +02:00
# tox.py can be called by callbacks
def LOG_ERROR ( a ) : print ( ' EROR> ' + a )
def LOG_WARN ( a ) : print ( ' WARN> ' + a )
def LOG_INFO ( a ) :
bVERBOSE = hasattr ( __builtins__ , ' app ' ) and app . oArgs . loglevel < = 20
if bVERBOSE : print ( ' INFO> ' + a )
def LOG_DEBUG ( a ) :
bVERBOSE = hasattr ( __builtins__ , ' app ' ) and app . oArgs . loglevel < = 10 - 1
if bVERBOSE : print ( ' DBUG> ' + a )
def LOG_TRACE ( a ) :
bVERBOSE = hasattr ( __builtins__ , ' app ' ) and app . oArgs . loglevel < 10
if bVERBOSE : print ( ' TRAC> ' + a )
# https://wiki.python.org/moin/SSL
2022-11-03 03:51:14 +01:00
def ssl_verify_cb ( host , override = False ) :
assert host
2022-10-26 10:44:57 +02:00
# wrapps host
def ssl_verify ( * args ) :
"""
callback for certificate validation
should return true if verification passes and false otherwise
"""
LOG . debug ( f " ssl_verify { len ( args ) } { args } " )
2022-11-03 03:51:14 +01:00
# app.ts WARNING SSL error: ([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],) # on .onion - fair enough
2022-11-02 09:18:52 +01:00
if override : return True
2022-11-03 03:51:14 +01:00
2022-10-26 10:44:57 +02:00
ssl_conn , x509 , error_num , depth , return_code = args
if error_num != 0 :
2022-11-03 03:51:14 +01:00
LOG . warn ( f " ssl_verify error_num= { error_num } { errorcode . get ( error_num ) } " )
2022-10-26 10:44:57 +02:00
return False
if depth != 0 :
# don't validate names of root certificates
return True
2022-11-03 03:51:14 +01:00
if x509 . get_subject ( ) . commonName == host :
2022-10-26 10:44:57 +02:00
return True
# allow matching subdomains
2022-11-03 03:51:14 +01:00
have , want = x509 . get_subject ( ) . commonName , host
2022-10-26 10:44:57 +02:00
if len ( have . split ( ' . ' ) ) == len ( want . split ( ' . ' ) ) and len ( want . split ( ' . ' ) ) > 2 :
if have . split ( ' . ' ) [ 1 : ] == want . split ( ' . ' ) [ 1 : ] :
2022-11-03 03:51:14 +01:00
LOG . warn ( f " ssl_verify accepting { x509 . get_subject ( ) . commonName } for { host } " )
2022-10-26 10:44:57 +02:00
return True
return False
return ssl_verify
2022-11-03 03:51:14 +01:00
2022-10-24 00:11:12 +02:00
class SyniTox ( Tox ) :
2022-10-26 10:44:57 +02:00
def __init__ ( self ,
oArgs ,
oOpts ,
2022-10-24 00:11:12 +02:00
GROUP_BOT_PK = ' ' ,
sMEMORY_DB = ' '
) :
2022-10-26 10:44:57 +02:00
opts = oTOX_OPTIONS
2022-10-24 00:11:12 +02:00
self . _opts = opts
2022-10-26 10:44:57 +02:00
self . _oArgs = oArgs
# self._oArgs.profile
self . load_profile ( self . _opts , self . _oArgs , self . _oArgs . password )
Tox . __init__ ( self , tox_options = self . _opts )
self . _address = self . self_get_address ( )
2022-10-24 00:11:12 +02:00
self . _app = None
self . _settings = { }
self . av = self . AV
self . irc = None
self . bid = - 1
self . _bRouted = None
2022-10-26 10:44:57 +02:00
self . _ssl_context = None
self . _irc_id = ' '
self . _toxes = None
self . joined = None
self . request = None
self . memory = { }
self . readbuffer = b ' '
#? tox_group_id
self . _peers = [ ]
self . _groups = { }
2022-10-24 00:11:12 +02:00
2022-10-26 10:44:57 +02:00
self . sMEMORY_DB = sMEMORY_DB
self . sGROUP_BOT_PK = GROUP_BOT_PK
self . sGROUP_BOT_NUM = - 1
def load_profile ( self , tox_options , oArgs , password = ' ' ) :
if oArgs . profile and os . path . exists ( oArgs . profile ) :
data = open ( oArgs . profile , ' rb ' ) . read ( )
else :
data = None
if data and self . has_password ( ) :
data = self . pass_decrypt ( data )
if data : # load existing profile
tox_options . contents . savedata_type = enums . TOX_SAVEDATA_TYPE [ ' TOX_SAVE ' ]
tox_options . contents . savedata_data = ctypes . c_char_p ( data )
tox_options . contents . savedata_length = len ( data )
else : # create new profile
tox_options . contents . savedata_type = enums . TOX_SAVEDATA_TYPE [ ' NONE ' ]
tox_options . contents . savedata_data = None
tox_options . contents . savedata_length = 0
def _save_profile ( self , data = None ) :
LOG . debug ( " _save_profile " )
data = data or self . get_savedata ( )
if self . has_password ( ) :
data = self . pass_encrypt ( data )
try :
suf = f " { os . getpid ( ) } "
with open ( self . _oArgs . profile + suf , ' wb ' ) as fl :
fl . write ( data )
stat = os . stat ( self . _oArgs . profile + suf )
if hasattr ( stat , ' st_blocks ' ) :
assert stat . st_blocks > 0 , f " Zero length file { self . _oArgs . profile + suf } "
os . rename ( self . _oArgs . profile + suf , self . _oArgs . profile )
LOG . info ( ' Profile saved successfully to ' + self . _oArgs . profile )
except Exception as e :
LOG . warn ( f " Profile save failed to { self . _oArgs . profile } \n { e } " )
2022-10-24 00:11:12 +02:00
def start ( self ) :
2022-10-26 10:44:57 +02:00
self . _tox = self
self . _toxes = tox_encrypt_save . ToxEncryptSave ( )
self . self_set_name ( self . _oArgs . bot_name )
2022-10-24 00:11:12 +02:00
self . self_set_status_message ( " Send me a message with the word ' invite ' " )
LOG . info ( ' Our ToxID: %s ' % self . self_get_toxid ( ) )
2013-12-01 16:09:54 +01:00
self . tox_group_id = None
2022-10-26 10:44:57 +02:00
self . init_callbacks ( )
2022-10-24 00:11:12 +02:00
if os . path . exists ( self . sMEMORY_DB ) :
with open ( self . sMEMORY_DB , ' r ' ) as f :
2014-02-20 12:38:20 +01:00
self . memory = pickle . load ( f )
2022-10-26 10:44:57 +02:00
def start_ssl ( self , HOST ) :
if not self . _ssl_context :
2022-11-03 03:51:14 +01:00
try :
OP_NO_TLSv1_3 = SSL . _lib . SSL_OP_NO_TLSv1_3
except AttributeError :
if self . _oArgs . irc_ssl == ' tlsv1.3 ' :
LOG . warning ( " SSL._lib.SSL_OP_NO_TLSv1_3 is not supported " )
LOG . warning ( " Downgrading SSL to tlsv1.2 " )
self . _oArgs . irc_ssl = ' tlsv1.2 '
else :
LOG . debug ( " SSL._lib.SSL_OP_NO_TLSv1_3 is not supported " )
else :
LOG . debug ( " SSL._lib.SSL_OP_NO_TLSv1_3 is supported " )
if self . _oArgs . irc_connect . endswith ( ' .onion ' ) or \
self . _oArgs . irc_connect . startswith ( SSL_TOR_RANGE ) :
2022-11-02 09:18:52 +01:00
override = True
else :
override = False
2022-10-26 10:44:57 +02:00
# TLSv1_3_METHOD does not exist
2022-11-03 06:31:50 +01:00
context = SSL . Context ( SSL . TLS_CLIENT_METHOD ) # TLSv1_2_METHOD
2022-11-03 03:51:14 +01:00
# SSL.OP_NO_TLSv1_1 is allowed
2022-10-26 10:44:57 +02:00
context . set_options ( SSL . OP_NO_SSLv2 | SSL . OP_NO_SSLv3 | SSL . OP_NO_TLSv1 )
2022-11-03 03:51:14 +01:00
2022-11-06 04:57:13 +01:00
if self . _oArgs . irc_crt and self . _oArgs . irc_key :
2022-11-02 09:18:52 +01:00
assert os . path . exists ( key ) , key
2022-10-26 10:44:57 +02:00
val = SSL . VERIFY_PEER | SSL . VERIFY_FAIL_IF_NO_PEER_CERT
2022-11-06 04:57:13 +01:00
LOG . info ( ' Using keyfile: %s ' % key )
if True : # required!
key = self . _oArgs . irc_crt
2022-11-03 06:31:50 +01:00
assert os . path . exists ( key ) , key
2022-11-02 09:18:52 +01:00
context . use_certificate_file ( key , filetype = SSL . FILETYPE_PEM )
2022-11-06 04:57:13 +01:00
if True : # required!
key = self . _oArgs . irc_key
2022-11-02 09:18:52 +01:00
assert os . path . exists ( key ) , key
context . use_privatekey_file ( key , filetype = SSL . FILETYPE_PEM )
2022-11-03 06:31:50 +01:00
#? load_client_ca
def SSL_hands_cb ( oConn , iLine , iRet ) :
# where in the SSL handshake the function was called, and
# the return code from a internal function call
print ( f " iLine= { iLine } , iRet= { iRet } " )
2022-11-06 04:57:13 +01:00
context . set_info_callback ( SSL_hands_cb )
2022-11-03 06:31:50 +01:00
def keylog_callback ( oConn , s ) :
print ( s )
2022-11-06 04:57:13 +01:00
# context.set_keylog_callback(keylog_callback)
2022-10-26 10:44:57 +02:00
else :
val = SSL . VERIFY_PEER
2022-11-02 09:18:52 +01:00
context . set_verify ( val , ssl_verify_cb ( HOST , override ) )
2022-10-26 10:44:57 +02:00
2022-11-03 03:51:14 +01:00
if self . _oArgs . irc_cafile :
# context.load_verify_locations(capath=self._oArgs.irc_ca)
context . load_verify_locations ( self . _oArgs . irc_cafile , capath = self . _oArgs . irc_cadir )
elif self . _oArgs . irc_cadir :
context . load_verify_locations ( None , capath = self . _oArgs . irc_cadir )
if self . _oArgs . irc_ssl == ' tlsv1.1 ' :
context . set_min_proto_version ( SSL . TLS1_1_VERSION )
elif self . _oArgs . irc_ssl == ' tlsv1.2 ' :
2022-11-03 06:31:50 +01:00
context . set_cipher_list ( bytes ( ' : ' . join ( [ ' DEFAULT@SECLEVEL=1 ' ] + lOPENSSL_12_CIPHERS ) , ' UTF-8 ' ) )
2022-10-26 10:44:57 +02:00
context . set_min_proto_version ( SSL . TLS1_2_VERSION )
2022-11-03 03:51:14 +01:00
elif self . _oArgs . irc_ssl == ' tlsv1.3 ' :
2022-11-03 06:31:50 +01:00
context . set_cipher_list ( bytes ( ' : ' . join ( [ ' DEFAULT@SECLEVEL=1 ' ] + lOPENSSL_13_CIPHERS ) , ' UTF-8 ' ) )
2022-10-26 10:44:57 +02:00
context . set_min_proto_version ( SSL . TLS1_3_VERSION )
self . _ssl_context = context
return self . _ssl_context
2022-10-24 00:11:12 +02:00
def bRouted ( self ) :
2022-10-26 10:44:57 +02:00
if self . _oArgs . network in [ ' local ' ] :
return True
b = ts . bAreWeConnected ( )
if b is None :
i = os . system ( ' ip route|grep ^def ' )
if i > 0 :
b = False
else :
b = True
self . _bRouted = b
return b
2022-10-24 00:11:12 +02:00
def test_net ( self , lElts = None , oThread = None , iMax = 4 ) :
2022-10-26 10:44:57 +02:00
LOG . debug ( " test_net network= " + self . _oArgs . network )
2022-10-24 00:11:12 +02:00
# bootstrap
lNodes = ts . generate_nodes ( oArgs = self . _oArgs ,
ipv = ' ipv4 ' ,
udp_not_tcp = True )
2022-11-06 04:57:13 +01:00
self . _settings [ ' current_nodes_udp ' ] = ts . lDNSClean ( lNodes )
2022-10-24 00:11:12 +02:00
if not lNodes :
LOG . warn ( ' empty generate_nodes udp ' )
else :
2022-10-26 10:44:57 +02:00
LOG . info ( f ' Called generate_nodes: udp { len ( lNodes ) } ' )
2022-10-24 00:11:12 +02:00
lNodes = ts . generate_nodes ( oArgs = self . _oArgs ,
ipv = ' ipv4 ' ,
udp_not_tcp = False )
2022-11-06 04:57:13 +01:00
self . _settings [ ' current_nodes_tcp ' ] = ts . lDNSClean ( lNodes )
2022-10-24 00:11:12 +02:00
if not lNodes :
LOG . warn ( ' empty generate_nodes tcp ' )
else :
2022-10-26 10:44:57 +02:00
LOG . info ( f ' Called generate_nodes: tcp { len ( lNodes ) } ' )
2022-10-24 00:11:12 +02:00
# if oThread and oThread._stop_thread: return
return True
2022-10-26 10:44:57 +02:00
def add_friend ( self , pk ) :
self . friend_add_norequest ( pk )
assert self . friend_exists ( pk )
assert pk in self . self_get_friend_list ( )
friend_number = self . friend_by_public_key ( pk )
return friend_number
def start_groups ( self ) :
if not self . bRouted ( ) : return False
if not self . group_is_connected ( self . sGROUP_BOT_NUM ) :
self . group_reconnect ( self . sGROUP_BOT_NUM )
if not self . group_is_connected ( self . sGROUP_BOT_NUM ) :
return False
assert self . sGROUP_BOT_NUM
num = self . sGROUP_BOT_NUM
self . group_self_set_status ( num , TOX_USER_STATUS [ ' NONE ' ] )
# add myself as a peer in the group or am I in as founder?
2022-10-24 00:11:12 +02:00
self . group_send_message ( num , TOX_MESSAGE_TYPE [ ' NORMAL ' ] , " hi " )
2022-10-26 10:44:57 +02:00
# The code in tests_wrapper need extending and then
# wiring up to here.
#
if self . _oArgs . group_invite :
pk = self . _oArgs . group_invite
if pk not in self . self_get_friend_list ( ) :
friend_number = self . add_friend ( pk )
else :
friend_number = self . friend_by_public_key ( pk )
b = self . group_invite_friend ( num , friend_number )
LOG . info ( f " A PK to invite to the group { b } " )
return True
if self . _oArgs . group_moderator :
pk = self . _oArgs . group_moderator
if pk not in self . self_get_friend_list ( ) :
friend_number = self . add_friend ( pk )
else :
friend_number = self . friend_by_public_key ( pk )
role = TOX_GROUP_ROLE [ ' MODERATOR ' ]
# dunno
peer_id = friend_number
b = self . group_mod_set_role ( num , peer_id , role )
LOG . info ( " A PK to invite to the group as moderator {b} " )
return True
if self . _oArgs . group_ignore :
pk = self . _oArgs . group_ignore
if pk not in self . self_get_friend_list ( ) :
friend_number = self . add_friend ( pk )
else :
friend_number = self . friend_by_public_key ( pk )
# dunno
peer_id = friend_number
b = self . group_toggle_set_ignore ( num , peer_id , True )
LOG . info ( " A PK to ignore in the group {b} " )
return True
return None
def create_group ( self ) :
privacy_state = TOX_GROUP_PRIVACY_STATE [ self . _oArgs . group_state . upper ( ) ]
nick = self . _oArgs . group_nick
group_name = self . _oArgs . group_name
if not group_name :
group_name = self . _oArgs . bot_name + self . _oArgs . irc_chan
self . _oArgs . group_name = group_name
status = TOX_USER_STATUS [ ' NONE ' ]
num = self . group_new ( privacy_state , group_name , nick , status )
assert num > = 0 , num
self . group_set_topic ( num , f " { group_name } IRC on { self . _oArgs . irc_host } " )
# self.tox_group_id = self.group_invite_accept(b'', friendid, nick)
chat_id = self . group_get_chat_id ( num )
if self . _oArgs . profile and os . path . exists ( os . path . dirname ( self . _oArgs . profile ) ) :
f = os . path . splitext ( self . _oArgs . profile ) [ 0 ] + ' .chatid '
open ( f , ' rt ' ) . write ( chat_id )
LOG . info ( f " Chat Id: { chat_id } written to { f } " )
else :
LOG . info ( f " Chat Id: { chat_id } " )
# dunno
if self . self_get_friend_list ( ) :
friendid = self . self_get_friend_list ( ) [ 0 ]
i = on_group_invite ( friendid , b ' ' , 0 )
assert i
self . tox_group_id = i
return num
def join_group ( self ) :
password = self . _oArgs . group_pass
nick = self . _oArgs . group_nick
# is the chat_id the pk?
chat_id = self . _oArgs . group_chatid
2022-10-26 11:13:01 +02:00
if not chat_id : return - 1
2022-10-26 10:44:57 +02:00
num = self . group_join ( chat_id , password , nick , status = ' ' )
self . sGROUP_BOT_NUM = num
self . group_self_set_status ( num , TOX_USER_STATUS [ ' NONE ' ] )
return num
def init_groups ( self ) :
LOG . debug ( f " init_groups proxy= { self . _oArgs . proxy_type } " )
group_name = self . _oArgs . bot_name + ' Test ' + self . _oArgs . irc_chan
2022-11-02 09:18:52 +01:00
if not self . bRouted ( ) : return
try :
if self . sGROUP_BOT_NUM < 0 :
# ToDo: look for the first group of the profile
i = self . group_get_number_groups ( )
if i == 0 :
if not self . bRouted ( ) : return False
num = self . create_group ( )
self . sGROUP_BOT_NUM = num
elif i > 1 :
LOG . error ( ' There are more than one groups in this profile ' )
for ig in range ( i ) :
LOG . warn ( f " group # { ig } { self . group_self_get_name ( ig ) } " )
raise RuntimeError ( " select one of the groups at the cmdline " )
else :
if not self . bRouted ( ) : return False
num = self . join_group ( )
LOG . info ( f " init_groups GROUP_BOT_PK= { self . sGROUP_BOT_PK } " )
self . start_groups ( )
except Exception as e :
LOG . warn ( f " init_groups self.start_groups { e } " )
return False
2022-10-26 10:44:57 +02:00
# TOX_GROUP_ROLE['FOUNDER']
return True
2022-10-24 00:11:12 +02:00
def init_callbacks ( self ) :
2022-10-29 20:44:37 +02:00
return
2022-10-26 10:44:57 +02:00
# wraps self with
LOG . info ( " Adding Tox init_callbacks " )
2022-10-24 00:11:12 +02:00
def gi_wrapped ( iTox , friendid , invite_data , invite_len , * args ) :
invite_data = str ( invite_data , ' UTF-8 ' )
2022-10-26 10:44:57 +02:00
LOG . debug ( f ' on_group_invite { friendid } { invite_data } ' )
self . on_group_invite ( friendid , invite_data , 0 )
2022-10-24 00:11:12 +02:00
self . callback_group_invite ( gi_wrapped , 0 )
2022-10-26 10:44:57 +02:00
def scs_wrapped ( iTox , friendid , status , * args ) :
LOG . debug ( f ' on_connection_status { friendId } { status } . ' )
self . on_connection_status ( friendid , status )
2022-10-24 00:11:12 +02:00
self . callback_self_connection_status ( scs_wrapped )
2022-10-26 10:44:57 +02:00
2022-10-24 00:11:12 +02:00
def gm_wrapped ( iTox , groupnumber , peer_id , type_ , message , mlen , * args ) :
message = str ( message , ' UTF-8 ' )
2022-10-26 10:44:57 +02:00
LOG . debug ( f ' on_group_message { groupnumber } { peer_id } { message } ' )
2022-10-24 00:11:12 +02:00
self . on_group_message ( groupnumber , peer_id , message )
self . callback_group_message ( gm_wrapped , 0 )
2022-10-26 10:44:57 +02:00
2022-10-24 00:11:12 +02:00
def ga_wrapped ( iTox , groupnumber , peer_id , type_ , action , mlen , * args ) :
2022-10-26 10:44:57 +02:00
LOG . debug ( f ' on_group_action(groupnumber, peer_id, action) ' )
2022-10-24 00:11:12 +02:00
self . on_group_action ( groupnumber , peer_id , action )
2022-10-26 10:44:57 +02:00
2022-10-24 00:11:12 +02:00
#? self.callback_group_action(ga_wrapped, 0)
def fr_wrapped ( iTox , pk , message , mlen , * args ) :
message = str ( message , ' UTF-8 ' )
2022-10-26 10:44:57 +02:00
LOG . debug ( f ' on_friend_request(pk, message) ' )
self . on_friend_request ( pk , message )
2022-10-24 00:11:12 +02:00
self . callback_friend_request ( fr_wrapped )
2022-10-26 10:44:57 +02:00
2022-10-24 00:11:12 +02:00
def fm_wrapped ( iTox , peer_id , message , mlen , * args ) :
message = str ( message , ' UTF-8 ' )
2022-10-26 10:44:57 +02:00
LOG . debug ( f ' on_friend_request(peer_id, message) ' )
self . on_friend_request ( peer_id , message )
2022-10-24 00:11:12 +02:00
self . callback_friend_request ( fm_wrapped )
def del_callbacks ( self ) :
self . callback_group_invite ( None , 0 )
self . callback_self_connection_status ( None )
self . callback_group_message ( None , 0 )
# self.callback_group_action(None, 0)
self . callback_friend_request ( None )
self . callback_friend_request ( None )
2014-02-02 13:46:06 +01:00
2022-11-03 03:51:14 +01:00
def diagnose_ciphers ( self , irc ) :
cipher_name = irc . get_cipher_name ( )
2022-11-06 04:57:13 +01:00
LOG . info ( f " diagnose_ciphers cipher_name= { irc . get_cipher_name ( ) } " )
LOG . debug ( f " diagnose_ciphers get_cipher_list= { irc . get_cipher_list ( ) } " )
2022-11-03 03:51:14 +01:00
cipher_list = irc . get_cipher_list ( )
for ci in lOPENSSL_13_CIPHERS :
2022-11-06 04:57:13 +01:00
if ci in cipher_list : LOG . debug ( f " server supports v1.3 cipher { ci } " )
for cert in irc . get_peer_cert_chain ( ) :
# x509 objects - just want the /CN
LOG . debug ( f " { cert . get_subject ( ) . CN } { cert . get_issuer ( ) } " )
2022-11-03 03:51:14 +01:00
cipher_name = irc . get_cipher_name ( )
if self . _oArgs . irc_ssl == ' tlsv1.2 ' :
2022-11-06 04:57:13 +01:00
assert cipher_name in lOPENSSL_12_CIPHERS or \
cipher_name in lOPENSSL_13_CIPHERS , cipher_name
2022-11-03 03:51:14 +01:00
elif self . _oArgs . irc_ssl == ' tlsv1.3 ' :
assert cipher_name in lOPENSSL_13_CIPHERS , cipher_name
2022-11-06 04:57:13 +01:00
got = irc . get_protocol_version_name ( ) . lower ( )
if got > self . _oArgs . irc_ssl :
LOG . debug ( f " Got: { irc . get_protocol_version_name ( ) . lower ( ) } asked for { self . _oArgs . irc_ssl } " )
elif got < self . _oArgs . irc_ssl :
LOG . warn ( f " Got: { irc . get_protocol_version_name ( ) . lower ( ) } asked for { self . _oArgs . irc_ssl } " )
LOG . info ( f " diagnose_ciphers { str ( irc . get_state_string ( ) , ' UTF-8 ' ) } " )
2014-02-02 13:46:06 +01:00
def irc_init ( self ) :
2022-11-03 03:51:14 +01:00
global iSocks5Error
2022-10-24 00:11:12 +02:00
if not self . bRouted ( ) : return
2022-10-26 10:44:57 +02:00
nick = self . _oArgs . irc_nick
realname = self . _oArgs . irc_name
ident = self . _oArgs . irc_ident
2022-11-03 03:51:14 +01:00
LOG . info ( f " irc_init proxy= { self . _oArgs . proxy_type } SSL= { self . _oArgs . irc_ssl } " )
2022-10-24 00:11:12 +02:00
try :
2022-10-26 10:44:57 +02:00
if self . _oArgs . proxy_type == 2 :
socks . setdefaultproxy ( socks . PROXY_TYPE_SOCKS5 ,
self . _oArgs . proxy_host ,
self . _oArgs . proxy_port )
irc = socks . socksocket ( )
2022-11-02 09:18:52 +01:00
iTIMEOUT = 15
2022-10-26 10:44:57 +02:00
elif self . _oArgs . proxy_type == 1 :
socks . setdefaultproxy ( socks . PROXY_TYPE_HTTP ,
self . _oArgs . proxy_host ,
self . _oArgs . proxy_port )
irc = socks . socksocket ( )
2022-11-02 09:18:52 +01:00
iTIMEOUT = 15
2022-10-26 10:44:57 +02:00
else :
irc = socket . socket ( )
2022-11-02 09:18:52 +01:00
iTIMEOUT = 10
try :
ip = ts . sDNSLookup ( self . _oArgs . irc_connect )
except Exception as e :
LOG . warn ( f " { self . _oArgs . irc_host } errored in resolve { e } " )
ip = self . _oArgs . irc_connect
else :
if not ip :
LOG . warn ( f " { self . _oArgs . irc_host } did not resolve. " )
ip = self . _oArgs . irc_connect
# https://github.com/pyca/pyopenssl/issues/168
2022-10-26 10:44:57 +02:00
if self . _oArgs . irc_ssl :
if not self . _ssl_context :
2022-11-02 09:18:52 +01:00
self . start_ssl ( self . _oArgs . irc_connect )
2022-10-26 10:44:57 +02:00
irc = SSL . Connection ( self . _ssl_context , irc )
2022-11-02 09:18:52 +01:00
irc . connect ( ( ip , self . _oArgs . irc_port ) )
if ip . endswith ( ' .onion ' ) :
irc . set_tlsext_host_name ( None )
2022-10-29 20:44:37 +02:00
else :
2022-11-02 09:18:52 +01:00
irc . set_tlsext_host_name ( bytes ( self . _oArgs . irc_host , ' UTF-8 ' ) )
while True :
try :
irc . do_handshake ( )
2022-11-03 03:51:14 +01:00
except SSL . WantReadError :
2022-11-02 09:18:52 +01:00
rd , _ , _ = select . select ( [ irc ] , [ ] , [ ] , irc . gettimeout ( ) )
if not rd :
raise socket . timeout ( ' timeout ' )
continue
2022-11-03 03:51:14 +01:00
except SSL . Error as e :
2022-11-02 09:18:52 +01:00
raise
break
2022-11-03 03:51:14 +01:00
self . diagnose_ciphers ( irc )
2022-10-26 10:44:57 +02:00
else :
2022-11-02 09:18:52 +01:00
irc . connect ( ( ip , self . _oArgs . irc_port ) )
2022-11-03 06:31:50 +01:00
LOG . info ( f " IRC SSL= { self . _oArgs . irc_ssl } connected " )
2022-11-02 09:18:52 +01:00
2022-11-06 04:57:13 +01:00
except ( wrapper_tests . socks . GeneralProxyError , wrapper_tests . socks . Socks5Error ) as e :
2022-11-03 03:51:14 +01:00
iSocks5Error + = 1
if iSocks5Error > = iSocks5ErrorMax :
raise SyniToxError ( f " { e . args } " )
2022-11-06 04:57:13 +01:00
if len ( e . args [ 0 ] ) == 2 :
if e . args [ 0 ] [ 0 ] == 2 :
LOG . warn ( f " Socks5Error: do you have Tor SafeSocks set? { e . args [ 0 ] } " )
elif e . args [ 0 ] [ 0 ] == 5 :
# (5, 'Connection refused')
LOG . warn ( f " Socks5Error: do you have Tor running? { e . args [ 0 ] } " )
raise SyniToxError ( f " { e . args } " )
elif e . args [ 0 ] [ 0 ] in [ 1 , 6 , 0 ] :
# (0, "connection closed unexpectedly")
# (6, 'TTL expired'),
# 1, ('general SOCKS server failure')
# Missing mapping for virtual address '172.17.140.117'. Refusing.
LOG . warn ( f " Socks5Error: { e . args [ 0 ] } " )
return
2022-11-02 09:18:52 +01:00
else :
LOG . error ( f " Socks5Error: { e . args } " )
raise SyniToxError ( f " { e . args } " )
except socket . timeout as e :
LOG . warn ( f " socket error: { e . args } " )
return
2022-11-03 03:51:14 +01:00
except ( ConnectionRefusedError ) as e :
raise SyniToxError ( f " { e . args } " )
2022-10-26 10:44:57 +02:00
except ( SSL . Error , ) as e :
2022-11-03 03:51:14 +01:00
iSocks5Error + = 1
if iSocks5Error > = iSocks5ErrorMax :
raise SyniToxError ( f " { e . args } " )
2022-10-26 10:44:57 +02:00
LOG . warn ( f " SSL error: { e . args } " )
return
except ( SSL . SysCallError , ) as e :
2022-11-02 09:18:52 +01:00
LOG . warn ( f " SSLSyscall error: { e . args } " )
LOG . warn ( traceback . format_exc ( ) )
2022-10-26 10:44:57 +02:00
return
2022-10-24 00:11:12 +02:00
except Exception as e :
2022-10-26 10:44:57 +02:00
LOG . warn ( f " Error: { e } " )
2022-11-02 09:18:52 +01:00
LOG . warn ( traceback . format_exc ( ) )
2022-10-26 10:44:57 +02:00
return
2022-10-29 20:44:37 +02:00
self . irc = irc
2022-11-06 04:57:13 +01:00
self . irc . send ( bytes ( ' CAP ' + ' LS ' + ' \r \n ' , ' UTF-8 ' ) )
self . irc . send ( bytes ( ' CAP ' + ' REQ :multi-prefix ' + ' \r \n ' , ' UTF-8 ' ) )
self . irc . send ( bytes ( ' CAP ' + ' END ' + ' \r \n ' , ' UTF-8 ' ) )
# withh or without self._oArgs.irc_pem:
LOG . info ( " Sent CAP sending NICK and USER " )
self . irc . send ( bytes ( ' NICK ' + nick + ' \r \n ' , ' UTF-8 ' ) )
self . irc . send ( bytes ( ' USER %s %s bla : %s \r \n ' % (
self . _oArgs . irc_ident ,
self . _oArgs . irc_host ,
self . _oArgs . irc_name ) , ' UTF-8 ' ) )
2022-11-03 06:31:50 +01:00
2022-11-03 03:51:14 +01:00
# OSError: [Errno 9] Bad file descriptor
2022-10-24 00:11:12 +02:00
def dht_init ( self ) :
if not self . bRouted ( ) : return
if ' current_nodes_udp ' not in self . _settings :
self . test_net ( )
lNodes = self . _settings [ ' current_nodes_udp ' ]
shuffle ( lNodes )
if self . _oArgs . proxy_type == 0 :
2022-10-26 10:44:57 +02:00
ts . bootstrap_udp ( lNodes [ : 6 ] , [ self ] )
2022-10-24 00:11:12 +02:00
else :
2022-10-26 10:44:57 +02:00
if self . _bRouted is None :
LOG . info ( f ' UDP bootstapping 1 ' )
ts . bootstrap_udp ( [ lNodes [ 0 ] ] , [ self ] )
2022-10-24 00:11:12 +02:00
if ' current_nodes_tcp ' not in self . _settings :
self . test_net ( )
lNodes = self . _settings [ ' current_nodes_tcp ' ]
shuffle ( lNodes )
2022-11-06 04:57:13 +01:00
LOG . info ( f ' TCP bootstapping 6 ' )
2022-10-26 10:44:57 +02:00
ts . bootstrap_tcp ( lNodes [ : 6 ] , [ self ] )
def get_all_groups ( self ) :
try :
group_numbers = range ( self . _tox . group_get_number_groups ( ) )
except Exception as e :
return None
groups = map ( lambda n : self . get_group_by_number ( n ) , group_numbers )
return list ( groups )
def get_group_by_number ( self , group_number ) :
try :
public_key = self . _tox . group_get_chat_id ( group_number )
# LOG.info(f"group_get_chat_id {group_number} {public_key}")
return self . get_group_by_public_key ( public_key )
except Exception as e :
LOG . warn ( f " group_get_chat_id { group_number } { e } " )
return None
def get_group_by_public_key ( self , public_key , group ) :
self . _groups [ public_key ] = group
# -----------------------------------------------------------------------------------------------------------------
# Group peers
# -----------------------------------------------------------------------------------------------------------------
def get_all_group_peers ( self ) :
return list ( )
def get_group_peer_by_public_key ( self , group , public_key ) :
peer = group . get_peer_by_public_key ( public_key )
return self . _get_group_peer ( group , peer )
def get_peer_by_id ( self , peer_id ) :
peers = list ( filter ( lambda p : p . id == peer_id , self . _peers ) )
if peers :
return peers [ 0 ]
else :
LOG_WARN ( f " get_peer_by_id empty peers for { peer_id } " )
return [ ]
2022-10-24 00:11:12 +02:00
def ensure_exe ( self , func , * args ) :
2013-12-11 16:55:40 +01:00
count = 0
2013-12-11 17:02:01 +01:00
THRESHOLD = 50
2013-12-11 16:55:40 +01:00
while True :
try :
return func ( * args )
except :
assert count < THRESHOLD
count + = 1
2022-10-24 00:11:12 +02:00
self . do ( )
2013-12-01 16:09:54 +01:00
2022-10-24 00:11:12 +02:00
def do ( self , n = 50 ) :
interval = self . iteration_interval ( )
for i in range ( n ) :
self . iterate ( )
sleep ( interval / 1000.0 * 10 )
def unroute ( self ) :
if self . irc :
2022-10-26 10:44:57 +02:00
try : self . irc . close ( )
2022-10-24 00:11:12 +02:00
except : pass
self . irc = None
def irc_check ( self , lines ) :
if b ' NOTICE AUTH ' in lines [ 0 ] :
for line in lines [ : 99 ] :
if b ' NOTICE AUTH ' not in line : return
2022-11-06 04:57:13 +01:00
lines = str ( line , ' UTF-8 ' ) . strip ( ) . split ( )
print ( ' ' . join ( lines [ 1 : ] ) )
2022-10-24 00:11:12 +02:00
else :
for line in lines [ : 5 ] :
line = str ( line , ' UTF-8 ' ) . strip ( ) . lower ( )
if ' banned ' in line :
2022-11-02 09:18:52 +01:00
raise SyniToxError ( line )
2022-10-24 00:11:12 +02:00
if ' error ' in line and ' closing ' in line :
2022-11-02 09:18:52 +01:00
raise SyniToxError ( line )
2022-10-26 10:44:57 +02:00
def irc_readlines ( self ) :
nick = self . _oArgs . irc_nick
pwd = self . _oArgs . irc_pass
fp = self . _oArgs . irc_fp
email = self . _oArgs . irc_email
self . readbuffer + = self . irc . recv ( 4096 )
lines = self . readbuffer . split ( b ' \n ' )
self . irc_check ( lines )
LOG . debug ( f ' Waited on IRC and got { len ( lines ) } lines. ' )
self . readbuffer = lines . pop ( )
for line in lines :
line = str ( line , ' UTF-8 ' )
l = line . rstrip ( ) . split ( )
if len ( l ) < 2 :
print ( line )
2022-11-06 04:57:13 +01:00
elif l [ 1 ] in [ ' PING ' ] :
print ( line )
elif l [ 1 ] in [ ' 372 ' ] :
LOG . info ( ' MOTD ' )
elif l [ 1 ] not in [ ' 372 ' , ' 353 ' ] :
2022-10-26 10:44:57 +02:00
i = line . find ( ' ' )
print ( line [ i + 1 : ] )
2022-11-06 04:57:13 +01:00
2022-10-26 10:44:57 +02:00
rx = re . match ( r ' :(.*?)!.*? PRIVMSG %s :(.*?) \ r ' %
self . _oArgs . irc_chan , line , re . S )
2022-11-06 04:57:13 +01:00
if l [ 0 ] == ' QUIT ' :
LOG . info ( ' QUIT ' )
return
if len ( l ) == 1 :
self . irc_send ( ' PING %s \r \n ' % ' #tor ' )
2022-10-26 10:44:57 +02:00
elif l [ 0 ] == ' PING ' :
self . irc_send ( ' PONG %s \r \n ' % l [ 1 ] )
2022-11-06 04:57:13 +01:00
elif rx :
self . relay_message ( rx )
2022-10-26 10:44:57 +02:00
elif len ( l ) < 2 :
pass
2022-11-02 09:18:52 +01:00
elif l [ 1 ] in [ ' 461 ' , ' 431 ' ] :
pass
2022-11-06 04:57:13 +01:00
elif l [ 1 ] in [ ' 433 ' ] :
# maybe should be an outright fail
2022-11-02 09:18:52 +01:00
if self . _oArgs . irc_ssl :
LOG . warn ( " Maybe the certificate was not received " )
2022-11-06 04:57:13 +01:00
#? raise SyniToxError(line)
# sometimes but not always:
# 433 * SyniTox :Nickname is already in use.
# app.ts ERROR SSL error: (32, 'EPIPE')
# or instead
# 451 * :Register first.
# error :closing link: 185.38.175.131 (registration timed out)
# or instead: just
# app.ts ERROR SSL error: (32, 'EPIPE')
pass
elif l [ 1 ] in [ ' 451 ' , ' 462 ' , ' 477 ' ] :
if self . _oArgs . irc_crt and self . _oArgs . irc_key :
LOG . warn ( " Maybe the certificate was not received " )
2022-11-02 09:18:52 +01:00
raise SyniToxError ( line )
elif l [ 1 ] in [ ' 376 ' ] :
2022-10-26 10:44:57 +02:00
# :End of /MOTD command
2022-11-06 04:57:13 +01:00
if self . _oArgs . irc_crt and self . _oArgs . irc_key :
2022-11-02 09:18:52 +01:00
pass
elif email == ' ' and pwd :
2022-11-06 04:57:13 +01:00
LOG . info ( bytes ( sMSG + ' NickServ IDENTIFY %s %s \r \n '
2022-10-26 10:44:57 +02:00
% ( nick , pwd , ) , ' UTF-8 ' ) )
2022-11-06 04:57:13 +01:00
self . irc . send ( bytes ( sMSG + ' NickServ IDENTIFY %s %s \r \n '
% ( pwd , nick , ) , ' UTF-8 ' ) )
2022-11-02 09:18:52 +01:00
elif email != ' ' and pwd :
2022-11-06 04:57:13 +01:00
LOG . info ( bytes ( sMSG + ' NickServ REGISTER %s %s \r \n '
2022-11-02 09:18:52 +01:00
% ( pwd , email , ) , ' UTF-8 ' ) )
2022-11-06 04:57:13 +01:00
self . irc . send ( bytes ( sMSG + ' NickServ REGISTER %s %s \r \n '
2022-10-26 10:44:57 +02:00
% ( pwd , email , ) , ' UTF-8 ' ) )
2022-11-02 09:18:52 +01:00
else :
LOG . error ( " you must provide a password to register " )
raise RuntimeError ( " you must provide a password to register " )
2022-11-03 03:51:14 +01:00
try :
2022-11-06 04:57:13 +01:00
self . irc . send ( bytes ( sMSG + ' NickServ set cloak on \r \n ' , ' UTF-8 ' ) )
if self . _oArgs . irc_chan :
self . irc . send ( bytes ( ' JOIN %s \r \n ' % self . _oArgs . irc_chan , ' UTF-8 ' ) )
2022-11-03 03:51:14 +01:00
except BrokenPipeError :
raise SyniToxError ( ' BrokenPipeError ' )
2022-10-26 10:44:57 +02:00
# put off init_groups until you have joined IRC
self . init_groups ( )
# Make sure we are in
elif l [ 1 ] == ' 042 ' :
# 042 SyniTox 8VQAADOD0 :your unique ID
self . _irc_id = line . replace ( ' :your unique ID ' , ' ' ) . \
replace ( ' 042 ' + nick + ' ' , ' ' )
elif l [ 1 ] == ' 421 ' :
# 421 SyniTox .PRIVMSG :Unknown command
pass
elif l [ 1 ] == ' 477 ' :
#477 SyniTox #tor :Cannot join channel (Need to be identified and verified to join this channel, '/msg NickServ help' to learn how to register and verify.)
LOG . info ( f " PRIVMSG NickServ STATUS { nick } " )
i = line . find ( " ' /msg NickServ help ' " )
if i > 0 :
line = line [ : i ]
raise RuntimeError ( line )
def relay_message ( self , rx ) :
print ( ' IRC> %s : %s ' % rx . groups ( ) )
msg = ' [ %s ]: %s ' % rx . groups ( )
content = rx . group ( 2 )
if self . sGROUP_BOT_NUM > = 0 :
if content [ 1 : ] . startswith ( ' ACTION ' ) :
action = ' [ %s ]: %s ' % ( rx . group ( 1 ) ,
rx . group ( 2 ) [ 8 : - 1 ] )
type_ = TOX_MESSAGE_TYPE [ ' ACTION ' ]
self . ensure_exe ( self . group_send_message ,
self . sGROUP_BOT_NUM , type_ , action )
else :
type_ = TOX_MESSAGE_TYPE [ ' NORMAL ' ]
self . ensure_exe ( self . group_send_message ,
self . sGROUP_BOT_NUM , type_ , msg )
if content . startswith ( ' ^ ' ) :
self . handle_command ( content )
2022-11-02 09:18:52 +01:00
def spin ( self , n = 20 , iMax = 1000 ) :
2022-10-26 10:44:57 +02:00
readable = False
waiti = 0
while not readable :
waiti + = 1
2022-11-02 09:18:52 +01:00
readable , _ , _ = select . select ( [ self . irc ] , [ ] , [ ] , n / 100.0 )
if readable and len ( readable ) and readable [ 0 ] : return readable
2022-10-26 10:44:57 +02:00
self . do ( n )
2022-11-02 09:18:52 +01:00
if waiti > iMax : break
2022-10-26 10:44:57 +02:00
return readable
2022-10-24 00:11:12 +02:00
def iLoop ( self ) :
2022-10-26 10:44:57 +02:00
group_connected = False
routed = None
2013-12-01 16:09:54 +01:00
self . joined = False
2013-12-11 17:02:01 +01:00
self . request = False
2022-10-26 10:44:57 +02:00
iCount = 0
iDelay = 10
nick = self . _oArgs . irc_nick
2022-11-02 09:18:52 +01:00
realname = self . _oArgs . irc_name
ident = self . _oArgs . irc_ident
2022-10-26 10:44:57 +02:00
pwd = self . _oArgs . irc_pass
email = self . _oArgs . irc_email
LOG . info ( f " Looping for Tox and IRC connections " )
if iCount < self . _oArgs . max_sleep :
2013-12-01 16:09:54 +01:00
while True :
2022-10-26 10:44:57 +02:00
iCount + = 1
# LOG.debug(f"Looping {iCount}")
2022-10-24 00:11:12 +02:00
b = self . bRouted ( )
if not b :
self . unroute ( )
2022-10-26 10:44:57 +02:00
group_connected = False
iDelay = iDelay + iDelay / / 10
if routed != b :
if iCount % 10 == 1 :
LOG . info ( f ' Not routed { iCount } sleeping { iDelay } seconds ' )
sleep ( iDelay )
2022-10-24 00:11:12 +02:00
continue
2022-10-26 10:44:57 +02:00
elif b != routed or routed is None :
LOG . debug ( f ' Routed { iCount } - resetting count ' )
iDelay = 10
routed = b
dht_conneted = self . self_get_connection_status ( )
if not dht_conneted :
self . dht_init ( )
2022-11-06 04:57:13 +01:00
LOG . info ( f ' Not DHT connected { iCount } iterating { iDelay } seconds ' )
2022-10-29 20:44:37 +02:00
iDelay = iDelay + iDelay / / 10
2022-11-02 09:18:52 +01:00
self . do ( iDelay )
2022-10-26 10:44:57 +02:00
#drop through
if not group_connected and dht_conneted :
2022-10-24 00:11:12 +02:00
LOG . info ( ' Connected to DHT. ' )
2022-10-26 10:44:57 +02:00
group_connected = True
2013-12-11 17:02:01 +01:00
try :
2022-10-26 10:44:57 +02:00
#? self.bid = self.friend_by_public_key(self.sGROUP_BOT_PK)
r = self . group_reconnect ( self . sGROUP_BOT_NUM )
LOG . info ( f ' Connected to group { r } ' )
2022-10-24 00:11:12 +02:00
except ctypes . ArgumentError as e :
self . bid = None
2022-10-26 10:44:57 +02:00
2022-10-24 00:11:12 +02:00
if self . bid == None :
self . ensure_exe ( self . friend_add_norequest , self . sGROUP_BOT_PK )
LOG . info ( f ' friend_add_n to group { self . sGROUP_BOT_PK [ : 8 ] } ' )
self . bid = self . friend_by_public_key ( self . sGROUP_BOT_PK )
LOG . info ( f ' Added to group { self . bid } ' )
2022-10-26 10:44:57 +02:00
num = self . sGROUP_BOT_NUM
2022-10-24 00:11:12 +02:00
my_pk = self . group_self_get_public_key ( num )
LOG . info ( f ' Connected to group as { my_pk [ : 8 ] } ' )
2022-10-26 10:44:57 +02:00
if group_connected and not dht_conneted :
2022-10-24 00:11:12 +02:00
LOG . info ( ' Disconnected from DHT. ' )
self . dht_init ( )
2022-10-26 10:44:57 +02:00
group_connected = False
2022-10-24 00:11:12 +02:00
if not self . irc :
2022-10-26 10:44:57 +02:00
self . irc_init ( )
2022-10-24 00:11:12 +02:00
if not self . irc :
2022-10-26 10:44:57 +02:00
self . do ( 20 )
2022-10-24 00:11:12 +02:00
continue
2022-10-26 10:44:57 +02:00
2022-11-02 09:18:52 +01:00
2022-10-29 20:44:37 +02:00
LOG . info ( f ' Waiting on IRC to { self . _oArgs . irc_host } on { self . _oArgs . irc_port } ' )
2022-10-24 00:11:12 +02:00
2022-10-26 10:44:57 +02:00
readable = self . spin ( 20 )
2022-11-02 09:18:52 +01:00
if not readable or not readable [ 0 ] :
2022-10-24 00:11:12 +02:00
LOG . info ( ' Waited on IRC but nothing to read. ' )
2022-10-29 20:44:37 +02:00
iDelay = iDelay + iDelay / / 10
2022-10-26 10:44:57 +02:00
continue
try :
2022-11-02 09:18:52 +01:00
pass
2022-10-26 10:44:57 +02:00
except Exception as e :
2022-11-02 09:18:52 +01:00
if len ( e . args ) > 1 and e . args [ 0 ] == 32 :
raise
elif f " { e } " != " 2 " :
LOG . warn ( f ' IRC Error during read: { e } ' )
# close irc?
try :
self . irc . close ( )
self . irc = None
except : pass
continue
else :
iDelay = 10
else :
iDelay = 10
2022-10-29 20:44:37 +02:00
2022-11-02 09:18:52 +01:00
self . irc_readlines ( )
2022-11-06 04:57:13 +01:00
self . do ( iDelay )
2022-10-26 10:44:57 +02:00
return 0
2013-12-01 16:24:33 +01:00
2022-10-24 00:11:12 +02:00
def quit ( self ) :
self . del_callbacks ( )
self . save_to_file ( )
def save_to_file ( self ) :
pass
2013-12-01 16:09:54 +01:00
2014-02-02 13:46:06 +01:00
def irc_send ( self , msg ) :
success = False
while not success :
try :
2022-10-24 00:11:12 +02:00
self . irc . send ( bytes ( msg , ' UTF-8 ' ) )
2014-02-02 13:46:06 +01:00
success = True
break
except socket . error :
sleep ( 1 )
2013-12-11 16:55:40 +01:00
def on_connection_status ( self , friendId , status ) :
2022-10-26 10:44:57 +02:00
# scs_wrapped
2013-12-11 17:02:01 +01:00
if not self . request and not self . joined \
and friendId == self . bid and status :
2022-10-26 10:44:57 +02:00
LOG . info ( ' Groupbot online, trying to get invited to group chat. ' )
2013-12-11 17:02:01 +01:00
self . request = True
2022-10-26 10:44:57 +02:00
type_ = TOX_MESSAGE_TYPE [ ' NORMAL ' ]
# the bot is sending a message to myself self.bid
self . ensure_exe ( self . friend_send_message , self . bid , type_ , ' invite ' )
2013-12-02 20:18:52 +01:00
2022-10-26 10:44:57 +02:00
# gi_wrapped
2022-10-24 00:11:12 +02:00
def on_group_invite ( self , friendid , invite_data , user_data ) :
2013-12-01 16:09:54 +01:00
if not self . joined :
self . joined = True
2022-10-26 10:44:57 +02:00
nick = self . _oArgs . group_nick
self . tox_group_id = self . group_invite_accept ( invite_data , friendid , nick )
2022-10-24 00:11:12 +02:00
LOG . info ( ' Joined groupchat. ' )
2013-12-01 16:09:54 +01:00
2022-10-26 10:44:57 +02:00
def group_peername ( self , groupnumber , peer_id ) :
#dunno
return ' '
2022-10-24 00:11:12 +02:00
def on_group_message ( self , groupnumber , peer_id , message ) :
name = self . group_peername ( groupnumber , peer_id )
2014-04-06 02:12:34 +02:00
if len ( name ) and name != NAME :
2013-12-01 16:09:54 +01:00
print ( ' TOX> %s : %s ' % ( name , message ) )
2014-02-20 19:28:35 +01:00
if message . startswith ( ' > ' ) :
2014-02-20 19:34:59 +01:00
message = ' \x03 09 %s \x03 ' % message
2014-03-13 20:31:26 +01:00
2022-11-06 04:57:13 +01:00
self . irc_send ( bsMSG + ' %s :[ %s ]: %s \r \n ' %
2022-10-26 10:44:57 +02:00
( self . _oArgs . irc_chan , name , message ) )
2014-02-20 10:17:17 +01:00
if message . startswith ( ' ^ ' ) :
2014-02-20 12:04:54 +01:00
self . handle_command ( message )
2013-12-01 16:09:54 +01:00
2022-10-24 00:11:12 +02:00
def on_group_action ( self , groupnumber , peer_id , action ) :
""" old? message type action? """
name = self . group_peername ( groupnumber , peer_id )
2022-10-26 10:44:57 +02:00
if name and name != NAME :
2013-12-22 17:33:15 +01:00
print ( ' TOX> %s : %s ' % ( name , action ) )
2014-02-20 19:28:35 +01:00
if action . startswith ( ' > ' ) :
2014-02-20 19:34:59 +01:00
action = ' \x03 09 %s \x03 ' % action
2022-11-06 04:57:13 +01:00
self . irc_send ( bytes ( sMSG + ' %s : \x01 ACTION [ %s ]: %s \x01 \r \n ' %
( self . _oArgs . irc_chan , name , action ) , ' UTF-8 ' ) )
2013-12-22 17:33:15 +01:00
2013-12-02 07:50:28 +01:00
def on_friend_request ( self , pk , message ) :
2022-10-24 00:11:12 +02:00
LOG . info ( ' Friend request from %s : %s ' % ( pk , message ) )
2022-10-26 10:44:57 +02:00
self . friend_add_norequest ( pk )
2022-10-24 00:11:12 +02:00
LOG . info ( ' Accepted. ' )
2013-12-02 07:50:28 +01:00
def on_friend_message ( self , friendid , message ) :
2022-10-26 10:44:57 +02:00
if message . startswith ( ' invite ' ) :
2014-03-13 20:33:34 +01:00
if not self . tox_group_id is None :
2022-10-26 10:44:57 +02:00
LOG . info ( ' Inviting %s ' % self . friend_get_name ( friendid ) )
self . group_invite_friend ( self . sGROUP_BOT_NUM , friendid )
2014-03-13 20:33:34 +01:00
return
else :
message = ' Waiting for GroupBot, please try again in 1 min. '
2022-10-26 10:44:57 +02:00
type_ = TOX_MESSAGE_TYPE [ ' NORMAL ' ]
self . ensure_exe ( self . friend_send_message , friendid , type_ , message )
2013-12-02 07:50:28 +01:00
2014-02-20 12:27:49 +01:00
def send_both ( self , content ) :
2022-10-26 10:44:57 +02:00
type_ = TOX_MESSAGE_TYPE [ ' NORMAL ' ]
self . ensure_exe ( self . group_send_message , self . sGROUP_BOT_NUM , type_ , content )
2022-11-06 04:57:13 +01:00
self . irc_send ( bytes ( sMSG + ' %s : %s \r \n ' % ( self . _oArgs . irc_chan , content ) , ' UTF-8 ' ) )
2014-02-20 12:27:49 +01:00
2014-02-20 12:02:07 +01:00
def handle_command ( self , cmd ) :
2014-02-20 12:27:49 +01:00
cmd = cmd [ 1 : ]
if cmd in [ ' syncbot ' , ' echobot ' ] :
2022-10-26 10:44:57 +02:00
self . send_both ( self . self_get_address ( ) )
2014-03-13 20:33:34 +01:00
elif cmd == ' resync ' :
sys . exit ( 0 )
2014-02-20 12:27:49 +01:00
elif cmd . startswith ( ' remember ' ) :
args = cmd [ 9 : ] . split ( ' ' )
subject = args [ 0 ]
desc = ' ' . join ( args [ 1 : ] )
self . memory [ subject ] = desc
2022-10-24 00:11:12 +02:00
if self . sMEMORY_DB :
with open ( self . sMEMORY_DB , ' w ' ) as f :
pickle . dump ( self . memory , f )
2014-02-20 12:27:49 +01:00
self . send_both ( ' Remembering ^ %s : %s ' % ( subject , desc ) )
elif self . memory . has_key ( cmd ) :
self . send_both ( self . memory [ cmd ] )
2022-10-26 10:44:57 +02:00
def is_data_encrypted ( self , data ) :
return len ( data ) > 0 and self . _toxes . is_data_encrypted ( data )
def pass_encrypt ( self , data ) :
return self . _toxes . pass_encrypt ( data , self . _oArgs . password )
def has_password ( self ) :
return self . _oArgs . password
def pass_decrypt ( self , data ) :
return self . _toxes . pass_decrypt ( data , self . _oArgs . password )
2014-02-20 12:02:07 +01:00
2022-10-26 10:44:57 +02:00
def iMain ( oArgs , oOpts ) :
2022-10-24 00:11:12 +02:00
assert oTOX_OPTIONS
assert oTOX_OARGS
2022-10-26 10:44:57 +02:00
try :
o = SyniTox ( oArgs , oOpts )
__builtins__ . app = o
o . start ( )
ret = o . iLoop ( )
2022-10-29 20:44:37 +02:00
o . quit ( )
2022-10-26 10:44:57 +02:00
except KeyboardInterrupt :
ret = 0
except ( SSL . Error , ) as e :
LOG . error ( f " SSL error: { e . args } " )
ret = 1
except ( SSL . SysCallError , ) as e :
# OpenSSL.SSL.SysCallError: (9, 'EBADF')
LOG . error ( f " SSL error: { e . args } " )
ret = 1
2022-11-02 09:18:52 +01:00
except SyniToxError as e :
LOG . error ( f ' Error running program: \n { e } ' )
ret = 2
2022-10-26 10:44:57 +02:00
except Exception as e :
LOG . exception ( f ' Error running program: \n { e } ' )
2022-11-02 09:18:52 +01:00
ret = 3
2022-10-26 10:44:57 +02:00
else :
ret = 0
2022-10-24 00:11:12 +02:00
return ret
def oToxygenToxOptions ( oArgs ) :
tox_options = wrapper . tox . Tox . options_new ( )
if oArgs . proxy_type :
tox_options . contents . proxy_type = int ( oArgs . proxy_type )
tox_options . contents . proxy_host = bytes ( oArgs . proxy_host , ' UTF-8 ' )
tox_options . contents . proxy_port = int ( oArgs . proxy_port )
tox_options . contents . udp_enabled = False
else :
tox_options . contents . udp_enabled = oArgs . udp_enabled
if not os . path . exists ( ' /proc/sys/net/ipv6 ' ) :
oArgs . ipv6_enabled = False
tox_options . contents . tcp_port = int ( oArgs . tcp_port )
# overrides
tox_options . contents . local_discovery_enabled = False
tox_options . contents . dht_announcements_enabled = True
tox_options . contents . hole_punching_enabled = False
tox_options . contents . experimental_thread_safety = False
# REQUIRED!!
if oArgs . ipv6_enabled and not os . path . exists ( ' /proc/sys/net/ipv6 ' ) :
LOG . warn ( ' Disabling IPV6 because /proc/sys/net/ipv6 does not exist ' + repr ( oArgs . ipv6_enabled ) )
tox_options . contents . ipv6_enabled = False
else :
tox_options . contents . ipv6_enabled = bool ( oArgs . ipv6_enabled )
#? tox_options.contents.log_callback = LOG
2022-10-26 10:44:57 +02:00
if oArgs . trace_enabled and tox_options . _options_pointer :
2022-10-24 00:11:12 +02:00
# LOG.debug("Adding logging to tox_options._options_pointer ")
ts . vAddLoggerCallback ( tox_options , ts . on_log )
else :
LOG . warn ( " No tox_options._options_pointer " + repr ( tox_options . _options_pointer ) )
return tox_options
def oArgparse ( lArgv ) :
parser = ts . oMainArgparser ( )
parser . add_argument ( ' profile ' , type = str , nargs = ' ? ' , default = None ,
2022-10-26 10:44:57 +02:00
help = ' Path to Tox profile - new groups will be saved there ' )
CAcs = [ ]
for elt in lCAs :
if os . path . exists ( elt ) :
CAcs . append ( elt )
2022-11-03 03:51:14 +01:00
CAfs = [ ]
for elt in lCAfs :
if os . path . exists ( elt ) :
CAfs . append ( elt )
2022-10-26 10:44:57 +02:00
2022-10-29 20:44:37 +02:00
parser . add_argument ( ' --log_level ' , type = int , default = 10 )
2022-10-26 10:44:57 +02:00
parser . add_argument ( ' --bot_name ' , type = str , default = bot_toxname )
parser . add_argument ( ' --max_sleep ' , type = int , default = 3600 ,
help = " max time to sleep waiting for routing before exiting " )
parser . add_argument ( ' --password ' , type = str , default = ' ' ,
help = " password for the profile if encrypted " )
# parser.add_argument('--irc_type', type=str, default='',
# choices=['', 'startls', 'direct')
# does host == connect ?
2022-10-29 20:44:37 +02:00
# oftcnet6xg6roj6d7id4y4cu6dchysacqj2ldgea73qzdagufflqxrid.onion:6697
2022-11-02 09:18:52 +01:00
# irc.oftc.net
parser . add_argument ( ' --irc_host ' , type = str , default = ' ' ,
2022-10-26 10:44:57 +02:00
help = " irc.libera.chat will not work over Tor " )
2022-11-02 09:18:52 +01:00
parser . add_argument ( ' --irc_connect ' , type = str , default = ' ' ,
help = " defaults to irc_host " )
2022-10-26 10:44:57 +02:00
parser . add_argument ( ' --irc_port ' , type = int , default = 6667 ,
help = " default 6667, but may be 6697 with SSL " )
parser . add_argument ( ' --irc_chan ' , type = str , default = ' #tor ' ,
help = " IRC channel to join - include the # " )
#
parser . add_argument ( ' --irc_ssl ' , type = str , default = ' ' ,
help = " TLS version; empty is no SSL " ,
2022-11-03 03:51:14 +01:00
choices = [ ' ' , ' tlsv1.1 ' , ' tlsv1.2 ' , ' tlsv1.3 ' ] )
parser . add_argument ( ' --irc_cafile ' , type = str ,
help = " Certificate Authority file " ,
default = CAfs [ 0 ] )
parser . add_argument ( ' --irc_cadir ' , type = str ,
help = " Certificate Authority directory " ,
2022-10-26 10:44:57 +02:00
default = CAcs [ 0 ] )
2022-11-06 04:57:13 +01:00
parser . add_argument ( ' --irc_crt ' , type = str , default = ' ' ,
help = " Certificate as pem; use openssl req -x509 -nodes -newkey rsa:2048 " )
parser . add_argument ( ' --irc_key ' , type = str , default = ' ' ,
help = " Key as pem; use openssl req -x509 -nodes -newkey rsa:2048 " )
2022-10-26 10:44:57 +02:00
parser . add_argument ( ' --irc_fp ' , type = str , default = ' ' ,
help = " fingerprint of the pem added with CERT ADD; use openssl x509 -noout -fingerprint -SHA1 -text " )
parser . add_argument ( ' --irc_nick ' , type = str , default = ' ' ,
help = " IRC Nickname " )
parser . add_argument ( ' --irc_name ' , type = str , default = ' ' ,
help = " Third field in USER " )
parser . add_argument ( ' --irc_ident ' , type = str , default = ' ' ,
help = " First field in USER " )
parser . add_argument ( ' --irc_pass ' , type = str , default = ' ' ,
help = " password for INDENTIFY or REGISTER " )
parser . add_argument ( ' --irc_email ' , type = str , default = ' ' ,
help = " Use email to REGISTER with _pass " )
#
parser . add_argument ( ' --group_pass ' , type = str , default = ' ' ,
help = " password for the group - optional " )
parser . add_argument ( ' --group_state ' , type = str , default = ' public ' ,
choices = [ ' public ' , ' private ' ] ,
help = " state for the group - default public " )
parser . add_argument ( ' --group_chatid ' , type = str , default = ' ' ,
2022-10-26 11:13:01 +02:00
help = " chat_id of the group - leave empty and will be created on first use " )
2022-10-26 10:44:57 +02:00
parser . add_argument ( ' --group_name ' , type = str , default = ' ' ,
help = " name for the group " )
parser . add_argument ( ' --group_nick ' , type = str , default = ' ' ,
help = " Nickname of the group founder " )
parser . add_argument ( ' --group_invite ' , type = str , default = ' ' ,
help = " A PK to invite to the group " )
parser . add_argument ( ' --group_moderator ' , type = str , default = ' ' ,
help = " A PK to invite to the group as moderator " )
parser . add_argument ( ' --group_ignore ' , type = str , default = ' ' ,
help = " A PK to ignore by the group " )
2022-10-24 00:11:12 +02:00
oArgs = parser . parse_args ( lArgv )
for key in ts . lBOOLEANS :
if key not in oArgs : continue
val = getattr ( oArgs , key )
setattr ( oArgs , key , bool ( val ) )
if hasattr ( oArgs , ' sleep ' ) :
if oArgs . sleep == ' qt ' :
pass # broken or gevent.sleep(idle_period)
elif oArgs . sleep == ' gevent ' :
pass # broken or gevent.sleep(idle_period)
else :
oArgs . sleep = ' time '
return oArgs
def main ( lArgs = None ) :
2022-10-26 10:44:57 +02:00
2022-10-24 00:11:12 +02:00
if lArgs is None : lArgs = [ ]
2022-10-26 10:44:57 +02:00
global oTOX_OARGS
oTOX_OARGS = oArgparse ( lArgs )
2022-11-06 04:57:13 +01:00
ts . clean_booleans ( oTOX_OARGS )
2022-11-02 09:18:52 +01:00
assert oTOX_OARGS . irc_host or oTOX_OARGS . irc_connect
if not oTOX_OARGS . irc_connect :
oTOX_OARGS . irc_connect = oTOX_OARGS . irc_host
2022-11-03 03:51:14 +01:00
if oTOX_OARGS . irc_cadir :
assert os . path . isdir ( oTOX_OARGS . irc_cadir )
if oTOX_OARGS . irc_cafile :
assert os . path . isfile ( oTOX_OARGS . irc_cafile )
2022-11-06 04:57:13 +01:00
2022-10-24 00:11:12 +02:00
global oTOX_OPTIONS
2022-10-26 10:44:57 +02:00
oTOX_OPTIONS = oToxygenToxOptions ( oTOX_OARGS )
2022-11-06 04:57:13 +01:00
2022-10-26 10:44:57 +02:00
ts . vSetupLogging ( oTOX_OARGS )
2022-10-24 00:11:12 +02:00
# ts.setup_logging(oArgs)
2022-10-26 10:44:57 +02:00
return iMain ( oTOX_OARGS , oTOX_OPTIONS )
2022-10-24 00:11:12 +02:00
if __name__ == ' __main__ ' :
sys . exit ( main ( sys . argv [ 1 : ] ) )
# Ran 34 tests in 86.589s OK (skipped=12)