diff --git a/toxygen/wrapper/__init__.py b/toxygen/wrapper/__init__.py deleted file mode 100644 index 2b8d997..0000000 --- a/toxygen/wrapper/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- -# You need a libs directory beside this directory -# and you need to link your libtoxcore.so and libtoxav.so -# and libtoxencryptsave.so into ../libs/ -# Link all 3 to libtoxcore.so if you have only libtoxcore.so diff --git a/toxygen/wrapper/libtox.py b/toxygen/wrapper/libtox.py deleted file mode 100644 index bf39dd0..0000000 --- a/toxygen/wrapper/libtox.py +++ /dev/null @@ -1,87 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- -import os -import sys -from ctypes import CDLL - -# You need a libs directory beside this directory -# and you need to link your libtoxcore.so and libtoxav.so -# and libtoxencryptsave.so into ../libs/ -# Link all 3 to libtoxcore.so if you have only libtoxcore.so -try: - import utils.util as util - sLIBS_DIR = util.get_libs_directory() -except ImportError: - sLIBS_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), - 'libs') - -# environment variable TOXCORE_LIBS overrides -d = os.environ.get('TOXCORE_LIBS', '') -if d and os.path.exists(d): - sLIBS_DIR = d - if os.environ.get('DEBUG', ''): - print ('DBUG: Setting TOXCORE_LIBS to ' +d) -del d - -class LibToxCore: - - def __init__(self): - platform = sys.platform - if platform == 'win32': - libtoxcore = 'libtox.dll' - elif platform == 'darwin': - libtoxcore = 'libtoxcore.dylib' - else: - libtoxcore = 'libtoxcore.so' - - # libtoxcore and libsodium may be installed in your os - # give libs/ precedence - libFile = os.path.join(sLIBS_DIR, libtoxcore) - if os.path.isfile(libFile): - self._libtoxcore = CDLL(libFile) - else: - self._libtoxcore = CDLL(libtoxcore) - - def __getattr__(self, item): - return self._libtoxcore.__getattr__(item) - -class LibToxAV: - - def __init__(self): - platform = sys.platform - if platform == 'win32': - # on Windows av api is in libtox.dll - self._libtoxav = CDLL(os.path.join(sLIBS_DIR, 'libtox.dll')) - elif platform == 'darwin': - self._libtoxav = CDLL('libtoxcore.dylib') - else: - libFile = os.path.join(sLIBS_DIR, 'libtoxav.so') - if os.path.isfile(libFile): - self._libtoxav = CDLL(libFile) - else: - self._libtoxav = CDLL('libtoxav.so') - - def __getattr__(self, item): - return self._libtoxav.__getattr__(item) - -# figure out how to see if we have a combined library - -class LibToxEncryptSave: - - def __init__(self): - platform = sys.platform - if platform == 'win32': - # on Windows profile encryption api is in libtox.dll - self._lib_tox_encrypt_save = CDLL(os.path.join(sLIBS_DIR, 'libtox.dll')) - elif platform == 'darwin': - self._lib_tox_encrypt_save = CDLL('libtoxcore.dylib') - else: - libFile = os.path.join(sLIBS_DIR, 'libtoxencryptsave.so') - if os.path.isfile(libFile): - self._lib_tox_encrypt_save = CDLL(libFile) - else: - self._lib_tox_encrypt_save = CDLL('libtoxencryptsave.so') - - def __getattr__(self, item): - return self._lib_tox_encrypt_save.__getattr__(item) - -# figure out how to see if we have a combined library diff --git a/toxygen/wrapper/tox.c-toxcore.missing b/toxygen/wrapper/tox.c-toxcore.missing deleted file mode 100644 index 82fd0e7..0000000 --- a/toxygen/wrapper/tox.c-toxcore.missing +++ /dev/null @@ -1,80 +0,0 @@ -tox_version_major -tox_version_minor -tox_version_patch -tox_version_is_compatible -tox_public_key_size -tox_secret_key_size -tox_conference_uid_size -tox_conference_id_size -tox_nospam_size -tox_address_size -tox_max_name_length -tox_max_status_message_length -tox_max_friend_request_length -tox_max_message_length -tox_max_custom_packet_size -tox_hash_length -tox_file_id_length -tox_max_filename_length -tox_max_hostname_length -tox_options_get_ipv6_enabled -tox_options_get_udp_enabled -tox_options_get_local_discovery_enabled -tox_options_get_dht_announcements_enabled -tox_options_get_proxy_type -tox_options_get_proxy_port -tox_options_get_start_port -tox_options_get_end_port -tox_options_get_tcp_port -tox_options_get_hole_punching_enabled -tox_options_get_savedata_type -tox_options_get_savedata_length -tox_options_get_experimental_thread_safety -tox_file_seek -tox_callback_conference_connected -tox_callback_conference_message -tox_callback_conference_title -tox_callback_conference_peer_list_changed -tox_conference_new -tox_conference_delete -tox_conference_peer_count -tox_conference_peer_get_name_size -tox_conference_peer_get_name -tox_conference_peer_get_public_key -tox_conference_peer_number_is_ours -tox_conference_offline_peer_count -tox_conference_offline_peer_get_name_size -tox_conference_offline_peer_get_name -tox_conference_offline_peer_get_public_key -tox_conference_offline_peer_get_last_active -tox_conference_set_max_offline -tox_conference_invite -tox_conference_join -tox_conference_send_message -tox_conference_get_title_size -tox_conference_get_title -tox_conference_set_title -tox_conference_get_chatlist_size -tox_conference_get_chatlist -tox_conference_get_type -tox_conference_get_id -tox_conference_by_id -tox_conference_get_uid -tox_conference_by_uid -tox_group_max_topic_length -tox_group_max_part_length -tox_group_max_group_name_length -tox_group_max_password_size -tox_group_chat_id_size -tox_group_peer_public_key_size -tox_group_peer_get_connection_status -tox_group_get_voice_state -tox_callback_group_voice_state -tox_group_get_topic_lock -tox_callback_group_topic_lock -tox_group_send_custom_private_packet -tox_callback_group_custom_private_packet -tox_group_founder_set_topic_lock -tox_group_founder_set_voice_state -tox_group_set_ignore -tox_group_mod_kick_peer diff --git a/toxygen/wrapper/tox.py b/toxygen/wrapper/tox.py deleted file mode 100644 index fec93c7..0000000 --- a/toxygen/wrapper/tox.py +++ /dev/null @@ -1,3132 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- -from ctypes import * -from datetime import datetime - -try: - from wrapper.libtox import LibToxCore - from wrapper.toxav import ToxAV - from wrapper.toxcore_enums_and_consts import * -except: - from libtox import LibToxCore - from toxav import ToxAV - from toxcore_enums_and_consts import * - -# callbacks can be called in any thread so were being careful -# 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 - if bVERBOSE: print('DBUG> '+a) -def LOG_TRACE(a): - bVERBOSE = hasattr(__builtins__, 'app') and app.oArgs.loglevel < 10 - if bVERBOSE: print('TRAC> '+a) - -UINT32_MAX = 2 ** 32 -1 -class ToxError(RuntimeError): pass - -global aTIMES -aTIMES=dict() -def bTooSoon(key, sSlot, fSec=10.0): - # rate limiting - global aTIMES - if sSlot not in aTIMES: - aTIMES[sSlot] = dict() - OTIME = aTIMES[sSlot] - now = datetime.now() - if key not in OTIME: - OTIME[key] = now - return False - delta = now - OTIME[key] - OTIME[key] = now - if delta.total_seconds() < fSec: return True - return False - - -class ToxOptions(Structure): - _fields_ = [ - ('ipv6_enabled', c_bool), - ('udp_enabled', c_bool), - ('local_discovery_enabled', c_bool), - ('dht_announcements_enabled', c_bool), - ('proxy_type', c_int), - ('proxy_host', c_char_p), - ('proxy_port', c_uint16), - ('start_port', c_uint16), - ('end_port', c_uint16), - ('tcp_port', c_uint16), - ('hole_punching_enabled', c_bool), - ('savedata_type', c_int), - ('savedata_data', c_char_p), - ('savedata_length', c_size_t), - ('log_callback', c_void_p), - ('log_user_data', c_void_p), - ('experimental_thread_safety', c_bool), - ] - - -class GroupChatSelfPeerInfo(Structure): - _fields_ = [ - ('nick', c_char_p), - ('nick_length', c_uint8), - ('user_status', c_int) - ] - - -def string_to_bin(tox_id): - return c_char_p(bytes.fromhex(tox_id)) if tox_id is not None else None - - -def bin_to_string(raw_id, length): - res = ''.join('{:02x}'.format(ord(raw_id[i])) for i in range(length)) - return res.upper() - -def sGetError(value, a): - for k,v in a.items(): - if v == value: - s = k - return s - return '' - -class Tox: - libtoxcore = LibToxCore() - - def __init__(self, tox_options=None, tox_pointer=None, app=None): - """Creates and initialises a new Tox instance with the options passed. - - This function will bring the instance into a valid state. - Running the event loop with a new instance will operate correctly. - - :param tox_options: An options object. If this parameter is None, the default options are used. - :param tox_pointer: Tox instance pointer. If this parameter is not None, tox_options will be ignored. - - """ - self._app = app # QtWidgets.QApplication.instance() - if tox_pointer is not None: - self._tox_pointer = tox_pointer - else: - tox_err_new = c_int() - f = Tox.libtoxcore.tox_new - f.restype = POINTER(c_void_p) - self._tox_pointer = f(tox_options, byref(tox_err_new)) - tox_err_new = tox_err_new.value - if tox_err_new == TOX_ERR_NEW['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - if tox_err_new == TOX_ERR_NEW['MALLOC']: - raise MemoryError('The function was unable to allocate enough ' - 'memory to store the internal structures for the Tox object.') - if tox_err_new == TOX_ERR_NEW['PORT_ALLOC']: - raise ToxError('The function was unable to bind to a port. This may mean that all ports have ' - 'already been bound, e.g. by other Tox instances, or it may mean a permission error.' - ' You may be able to gather more information from errno.') - if tox_err_new == TOX_ERR_NEW['TCP_SERVER_ALLOC']: - raise ToxError('The function was unable to bind the tcp server port.') - if tox_err_new == TOX_ERR_NEW['PROXY_BAD_TYPE']: - raise ArgumentError('proxy_type was invalid.') - if tox_err_new == TOX_ERR_NEW['PROXY_BAD_HOST']: - raise ArgumentError('proxy_type was valid but the proxy_host passed had an invalid format or was NULL.') - if tox_err_new == TOX_ERR_NEW['PROXY_BAD_PORT']: - raise ArgumentError('proxy_type was valid, but the proxy_port was invalid.') - if tox_err_new == TOX_ERR_NEW['PROXY_NOT_FOUND']: - raise ArgumentError('The proxy address passed could not be resolved.') - if tox_err_new == TOX_ERR_NEW['LOAD_ENCRYPTED']: - raise ArgumentError('The byte array to be loaded contained an encrypted save.') - if tox_err_new == TOX_ERR_NEW['LOAD_BAD_FORMAT']: - raise ArgumentError('The data format was invalid. This can happen when loading data that was saved by' - ' an older version of Tox, or when the data has been corrupted. When loading from' - ' badly formatted data, some data may have been loaded, and the rest is discarded.' - ' Passing an invalid length parameter also causes this error.') - - self.self_connection_status_cb = None - self.self_logger_cb = None - self.friend_name_cb = None - self.friend_status_message_cb = None - self.friend_status_cb = None - self.friend_connection_status_cb = None - self.friend_request_cb = None - self.friend_read_receipt_cb = None - self.friend_typing_cb = None - self.friend_message_cb = None - self.file_recv_control_cb = None - self.file_chunk_request_cb = None - self.file_recv_cb = None - self.file_recv_chunk_cb = None - self.friend_lossy_packet_cb = None - self.friend_lossless_packet_cb = None - self.group_moderation_cb = None - self.group_join_fail_cb = None - self.group_self_join_cb = None - self.group_invite_cb = None - self.group_custom_packet_cb = None - self.group_private_message_cb = None - self.group_message_cb = None - self.group_password_cb = None - self.group_peer_limit_cb = None - self.group_privacy_state_cb = None - self.group_topic_cb = None - self.group_peer_status_cb = None - self.group_peer_name_cb = None - self.group_peer_exit_cb = None - self.group_peer_join_cb = None - self.AV = ToxAV(self._tox_pointer) - - def kill(self): - if hasattr(self, 'AV'): del self.AV - LOG_INFO(f"tox_kill") - try: - Tox.libtoxcore.tox_kill(self._tox_pointer) - except Exception as e: - LOG_ERROR(f"tox_kill {e!s}") - else: - LOG_DEBUG(f"tox_kill") - - # ----------------------------------------------------------------------------------------------------------------- - # Startup options - # ----------------------------------------------------------------------------------------------------------------- - - @staticmethod - def options_default(tox_options): - """ - Initialises a Tox_Options object with the default options. - - The result of this function is independent of the original options. All values will be overwritten, no values - will be read (so it is permissible to pass an uninitialised object). - - If options is NULL, this function has no effect. - - :param tox_options: A pointer to options object to be filled with default options. - """ - LOG_DEBUG(f"tox_options_default") - Tox.libtoxcore.tox_options_default(tox_options) - - @staticmethod - def options_new(): - """ - Allocates a new Tox_Options object and initialises it with the default options. This function can be used to - preserve long term ABI compatibility by giving the responsibility of allocation and deallocation to the Tox - library. - - Objects returned from this function must be freed using the tox_options_free function. - - :return: A pointer to new ToxOptions object with default options or raise MemoryError. - """ - tox_err_options_new = c_int() - f = Tox.libtoxcore.tox_options_new - f.restype = POINTER(ToxOptions) - result = f(byref(tox_err_options_new)) - result._options_pointer = result - tox_err_options_new = tox_err_options_new.value - if tox_err_options_new == TOX_ERR_OPTIONS_NEW['OK']: - return result - if tox_err_options_new == TOX_ERR_OPTIONS_NEW['MALLOC']: - raise MemoryError('The function failed to allocate enough memory for the options struct.') - raise ToxError('The function did not return OK for the options struct.') - - @staticmethod - def options_free(tox_options): - """ - Releases all resources associated with an options objects. - - Passing a pointer that was not returned by tox_options_new results in undefined behaviour. - - :param tox_options: A pointer to new ToxOptions object - """ - LOG_DEBUG(f"tox_options_free") - Tox.libtoxcore.tox_options_free(tox_options) - - # ----------------------------------------------------------------------------------------------------------------- - # Creation and destruction - # ----------------------------------------------------------------------------------------------------------------- - - def get_savedata_size(self): - """ - Calculates the number of bytes required to store the tox instance with tox_get_savedata. - This function cannot fail. The result is always greater than 0. - - :return: number of bytes - """ - return Tox.libtoxcore.tox_get_savedata_size(self._tox_pointer) - - def get_savedata(self, savedata=None): - """ - Store all information associated with the tox instance to a byte array. - - :param savedata: pointer (c_char_p) to a memory region large enough to store the tox instance data. - Call tox_get_savedata_size to find the number of bytes required. If this parameter is None, this function - allocates memory for the tox instance data. - :return: pointer (c_char_p) to a memory region with the tox instance data - """ - if savedata is None: - savedata_size = self.get_savedata_size() - savedata = create_string_buffer(savedata_size) - LOG_DEBUG(f"tox_get_savedata") - Tox.libtoxcore.tox_get_savedata(self._tox_pointer, savedata) - return savedata[:] - - # ----------------------------------------------------------------------------------------------------------------- - # Connection lifecycle and event loop - # ----------------------------------------------------------------------------------------------------------------- - - def bootstrap(self, address, port, public_key): - """Sends a "get nodes" request to the given bootstrap node with IP, port, and public key to setup connections. - - This function will attempt to connect to the node using UDP. - You must use this function even if Tox_Options.udp_enabled was - set to false. - - :param address: The hostname or IP address (IPv4 or IPv6) of the node. - :param port: The port on the host on which the bootstrap Tox instance is listening. - :param public_key: The long term public key of the bootstrap node (TOX_PUBLIC_KEY_SIZE bytes). - :return: True on success. - - """ - LOG_TRACE(f"tox_bootstrap={address}") - address = bytes(address, 'utf-8') - tox_err_bootstrap = c_int() - try: - result = Tox.libtoxcore.tox_bootstrap(self._tox_pointer, - c_char_p(address), - c_uint16(port), - string_to_bin(public_key), - byref(tox_err_bootstrap)) - except Exception as e: - # Fatal Python error: Segmentation fault - LOG_ERROR(f"libtoxcore.tox_bootstrap {e}") - # dunno - raise - - tox_err_bootstrap = tox_err_bootstrap.value - if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']: - return bool(result) - if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_HOST']: - raise ArgumentError('The address could not be resolved to an IP ' - 'address, or the address passed was invalid.') - if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_PORT']: - raise ArgumentError('The port passed was invalid. The valid port range is (1, 65535).') - # me - this seems wrong - should be False - return False - - def add_tcp_relay(self, address, port, public_key): - """Adds additional host:port pair as TCP relay. - - This function can be used to initiate TCP connections to - different ports on the same bootstrap node, or to add TCP - relays without using them as bootstrap nodes. - - :param address: The hostname or IP address (IPv4 or IPv6) of the TCP relay. - :param port: The port on the host on which the TCP relay is listening. - :param public_key: The long term public key of the TCP relay (TOX_PUBLIC_KEY_SIZE bytes). - :return: True on success. - - """ - LOG_TRACE(f"tox_add_tcp_relay address={address}") - address = bytes(address, 'utf-8') - tox_err_bootstrap = c_int() - result = Tox.libtoxcore.tox_add_tcp_relay(self._tox_pointer, - c_char_p(address), - c_uint16(port), - string_to_bin(public_key), - byref(tox_err_bootstrap)) - tox_err_bootstrap = tox_err_bootstrap.value - if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']: - return bool(result) - if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_HOST']: - raise ArgumentError('The address could not be resolved to an IP ' - 'address, or the IP address passed was invalid.') - if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['BAD_PORT']: - raise ArgumentError('The port passed was invalid. The valid port range is (1, 65535).') - - def self_get_connection_status(self): - """ - Return whether we are connected to the DHT. - The return value is equal to the last value received through the - `self_connection_status` callback. - - :return: TOX_CONNECTION - """ - iRet = Tox.libtoxcore.tox_self_get_connection_status(self._tox_pointer) - if iRet > 2: - LOG_ERROR(f"self_get_connection_status {iRet} > 2") - return 0 - LOG_TRACE(f"self_get_connection_status {iRet}") - return iRet - - def callback_self_connection_status(self, callback): - """Set the callback for the `self_connection_status` event. - Pass None to unset. - - This event is triggered whenever there is a change in the DHT - connection state. When disconnected, a client may choose to - call tox_bootstrap again, to reconnect to the DHT. Note that - this state may frequently change for short amounts of - time. Clients should therefore not immediately bootstrap on - receiving a disconnect. - - :param callback: Python function. Should take - pointer (c_void_p) to Tox object, - TOX_CONNECTION (c_int), - pointer (c_void_p) to user_data - - """ - if callback is None: - Tox.libtoxcore.tox_callback_self_connection_status(self._tox_pointer, - POINTER(None)()) - self.self_connection_status_cb = None - return - - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_void_p) - self.self_connection_status_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_self_connection_status") - Tox.libtoxcore.tox_callback_self_connection_status(self._tox_pointer, - self.self_connection_status_cb) - - def iteration_interval(self): - """ - Return the time in milliseconds before tox_iterate() should be - called again for optimal performance. - :return: time in milliseconds - - """ - return Tox.libtoxcore.tox_iteration_interval(self._tox_pointer) - - def iterate(self, user_data=None): - """ - The main loop that needs to be run in intervals of tox_iteration_interval() milliseconds. - """ - if user_data is not None: - user_data = c_char_p(user_data) - try: - LOG_TRACE(f"tox_iterate") - Tox.libtoxcore.tox_iterate(self._tox_pointer, user_data) - except Exception as e: - # Fatal Python error: Segmentation fault - LOG_ERROR(f"iterate {e!s}") - else: - LOG_TRACE(f"iterate") - - # ----------------------------------------------------------------------------------------------------------------- - # Internal client information (Tox address/id) - # ----------------------------------------------------------------------------------------------------------------- - - def self_get_toxid(self, address=None): - return self.self_get_address(address) - - def self_get_address(self, address=None): - """ - Writes the Tox friend address of the client to a byte array. The address is not in human-readable format. If a - client wants to display the address, formatting is required. - - :param address: pointer (c_char_p) to a memory region of at least TOX_ADDRESS_SIZE bytes. If this parameter is - None, this function allocates memory for address. - :return: Tox friend address - """ - if address is None: - address = create_string_buffer(TOX_ADDRESS_SIZE) - LOG_DEBUG(f"tox_self_get_address") - Tox.libtoxcore.tox_self_get_address(self._tox_pointer, address) - return bin_to_string(address, TOX_ADDRESS_SIZE) - - def self_set_nospam(self, nospam): - """ - Set the 4-byte nospam part of the address. - - :param nospam: Any 32 bit unsigned integer. - """ - LOG_DEBUG(f"tox_self_set_nospam") - Tox.libtoxcore.tox_self_set_nospam(self._tox_pointer, c_uint32(nospam)) - - def self_get_nospam(self): - """ - Get the 4-byte nospam part of the address. - - :return: nospam part of the address - """ - return Tox.libtoxcore.tox_self_get_nospam(self._tox_pointer) - - def self_get_public_key(self, public_key=None): - """ - Copy the Tox Public Key (long term) from the Tox object. - - :param public_key: A memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this parameter is NULL, this - function allocates memory for Tox Public Key. - :return: Tox Public Key - """ - if public_key is None: - public_key = create_string_buffer(TOX_PUBLIC_KEY_SIZE) - LOG_DEBUG(f"tox_self_get_public_key") - Tox.libtoxcore.tox_self_get_public_key(self._tox_pointer, public_key) - return bin_to_string(public_key, TOX_PUBLIC_KEY_SIZE) - - def self_get_secret_key(self, secret_key=None): - """ - Copy the Tox Secret Key from the Tox object. - - :param secret_key: pointer (c_char_p) to a memory region of at least TOX_SECRET_KEY_SIZE bytes. If this - parameter is NULL, this function allocates memory for Tox Secret Key. - :return: Tox Secret Key - """ - if secret_key is None: - secret_key = create_string_buffer(TOX_SECRET_KEY_SIZE) - LOG_DEBUG(f"tox_self_get_secret_key") - Tox.libtoxcore.tox_self_get_secret_key(self._tox_pointer, secret_key) - return bin_to_string(secret_key, TOX_SECRET_KEY_SIZE) - - # ----------------------------------------------------------------------------------------------------------------- - # User-visible client information (nickname/status) - # ----------------------------------------------------------------------------------------------------------------- - - def self_set_name(self, name): - """ - Set the nickname for the Tox client. - - Nickname length cannot exceed TOX_MAX_NAME_LENGTH. If length is 0, the name parameter is ignored - (it can be None), and the nickname is set back to empty. - :param name: New nickname. - :return: True on success. - """ - tox_err_set_info = c_int() - name = bytes(name, 'utf-8') - LOG_DEBUG(f"tox_self_set_name") - result = Tox.libtoxcore.tox_self_set_name(self._tox_pointer, - c_char_p(name), - c_size_t(len(name)), - byref(tox_err_set_info)) - tox_err_set_info = tox_err_set_info.value - if tox_err_set_info == TOX_ERR_SET_INFO['OK']: - return True # was bool(result) - elif tox_err_set_info == TOX_ERR_SET_INFO['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_set_info == TOX_ERR_SET_INFO['TOO_LONG']: - raise ArgumentError('Information length exceeded maximum permissible size.') - return False # was - - def self_get_name_size(self): - """ - Return the length of the current nickname as passed to tox_self_set_name. - - If no nickname was set before calling this function, the name is empty, and this function returns 0. - - :return: length of the current nickname - """ - return Tox.libtoxcore.tox_self_get_name_size(self._tox_pointer) - - def self_get_name(self, name=None): - """ - Write the nickname set by tox_self_set_name to a byte array. - - If no nickname was set before calling this function, the name is empty, and this function has no effect. - - Call tox_self_get_name_size to find out how much memory to allocate for the result. - - :param name: pointer (c_char_p) to a memory region location large enough to hold the nickname. If this parameter - is NULL, the function allocates memory for the nickname. - :return: nickname - """ - if name is None: - name = create_string_buffer(self.self_get_name_size()) - LOG_DEBUG(f"tox_self_get_name") - Tox.libtoxcore.tox_self_get_name(self._tox_pointer, name) - return str(name.value, 'utf-8', errors='ignore') - - def self_set_status_message(self, status_message): - """Set the client's status message. - - Status message length cannot exceed TOX_MAX_STATUS_MESSAGE_LENGTH. - If length is 0, the status parameter is ignored, and the user status is - set back to empty. - - :param status_message: new status message - :return: True on success. - """ - tox_err_set_info = c_int() - status_message = bytes(str(status_message[:80]), 'utf-8') - LOG_DEBUG(f"tox_self_set_status_message") - result = Tox.libtoxcore.tox_self_set_status_message(self._tox_pointer, - c_char_p(status_message), - c_size_t(len(status_message)), - byref(tox_err_set_info)) - tox_err_set_info = tox_err_set_info.value - if tox_err_set_info == TOX_ERR_SET_INFO['OK']: - return bool(result) - elif tox_err_set_info == TOX_ERR_SET_INFO['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_set_info == TOX_ERR_SET_INFO['TOO_LONG']: - raise ArgumentError('Information length exceeded maximum permissible size.') - - def self_get_status_message_size(self): - """ - Return the length of the current status message as passed to tox_self_set_status_message. - - If no status message was set before calling this function, the status is empty, and this function returns 0. - - :return: length of the current status message - """ - return Tox.libtoxcore.tox_self_get_status_message_size(self._tox_pointer) - - def self_get_status_message(self, status_message=None): - """ - Write the status message set by tox_self_set_status_message to a byte array. - - If no status message was set before calling this function, the status is empty, and this function has no effect. - - Call tox_self_get_status_message_size to find out how much memory to allocate for the result. - - :param status_message: pointer (c_char_p) to a valid memory location large enough to hold the status message. - If this parameter is None, the function allocates memory for the status message. - :return: status message - """ - if status_message is None: - status_message = create_string_buffer(self.self_get_status_message_size()) - LOG_DEBUG(f"tox_self_get_status_message") - Tox.libtoxcore.tox_self_get_status_message(self._tox_pointer, status_message) - return str(status_message.value, 'utf-8', errors='ignore') - - def self_set_status(self, status): - """ - Set the client's user status. - - :param status: One of the user statuses listed in the enumeration TOX_USER_STATUS. - """ - return - if bTooSoon('self', 'tox_self_set_status', 5.0): return None - LOG_DEBUG(f"tox_self_set_status {status}") - Tox.libtoxcore.tox_self_set_status(self._tox_pointer, c_int(status)) - - def self_get_status(self): - """ - Returns the client's user status. - - :return: client's user status - """ - if bTooSoon('self', 'self_set_status', 10.0): return None - LOG_TRACE(f"tox_get_status") - return Tox.libtoxcore.tox_self_get_status(self._tox_pointer) - - # ----------------------------------------------------------------------------------------------------------------- - # Friend list management - # ----------------------------------------------------------------------------------------------------------------- - - def friend_add(self, address, message): - """Add a friend to the friend list and send a friend request. - - A friend request message must be at least 1 byte long and at - most TOX_MAX_FRIEND_REQUEST_LENGTH. - - Friend numbers are unique identifiers used in all functions - that operate on friends. Once added, a friend number is stable - for the lifetime of the Tox object. After saving the state and - reloading it, the friend numbers may not be the same as - before. Deleting a friend creates a gap in the friend number - set, which is filled by the next adding of a friend. Any - pattern in friend numbers should not be relied on. - - If more than INT32_MAX friends are added, this function causes - undefined behaviour. - - :param address: The address of the friend (returned by tox_self_get_address of the friend you wish to add) it - must be TOX_ADDRESS_SIZE bytes. - :param message: The message that will be sent along with the friend request. - :return: the friend number on success, UINT32_MAX on failure. - - """ - tox_err_friend_add = c_int() - LOG_DEBUG(f"tox_friend_add") - result = Tox.libtoxcore.tox_friend_add(self._tox_pointer, - string_to_bin(address), - c_char_p(message), - c_size_t(len(message)), - byref(tox_err_friend_add)) - tox_err_friend_add = tox_err_friend_add.value - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['OK']: - return result - - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['TOO_LONG']: - raise ArgumentError('The length of the friend request message exceeded TOX_MAX_FRIEND_REQUEST_LENGTH.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['NO_MESSAGE']: - raise ArgumentError('The friend request message was empty. This, and the TOO_LONG code will never be' - ' returned from tox_friend_add_norequest.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['OWN_KEY']: - raise ArgumentError('The friend address belongs to the sending client.') - elif tox_err_friend_add == TOX_ERR_FRIEND_ADD['ALREADY_SENT']: - raise ArgumentError('A friend request has already been sent, or the address belongs to a friend that is' - ' already on the friend list.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['BAD_CHECKSUM']: - raise ArgumentError('The friend address checksum failed.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['SET_NEW_NOSPAM']: - raise ArgumentError('The friend was already there, but the nospam value was different.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['MALLOC']: - raise MemoryError('A memory allocation failed when trying to increase the friend list size.') - raise ToxError('The function did not return OK for the friend add.') - - def friend_add_norequest(self, public_key): - """Add a friend without sending a friend request. - - This function is used to add a friend in response to a friend - request. If the client receives a friend request, it can be - reasonably sure that the other client added this client as a - friend, eliminating the need for a friend request. - - This function is also useful in a situation where both - instances are controlled by the same entity, so that this - entity can perform the mutual friend adding. In this case, - there is no need for a friend request, either. - - :param public_key: A byte array of length TOX_PUBLIC_KEY_SIZE containing the Public Key (not the Address) of the - friend to add. - :return: the friend number on success, UINT32_MAX on failure. - - """ - tox_err_friend_add = c_int() - LOG_DEBUG(f"tox_friend_add_norequest") - result = Tox.libtoxcore.tox_friend_add_norequest(self._tox_pointer, - string_to_bin(public_key), - byref(tox_err_friend_add)) - tox_err_friend_add = tox_err_friend_add.value - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['OK']: - return result - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['TOO_LONG']: - raise ArgumentError('The length of the friend request message exceeded TOX_MAX_FRIEND_REQUEST_LENGTH.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['NO_MESSAGE']: - raise ArgumentError('The friend request message was empty. This, and the TOO_LONG code will never be' - ' returned from tox_friend_add_norequest.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['OWN_KEY']: - raise ArgumentError('The friend address belongs to the sending client.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['ALREADY_SENT']: - raise ArgumentError('A friend request has already been sent, or the address belongs to a friend that is' - ' already on the friend list.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['BAD_CHECKSUM']: - raise ArgumentError('The friend address checksum failed.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['SET_NEW_NOSPAM']: - raise ArgumentError('The friend was already there, but the nospam value was different.') - if tox_err_friend_add == TOX_ERR_FRIEND_ADD['MALLOC']: - raise MemoryError('A memory allocation failed when trying to increase the friend list size.') - raise ToxError('The function did not return OK for the friend add.') - - def friend_delete(self, friend_number): - """ - Remove a friend from the friend list. - - This does not notify the friend of their deletion. After calling this function, this client will appear offline - to the friend and no communication can occur between the two. - - :param friend_number: Friend number for the friend to be deleted. - :return: True on success. - """ - tox_err_friend_delete = c_int() - LOG_DEBUG(f"tox_friend_delete") - result = Tox.libtoxcore.tox_friend_delete(self._tox_pointer, - c_uint32(friend_number), - byref(tox_err_friend_delete)) - tox_err_friend_delete = tox_err_friend_delete.value - if tox_err_friend_delete == TOX_ERR_FRIEND_DELETE['OK']: - return bool(result) - elif tox_err_friend_delete == TOX_ERR_FRIEND_DELETE['FRIEND_NOT_FOUND']: - raise ArgumentError('There was no friend with the given friend number. No friends were deleted.') - - # ----------------------------------------------------------------------------------------------------------------- - # Friend list queries - # ----------------------------------------------------------------------------------------------------------------- - - def friend_by_public_key(self, public_key): - """ - Return the friend number associated with that Public Key. - - :param public_key: A byte array containing the Public Key. - :return: friend number - """ - tox_err_friend_by_public_key = c_int() - LOG_DEBUG(f"tox_friend_by_public_key") - result = Tox.libtoxcore.tox_friend_by_public_key(self._tox_pointer, - string_to_bin(public_key), - byref(tox_err_friend_by_public_key)) - tox_err_friend_by_public_key = tox_err_friend_by_public_key.value - if tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['OK']: - return result - if tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - if tox_err_friend_by_public_key == TOX_ERR_FRIEND_BY_PUBLIC_KEY['NOT_FOUND']: - raise ArgumentError('No friend with the given Public Key exists on the friend list.') - raise ToxError('The function did not return OK for the friend by public key.') - - def friend_exists(self, friend_number): - """ - Checks if a friend with the given friend number exists and returns true if it does. - """ - # bool() -> TypeError: 'str' object cannot be interpreted as an integer - return Tox.libtoxcore.tox_friend_exists(self._tox_pointer, c_uint32(friend_number)) - - def self_get_friend_list_size(self): - """ - Return the number of friends on the friend list. - - This function can be used to determine how much memory to allocate for tox_self_get_friend_list. - - :return: number of friends - """ - return Tox.libtoxcore.tox_self_get_friend_list_size(self._tox_pointer) - - def self_get_friend_list(self, friend_list=None): - """ - Copy a list of valid friend numbers into an array. - - Call tox_self_get_friend_list_size to determine the number of elements to allocate. - - :param friend_list: pointer (c_char_p) to a memory region with enough space to hold the friend list. If this - parameter is None, this function allocates memory for the friend list. - :return: friend list - """ - friend_list_size = self.self_get_friend_list_size() - if friend_list is None: - friend_list = create_string_buffer(sizeof(c_uint32) * friend_list_size) - friend_list = POINTER(c_uint32)(friend_list) - LOG_TRACE(f"tox_self_get_friend_list") - Tox.libtoxcore.tox_self_get_friend_list(self._tox_pointer, friend_list) - return friend_list[0:friend_list_size] - - def friend_get_public_key(self, friend_number, public_key=None): - """ - Copies the Public Key associated with a given friend number to a byte array. - - :param friend_number: The friend number you want the Public Key of. - :param public_key: pointer (c_char_p) to a memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this - parameter is None, this function allocates memory for Tox Public Key. - :return: Tox Public Key - """ - if public_key is None: - public_key = create_string_buffer(TOX_PUBLIC_KEY_SIZE) - tox_err_friend_get_public_key = c_int() - LOG_TRACE(f"tox_friend_get_public_key") - Tox.libtoxcore.tox_friend_get_public_key(self._tox_pointer, c_uint32(friend_number), public_key, - byref(tox_err_friend_get_public_key)) - tox_err_friend_get_public_key = tox_err_friend_get_public_key.value - if tox_err_friend_get_public_key == TOX_ERR_FRIEND_GET_PUBLIC_KEY['OK']: - return bin_to_string(public_key, TOX_PUBLIC_KEY_SIZE) - elif tox_err_friend_get_public_key == TOX_ERR_FRIEND_GET_PUBLIC_KEY['FRIEND_NOT_FOUND']: - raise ArgumentError('No friend with the given number exists on the friend list.') - - def friend_get_last_online(self, friend_number): - """ - Return a unix-time timestamp of the last time the friend associated with a given friend number was seen online. - This function will return UINT64_MAX on error. - - :param friend_number: The friend number you want to query. - :return: unix-time timestamp - """ - tox_err_last_online = c_int() - LOG_DEBUG(f"tox_friend_get_last_online") - result = Tox.libtoxcore.tox_friend_get_last_online(self._tox_pointer, - c_uint32(friend_number), - byref(tox_err_last_online)) - tox_err_last_online = tox_err_last_online.value - if tox_err_last_online == TOX_ERR_FRIEND_GET_LAST_ONLINE['OK']: - return result - elif tox_err_last_online == TOX_ERR_FRIEND_GET_LAST_ONLINE['FRIEND_NOT_FOUND']: - raise ArgumentError('No friend with the given number exists on the friend list.') - raise ToxError('The function did not return OK') - - # ----------------------------------------------------------------------------------------------------------------- - # Friend-specific state queries (can also be received through callbacks) - # ----------------------------------------------------------------------------------------------------------------- - - def friend_get_name_size(self, friend_number): - """ - Return the length of the friend's name. If the friend number is invalid, the return value is unspecified. - - The return value is equal to the `length` argument received by the last `friend_name` callback. - """ - tox_err_friend_query = c_int() - LOG_TRACE(f"tox_friend_get_name_size") - result = Tox.libtoxcore.tox_friend_get_name_size(self._tox_pointer, - c_uint32(friend_number), - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return result - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - raise ToxError('The function did not return OK') - - def friend_get_name(self, friend_number, name=None): - """ - Write the name of the friend designated by the given friend number to a byte array. - - Call tox_friend_get_name_size to determine the allocation size for the `name` parameter. - - The data written to `name` is equal to the data received by the last `friend_name` callback. - - :param friend_number: number of friend - :param name: pointer (c_char_p) to a valid memory region large enough to store the friend's name. - :return: name of the friend - """ - if name is None: - name = create_string_buffer(self.friend_get_name_size(friend_number)) - tox_err_friend_query = c_int() - LOG_DEBUG(f"tox_friend_get_name") - Tox.libtoxcore.tox_friend_get_name(self._tox_pointer, c_uint32(friend_number), name, - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return str(name.value, 'utf-8', errors='ignore') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - raise ToxError('The function did not return OK') - - def callback_friend_name(self, callback): - """ - Set the callback for the `friend_name` event. Pass None to unset. - - This event is triggered when a friend changes their name. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend whose name changed, - A byte array (c_char_p) containing the same data as tox_friend_get_name would write to its `name` parameter, - A value (c_size_t) equal to the return value of tox_friend_get_name_size, - pointer (c_void_p) to user_data - """ - LOG_DEBUG(f"tox_callback_friend_name") - if callback is None: - Tox.libtoxcore.tox_callback_friend_name(self._tox_pointer, - POINTER(None)()) - self.friend_name_cb = None - return - - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) - self.friend_name_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_friend_name") - Tox.libtoxcore.tox_callback_friend_name(self._tox_pointer, self.friend_name_cb) - - def friend_get_status_message_size(self, friend_number): - """ - Return the length of the friend's status message. If the friend number is invalid, the return value is SIZE_MAX. - - :return: length of the friend's status message - """ - tox_err_friend_query = c_int() - LOG_TRACE(f"tox_friend_get_status_message_size") - result = Tox.libtoxcore.tox_friend_get_status_message_size(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return result - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - - def friend_get_status_message(self, friend_number, status_message=None): - """ - Write the status message of the friend designated by the given friend number to a byte array. - - Call tox_friend_get_status_message_size to determine the allocation size for the `status_name` parameter. - - The data written to `status_message` is equal to the data received by the last `friend_status_message` callback. - - :param friend_number: - :param status_message: pointer (c_char_p) to a valid memory region large enough to store the friend's status - message. - :return: status message of the friend - """ - if status_message is None: - status_message = create_string_buffer(self.friend_get_status_message_size(friend_number)) - tox_err_friend_query = c_int() - LOG_DEBUG(f"tox_friend_get_status_message") - Tox.libtoxcore.tox_friend_get_status_message(self._tox_pointer, c_uint32(friend_number), status_message, - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - # 'utf-8' codec can't decode byte 0xb7 in position 2: invalid start byte - return str(status_message.value, 'utf-8', errors='ignore') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - raise ToxError('The function did not return OK') - - def callback_friend_status_message(self, callback): - """ - Set the callback for the `friend_status_message` event. Pass NULL to unset. - - This event is triggered when a friend changes their status message. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend whose status message changed, - A byte array (c_char_p) containing the same data as tox_friend_get_status_message would write to its - `status_message` parameter, - A value (c_size_t) equal to the return value of tox_friend_get_status_message_size, - pointer (c_void_p) to user_data - """ - LOG_DEBUG(f"tox_callback_friend_status_message") - if callback is None: - Tox.libtoxcore.tox_callback_friend_status_message(self._tox_pointer, - POINTER(None)()) - self.friend_status_message_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) - self.friend_status_message_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_friend_status_message") - Tox.libtoxcore.tox_callback_friend_status_message(self._tox_pointer, - self.friend_status_message_cb) - - def friend_get_status(self, friend_number): - """ - Return the friend's user status (away/busy/...). If the friend number is invalid, the return value is - unspecified. - - The status returned is equal to the last status received through the `friend_status` callback. - - :return: TOX_USER_STATUS - """ - tox_err_friend_query = c_int() - LOG_DEBUG(f"tox_friend_get_status") - result = Tox.libtoxcore.tox_friend_get_status(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return result - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - - def callback_friend_status(self, callback): - """ - Set the callback for the `friend_status` event. Pass None to unset. - - This event is triggered when a friend changes their user status. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - :param The friend number (c_uint32) of the friend whose user status changed, - :param The new user status (TOX_USER_STATUS), - :param user_data: pointer (c_void_p) to user data - """ - LOG_DEBUG(f"tox_callback_friend_status") - if callback is None: - Tox.libtoxcore.tox_callback_friend_status(self._tox_pointer, - POINTER(None)()) - self.friend_status_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) - self.friend_status_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_friend_status") - Tox.libtoxcore.tox_callback_friend_status(self._tox_pointer, self.friend_status_cb) - - def friend_get_connection_status(self, friend_number): - """ - Check whether a friend is currently connected to this client. - - The result of this function is equal to the last value received by the `friend_connection_status` callback. - - :param friend_number: The friend number for which to query the connection status. - :return: the friend's connection status (TOX_CONNECTION) as it was received through the - `friend_connection_status` event. - """ - tox_err_friend_query = c_int() - LOG_DEBUG(f"tox_friend_get_connection_status") - result = Tox.libtoxcore.tox_friend_get_connection_status(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return result - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - raise ToxError('The function did not return OK for friend get connection status.') - - def callback_friend_connection_status(self, callback): - """ - Set the callback for the `friend_connection_status` event. Pass NULL to unset. - - This event is triggered when a friend goes offline after having been online, or when a friend goes online. - - This callback is not called when adding friends. It is assumed that when adding friends, their connection status - is initially offline. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend whose connection status changed, - The result of calling tox_friend_get_connection_status (TOX_CONNECTION) on the passed friend_number, - pointer (c_void_p) to user_data - """ - LOG_DEBUG(f"tox_callback_friend_connection_status") - if callback is None: - Tox.libtoxcore.tox_callback_friend_connection_status(self._tox_pointer, - POINTER(None)()) - self.friend_connection_status_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) - self.friend_connection_status_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_friend_connection_status") - Tox.libtoxcore.tox_callback_friend_connection_status(self._tox_pointer, - self.friend_connection_status_cb) - - def friend_get_typing(self, friend_number): - """ - Check whether a friend is currently typing a message. - - :param friend_number: The friend number for which to query the typing status. - :return: true if the friend is typing. - """ - tox_err_friend_query = c_int() - LOG_DEBUG(f"tox_friend_get_typing") - result = Tox.libtoxcore.tox_friend_get_typing(self._tox_pointer, c_uint32(friend_number), - byref(tox_err_friend_query)) - tox_err_friend_query = tox_err_friend_query.value - if tox_err_friend_query == TOX_ERR_FRIEND_QUERY['OK']: - return bool(result) - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['NULL']: - raise ArgumentError('The pointer parameter for storing the query result (name, message) was NULL. Unlike' - ' the `_self_` variants of these functions, which have no effect when a parameter is' - ' NULL, these functions return an error in that case.') - elif tox_err_friend_query == TOX_ERR_FRIEND_QUERY['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number did not designate a valid friend.') - - def callback_friend_typing(self, callback): - """ - Set the callback for the `friend_typing` event. Pass NULL to unset. - - This event is triggered when a friend starts or stops typing. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who started or stopped typing, - The result of calling tox_friend_get_typing (c_bool) on the passed friend_number, - pointer (c_void_p) to user_data - """ - LOG_DEBUG(f"tox_callback_friend_typing") - if callback is None: - Tox.libtoxcore.tox_callback_friend_typing(self._tox_pointer, - POINTER(None)()) - self.friend_typing_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_void_p) - self.friend_typing_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_friend_typing") - Tox.libtoxcore.tox_callback_friend_typing(self._tox_pointer, self.friend_typing_cb) - - # ----------------------------------------------------------------------------------------------------------------- - # Sending private messages - # ----------------------------------------------------------------------------------------------------------------- - - def self_set_typing(self, friend_number, typing): - """ - Set the client's typing status for a friend. - - The client is responsible for turning it on or off. - - :param friend_number: The friend to which the client is typing a message. - :param typing: The typing status. True means the client is typing. - :return: True on success. - """ - tox_err_set_typing = c_int() - LOG_DEBUG(f"tox_self_set_typing") - result = Tox.libtoxcore.tox_self_set_typing(self._tox_pointer, c_uint32(friend_number), - c_bool(typing), byref(tox_err_set_typing)) - tox_err_set_typing = tox_err_set_typing.value - if tox_err_set_typing == TOX_ERR_SET_TYPING['OK']: - return bool(result) - if tox_err_set_typing == TOX_ERR_SET_TYPING['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - raise ToxError('The function did not return OK for set typing.') - - def friend_send_message(self, friend_number, message_type, message): - """Send a text chat message to an online friend. - - This function creates a chat message packet and pushes it into the send queue. - - The message length may not exceed - TOX_MAX_MESSAGE_LENGTH. Larger messages must be split by the - client and sent as separate messages. Other clients can then - reassemble the fragments. Messages may not be empty. - - The return value of this function is the message ID. If a read - receipt is received, the triggered `friend_read_receipt` event - will be passed this message ID. - - Message IDs are unique per friend. The first message ID is 0. - Message IDs are incremented by 1 each time a message is sent. - If UINT32_MAX messages were sent, the next message ID is 0. - - :param friend_number: The friend number of the friend to send the message to. - :param message_type: Message type (TOX_MESSAGE_TYPE). - :param message: A non-None message text. - :return: message ID - - """ - tox_err_friend_send_message = c_int() - LOG_DEBUG(f"tox_friend_send_message") - result = Tox.libtoxcore.tox_friend_send_message(self._tox_pointer, c_uint32(friend_number), - c_int(message_type), c_char_p(message), c_size_t(len(message)), - byref(tox_err_friend_send_message)) - tox_err_friend_send_message = tox_err_friend_send_message.value - if tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['OK']: - return result - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['SENDQ']: - raise MemoryError('An allocation error occurred while increasing the send queue size.') - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['TOO_LONG']: - raise ArgumentError('Message length exceeded TOX_MAX_MESSAGE_LENGTH.') - elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['EMPTY']: - raise ArgumentError('Attempted to send a zero-length message.') - raise ToxError('The function did not return OK for friend send message.') - - def callback_friend_read_receipt(self, callback): - """ - Set the callback for the `friend_read_receipt` event. Pass None to unset. - - This event is triggered when the friend receives the message sent with tox_friend_send_message with the - corresponding message ID. - - :param callback: Python function. Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who received the message, - The message ID (c_uint32) as returned from tox_friend_send_message corresponding to the message sent, - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - LOG_DEBUG(f"tox_callback_friend_read_receipt") - if callback is None: - Tox.libtoxcore.tox_callback_friend_read_receipt(self._tox_pointer, - POINTER(None)()) - self.friend_read_receipt_cb = None - return - - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) - self.friend_read_receipt_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_friend_read_receipt") - Tox.libtoxcore.tox_callback_friend_read_receipt(self._tox_pointer, - self.friend_read_receipt_cb) - - # ----------------------------------------------------------------------------------------------------------------- - # Receiving private messages and friend requests - # ----------------------------------------------------------------------------------------------------------------- - - def callback_friend_request(self, callback): - """ - Set the callback for the `friend_request` event. Pass None to unset. - - This event is triggered when a friend request is received. - - :param callback: Python function. Should take - pointer (c_void_p) to Tox object, - The Public Key (c_uint8 array) of the user who sent the friend request, - The message (c_char_p) they sent along with the request, - The size (c_size_t) of the message byte array, - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - if callback is None: - Tox.libtoxcore.tox_callback_friend_request(self._tox_pointer, - POINTER(None)()) - self.friend_request_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, POINTER(c_uint8), c_char_p, c_size_t, c_void_p) - self.friend_request_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_friend_request") - Tox.libtoxcore.tox_callback_friend_request(self._tox_pointer, self.friend_request_cb) - - def callback_friend_message(self, callback): - """ - Set the callback for the `friend_message` event. Pass None to unset. - - This event is triggered when a message from a friend is received. - - :param callback: Python function. Should take - pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who sent the message, - Message type (TOX_MESSAGE_TYPE), - The message data (c_char_p) they sent, - The size (c_size_t) of the message byte array. - pointer (c_void_p) to user_data - """ - LOG_DEBUG(f"tox_callback_friend_message") - if callback is None: - Tox.libtoxcore.tox_callback_friend_message(self._tox_pointer, - POINTER(None)()) - self.friend_message_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_char_p, c_size_t, c_void_p) - self.friend_message_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_friend_message") - Tox.libtoxcore.tox_callback_friend_message(self._tox_pointer, self.friend_message_cb) - - # ----------------------------------------------------------------------------------------------------------------- - # File transmission: common between sending and receiving - # ----------------------------------------------------------------------------------------------------------------- - - @staticmethod - def hash(data, hash=None): - """ - Generates a cryptographic hash of the given data. - - This function may be used by clients for any purpose, but is provided primarily for validating cached avatars. - This use is highly recommended to avoid unnecessary avatar updates. - - If hash is NULL or data is NULL while length is not 0 the function returns false, otherwise it returns true. - - This function is a wrapper to internal message-digest functions. - - :param hash: A valid memory location the hash data. It must be at least TOX_HASH_LENGTH bytes in size. - :param data: Data to be hashed or NULL. - :return: true if hash was not NULL. - """ - if hash is None: - hash = create_string_buffer(TOX_HASH_LENGTH) - LOG_DEBUG(f"tox_hash") - Tox.libtoxcore.tox_hash(hash, c_char_p(data), len(data)) - return bin_to_string(hash, TOX_HASH_LENGTH) - - def file_control(self, friend_number, file_number, control): - """ - Sends a file control command to a friend for a given file transfer. - - :param friend_number: The friend number of the friend the file is being transferred to or received from. - :param file_number: The friend-specific identifier for the file transfer. - :param control: The control (TOX_FILE_CONTROL) command to send. - :return: True on success. - """ - tox_err_file_control = c_int() - LOG_DEBUG(f"tox_file_control") - result = Tox.libtoxcore.tox_file_control(self._tox_pointer, c_uint32(friend_number), c_uint32(file_number), - c_int(control), byref(tox_err_file_control)) - tox_err_file_control = tox_err_file_control.value - if tox_err_file_control == TOX_ERR_FILE_CONTROL['OK']: - return bool(result) - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_FOUND']: - raise ArgumentError('No file transfer with the given file number was found for the given friend.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_PAUSED']: - raise ToxError('A RESUME control was sent, but the file transfer is running normally.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['DENIED']: - raise ToxError('A RESUME control was sent, but the file transfer was paused by the other party. Only ' - 'the party that paused the transfer can resume it.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['ALREADY_PAUSED']: - raise ToxError('A PAUSE control was sent, but the file transfer was already paused.') - elif tox_err_file_control == TOX_ERR_FILE_CONTROL['SENDQ']: - raise ToxError('Packet queue is full.') - raise ToxError('The function did not return OK for file control.') - - def callback_file_recv_control(self, callback): - """ - Set the callback for the `file_recv_control` event. Pass NULL to unset. - - This event is triggered when a file control command is received from a friend. - - :param callback: Python function. - When receiving TOX_FILE_CONTROL_CANCEL, the client should release the resources associated with the file number - and consider the transfer failed. - - Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who is sending the file. - The friend-specific file number (c_uint32) the data received is associated with. - The file control (TOX_FILE_CONTROL) command received. - pointer (c_void_p) to user_data - """ - if callback is None: - Tox.libtoxcore.tox_callback_file_recv_control(self._tox_pointer, - POINTER(None)()) - self.file_recv_control_cb = None - return - - LOG_DEBUG(f"tox_callback_file_recv_control") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p) - self.file_recv_control_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_file_recv_control") - Tox.libtoxcore.tox_callback_file_recv_control(self._tox_pointer, - self.file_recv_control_cb) - - def file_seek(self, friend_number, file_number, position): - """ - Sends a file seek control command to a friend for a given file transfer. - - This function can only be called to resume a file transfer right before TOX_FILE_CONTROL_RESUME is sent. - - :param friend_number: The friend number of the friend the file is being received from. - :param file_number: The friend-specific identifier for the file transfer. - :param position: The position that the file should be seeked to. - :return: True on success. - """ - tox_err_file_seek = c_int() - LOG_DEBUG(f"tox_file_control") - result = Tox.libtoxcore.tox_file_control(self._tox_pointer, - c_uint32(friend_number), - c_uint32(file_number), - c_uint64(position), - byref(tox_err_file_seek)) - tox_err_file_seek = tox_err_file_seek.value - if tox_err_file_seek == TOX_ERR_FILE_SEEK['OK']: - return bool(result) - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['NOT_FOUND']: - raise ArgumentError('No file transfer with the given file number was found for the given friend.') - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SEEK_DENIED']: - raise IOError('File was not in a state where it could be seeked.') - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['INVALID_POSITION']: - raise ArgumentError('Seek position was invalid') - elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SENDQ']: - raise ToxError('Packet queue is full.') - raise ToxError('The function did not return OK') - - def file_get_file_id(self, friend_number, file_number, file_id=None): - """ - Copy the file id associated to the file transfer to a byte array. - - :param friend_number: The friend number of the friend the file is being transferred to or received from. - :param file_number: The friend-specific identifier for the file transfer. - :param file_id: A pointer (c_char_p) to memory region of at least TOX_FILE_ID_LENGTH bytes. If this parameter is - None, this function has no effect. - :return: file id. - """ - if file_id is None: - file_id = create_string_buffer(TOX_FILE_ID_LENGTH) - tox_err_file_get = c_int() - LOG_DEBUG(f"tox_file_get_file_id") - Tox.libtoxcore.tox_file_get_file_id(self._tox_pointer, - c_uint32(friend_number), - c_uint32(file_number), - file_id, - byref(tox_err_file_get)) - err_val = tox_err_file_get.value - if err_val == TOX_ERR_FILE_GET['OK']: - return bin_to_string(file_id, TOX_FILE_ID_LENGTH) - if tox_err_file_get == TOX_ERR_FILE_GET['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - if err_val == TOX_ERR_FILE_GET['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - if err_val == TOX_ERR_FILE_GET['NOT_FOUND']: - raise ArgumentError('No file transfer with the given file number was found for the given friend.') - - # ----------------------------------------------------------------------------------------------------------------- - # File transmission: sending - # ----------------------------------------------------------------------------------------------------------------- - - def file_send(self, friend_number, kind, file_size, file_id, filename): - """Send a file transmission request. - - Maximum filename length is TOX_MAX_FILENAME_LENGTH bytes. The - filename should generally just be a file name, not a path with - directory names. - - If a non-UINT64_MAX file size is provided, it can be used by - both sides to determine the sending progress. File size can be - set to UINT64_MAX for streaming data of unknown size. - - File transmission occurs in chunks, which are requested - through the `file_chunk_request` event. - - When a friend goes offline, all file transfers associated with the friend are purged from core. - - If the file contents change during a transfer, the behaviour - is unspecified in general. What will actually happen depends - on the mode in which the file was modified and how the client - determines the file size. - - - If the file size was increased - - and sending mode was streaming (file_size = UINT64_MAX), the behaviour will be as expected. - - and sending mode was file (file_size != UINT64_MAX), the file_chunk_request callback will receive length = - 0 when Core thinks the file transfer has finished. If the client remembers the file size as it was when - sending the request, it will terminate the transfer normally. If the client re-reads the size, it will think - the friend cancelled the transfer. - - If the file size was decreased - - and sending mode was streaming, the behaviour is as expected. - - and sending mode was file, the callback will return 0 at the new (earlier) end-of-file, signalling to the - friend that the transfer was cancelled. - - If the file contents were modified - - at a position before the current read, the two files (local and remote) will differ after the transfer - terminates. - - at a position after the current read, the file transfer will succeed as expected. - - In either case, both sides will regard the transfer as complete and successful. - - :param friend_number: The friend number of the friend the file send request should be sent to. - :param kind: The meaning of the file to be sent. - :param file_size: Size in bytes of the file the client wants to send, UINT64_MAX if unknown or streaming. - :param file_id: A file identifier of length TOX_FILE_ID_LENGTH that can be used to uniquely identify file - transfers across core restarts. If NULL, a random one will be generated by core. It can then be obtained by - using tox_file_get_file_id(). - :param filename: Name of the file. Does not need to be the actual name. This name will be sent along with the - file send request. - :return: A file number used as an identifier in subsequent callbacks. This number is per friend. File numbers - are reused after a transfer terminates. On failure, this function returns UINT32_MAX. Any pattern in file - numbers should not be relied on. - - """ - LOG_DEBUG(f"tox_file_send") - tox_err_file_send = c_int() - result = self.libtoxcore.tox_file_send(self._tox_pointer, - c_uint32(friend_number), - c_uint32(kind), - c_uint64(file_size), - string_to_bin(file_id), - c_char_p(filename), - c_size_t(len(filename)), - byref(tox_err_file_send)) - err_file = tox_err_file_send.value - if err_file == TOX_ERR_FILE_SEND['OK']: - # UINT32_MAX - return result - if err_file == TOX_ERR_FILE_SEND['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - if err_file == TOX_ERR_FILE_SEND['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - if err_file == TOX_ERR_FILE_SEND['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - if err_file == TOX_ERR_FILE_SEND['NAME_TOO_LONG']: - raise ArgumentError('Filename length exceeded TOX_MAX_FILENAME_LENGTH bytes.') - if err_file == TOX_ERR_FILE_SEND['TOO_MANY']: - raise ToxError('Too many ongoing transfers. The maximum number of concurrent file transfers is 256 per' - 'friend per direction (sending and receiving).') - raise ToxError('The function did not return OK') - - def file_send_chunk(self, friend_number, file_number, position, data): - """ - Send a chunk of file data to a friend. - - This function is called in response to the `file_chunk_request` callback. The length parameter should be equal - to the one received though the callback. If it is zero, the transfer is assumed complete. For files with known - size, Core will know that the transfer is complete after the last byte has been received, so it is not necessary - (though not harmful) to send a zero-length chunk to terminate. For streams, core will know that the transfer is - finished if a chunk with length less than the length requested in the callback is sent. - - :param friend_number: The friend number of the receiving friend for this file. - :param file_number: The file transfer identifier returned by tox_file_send. - :param position: The file or stream position from which to continue reading. - :param data: Chunk of file data - :return: true on success. - """ - LOG_DEBUG(f"tox_file_send_chunk") - tox_err_file_send_chunk = c_int() - result = self.libtoxcore.tox_file_send_chunk(self._tox_pointer, - c_uint32(friend_number), c_uint32(file_number), - c_uint64(position), c_char_p(data), c_size_t(len(data)), - byref(tox_err_file_send_chunk)) - tox_err_file_send_chunk = tox_err_file_send_chunk.value - if tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['OK']: - return bool(result) - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NULL']: - raise ArgumentError('The length parameter was non-zero, but data was NULL.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['FRIEND_NOT_FOUND']: - ArgumentError('The friend_number passed did not designate a valid friend.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NOT_FOUND']: - raise ArgumentError('No file transfer with the given file number was found for the given friend.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['NOT_TRANSFERRING']: - raise ArgumentError('File transfer was found but isn\'t in a transferring state: (paused, done, broken, ' - 'etc...) (happens only when not called from the request chunk callback).') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['INVALID_LENGTH']: - raise ArgumentError('Attempted to send more or less data than requested. The requested data size is ' - 'adjusted according to maximum transmission unit and the expected end of the file. ' - 'Trying to send less or more than requested will return this error.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['SENDQ']: - raise ToxError('Packet queue is full.') - elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['WRONG_POSITION']: - raise ArgumentError('Position parameter was wrong.') - raise ToxError('The function did not return OK') - - def callback_file_chunk_request(self, callback): - """ - Set the callback for the `file_chunk_request` event. Pass None to unset. - - This event is triggered when Core is ready to send more file data. - - :param callback: Python function. - If the length parameter is 0, the file transfer is finished, and the client's resources associated with the file - number should be released. After a call with zero length, the file number can be reused for future file - transfers. - - If the requested position is not equal to the client's idea of the current file or stream position, it will need - to seek. In case of read-once streams, the client should keep the last read chunk so that a seek back can be - supported. A seek-back only ever needs to read from the last requested chunk. This happens when a chunk was - requested, but the send failed. A seek-back request can occur an arbitrary number of times for any given chunk. - - In response to receiving this callback, the client should call the function `tox_file_send_chunk` with the - requested chunk. If the number of bytes sent through that function is zero, the file transfer is assumed - complete. A client must send the full length of data requested with this callback. - - Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the receiving friend for this file. - The file transfer identifier (c_uint32) returned by tox_file_send. - The file or stream position (c_uint64) from which to continue reading. - The number of bytes (c_size_t) requested for the current chunk. - pointer (c_void_p) to user_data - """ - if callback is None: - Tox.libtoxcore.tox_callback_file_chunk_request(self._tox_pointer, - POINTER(None)()) - self.file_chunk_request_cb = None - return - LOG_DEBUG(f"tox_callback_file_chunk_request") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, c_size_t, c_void_p) - self.file_chunk_request_cb = c_callback(callback) - self.libtoxcore.tox_callback_file_chunk_request(self._tox_pointer, self.file_chunk_request_cb) - - # ----------------------------------------------------------------------------------------------------------------- - # File transmission: receiving - # ----------------------------------------------------------------------------------------------------------------- - - def callback_file_recv(self, callback): - """ - Set the callback for the `file_recv` event. Pass None to unset. - - This event is triggered when a file transfer request is received. - - :param callback: Python function. - The client should acquire resources to be associated with the file transfer. Incoming file transfers start in - the PAUSED state. After this callback returns, a transfer can be rejected by sending a TOX_FILE_CONTROL_CANCEL - control command before any other control commands. It can be accepted by sending TOX_FILE_CONTROL_RESUME. - - Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who is sending the file transfer request. - The friend-specific file number (c_uint32) the data received is associated with. - The meaning of the file (c_uint32) to be sent. - Size in bytes (c_uint64) of the file the client wants to send, UINT64_MAX if unknown or streaming. - Name of the file (c_char_p). Does not need to be the actual name. This name will be sent along with the file - send request. - Size in bytes (c_size_t) of the filename. - pointer (c_void_p) to user_data - """ - if callback is None: - Tox.libtoxcore.tox_callback_file_recv(self._tox_pointer, - POINTER(None)()) - self.file_recv_cb = None - return - - LOG_DEBUG(f"tox_callback_file_recv") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_uint64, c_char_p, c_size_t, c_void_p) - self.file_recv_cb = c_callback(callback) - self.libtoxcore.tox_callback_file_recv(self._tox_pointer, self.file_recv_cb) - - def callback_file_recv_chunk(self, callback): - """ - Set the callback for the `file_recv_chunk` event. Pass NULL to unset. - - This event is first triggered when a file transfer request is received, and subsequently when a chunk of file - data for an accepted request was received. - - :param callback: Python function. - When length is 0, the transfer is finished and the client should release the resources it acquired for the - transfer. After a call with length = 0, the file number can be reused for new file transfers. - - If position is equal to file_size (received in the file_receive callback) when the transfer finishes, the file - was received completely. Otherwise, if file_size was UINT64_MAX, streaming ended successfully when length is 0. - - Should take pointer (c_void_p) to Tox object, - The friend number (c_uint32) of the friend who is sending the file. - The friend-specific file number (c_uint32) the data received is associated with. - The file position (c_uint64) of the first byte in data. - A byte array (c_char_p) containing the received chunk. - The length (c_size_t) of the received chunk. - pointer (c_void_p) to user_data - """ - if callback is None: - Tox.libtoxcore.tox_callback_file_recv_chunk(self._tox_pointer, - POINTER(None)()) - self.file_recv_chunk_cb = None - return - - LOG_DEBUG(f"tox_callback_file_recv_chunk") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint64, POINTER(c_uint8), c_size_t, c_void_p) - self.file_recv_chunk_cb = c_callback(callback) - self.libtoxcore.tox_callback_file_recv_chunk(self._tox_pointer, self.file_recv_chunk_cb) - - # ----------------------------------------------------------------------------------------------------------------- - # Low-level custom packet sending and receiving - # ----------------------------------------------------------------------------------------------------------------- - - def friend_send_lossy_packet(self, friend_number, data): - """ - Send a custom lossy packet to a friend. - The first byte of data must be in the range 200-254. Maximum length of a - custom packet is TOX_MAX_CUSTOM_PACKET_SIZE. - - Lossy packets behave like UDP packets, meaning they might never reach the - other side or might arrive more than once (if someone is messing with the - connection) or might arrive in the wrong order. - - Unless latency is an issue, it is recommended that you use lossless custom packets instead. - - :param friend_number: The friend number of the friend this lossy packet - :param data: python string containing the packet data - :return: True on success. - """ - LOG_DEBUG(f"friend_send_lossy_packet") - tox_err_friend_custom_packet = c_int() - result = self.libtoxcore.tox_friend_send_lossy_packet(self._tox_pointer, c_uint32(friend_number), - c_char_p(data), c_size_t(len(data)), - byref(tox_err_friend_custom_packet)) - tox_err_friend_custom_packet = tox_err_friend_custom_packet.value - if tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['OK']: - return bool(result) - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['INVALID']: - raise ArgumentError('The first byte of data was not in the specified range for the packet type.' - 'This range is 200-254 for lossy, and 160-191 for lossless packets.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['EMPTY']: - raise ArgumentError('Attempted to send an empty packet.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['TOO_LONG']: - raise ArgumentError('Packet data length exceeded TOX_MAX_CUSTOM_PACKET_SIZE.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']: - raise ToxError('Packet queue is full.') - raise ToxError('The function did not return OK') - - def friend_send_lossless_packet(self, friend_number, data): - """ - Send a custom lossless packet to a friend. - The first byte of data must be in the range 160-191. Maximum length of a - custom packet is TOX_MAX_CUSTOM_PACKET_SIZE. - - Lossless packet behaviour is comparable to TCP (reliability, arrive in order) - but with packets instead of a stream. - - :param friend_number: The friend number of the friend this lossless packet - :param data: python string containing the packet data - :return: True on success. - """ - LOG_DEBUG(f"friend_send_lossless_packet") - tox_err_friend_custom_packet = c_int() - result = self.libtoxcore.tox_friend_send_lossless_packet(self._tox_pointer, c_uint32(friend_number), - c_char_p(data), c_size_t(len(data)), - byref(tox_err_friend_custom_packet)) - tox_err_friend_custom_packet = tox_err_friend_custom_packet.value - if tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['OK']: - return bool(result) - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_CONNECTED']: - raise ArgumentError('This client is currently not connected to the friend.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['INVALID']: - raise ArgumentError('The first byte of data was not in the specified range for the packet type.' - 'This range is 200-254 for lossy, and 160-191 for lossless packets.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['EMPTY']: - raise ArgumentError('Attempted to send an empty packet.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['TOO_LONG']: - raise ArgumentError('Packet data length exceeded TOX_MAX_CUSTOM_PACKET_SIZE.') - elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']: - raise ToxError('Packet queue is full.') - - def callback_friend_lossy_packet(self, callback): - """ - Set the callback for the `friend_lossy_packet` event. Pass NULL to unset. - - :param callback: Python function. - Should take pointer (c_void_p) to Tox object, - friend_number (c_uint32) - The friend number of the friend who sent a lossy packet, - A byte array (c_uint8 array) containing the received packet data, - length (c_size_t) - The length of the packet data byte array, - pointer (c_void_p) to user_data - """ - if callback is None: - self.libtoxcore.tox_callback_friend_lossy_packet(self._tox_pointer, POINTER(None)()) - self.friend_lossy_packet_cb = None - return - - LOG_DEBUG(f"callback_friend_lossy_packet") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) - self.friend_lossy_packet_cb = c_callback(callback) - self.libtoxcore.tox_callback_friend_lossy_packet(self._tox_pointer, self.friend_lossy_packet_cb) - - def callback_friend_lossless_packet(self, callback): - """ - Set the callback for the `friend_lossless_packet` event. Pass NULL to unset. - - :param callback: Python function. - Should take pointer (c_void_p) to Tox object, - friend_number (c_uint32) - The friend number of the friend who sent a lossless packet, - A byte array (c_uint8 array) containing the received packet data, - length (c_size_t) - The length of the packet data byte array, - pointer (c_void_p) to user_data - """ - if callback is None: - self.friend_lossless_packet_cb = None - self.libtoxcore.tox_callback_friend_lossless_packet(self._tox_pointer, POINTER(None)()) - return - - LOG_DEBUG(f"callback_friend_lossless_packet") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p) - self.friend_lossless_packet_cb = c_callback(callback) - self.libtoxcore.tox_callback_friend_lossless_packet(self._tox_pointer, self.friend_lossless_packet_cb) - - # ----------------------------------------------------------------------------------------------------------------- - # Low-level network information - # ----------------------------------------------------------------------------------------------------------------- - # def self_get_keys(self): pass - - def self_get_dht_id(self, dht_id=None): - """Writes the temporary DHT public key of this instance to a byte array. - - This can be used in combination with an externally accessible - IP address and the bound port (from tox_self_get_udp_port) to - run a temporary bootstrap node. - - Be aware that every time a new instance is created, the DHT - public key changes, meaning this cannot be used to run a - permanent bootstrap node. - - :param dht_id: pointer (c_char_p) to a memory region of at least TOX_PUBLIC_KEY_SIZE bytes. If this parameter is - None, this function allocates memory for dht_id. - :return: dht_id - - """ - if dht_id is None: - dht_id = create_string_buffer(TOX_PUBLIC_KEY_SIZE) - LOG_DEBUG(f"tox_self_get_dht_id") - Tox.libtoxcore.tox_self_get_dht_id(self._tox_pointer, dht_id) - return bin_to_string(dht_id, TOX_PUBLIC_KEY_SIZE) - - def self_get_udp_port(self): - """ - Return the UDP port this Tox instance is bound to. - """ - tox_err_get_port = c_int() - LOG_DEBUG(f"tox_self_get_udp_port") - result = Tox.libtoxcore.tox_self_get_udp_port(self._tox_pointer, byref(tox_err_get_port)) - tox_err_get_port = tox_err_get_port.value - if tox_err_get_port == TOX_ERR_GET_PORT['OK']: - return result - if tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']: - raise ToxError('The instance was not bound to any port.') - raise ToxError('The function did not return OK') - - def self_get_tcp_port(self): - """ - Return the TCP port this Tox instance is bound to. This is only relevant if the instance is acting as a TCP - relay. - """ - tox_err_get_port = c_int() - LOG_DEBUG(f"tox_self_get_tcp_port") - result = Tox.libtoxcore.tox_self_get_tcp_port(self._tox_pointer, byref(tox_err_get_port)) - tox_err_get_port = tox_err_get_port.value - if tox_err_get_port == TOX_ERR_GET_PORT['OK']: - return result - if tox_err_get_port == TOX_ERR_GET_PORT['NOT_BOUND']: - raise ToxError('The instance was not bound to any port.') - raise ToxError('The function did not return OK') - - # ----------------------------------------------------------------------------------------------------------------- - # Group chat instance management - # ----------------------------------------------------------------------------------------------------------------- - - def group_new(self, privacy_state, group_name, nick, status): - """Creates a new group chat. - - This function creates a new group chat object and adds it to the chats array. - - The client should initiate its peer list with self info after - calling this function, as the peer_join callback will not be - triggered. - - :param privacy_state: The privacy state of the group. If this is set to TOX_GROUP_PRIVACY_STATE_PUBLIC, - the group will attempt to announce itself to the DHT and anyone with the Chat ID may join. - Otherwise a friend invite will be required to join the group. - :param group_name: The name of the group. The name must be non-NULL. - - :return group number on success, UINT32_MAX on failure. - - """ - - LOG_DEBUG(f"tox_group_new") - error = c_int() - nick = bytes(nick, 'utf-8') - group_name = bytes(group_name, 'utf-8') # .encode('utf-8') - if False: # API change - peer_info = self.group_self_peer_info_new() - peer_info.contents.nick = c_char_p(nick) - peer_info.contents.nick_length = len(nick) - peer_info.contents.user_status = status - result = Tox.libtoxcore.tox_group_new(self._tox_pointer, - privacy_state, - group_name, - len(group_name), - peer_info, byref(error)) - else: - nick_length = len(nick) - cnick = c_char_p(nick) - result = Tox.libtoxcore.tox_group_new(self._tox_pointer, - privacy_state, - group_name, - len(group_name), - cnick, - nick_length, - byref(error)) - - if error.value: - # -1 TOX_ERR_GROUP_NEW_TOO_LONG - # -2 TOX_ERR_GROUP_NEW_EMPTY - # -3 TOX_ERR_GROUP_NEW_INIT - # -4 TOX_ERR_GROUP_NEW_STATE - # -5 TOX_ERR_GROUP_NEW_ANNOUNCE - if error.value in TOX_ERR_GROUP_NEW: - LOG_ERROR(f"group_new {error.value} {TOX_ERR_GROUP_NEW[error.value]}") - raise ToxError(f"group_new {error.value}") - return result - - def group_join(self, chat_id, password, nick, status=''): - """Joins a group chat with specified Chat ID. - - This function creates a new group chat object, adds it to the - chats array, and sends a DHT announcement to find peers in the - group associated with chat_id. Once a peer has been found a - join attempt will be initiated. - - :param chat_id: The Chat ID of the group you wish to join. This must be TOX_GROUP_CHAT_ID_SIZE bytes. - :param password: The password required to join the group. Set to NULL if no password is required. - - :return group_number on success, UINT32_MAX on failure. - - """ - - LOG_DEBUG(f"tox_group_join") - error = c_int() - nick = bytes(nick, 'utf-8') - if False: # API change - peer_info = self.group_self_peer_info_new() - peer_info.contents.nick = c_char_p(nick) - peer_info.contents.nick_length = len(nick) - peer_info.contents.user_status = status - result = Tox.libtoxcore.tox_group_join(self._tox_pointer, - string_to_bin(chat_id), - password, - len(password) if password is not None else 0, - peer_info, - byref(error)) - else: - nick_length = len(nick) - cnick = c_char_p(nick) - result = Tox.libtoxcore.tox_group_join(self._tox_pointer, - string_to_bin(chat_id), - cnick, - nick_length, - password, - len(password) if password is not None else 0, - - byref(error)) - if error.value: - LOG_ERROR(f"group_join {error.value} {TOX_ERR_GROUP_JOIN[error.value]}") - raise ToxError(f"group_join {error.value} {TOX_ERR_GROUP_JOIN[error.value]}") - return result - - def group_reconnect(self, group_number): - """ - Reconnects to a group. - - This function disconnects from all peers in the group, then attempts to reconnect with the group. - The caller's state is not changed (i.e. name, status, role, chat public key etc.) - - :param group_number: The group number of the group we wish to reconnect to. - :return True on success. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_reconnect") - result = Tox.libtoxcore.tox_group_reconnect(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f"group_reconnect {error.value}") - raise ToxError(f"group_reconnect {error.value}") - return result - - def group_is_connected(self, group_number): - error = c_int() - LOG_DEBUG(f"tox_group_is_connected") - result = Tox.libtoxcore.tox_group_is_connected(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f"group_is_connected {error.value}") - raise ToxError("group_is_connected {error.value}") - return result - - def group_disconnect(self, group_number): - error = c_int() - LOG_DEBUG(f"tox_group_disconnect") - result = Tox.libtoxcore.tox_group_disconnect(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f"group_disconnect {error.value}") - raise ToxError("group_disconnect {error.value}") - return result - - def group_leave(self, group_number, message=None): - """Leaves a group. - - This function sends a parting packet containing a custom - (non-obligatory) message to all peers in a group, and deletes - the group from the chat array. All group state information is - permanently lost, including keys and role credentials. - - :param group_number: The group number of the group we wish to leave. - :param message: The parting message to be sent to all the peers. Set to NULL if we do not wish to - send a parting message. - - :return True if the group chat instance was successfully deleted. - - """ - - LOG_DEBUG(f"tox_leave") - error = c_int() - f = Tox.libtoxcore.tox_group_leave - f.restype = c_bool - result = f(self._tox_pointer, group_number, message, - len(message) if message else 0, byref(error)) - if error.value: - LOG_ERROR(f"group_leave {error.value}") - raise ToxError("group_leave {error.value}") - return result - - # ----------------------------------------------------------------------------------------------------------------- - # Group user-visible client information (nickname/status/role/public key) - # ----------------------------------------------------------------------------------------------------------------- - - def group_self_set_name(self, group_number, name): - """Set the client's nickname for the group instance designated by the given group number. - - Nickname length cannot exceed TOX_MAX_NAME_LENGTH. If length - is equal to zero or name is a NULL pointer, the function call - will fail. - - :param name: A byte array containing the new nickname. - - :return True on success. - - """ - - error = c_int() - name = bytes(name, 'utf-8') - LOG_DEBUG(f"tox_group_self_set_name") - result = Tox.libtoxcore.tox_group_self_set_name(self._tox_pointer, group_number, name, len(name), byref(error)) - if error.value: - LOG_ERROR(f"group_self_set_name {error.value}") - raise ToxError("group_self_set_name {error.value}") - return result - - def group_self_get_name_size(self, group_number): - """ - Return the length of the client's current nickname for the group instance designated - by group_number as passed to tox_group_self_set_name. - - If no nickname was set before calling this function, the name is empty, - and this function returns 0. - """ - - error = c_int() - LOG_TRACE(f"tox_group_self_get_name_size") - result = Tox.libtoxcore.tox_group_self_get_name_size(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f"group_self_get_name_size {error.value}") - raise ToxError("group_self_get_name_size {error.value}") - return result - - def group_self_get_name(self, group_number): - """Write the nickname set by tox_group_self_set_name to a byte array. - - If no nickname was set before calling this function, the name is empty, - and this function has no effect. - - Call tox_group_self_get_name_size to find out how much memory - to allocate for the result. - - :return nickname - - """ - - error = c_int() - size = self.group_self_get_name_size(group_number) - name = create_string_buffer(size) - LOG_DEBUG(f"tox_group_self_get_name") - result = Tox.libtoxcore.tox_group_self_get_name(self._tox_pointer, group_number, name, byref(error)) - if error.value: - LOG_ERROR(f"group_self_get_name {error.value}") - raise ToxError("group_self_get_name {error.value}") - return str(name[:size], 'utf-8', errors='ignore') - - def group_self_set_status(self, group_number, status): - - """ - Set the client's status for the group instance. Status must be a TOX_USER_STATUS. - :return True on success. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_self_set_status") - result = Tox.libtoxcore.tox_group_self_set_status(self._tox_pointer, group_number, status, byref(error)) - if error.value: - LOG_ERROR(f"group_self_set_status {error.value}") - raise ToxError("group_self_set_status {error.value}") - return result - - def group_self_get_status(self, group_number): - """ - returns the client's status for the group instance on success. - return value is unspecified on failure. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_self_get_status") - result = Tox.libtoxcore.tox_group_self_get_status(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f"group_self_get_status {error.value}") - raise ToxError("group_self_get_status {error.value}") - return result - - def group_self_get_role(self, group_number): - """ - returns the client's role for the group instance on success. - return value is unspecified on failure. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_self_get_role") - result = Tox.libtoxcore.tox_group_self_get_role(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def group_self_get_peer_id(self, group_number): - """ - returns the client's peer id for the group instance on success. - return value is unspecified on failure. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_self_get_peer_id") - result = Tox.libtoxcore.tox_group_self_get_peer_id(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError("tox_group_self_get_peer_id {error.value}") - return result - - def group_self_get_public_key(self, group_number): - """ - Write the client's group public key designated by the given group number to a byte array. - - This key will be permanently tied to the client's identity for this particular group until - the client explicitly leaves the group or gets kicked/banned. This key is the only way for - other peers to reliably identify the client across client restarts. - - `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. - - :return public key - """ - - error = c_int() - key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE) - LOG_DEBUG(f"tox_group_self_get_public_key") - result = Tox.libtoxcore.tox_group_self_get_public_key(self._tox_pointer, group_number, - key, byref(error)) - if error.value: - LOG_ERROR(f" {TOX_ERR_FRIEND_GET_PUBLIC_KEY[error.value]}") - raise ToxError(f"{TOX_ERR_FRIEND_GET_PUBLIC_KEY[error.value]}") - return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) - - # ----------------------------------------------------------------------------------------------------------------- - # Peer-specific group state queries. - # ----------------------------------------------------------------------------------------------------------------- - - def group_peer_get_name_size(self, group_number, peer_id): - """ - Return the length of the peer's name. If the group number or ID is invalid, the - return value is unspecified. - - The return value is equal to the `length` argument received by the last - `group_peer_name` callback. - """ - - error = c_int() - result = Tox.libtoxcore.tox_group_peer_get_name_size(self._tox_pointer, group_number, peer_id, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - LOG_TRACE(f"tox_group_peer_get_name_size") - return result - - def group_peer_get_name(self, group_number, peer_id): - """Write the name of the peer designated by the given ID to a byte - array. - - Call tox_group_peer_get_name_size to determine the allocation - size for the `name` parameter. - - The data written to `name` is equal to the data received by the last - `group_peer_name` callback. - - :param group_number: The group number of the group we wish to query. - :param peer_id: The ID of the peer whose name we want to retrieve. - - :return name. - - """ - error = c_int() - size = self.group_peer_get_name_size(group_number, peer_id) - name = create_string_buffer(size) - LOG_DEBUG(f"tox_group_peer_get_name") - result = Tox.libtoxcore.tox_group_peer_get_name(self._tox_pointer, group_number, peer_id, name, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f"tox_group_peer_get_name {error.value}") - sRet = str(name[:], 'utf-8', errors='ignore') - return sRet - - def group_peer_get_status(self, group_number, peer_id): - """ - Return the peer's user status (away/busy/...). If the ID or group number is - invalid, the return value is unspecified. - - The status returned is equal to the last status received through the - `group_peer_status` callback. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_peer_get_status") - result = Tox.libtoxcore.tox_group_peer_get_status(self._tox_pointer, group_number, peer_id, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def group_peer_get_role(self, group_number, peer_id): - """ - Return the peer's role (user/moderator/founder...). If the ID or group number is - invalid, the return value is unspecified. - - The role returned is equal to the last role received through the - `group_moderation` callback. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_peer_get_role") - result = Tox.libtoxcore.tox_group_peer_get_role(self._tox_pointer, group_number, peer_id, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def group_peer_get_public_key(self, group_number, peer_id): - """Write the group public key with the designated peer_id for the designated group number to public_key. - - This key will be permanently tied to a particular peer until - they explicitly leave the group or get kicked/banned, and is - the only way to reliably identify the same peer across client - restarts. - - `public_key` should have room for at least TOX_GROUP_PEER_PUBLIC_KEY_SIZE bytes. - - :return public key - - """ - - error = c_int() - key = create_string_buffer(TOX_GROUP_PEER_PUBLIC_KEY_SIZE) - LOG_DEBUG(f"tox_group_peer_get_public_key") - result = Tox.libtoxcore.tox_group_peer_get_public_key(self._tox_pointer, group_number, peer_id, - key, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return bin_to_string(key, TOX_GROUP_PEER_PUBLIC_KEY_SIZE) - - def callback_group_peer_name(self, callback, user_data): - """ - Set the callback for the `group_peer_name` event. Pass NULL to unset. - This event is triggered when a peer changes their nickname. - """ - if callback is None: - Tox.libtoxcore.tox_callback_group_peer_name(self._tox_pointer, - POINTER(None)(), user_data) - self.group_peer_name_cb = None - return - - LOG_DEBUG(f"tox_callback_group_peer_name") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) - self.group_peer_name_cb = c_callback(callback) - try: - Tox.libtoxcore.tox_callback_group_peer_name(self._tox_pointer, self.group_peer_name_cb) - except Exception as e: # AttributeError - LOG_ERROR(f"tox_callback_conference_peer_name") - - def callback_group_peer_status(self, callback, user_data): - """ - Set the callback for the `group_peer_status` event. Pass NULL to unset. - This event is triggered when a peer changes their status. - """ - - if callback is None: - Tox.libtoxcore.tox_callback_group_peer_status(self._tox_pointer, POINTER(None)()) - self.group_peer_status_cb = None - return - - LOG_DEBUG(f"tox_callback_group_peer_status") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p) - self.group_peer_status_cb = c_callback(callback) - try: - Tox.libtoxcore.tox_callback_group_peer_status(self._tox_pointer, self.group_peer_status_cb) - except Exception as e: - LOG_WARN(f"callback_group_peer_status Exception {e}") - - # ----------------------------------------------------------------------------------------------------------------- - # Group chat state queries and events. - # ----------------------------------------------------------------------------------------------------------------- - - def group_set_topic(self, group_number, topic): - """Set the group topic and broadcast it to the rest of the group. - - topic length cannot be longer than TOX_GROUP_MAX_TOPIC_LENGTH. - If length is equal to zero or topic is set to NULL, the topic will be unset. - - :return True on success. - - """ - - error = c_int() - topic = bytes(topic, 'utf-8') - try: - LOG_DEBUG(f"tox_group_set_topic") - result = Tox.libtoxcore.tox_group_set_topic(self._tox_pointer, group_number, topic, len(topic), byref(error)) - except Exception as e: - LOG_WARN(f" Exception {e}") - result = None - else: - if error.value: - LOG_ERROR(f"group_set_topic {error.value}") - raise ToxError("group_set_topic {error.value}") - return result - - def group_get_topic_size(self, group_number): - """ - Return the length of the group topic. If the group number is invalid, the - return value is unspecified. - - The return value is equal to the `length` argument received by the last - `group_topic` callback. - """ - - error = c_int() - LOG_TRACE(f"tox_group_get_topic_size") - try: - result = Tox.libtoxcore.tox_group_get_topic_size(self._tox_pointer, group_number, byref(error)) - except Exception as e: - LOG_WARN(f" Exception {e}") - result = None - else: - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def group_get_topic(self, group_number): - """ - Write the topic designated by the given group number to a byte array. - Call tox_group_get_topic_size to determine the allocation size for the `topic` parameter. - The data written to `topic` is equal to the data received by the last - `group_topic` callback. - - :return topic - """ - - error = c_int() - size = self.group_get_topic_size(group_number) - topic = create_string_buffer(size) - LOG_DEBUG(f"tox_group_get_topic") - result = Tox.libtoxcore.tox_group_get_topic(self._tox_pointer, group_number, topic, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return str(topic[:size], 'utf-8', errors='ignore') - - def group_get_name_size(self, group_number): - """ - Return the length of the group name. If the group number is invalid, the - return value is unspecified. - """ - error = c_int() - result = Tox.libtoxcore.tox_group_get_name_size(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - LOG_TRACE(f"tox_group_get_name_size") - return int(result) - - def group_get_name(self, group_number): - """ - Write the name of the group designated by the given group number to a byte array. - Call tox_group_get_name_size to determine the allocation size for the `name` parameter. - :return true on success. - """ - - error = c_int() - size = self.group_get_name_size(group_number) - name = create_string_buffer(size) - LOG_DEBUG(f"tox_group_get_name") - result = Tox.libtoxcore.tox_group_get_name(self._tox_pointer, group_number, - name, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return str(name[:size], 'utf-8', errors='ignore') - - def group_get_chat_id(self, group_number): - """ - Write the Chat ID designated by the given group number to a byte array. - `chat_id` should have room for at least TOX_GROUP_CHAT_ID_SIZE bytes. - :return chat id. - """ - - LOG_INFO(f"tox_group_get_id group_number={group_number}") - error = c_int() - buff = create_string_buffer(TOX_GROUP_CHAT_ID_SIZE) - result = Tox.libtoxcore.tox_group_get_chat_id(self._tox_pointer, - group_number, - buff, byref(error)) - if error.value: - if error.value == 1: - LOG_ERROR(f"tox_group_get_chat_id ERROR GROUP_STATE_QUERIES_GROUP_NOT_FOUND group_number={group_number}") - else: - LOG_ERROR(f"tox_group_get_chat_id group_number={group_number} error={error.value}") - raise ToxError(f"tox_group_get_chat_id {error.value}") -# -# QObject::setParent: Cannot set parent, new parent is in a different thread -# QObject::installEventFilter(): Cannot filter events for objects in a different thread. -# QBasicTimer::start: Timers cannot be started from another thread - - LOG_TRACE(f"tox_group_get_chat_id") - return bin_to_string(buff, TOX_GROUP_CHAT_ID_SIZE) - - def group_get_number_groups(self): - """ - Return the number of groups in the Tox chats array. - """ - LOG_DEBUG(f"tox_group_get_number_groups") - try: - result = Tox.libtoxcore.tox_group_get_number_groups(self._tox_pointer) - except Exception as e: - LOG_WARN(f" Exception {e}") - result = 0 - return result - - def groups_get_list(self): - raise NotImplementedError('tox_groups_get_list') -# groups_list_size = self.group_get_number_groups() -# groups_list = create_string_buffer(sizeof(c_uint32) * groups_list_size) -# groups_list = POINTER(c_uint32)(groups_list) -# LOG_DEBUG(f"tox_groups_get_list") -# Tox.libtoxcore.tox_groups_get_list(self._tox_pointer, groups_list) -# return groups_list[0:groups_list_size] - - def group_get_privacy_state(self, group_number): - """ - Return the privacy state of the group designated by the given group number. If group number - is invalid, the return value is unspecified. - - The value returned is equal to the data received by the last - `group_privacy_state` callback. - - see the `Group chat founder controls` section for the respective set function. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_get_privacy_state") - result = Tox.libtoxcore.tox_group_get_privacy_state(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def group_get_peer_limit(self, group_number): - """ - Return the maximum number of peers allowed for the group designated by the given group number. - If the group number is invalid, the return value is unspecified. - - The value returned is equal to the data received by the last - `group_peer_limit` callback. - - see the `Group chat founder controls` section for the respective set function. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_get_peer_limit") - result = Tox.libtoxcore.tox_group_get_peer_limit(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def group_get_password_size(self, group_number): - """ - Return the length of the group password. If the group number is invalid, the - return value is unspecified. - """ - - error = c_int() - LOG_TRACE(f"tox_group_get_password_size") - result = Tox.libtoxcore.tox_group_get_password_size(self._tox_pointer, group_number, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def group_get_password(self, group_number): - """ - Write the password for the group designated by the given group number to a byte array. - - Call tox_group_get_password_size to determine the allocation size for the `password` parameter. - - The data received is equal to the data received by the last - `group_password` callback. - - see the `Group chat founder controls` section for the respective set function. - - :return password - """ - - error = c_int() - size = self.group_get_password_size(group_number) - password = create_string_buffer(size) - LOG_DEBUG(f"tox_group_get_password") - result = Tox.libtoxcore.tox_group_get_password(self._tox_pointer, group_number, - password, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return str(password[:size], 'utf-8', errors='ignore') - - def callback_group_topic(self, callback, user_data): - """ - Set the callback for the `group_topic` event. Pass NULL to unset. - This event is triggered when a peer changes the group topic. - """ - - LOG_DEBUG(f"tox_callback_group_topic") - if callback is None: - Tox.libtoxcore.tox_callback_group_topic(self._tox_pointer, POINTER(None)()) - self.group_topic_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_void_p) - self.group_topic_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_topic") - Tox.libtoxcore.tox_callback_group_topic(self._tox_pointer, self.group_topic_cb) - except Exception as e: - LOG_WARN(f" Exception {e}") - - def callback_group_privacy_state(self, callback, user_data): - """ - Set the callback for the `group_privacy_state` event. Pass NULL to unset. - This event is triggered when the group founder changes the privacy state. - """ - - LOG_DEBUG(f"tox_callback_group_privacy_state") - if callback is None: - Tox.libtoxcore.tox_callback_group_privacy_state(self._tox_pointer, POINTER(None)()) - self.group_privacy_state_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) - self.group_privacy_state_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_privacy_state") - Tox.libtoxcore.tox_callback_group_privacy_state(self._tox_pointer, self.group_privacy_state_cb) - except Exception as e: - LOG_WARN(f" Exception {e}") - - def callback_group_peer_limit(self, callback, user_data): - """ - Set the callback for the `group_peer_limit` event. Pass NULL to unset. - This event is triggered when the group founder changes the maximum peer limit. - """ - - LOG_DEBUG(f"tox_callback_group_peer_limit") - if callback is None: - Tox.libtoxcore.tox_callback_group_peer_limit(self._tox_pointer, POINTER(None)()) - self.group_peer_limit_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) - self.group_peer_limit_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_peer_limit") - Tox.libtoxcore.tox_callback_group_peer_limit(self._tox_pointer, self.group_peer_limit_cb) - except Exception as e: - LOG_WARN(f" Exception {e}") - - def callback_group_password(self, callback, user_data): - """ - Set the callback for the `group_password` event. Pass NULL to unset. - This event is triggered when the group founder changes the group password. - """ - - LOG_DEBUG(f"tox_callback_group_password") - if callback is None: - Tox.libtoxcore.tox_callback_group_password(self._tox_pointer, POINTER(None)()) - self.group_password_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) - self.group_password_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_password") - Tox.libtoxcore.tox_callback_group_password(self._tox_pointer, self.group_password_cb) - except Exception as e: - LOG_WARN(f"tox_callback_group_password Exception {e}") - - # ----------------------------------------------------------------------------------------------------------------- - # Group message sending - # ----------------------------------------------------------------------------------------------------------------- - - def group_send_custom_packet(self, group_number, lossless, data): - """Send a custom packet to the group. - - If lossless is true the packet will be lossless. Lossless - packet behaviour is comparable to TCP (reliability, arrive in - order) but with packets instead of a stream. - - If lossless is false, the packet will be lossy. Lossy packets - behave like UDP packets, meaning they might never reach the - other side or might arrive more than once (if someone is - messing with the connection) or might arrive in the wrong - order. - - Unless latency is an issue or message reliability is not - important, it is recommended that you use lossless custom - packets. - - :param group_number: The group number of the group the message is intended for. - :param lossless: True if the packet should be lossless. - :param data A byte array containing the packet data. - :return True on success. - - """ - - error = c_int() - LOG_DEBUG(f"tox_group_send_custom_packet") - result = Tox.libtoxcore.tox_group_send_custom_packet(self._tox_pointer, group_number, lossless, data, - len(data), byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def group_send_private_message(self, group_number, peer_id, message_type, message): - """ - Send a text chat message to the specified peer in the specified group. - - This function creates a group private message packet and pushes it into the send - queue. - - The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages - must be split by the client and sent as separate messages. Other clients can - then reassemble the fragments. Messages may not be empty. - - :param group_number: The group number of the group the message is intended for. - :param peer_id: The ID of the peer the message is intended for. - :param message: A non-NULL pointer to the first element of a byte array containing the message text. - - :return True on success. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_send_private_message") - result = Tox.libtoxcore.tox_group_send_private_message(self._tox_pointer, group_number, peer_id, - message_type, message, - len(message), byref(error)) - if error.value: - LOG_ERROR(f"group_send_private_message {TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE[error.value]}") - raise ToxError(f"group_send_private_message {TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE[error.value]}") - return result - - def group_send_message(self, group_number, type_, message): - """ - Send a text chat message to the group. - - This function creates a group message packet and pushes it into the send - queue. - - The message length may not exceed TOX_MAX_MESSAGE_LENGTH. Larger messages - must be split by the client and sent as separate messages. Other clients can - then reassemble the fragments. Messages may not be empty. - - :param group_number: The group number of the group the message is intended for. - :param type_: Message type (normal, action, ...). - :param message: A non-NULL pointer to the first element of a byte array containing the message text. - - :return True on success. - """ - - error = c_int() - # uint32_t message_id = 0; - message_id = c_int() # or POINTER(None)() - - LOG_DEBUG(f"tox_group_send_message") - # bool tox_group_send_message(const Tox *tox, uint32_t group_number, Tox_Message_Type type, const uint8_t *message, size_t length, uint32_t *message_id, Tox_Err_Group_Send_Message *error) - result = Tox.libtoxcore.tox_group_send_message(self._tox_pointer, - group_number, - type_, - message, - len(message), - # dunno - byref(message_id), - byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - # ----------------------------------------------------------------------------------------------------------------- - # Group message receiving - # ----------------------------------------------------------------------------------------------------------------- - - def callback_group_message(self, callback, user_data): - """ - Set the callback for the `group_message` event. Pass NULL to unset. - This event is triggered when the client receives a group message. - - Callback: python function with params: - tox Tox* instance - group_number The group number of the group the message is intended for. - peer_id The ID of the peer who sent the message. - type The type of message (normal, action, ...). - message The message data. - length The length of the message. - user_data - user data - """ - LOG_DEBUG(f"tox_callback_group_message") - if callback is None: - Tox.libtoxcore.tox_callback_group_message(self._tox_pointer, POINTER(None)()) - self.group_message_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_char_p, c_size_t, c_void_p) - self.group_message_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_message") - Tox.libtoxcore.tox_callback_group_message(self._tox_pointer, self.group_message_cb) - except Exception as e: - LOG_ERROR(f"tox_callback_group_message {e}") - - def callback_group_private_message(self, callback, user_data): - """ - Set the callback for the `group_private_message` event. Pass NULL to unset. - This event is triggered when the client receives a private message. - """ - - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint8, c_char_p, c_size_t, c_void_p) - self.group_private_message_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_private_message") - Tox.libtoxcore.tox_callback_group_private_message(self._tox_pointer, self.group_private_message_cb) - except Exception as e: - LOG_ERROR(f"tox_callback_group_private_message {e}") # req - - def callback_group_custom_packet(self, callback, user_data): - """ - Set the callback for the `group_custom_packet` event. Pass NULL to unset. - - This event is triggered when the client receives a custom packet. - """ - - LOG_DEBUG(f"tox_callback_group_custom_packet") - if callback is None: - Tox.libtoxcore.tox_callback_group_custom_packet(self._tox_pointer, POINTER(None)()) - self.group_custom_packet_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, POINTER(c_uint8), c_void_p) - self.group_custom_packet_cb = c_callback(callback) - LOG_DEBUG(f"tox_callback_group_custom_packet") - Tox.libtoxcore.tox_callback_group_custom_packet(self._tox_pointer, self.group_custom_packet_cb) - - # ----------------------------------------------------------------------------------------------------------------- - # Group chat inviting and join/part events - # ----------------------------------------------------------------------------------------------------------------- - - def group_invite_friend(self, group_number, friend_number): - """ - Invite a friend to a group. - - This function creates an invite request packet and pushes it to the send queue. - - :param group_number: The group number of the group the message is intended for. - :param friend_number: The friend number of the friend the invite is intended for. - - :return True on success. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_invite_friend") - result = Tox.libtoxcore.tox_group_invite_friend(self._tox_pointer, group_number, c_uint32(friend_number), byref(error)) - if error.value: - s = sGetError(error.value, TOX_ERR_GROUP_INVITE_FRIEND) - LOG_ERROR(f"group_invite_friend {error.value} {s}") - raise ToxError(f"group_invite_friend {error.value} {s}") - return result - - # API change - this no longer exists -# @staticmethod -# def group_self_peer_info_new(): -# error = c_int() -# f = Tox.libtoxcore.tox_group_self_peer_info_new -# f.restype = POINTER(GroupChatSelfPeerInfo) -# result = f(byref(error)) -# return result - - # status should be dropped - def group_invite_accept(self, invite_data, friend_number, nick, status='', password=None): - """ - Accept an invite to a group chat that the client previously received from a friend. The invite - is only valid while the inviter is present in the group. - - :param invite_data: The invite data received from the `group_invite` event. - :param password: The password required to join the group. Set to NULL if no password is required. - :return the group_number on success, UINT32_MAX on failure. - """ - - error = c_int() - f = Tox.libtoxcore.tox_group_invite_accept - f.restype = c_uint32 - try: - nick = bytes(nick, 'utf-8') - except: - nick = b'' - try: - if password is not None: - password = bytes(password, 'utf-8') - except: - password = None - invite_data = invite_data or b'' - - if False: # API change - peer_info = self.group_self_peer_info_new() - peer_info.contents.nick = c_char_p(nick) - peer_info.contents.nick_length = len(nick) - peer_info.contents.user_status = status - LOG_INFO(f"group_invite_accept friend_number={friend_number} nick={nick} {invite_data}") - try: - assert type(invite_data) == bytes - result = f(self._tox_pointer, - c_uint32(friend_number), - invite_data, len(invite_data), - c_char_p(nick), len(nick), - c_char_p(password), len(password) if password is not None else 0, - byref(error)) - except Exception as e: - LOG_ERROR(f"group_invite_accept ERROR {e}") - raise ToxError(f"group_invite_accept ERROR {e}") - if error.value: - # The invite data is not in the expected format. - LOG_ERROR(f"group_invite_accept {TOX_ERR_GROUP_INVITE_ACCEPT[error.value]}") - raise ToxError(f"group_invite_accept {TOX_ERR_GROUP_INVITE_ACCEPT[error.value]} {error.value}") - return result - - def callback_group_invite(self, callback, user_data): - """ - Set the callback for the `group_invite` event. Pass NULL to unset. - - This event is triggered when the client receives a group invite from a friend. The client must store - invite_data which is used to join the group via tox_group_invite_accept. - - Callback: python function with params: - tox - Tox* - friend_number The friend number of the contact who sent the invite. - invite_data The invite data. - length The length of invite_data. - user_data - user data - """ - if callback is None: - Tox.libtoxcore.tox_callback_group_invite(self._tox_pointer, POINTER(None)()) - self.group_invite_cb = None - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, - POINTER(c_uint8), c_size_t, c_void_p) - self.group_invite_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_invite") - Tox.libtoxcore.tox_callback_group_invite(self._tox_pointer, self.group_invite_cb) - except Exception as e: - LOG_DEBUG(f"tox_callback_conference_invite") - - def callback_group_peer_join(self, callback, user_data): - """ - Set the callback for the `group_peer_join` event. Pass NULL to unset. - - This event is triggered when a peer other than self joins the group. - Callback: python function with params: - tox - Tox* - group_number - group number - peer_id - peer id - user_data - user data - """ - - if callback is None: - Tox.libtoxcore.tox_callback_group_peer_join(self._tox_pointer, POINTER(None)()) - self.group_peer_join_cb = None - return - - LOG_DEBUG(f"tox_callback_group_peer_join") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) - self.group_peer_join_cb = c_callback(callback) - try: - Tox.libtoxcore.tox_callback_group_peer_join(self._tox_pointer, self.group_peer_join_cb) - except Exception as e: - LOG_ERROR(f" {e}") # req - - def callback_group_peer_exit(self, callback, user_data): - """ - Set the callback for the `group_peer_exit` event. Pass NULL to unset. - - This event is triggered when a peer other than self exits the group. - """ - - if callback is None: - Tox.libtoxcore.tox_callback_group_peer_exit(self._tox_pointer, POINTER(None)()) - self.group_peer_exit_cb = None - return - - LOG_DEBUG(f"tox_callback_group_peer_exit") - c_callback = CFUNCTYPE(None, c_void_p, - c_uint32, # group_number, - c_uint32, # peer_id, - c_int, # exit_type - c_char_p, # name - c_size_t, # name length - c_char_p, # message - c_size_t, # message length - c_void_p) # user_data - self.group_peer_exit_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_peer_exit") - Tox.libtoxcore.tox_callback_group_peer_exit(self._tox_pointer, self.group_peer_exit_cb) - except Exception as e: - LOG_ERROR(f"tox_callback_group_peer_exit {e}") # req - else: - LOG_DEBUG(f"tox_callback_group_peer_exit") - - def callback_group_self_join(self, callback, user_data): - """ - Set the callback for the `group_self_join` event. Pass NULL to unset. - - This event is triggered when the client has successfully joined a group. Use this to initialize - any group information the client may need. - Callback: python fucntion with params: - tox - *Tox - group_number - group number - user_data - user data - """ - - if callback is None: - Tox.libtoxcore.tox_callback_group_self_join(self._tox_pointer, POINTER(None)()) - self.group_self_join_cb = None - return - - LOG_DEBUG(f"tox_callback_group_self_join") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_void_p) - self.group_self_join_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_self_join") - Tox.libtoxcore.tox_callback_group_self_join(self._tox_pointer, self.group_self_join_cb) - except Exception as e: - LOG_ERROR(f"tox_callback_group_self_join {e}") # req - else: - LOG_DEBUG(f"tox_callback_group_self_join") - - def callback_group_join_fail(self, callback, user_data): - """ - Set the callback for the `group_join_fail` event. Pass NULL to unset. - - This event is triggered when the client fails to join a group. - """ - - if callback is None: - Tox.libtoxcore.tox_callback_group_join_fail(self._tox_pointer, POINTER(None)()) - self.group_join_fail_cb = None - return - - LOG_DEBUG(f"tox_callback_group_join_fail") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_uint32, c_void_p) - self.group_join_fail_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_join_fail") - Tox.libtoxcore.tox_callback_group_join_fail(self._tox_pointer, self.group_join_fail_cb) - except Exception as e: - LOG_ERROR(f"tox_callback_group_join_fail {e}") # req - - # ----------------------------------------------------------------------------------------------------------------- - # Group chat founder controls (these only work for the group founder) - # ----------------------------------------------------------------------------------------------------------------- - - def group_founder_set_password(self, group_number, password): - """ - Set or unset the group password. - - This function sets the groups password, creates a new group shared state including the change, - and distributes it to the rest of the group. - - :param group_number: The group number of the group for which we wish to set the password. - :param password: The password we want to set. Set password to NULL to unset the password. - - :return True on success. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_founder_set_password") - result = Tox.libtoxcore.tox_group_founder_set_password(self._tox_pointer, group_number, password, - len(password), byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def group_founder_set_privacy_state(self, group_number, privacy_state): - """ - Set the group privacy state. - - This function sets the group's privacy state, creates a new group shared state - including the change, and distributes it to the rest of the group. - - If an attempt is made to set the privacy state to the same state that the group is already - in, the function call will be successful and no action will be taken. - - :param group_number: The group number of the group for which we wish to change the privacy state. - :param privacy_state: The privacy state we wish to set the group to. - - :return true on success. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_founder_set_privacy_state") - result = Tox.libtoxcore.tox_group_founder_set_privacy_state(self._tox_pointer, group_number, privacy_state, - byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def group_founder_set_peer_limit(self, group_number, max_peers): - """ - Set the group peer limit. - - This function sets a limit for the number of peers who may be in the group, creates a new - group shared state including the change, and distributes it to the rest of the group. - - :param group_number: The group number of the group for which we wish to set the peer limit. - :param max_peers: The maximum number of peers to allow in the group. - - :return True on success. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_founder_set_peer_limit") - result = Tox.libtoxcore.tox_group_founder_set_peer_limit(self._tox_pointer, - group_number, - max_peers, - byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - # ----------------------------------------------------------------------------------------------------------------- - # Group chat moderation - # ----------------------------------------------------------------------------------------------------------------- - - def group_mod_set_role(self, group_number, peer_id, role): - """ - Set a peer's role. - - This function will first remove the peer's previous role and then assign them a new role. - It will also send a packet to the rest of the group, requesting that they perform - the role reassignment. Note: peers cannot be set to the founder role. - - :param group_number: The group number of the group the in which you wish set the peer's role. - :param peer_id: The ID of the peer whose role you wish to set. - :param role: The role you wish to set the peer to. - - :return True on success. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_mod_set_role") - result = Tox.libtoxcore.tox_group_mod_set_role(self._tox_pointer, group_number, peer_id, role, byref(error)) - if error.value: - LOG_ERROR(f" {error.value}") - raise ToxError(f" {error.value}") - return result - - def callback_group_moderation(self, callback, user_data): - """ - Set the callback for the `group_moderation` event. Pass NULL to unset. - - This event is triggered when a moderator or founder executes a moderation event. - (tox_data->tox, group_number, source_peer_number, target_peer_number, - (Tox_Group_Mod_Event)mod_type, tox_data->user_data); - TOX_GROUP_MOD_EVENT = [0,1,2,3,4] TOX_GROUP_MOD_EVENT['MODERATOR'] - """ - -# LOG_DEBUG(f"callback_group_moderation") - if callback is None: - self.group_moderation_cb = None - LOG_DEBUG(f"tox_callback_group_moderation") - Tox.libtoxcore.tox_callback_group_moderation(self._tox_pointer, POINTER(None)()) - return - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_uint32, c_int, c_void_p) - self.group_moderation_cb = c_callback(callback) - try: - LOG_DEBUG(f"tox_callback_group_moderation") - Tox.libtoxcore.tox_callback_group_moderation(self._tox_pointer, self.group_moderation_cb) - except Exception as e: - LOG_ERROR(f"tox_callback_group_moderation {e}") # req - else: - LOG_DEBUG(f"tox_callback_group_moderation") - - def group_toggle_set_ignore(self, group_number, peer_id, ignore): - return group_set_ignore(self, group_number, peer_id, ignore) - - def group_set_ignore(self, group_number, peer_id, ignore): - """ - Ignore or unignore a peer. - - :param group_number: The group number of the group the in which you wish to ignore a peer. - :param peer_id: The ID of the peer who shall be ignored or unignored. - :param ignore: True to ignore the peer, false to unignore the peer. - - :return True on success. - """ - - error = c_int() - LOG_DEBUG(f"tox_group_set_ignore") - result = Tox.libtoxcore.tox_group_set_ignore(self._tox_pointer, group_number, peer_id, ignore, byref(error)) - if error.value: - LOG_ERROR(f"tox_group_set_ignore {error.value}") - raise ToxError("tox_group_set_ignore {error.value}") - return result diff --git a/toxygen/wrapper/toxav.py b/toxygen/wrapper/toxav.py deleted file mode 100644 index 5480244..0000000 --- a/toxygen/wrapper/toxav.py +++ /dev/null @@ -1,403 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- - -from ctypes import (CFUNCTYPE, POINTER, ArgumentError, byref, c_bool, c_char_p, - c_int, c_int32, c_size_t, c_uint8, c_uint16, c_uint32, - c_void_p, cast) - -from wrapper.libtox import LibToxAV -from wrapper.toxav_enums import * - - -def LOG_ERROR(a): print('EROR> '+a) -def LOG_WARN(a): print('WARN> '+a) -def LOG_INFO(a): print('INFO> '+a) -def LOG_DEBUG(a): print('DBUG> '+a) -def LOG_TRACE(a): pass # print('DEBUGx: '+a) - -class ToxAV: - """ - The ToxAV instance type. Each ToxAV instance can be bound to only one Tox instance, and Tox instance can have only - one ToxAV instance. One must make sure to close ToxAV instance prior closing Tox instance otherwise undefined - behaviour occurs. Upon closing of ToxAV instance, all active calls will be forcibly terminated without notifying - peers. - """ - - # ----------------------------------------------------------------------------------------------------------------- - # Creation and destruction - # ----------------------------------------------------------------------------------------------------------------- - - def __init__(self, tox_pointer): - """ - Start new A/V session. There can only be only one session per Tox instance. - - :param tox_pointer: pointer to Tox instance - """ - self.libtoxav = LibToxAV() - toxav_err_new = c_int() - f = self.libtoxav.toxav_new - f.restype = POINTER(c_void_p) - self._toxav_pointer = f(tox_pointer, byref(toxav_err_new)) - toxav_err_new = toxav_err_new.value - if toxav_err_new == TOXAV_ERR_NEW['NULL']: - raise ArgumentError('One of the arguments to the function was NULL when it was not expected.') - elif toxav_err_new == TOXAV_ERR_NEW['MALLOC']: - raise MemoryError('Memory allocation failure while trying to allocate structures required for the A/V ' - 'session.') - elif toxav_err_new == TOXAV_ERR_NEW['MULTIPLE']: - raise RuntimeError('Attempted to create a second session for the same Tox instance.') - - self.call_state_cb = None - self.audio_receive_frame_cb = None - self.video_receive_frame_cb = None - self.call_cb = None - - def kill(self): - """ - Releases all resources associated with the A/V session. - - If any calls were ongoing, these will be forcibly terminated without notifying peers. After calling this - function, no other functions may be called and the av pointer becomes invalid. - """ - self.libtoxav.toxav_kill(self._toxav_pointer) - - def get_tox_pointer(self): - """ - Returns the Tox instance the A/V object was created for. - - :return: pointer to the Tox instance - """ - self.libtoxav.toxav_get_tox.restype = POINTER(c_void_p) - return self.libtoxav.toxav_get_tox(self._toxav_pointer) - - # ----------------------------------------------------------------------------------------------------------------- - # A/V event loop - # ----------------------------------------------------------------------------------------------------------------- - - def iteration_interval(self): - """ - Returns the interval in milliseconds when the next toxav_iterate call should be. If no call is active at the - moment, this function returns 200. - - :return: interval in milliseconds - """ - return self.libtoxav.toxav_iteration_interval(self._toxav_pointer) - - def iterate(self): - """ - Main loop for the session. This function needs to be called in intervals of toxav_iteration_interval() - milliseconds. It is best called in the separate thread from tox_iterate. - """ - self.libtoxav.toxav_iterate(self._toxav_pointer) - - # ----------------------------------------------------------------------------------------------------------------- - # Call setup - # ----------------------------------------------------------------------------------------------------------------- - - def call(self, friend_number, audio_bit_rate, video_bit_rate): - """ - Call a friend. This will start ringing the friend. - - It is the client's responsibility to stop ringing after a certain timeout, if such behaviour is desired. If the - client does not stop ringing, the library will not stop until the friend is disconnected. Audio and video - receiving are both enabled by default. - - :param friend_number: The friend number of the friend that should be called. - :param audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending. - :param video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending. - :return: True on success. - """ - toxav_err_call = c_int() - LOG_DEBUG(f"toxav_call") - result = self.libtoxav.toxav_call(self._toxav_pointer, c_uint32(friend_number), c_uint32(audio_bit_rate), - c_uint32(video_bit_rate), byref(toxav_err_call)) - toxav_err_call = toxav_err_call.value - if toxav_err_call == TOXAV_ERR_CALL['OK']: - return bool(result) - elif toxav_err_call == TOXAV_ERR_CALL['MALLOC']: - raise MemoryError('A resource allocation error occurred while trying to create the structures required for ' - 'the call.') - elif toxav_err_call == TOXAV_ERR_CALL['SYNC']: - raise RuntimeError('Synchronization error occurred.') - elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_NOT_CONNECTED']: - raise ArgumentError('The friend was valid, but not currently connected.') - elif toxav_err_call == TOXAV_ERR_CALL['FRIEND_ALREADY_IN_CALL']: - raise ArgumentError('Attempted to call a friend while already in an audio or video call with them.') - elif toxav_err_call == TOXAV_ERR_CALL['INVALID_BIT_RATE']: - raise ArgumentError('Audio or video bit rate is invalid.') - - def callback_call(self, callback, user_data): - """ - Set the callback for the `call` event. Pass None to unset. - - :param callback: The function for the call callback. - - Should take pointer (c_void_p) to ToxAV object, - The friend number (c_uint32) from which the call is incoming. - True (c_bool) if friend is sending audio. - True (c_bool) if friend is sending video. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - if callback is None: - self.libtoxav.toxav_callback_call(self._toxav_pointer, POINTER(None)(), user_data) - self.call_cb = None - return - LOG_DEBUG(f"toxav_callback_call") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_bool, c_void_p) - self.call_cb = c_callback(callback) - self.libtoxav.toxav_callback_call(self._toxav_pointer, self.call_cb, user_data) - - def answer(self, friend_number, audio_bit_rate, video_bit_rate): - """ - Accept an incoming call. - - If answering fails for any reason, the call will still be pending and it is possible to try and answer it later. - Audio and video receiving are both enabled by default. - - :param friend_number: The friend number of the friend that is calling. - :param audio_bit_rate: Audio bit rate in Kb/sec. Set this to 0 to disable audio sending. - :param video_bit_rate: Video bit rate in Kb/sec. Set this to 0 to disable video sending. - :return: True on success. - """ - toxav_err_answer = c_int() - LOG_DEBUG(f"toxav_answer") - result = self.libtoxav.toxav_answer(self._toxav_pointer, c_uint32(friend_number), c_uint32(audio_bit_rate), - c_uint32(video_bit_rate), byref(toxav_err_answer)) - toxav_err_answer = toxav_err_answer.value - if toxav_err_answer == TOXAV_ERR_ANSWER['OK']: - return bool(result) - elif toxav_err_answer == TOXAV_ERR_ANSWER['SYNC']: - raise RuntimeError('Synchronization error occurred.') - elif toxav_err_answer == TOXAV_ERR_ANSWER['CODEC_INITIALIZATION']: - raise RuntimeError('Failed to initialize codecs for call session. Note that codec initiation will fail if ' - 'there is no receive callback registered for either audio or video.') - elif toxav_err_answer == TOXAV_ERR_ANSWER['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend number did not designate a valid friend.') - elif toxav_err_answer == TOXAV_ERR_ANSWER['FRIEND_NOT_CALLING']: - raise ArgumentError('The friend was valid, but they are not currently trying to initiate a call. This is ' - 'also returned if this client is already in a call with the friend.') - elif toxav_err_answer == TOXAV_ERR_ANSWER['INVALID_BIT_RATE']: - raise ArgumentError('Audio or video bit rate is invalid.') - - # ----------------------------------------------------------------------------------------------------------------- - # Call state graph - # ----------------------------------------------------------------------------------------------------------------- - - def callback_call_state(self, callback, user_data): - """ - Set the callback for the `call_state` event. Pass None to unset. - - :param callback: Python function. - The function for the call_state callback. - - Should take pointer (c_void_p) to ToxAV object, - The friend number (c_uint32) for which the call state changed. - The bitmask of the new call state which is guaranteed to be different than the previous state. The state is set - to 0 when the call is paused. The bitmask represents all the activities currently performed by the friend. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - if callback is None: - self.libtoxav.toxav_callback_call_state(self._toxav_pointer, POINTER(None)(), user_data) - self.call_state_cb = None - return - LOG_DEBUG(f"callback_call_state") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) - self.call_state_cb = c_callback(callback) - self.libtoxav.toxav_callback_call_state(self._toxav_pointer, self.call_state_cb, user_data) - - # ----------------------------------------------------------------------------------------------------------------- - # Call control - # ----------------------------------------------------------------------------------------------------------------- - - def call_control(self, friend_number, control): - """ - Sends a call control command to a friend. - - :param friend_number: The friend number of the friend this client is in a call with. - :param control: The control command to send. - :return: True on success. - """ - toxav_err_call_control = c_int() - LOG_DEBUG(f"call_control") - result = self.libtoxav.toxav_call_control(self._toxav_pointer, c_uint32(friend_number), c_int(control), - byref(toxav_err_call_control)) - toxav_err_call_control = toxav_err_call_control.value - if toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['OK']: - return bool(result) - elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['SYNC']: - raise RuntimeError('Synchronization error occurred.') - elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['FRIEND_NOT_IN_CALL']: - raise RuntimeError('This client is currently not in a call with the friend. Before the call is answered, ' - 'only CANCEL is a valid control.') - elif toxav_err_call_control == TOXAV_ERR_CALL_CONTROL['INVALID_TRANSITION']: - raise RuntimeError('Happens if user tried to pause an already paused call or if trying to resume a call ' - 'that is not paused.') - - # ----------------------------------------------------------------------------------------------------------------- - # TODO Controlling bit rates - # ----------------------------------------------------------------------------------------------------------------- - - # ----------------------------------------------------------------------------------------------------------------- - # A/V sending - # ----------------------------------------------------------------------------------------------------------------- - - def audio_send_frame(self, friend_number, pcm, sample_count, channels, sampling_rate): - """ - Send an audio frame to a friend. - - The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]... - Meaning: sample 1 for channel 1, sample 1 for channel 2, ... - For mono audio, this has no meaning, every sample is subsequent. For stereo, this means the expected format is - LRLRLR... with samples for left and right alternating. - - :param friend_number: The friend number of the friend to which to send an audio frame. - :param pcm: An array of audio samples. The size of this array must be sample_count * channels. - :param sample_count: Number of samples in this frame. Valid numbers here are - ((sample rate) * (audio length) / 1000), where audio length can be 2.5, 5, 10, 20, 40 or 60 milliseconds. - :param channels: Number of audio channels. Sulpported values are 1 and 2. - :param sampling_rate: Audio sampling rate used in this frame. Valid sampling rates are 8000, 12000, 16000, - 24000, or 48000. - """ - toxav_err_send_frame = c_int() - LOG_TRACE(f"toxav_audio_send_frame") - assert sampling_rate in [8000, 12000, 16000, 24000, 48000] - result = self.libtoxav.toxav_audio_send_frame(self._toxav_pointer, - c_uint32(friend_number), - cast(pcm, c_void_p), - c_size_t(sample_count), c_uint8(channels), - c_uint32(sampling_rate), byref(toxav_err_send_frame)) - toxav_err_send_frame = toxav_err_send_frame.value - if toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['OK']: - return bool(result) - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']: - raise ArgumentError('The samples data pointer was NULL.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']: - raise RuntimeError('This client is currently not in a call with the friend.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['SYNC']: - raise RuntimeError('Synchronization error occurred.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['INVALID']: - raise ArgumentError('One of the frame parameters was invalid. E.g. the resolution may be too small or too ' - 'large, or the audio sampling rate may be unsupported.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']: - raise RuntimeError('Either friend turned off audio or video receiving or we turned off sending for the said' - 'payload.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['RTP_FAILED']: - RuntimeError('Failed to push frame through rtp interface.') - - def video_send_frame(self, friend_number, width, height, y, u, v): - """ - Send a video frame to a friend. - - Y - plane should be of size: height * width - U - plane should be of size: (height/2) * (width/2) - V - plane should be of size: (height/2) * (width/2) - - :param friend_number: The friend number of the friend to which to send a video frame. - :param width: Width of the frame in pixels. - :param height: Height of the frame in pixels. - :param y: Y (Luminance) plane data. - :param u: U (Chroma) plane data. - :param v: V (Chroma) plane data. - """ - toxav_err_send_frame = c_int() - LOG_TRACE(f"toxav_video_send_frame") - result = self.libtoxav.toxav_video_send_frame(self._toxav_pointer, c_uint32(friend_number), c_uint16(width), - c_uint16(height), c_char_p(y), c_char_p(u), c_char_p(v), - byref(toxav_err_send_frame)) - toxav_err_send_frame = toxav_err_send_frame.value - if toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['OK']: - return bool(result) - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['NULL']: - raise ArgumentError('One of Y, U, or V was NULL.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_FOUND']: - raise ArgumentError('The friend_number passed did not designate a valid friend.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['FRIEND_NOT_IN_CALL']: - raise RuntimeError('This client is currently not in a call with the friend.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['SYNC']: - raise RuntimeError('Synchronization error occurred.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['INVALID']: - raise ArgumentError('One of the frame parameters was invalid. E.g. the resolution may be too small or too ' - 'large, or the audio sampling rate may be unsupported.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['PAYLOAD_TYPE_DISABLED']: - raise RuntimeError('Either friend turned off audio or video receiving or we turned off sending for the said' - 'payload.') - elif toxav_err_send_frame == TOXAV_ERR_SEND_FRAME['RTP_FAILED']: - RuntimeError('Failed to push frame through rtp interface.') - - # ----------------------------------------------------------------------------------------------------------------- - # A/V receiving - # ----------------------------------------------------------------------------------------------------------------- - - def callback_audio_receive_frame(self, callback, user_data): - """ - Set the callback for the `audio_receive_frame` event. Pass None to unset. - - :param callback: Python function. - Function for the audio_receive_frame callback. The callback can be called multiple times per single - iteration depending on the amount of queued frames in the buffer. The received format is the same as in send - function. - - Should take pointer (c_void_p) to ToxAV object, - The friend number (c_uint32) of the friend who sent an audio frame. - An array (c_uint8) of audio samples (sample_count * channels elements). - The number (c_size_t) of audio samples per channel in the PCM array. - Number (c_uint8) of audio channels. - Sampling rate (c_uint32) used in this frame. - pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - if callback is None: - self.libtoxav.toxav_callback_audio_receive_frame(self._toxav_pointer, POINTER(None)(), user_data) - self.audio_receive_frame_cb = None - return - LOG_DEBUG(f"toxav_callback_audio_receive_frame") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_uint8, c_uint32, c_void_p) - self.audio_receive_frame_cb = c_callback(callback) - self.libtoxav.toxav_callback_audio_receive_frame(self._toxav_pointer, self.audio_receive_frame_cb, user_data) - - def callback_video_receive_frame(self, callback, user_data): - """ - Set the callback for the `video_receive_frame` event. Pass None to unset. - - :param callback: Python function. - The function type for the video_receive_frame callback. - - Should take - toxAV pointer (c_void_p) to ToxAV object, - friend_number The friend number (c_uint32) of the friend who sent a video frame. - width Width (c_uint16) of the frame in pixels. - height Height (c_uint16) of the frame in pixels. - y - u - v Plane data (POINTER(c_uint8)). - The size of plane data is derived from width and height where - Y = MAX(width, abs(ystride)) * height, - U = MAX(width/2, abs(ustride)) * (height/2) and - V = MAX(width/2, abs(vstride)) * (height/2). - ystride - ustride - vstride Strides data (c_int32). Strides represent padding for each plane that may or may not be present. You must - handle strides in your image processing code. Strides are negative if the image is bottom-up - hence why you MUST abs() it when calculating plane buffer size. - user_data pointer (c_void_p) to user_data - :param user_data: pointer (c_void_p) to user data - """ - if callback is None: - self.libtoxav.toxav_callback_video_receive_frame(self._toxav_pointer, POINTER(None)(), user_data) - self.video_receive_frame_cb = None - return - - LOG_DEBUG(f"toxav_callback_video_receive_frame") - c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint16, c_uint16, - POINTER(c_uint8), POINTER(c_uint8), POINTER(c_uint8), - c_int32, c_int32, c_int32, - c_void_p) - self.video_receive_frame_cb = c_callback(callback) - self.libtoxav.toxav_callback_video_receive_frame(self._toxav_pointer, self.video_receive_frame_cb, user_data) diff --git a/toxygen/wrapper/toxav_enums.py b/toxygen/wrapper/toxav_enums.py deleted file mode 100644 index f8817e1..0000000 --- a/toxygen/wrapper/toxav_enums.py +++ /dev/null @@ -1,133 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- - -TOXAV_ERR_NEW = { - # The function returned successfully. - 'OK': 0, - # One of the arguments to the function was NULL when it was not expected. - 'NULL': 1, - # Memory allocation failure while trying to allocate structures required for the A/V session. - 'MALLOC': 2, - # Attempted to create a second session for the same Tox instance. - 'MULTIPLE': 3, -} - -TOXAV_ERR_CALL = { - # The function returned successfully. - 'OK': 0, - # A resource allocation error occurred while trying to create the structures required for the call. - 'MALLOC': 1, - # Synchronization error occurred. - 'SYNC': 2, - # The friend number did not designate a valid friend. - 'FRIEND_NOT_FOUND': 3, - # The friend was valid, but not currently connected. - 'FRIEND_NOT_CONNECTED': 4, - # Attempted to call a friend while already in an audio or video call with them. - 'FRIEND_ALREADY_IN_CALL': 5, - # Audio or video bit rate is invalid. - 'INVALID_BIT_RATE': 6, -} - -TOXAV_ERR_ANSWER = { - # The function returned successfully. - 'OK': 0, - # Synchronization error occurred. - 'SYNC': 1, - # Failed to initialize codecs for call session. Note that codec initiation will fail if there is no receive callback - # registered for either audio or video. - 'CODEC_INITIALIZATION': 2, - # The friend number did not designate a valid friend. - 'FRIEND_NOT_FOUND': 3, - # The friend was valid, but they are not currently trying to initiate a call. This is also returned if this client - # is already in a call with the friend. - 'FRIEND_NOT_CALLING': 4, - # Audio or video bit rate is invalid. - 'INVALID_BIT_RATE': 5, -} - -TOXAV_FRIEND_CALL_STATE = { - # Set by the AV core if an error occurred on the remote end or if friend timed out. This is the final state after - # which no more state transitions can occur for the call. This call state will never be triggered in combination - # with other call states. - 'ERROR': 1, - # The call has finished. This is the final state after which no more state transitions can occur for the call. This - # call state will never be triggered in combination with other call states. - 'FINISHED': 2, - # The flag that marks that friend is sending audio. - 'SENDING_A': 4, - # The flag that marks that friend is sending video. - 'SENDING_V': 8, - # The flag that marks that friend is receiving audio. - 'ACCEPTING_A': 16, - # The flag that marks that friend is receiving video. - 'ACCEPTING_V': 32, -} - -TOXAV_CALL_CONTROL = { - # Resume a previously paused call. Only valid if the pause was caused by this client, if not, this control is - # ignored. Not valid before the call is accepted. - 'RESUME': 0, - # Put a call on hold. Not valid before the call is accepted. - 'PAUSE': 1, - # Reject a call if it was not answered, yet. Cancel a call after it was answered. - 'CANCEL': 2, - # Request that the friend stops sending audio. Regardless of the friend's compliance, this will cause the - # audio_receive_frame event to stop being triggered on receiving an audio frame from the friend. - 'MUTE_AUDIO': 3, - # Calling this control will notify client to start sending audio again. - 'UNMUTE_AUDIO': 4, - # Request that the friend stops sending video. Regardless of the friend's compliance, this will cause the - # video_receive_frame event to stop being triggered on receiving a video frame from the friend. - 'HIDE_VIDEO': 5, - # Calling this control will notify client to start sending video again. - 'SHOW_VIDEO': 6, -} - -TOXAV_ERR_CALL_CONTROL = { - # The function returned successfully. - 'OK': 0, - # Synchronization error occurred. - 'SYNC': 1, - # The friend_number passed did not designate a valid friend. - 'FRIEND_NOT_FOUND': 2, - # This client is currently not in a call with the friend. Before the call is answered, only CANCEL is a valid - # control. - 'FRIEND_NOT_IN_CALL': 3, - # Happens if user tried to pause an already paused call or if trying to resume a call that is not paused. - 'INVALID_TRANSITION': 4, -} - -TOXAV_ERR_BIT_RATE_SET = { - # The function returned successfully. - 'OK': 0, - # Synchronization error occurred. - 'SYNC': 1, - # The audio bit rate passed was not one of the supported values. - 'INVALID_AUDIO_BIT_RATE': 2, - # The video bit rate passed was not one of the supported values. - 'INVALID_VIDEO_BIT_RATE': 3, - # The friend_number passed did not designate a valid friend. - 'FRIEND_NOT_FOUND': 4, - # This client is currently not in a call with the friend. - 'FRIEND_NOT_IN_CALL': 5, -} - -TOXAV_ERR_SEND_FRAME = { - # The function returned successfully. - 'OK': 0, - # In case of video, one of Y, U, or V was NULL. In case of audio, the samples data pointer was NULL. - 'NULL': 1, - # The friend_number passed did not designate a valid friend. - 'FRIEND_NOT_FOUND': 2, - # This client is currently not in a call with the friend. - 'FRIEND_NOT_IN_CALL': 3, - # Synchronization error occurred. - 'SYNC': 4, - # One of the frame parameters was invalid. E.g. the resolution may be too small or too large, or the audio sampling - # rate may be unsupported. - 'INVALID': 5, - # Either friend turned off audio or video receiving or we turned off sending for the said payload. - 'PAYLOAD_TYPE_DISABLED': 6, - # Failed to push frame through rtp interface. - 'RTP_FAILED': 7, -} diff --git a/toxygen/wrapper/toxcore_enums_and_consts.py b/toxygen/wrapper/toxcore_enums_and_consts.py deleted file mode 100644 index d2f03e8..0000000 --- a/toxygen/wrapper/toxcore_enums_and_consts.py +++ /dev/null @@ -1,957 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- - -TOX_USER_STATUS = { - 'NONE': 0, - 'AWAY': 1, - 'BUSY': 2, -} - -TOX_MESSAGE_TYPE = { - 'NORMAL': 0, - 'ACTION': 1, -} - -TOX_PROXY_TYPE = { - 'NONE': 0, - 'HTTP': 1, - 'SOCKS5': 2, -} - -TOX_SAVEDATA_TYPE = { - 'NONE': 0, - 'TOX_SAVE': 1, - 'SECRET_KEY': 2, -} - -TOX_ERR_OPTIONS_NEW = { - 'OK': 0, - 'MALLOC': 1, -} - -TOX_ERR_NEW = { - 'OK': 0, - 'NULL': 1, - 'MALLOC': 2, - 'PORT_ALLOC': 3, - 'PROXY_BAD_TYPE': 4, - 'PROXY_BAD_HOST': 5, - 'PROXY_BAD_PORT': 6, - 'PROXY_NOT_FOUND': 7, - 'LOAD_ENCRYPTED': 8, - 'LOAD_BAD_FORMAT': 9, - 'TCP_SERVER_ALLOC': 10, -} - -TOX_ERR_BOOTSTRAP = { - 'OK': 0, - 'NULL': 1, - 'BAD_HOST': 2, - 'BAD_PORT': 3, -} - -TOX_CONNECTION = { - 'NONE': 0, - 'TCP': 1, - 'UDP': 2, -} - -TOX_ERR_SET_INFO = { - 'OK': 0, - 'NULL': 1, - 'TOO_LONG': 2, -} - -TOX_ERR_FRIEND_ADD = { - 'OK': 0, - 'NULL': 1, - 'TOO_LONG': 2, - 'NO_MESSAGE': 3, - 'OWN_KEY': 4, - 'ALREADY_SENT': 5, - 'BAD_CHECKSUM': 6, - 'SET_NEW_NOSPAM': 7, - 'MALLOC': 8, -} - -TOX_ERR_FRIEND_DELETE = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_BY_PUBLIC_KEY = { - 'OK': 0, - 'NULL': 1, - 'NOT_FOUND': 2, -} - -TOX_ERR_FRIEND_GET_PUBLIC_KEY = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_GET_LAST_ONLINE = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_QUERY = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, -} - -TOX_ERR_SET_TYPING = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, -} - -TOX_ERR_FRIEND_SEND_MESSAGE = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'SENDQ': 4, - 'TOO_LONG': 5, - 'EMPTY': 6, -} - -TOX_FILE_KIND = { - 'DATA': 0, - 'AVATAR': 1, -} - -TOX_FILE_CONTROL = { - 'RESUME': 0, - 'PAUSE': 1, - 'CANCEL': 2, -} - -TOX_ERR_FILE_CONTROL = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, - 'FRIEND_NOT_CONNECTED': 2, - 'NOT_FOUND': 3, - 'NOT_PAUSED': 4, - 'DENIED': 5, - 'ALREADY_PAUSED': 6, - 'SENDQ': 7, -} - -TOX_ERR_FILE_SEEK = { - 'OK': 0, - 'FRIEND_NOT_FOUND': 1, - 'FRIEND_NOT_CONNECTED': 2, - 'NOT_FOUND': 3, - 'DENIED': 4, - 'INVALID_POSITION': 5, - 'SENDQ': 6, -} - -TOX_ERR_FILE_GET = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'NOT_FOUND': 3, -} - -TOX_ERR_FILE_SEND = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'NAME_TOO_LONG': 4, - 'TOO_MANY': 5, -} - -TOX_ERR_FILE_SEND_CHUNK = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'NOT_FOUND': 4, - 'NOT_TRANSFERRING': 5, - 'INVALID_LENGTH': 6, - 'SENDQ': 7, - 'WRONG_POSITION': 8, -} - -TOX_ERR_FRIEND_CUSTOM_PACKET = { - 'OK': 0, - 'NULL': 1, - 'FRIEND_NOT_FOUND': 2, - 'FRIEND_NOT_CONNECTED': 3, - 'INVALID': 4, - 'EMPTY': 5, - 'TOO_LONG': 6, - 'SENDQ': 7, -} - -TOX_ERR_GET_PORT = { - 'OK': 0, - 'NOT_BOUND': 1, -} - -TOX_GROUP_PRIVACY_STATE = { - - # - # The group is considered to be public. Anyone may join the group using the Chat ID. - # - # If the group is in this state, even if the Chat ID is never explicitly shared - # with someone outside of the group, information including the Chat ID, IP addresses, - # and peer ID's (but not Tox ID's) is visible to anyone with access to a node - # storing a DHT entry for the given group. - # - 'PUBLIC': 0, - - # - # The group is considered to be private. The only way to join the group is by having - # someone in your contact list send you an invite. - # - # If the group is in this state, no group information (mentioned above) is present in the DHT; - # the DHT is not used for any purpose at all. If a public group is set to private, - # all DHT information related to the group will expire shortly. - # - 'PRIVATE': 1 -} - -TOX_GROUP_ROLE = { - - # - # May kick and ban all other peers as well as set their role to anything (except founder). - # Founders may also set the group password, toggle the privacy state, and set the peer limit. - # - 'FOUNDER': 0, - - # - # May kick, ban and set the user and observer roles for peers below this role. - # May also set the group topic. - # - 'MODERATOR': 1, - - # - # May communicate with other peers normally. - # - 'USER': 2, - - # - # May observe the group and ignore peers; may not communicate with other peers or with the group. - # - 'OBSERVER': 3 -} - -TOX_ERR_GROUP_NEW = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_NEW_OK': 0, - - # - # The group name exceeded TOX_GROUP_MAX_GROUP_NAME_LENGTH. - # - 'TOX_ERR_GROUP_NEW_TOO_LONG': 1, - - # - # group_name is NULL or length is zero. - # - 'TOX_ERR_GROUP_NEW_EMPTY': 2, - - # - # TOX_GROUP_PRIVACY_STATE is an invalid type. - # - 'TOX_ERR_GROUP_NEW_PRIVACY': 3, - - # - # The group instance failed to initialize. - # - 'TOX_ERR_GROUP_NEW_INIT': 4, - - # - # The group state failed to initialize. This usually indicates that something went wrong - # related to cryptographic signing. - # - 'TOX_ERR_GROUP_NEW_STATE': 5, - - # - # The group failed to announce to the DHT. This indicates a network related error. - # - 'TOX_ERR_GROUP_NEW_ANNOUNCE': 6, -} - -TOX_ERR_GROUP_JOIN = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_JOIN_OK': 0, - - # - # The group instance failed to initialize. - # - 'TOX_ERR_GROUP_JOIN_INIT': 1, - - # - # The chat_id pointer is set to NULL or a group with chat_id already exists. This usually - # happens if the client attempts to create multiple sessions for the same group. - # - 'TOX_ERR_GROUP_JOIN_BAD_CHAT_ID': 2, - - # - # Password length exceeded TOX_GROUP_MAX_PASSWORD_SIZE. - # - 'TOX_ERR_GROUP_JOIN_TOO_LONG': 3, -} - -TOX_ERR_GROUP_RECONNECT = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_RECONNECT_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_RECONNECT_GROUP_NOT_FOUND': 1, -} - -TOX_ERR_GROUP_LEAVE = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_LEAVE_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_LEAVE_GROUP_NOT_FOUND': 1, - - # - # Message length exceeded 'TOX_GROUP_MAX_PART_LENGTH. - # - 'TOX_ERR_GROUP_LEAVE_TOO_LONG': 2, - - # - # The parting packet failed to send. - # - 'TOX_ERR_GROUP_LEAVE_FAIL_SEND': 3, - - # - # The group chat instance failed to be deleted. This may occur due to memory related errors. - # - 'TOX_ERR_GROUP_LEAVE_DELETE_FAIL': 4, -} - -TOX_ERR_GROUP_SELF_QUERY = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_SELF_QUERY_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_SELF_QUERY_GROUP_NOT_FOUND': 1, -} - - -TOX_ERR_GROUP_SELF_NAME_SET = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_SELF_NAME_SET_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_SELF_NAME_SET_GROUP_NOT_FOUND': 1, - - # - # Name length exceeded 'TOX_MAX_NAME_LENGTH. - # - 'TOX_ERR_GROUP_SELF_NAME_SET_TOO_LONG': 2, - - # - # The length given to the set function is zero or name is a NULL pointer. - # - 'TOX_ERR_GROUP_SELF_NAME_SET_INVALID': 3, - - # - # The name is already taken by another peer in the group. - # - 'TOX_ERR_GROUP_SELF_NAME_SET_TAKEN': 4, - - # - # The packet failed to send. - # - 'TOX_ERR_GROUP_SELF_NAME_SET_FAIL_SEND': 5 -} - -TOX_ERR_GROUP_SELF_STATUS_SET = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_SELF_STATUS_SET_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_SELF_STATUS_SET_GROUP_NOT_FOUND': 1, - - # - # An invalid type was passed to the set function. - # - 'TOX_ERR_GROUP_SELF_STATUS_SET_INVALID': 2, - - # - # The packet failed to send. - # - 'TOX_ERR_GROUP_SELF_STATUS_SET_FAIL_SEND': 3 -} - -TOX_ERR_GROUP_PEER_QUERY = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_PEER_QUERY_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_PEER_QUERY_GROUP_NOT_FOUND': 1, - - # - # The ID passed did not designate a valid peer. - # - 'TOX_ERR_GROUP_PEER_QUERY_PEER_NOT_FOUND': 2 -} - -TOX_ERR_GROUP_STATE_QUERIES = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_STATE_QUERIES_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_STATE_QUERIES_GROUP_NOT_FOUND': 1 -} - - -TOX_ERR_GROUP_TOPIC_SET = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_TOPIC_SET_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_TOPIC_SET_GROUP_NOT_FOUND': 1, - - # - # Topic length exceeded 'TOX_GROUP_MAX_TOPIC_LENGTH. - # - 'TOX_ERR_GROUP_TOPIC_SET_TOO_LONG': 2, - - # - # The caller does not have the required permissions to set the topic. - # - 'TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS': 3, - - # - # The packet could not be created. This error is usually related to cryptographic signing. - # - 'TOX_ERR_GROUP_TOPIC_SET_FAIL_CREATE': 4, - - # - # The packet failed to send. - # - 'TOX_ERR_GROUP_TOPIC_SET_FAIL_SEND': 5 -} - -TOX_ERR_GROUP_SEND_MESSAGE = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_SEND_MESSAGE_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_SEND_MESSAGE_GROUP_NOT_FOUND': 1, - - # - # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. - # - 'TOX_ERR_GROUP_SEND_MESSAGE_TOO_LONG': 2, - - # - # The message pointer is null or length is zero. - # - 'TOX_ERR_GROUP_SEND_MESSAGE_EMPTY': 3, - - # - # The message type is invalid. - # - 'TOX_ERR_GROUP_SEND_MESSAGE_BAD_TYPE': 4, - - # - # The caller does not have the required permissions to send group messages. - # - 'TOX_ERR_GROUP_SEND_MESSAGE_PERMISSIONS': 5, - - # - # Packet failed to send. - # - 'TOX_ERR_GROUP_SEND_MESSAGE_FAIL_SEND': 6 -} - -TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_GROUP_NOT_FOUND': 1, - - # - # The ID passed did not designate a valid peer. - # - 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PEER_NOT_FOUND': 2, - - # - # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. - # - 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_TOO_LONG': 3, - - # - # The message pointer is null or length is zero. - # - 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY': 4, - - # - # The caller does not have the required permissions to send group messages. - # - 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS': 5, - - # - # Packet failed to send. - # - 'TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_FAIL_SEND': 6 -} - -TOX_ERR_GROUP_SEND_CUSTOM_PACKET = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_GROUP_NOT_FOUND': 1, - - # - # Message length exceeded 'TOX_MAX_MESSAGE_LENGTH. - # - 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_TOO_LONG': 2, - - # - # The message pointer is null or length is zero. - # - 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_EMPTY': 3, - - # - # The caller does not have the required permissions to send group messages. - # - 'TOX_ERR_GROUP_SEND_CUSTOM_PACKET_PERMISSIONS': 4 -} - -TOX_ERR_GROUP_INVITE_FRIEND = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_INVITE_FRIEND_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_INVITE_FRIEND_GROUP_NOT_FOUND': 1, - - # - # The friend number passed did not designate a valid friend. - # - 'TOX_ERR_GROUP_INVITE_FRIEND_FRIEND_NOT_FOUND': 2, - - # - # Creation of the invite packet failed. This indicates a network related error. - # - 'TOX_ERR_GROUP_INVITE_FRIEND_INVITE_FAIL': 3, - - # - # Packet failed to send. - # - 'TOX_ERR_GROUP_INVITE_FRIEND_FAIL_SEND': 4 -} - -TOX_ERR_GROUP_INVITE_ACCEPT = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_INVITE_ACCEPT_OK': 0, - - # - # The invite data is not in the expected format. - # - 'TOX_ERR_GROUP_INVITE_ACCEPT_BAD_INVITE': 1, - - # - # The group instance failed to initialize. - # - 'TOX_ERR_GROUP_INVITE_ACCEPT_INIT_FAILED': 2, - - # - # Password length exceeded 'TOX_GROUP_MAX_PASSWORD_SIZE. - # - 'TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG': 3 -} - -TOX_GROUP_JOIN_FAIL = { - - # - # You are using the same nickname as someone who is already in the group. - # - 'TOX_GROUP_JOIN_FAIL_NAME_TAKEN': 0, - - # - # The group peer limit has been reached. - # - 'TOX_GROUP_JOIN_FAIL_PEER_LIMIT': 1, - - # - # You have supplied an invalid password. - # - 'TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD': 2, - - # - # The join attempt failed due to an unspecified error. This often occurs when the group is - # not found in the DHT. - # - 'TOX_GROUP_JOIN_FAIL_UNKNOWN': 3 -} - -TOX_ERR_GROUP_FOUNDER_SET_PASSWORD = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_GROUP_NOT_FOUND': 1, - - # - # The caller does not have the required permissions to set the password. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS': 2, - - # - # Password length exceeded 'TOX_GROUP_MAX_PASSWORD_SIZE. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG': 3, - - # - # The packet failed to send. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_FAIL_SEND': 4 -} - -TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_GROUP_NOT_FOUND': 1, - - # - # 'TOX_GROUP_PRIVACY_STATE is an invalid type. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_INVALID': 2, - - # - # The caller does not have the required permissions to set the privacy state. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS': 3, - - # - # The privacy state could not be set. This may occur due to an error related to - # cryptographic signing of the new shared state. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SET': 4, - - # - # The packet failed to send. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_FAIL_SEND': 5 -} - -TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_GROUP_NOT_FOUND': 1, - - # - # The caller does not have the required permissions to set the peer limit. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS': 2, - - # - # The peer limit could not be set. This may occur due to an error related to - # cryptographic signing of the new shared state. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SET': 3, - - # - # The packet failed to send. - # - 'TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_FAIL_SEND': 4 -} - -TOX_ERR_GROUP_TOGGLE_IGNORE = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_TOGGLE_IGNORE_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_TOGGLE_IGNORE_GROUP_NOT_FOUND': 1, - - # - # The ID passed did not designate a valid peer. - # - 'TOX_ERR_GROUP_TOGGLE_IGNORE_PEER_NOT_FOUND': 2 -} - -TOX_ERR_GROUP_MOD_SET_ROLE = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_MOD_SET_ROLE_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_MOD_SET_ROLE_GROUP_NOT_FOUND': 1, - - # - # The ID passed did not designate a valid peer. Note: you cannot set your own role. - # - 'TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND': 2, - - # - # The caller does not have the required permissions for this action. - # - 'TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS': 3, - - # - # The role assignment is invalid. This will occur if you try to set a peer's role to - # the role they already have. - # - 'TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT': 4, - - # - # The role was not successfully set. This may occur if something goes wrong with role setting': , - # or if the packet fails to send. - # - 'TOX_ERR_GROUP_MOD_SET_ROLE_FAIL_ACTION': 5 -} - -TOX_ERR_GROUP_MOD_REMOVE_PEER = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_MOD_REMOVE_PEER_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_MOD_REMOVE_PEER_GROUP_NOT_FOUND': 1, - - # - # The ID passed did not designate a valid peer. - # - 'TOX_ERR_GROUP_MOD_REMOVE_PEER_PEER_NOT_FOUND': 2, - - # - # The caller does not have the required permissions for this action. - # - 'TOX_ERR_GROUP_MOD_REMOVE_PEER_PERMISSIONS': 3, - - # - # The peer could not be removed from the group. - # - # If a ban was set': , this error indicates that the ban entry could not be created. - # This is usually due to the peer's IP address already occurring in the ban list. It may also - # be due to the entry containing invalid peer information': , or a failure to cryptographically - # authenticate the entry. - # - 'TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_ACTION': 4, - - # - # The packet failed to send. - # - 'TOX_ERR_GROUP_MOD_REMOVE_PEER_FAIL_SEND': 5 -} - -TOX_ERR_GROUP_MOD_REMOVE_BAN = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_MOD_REMOVE_BAN_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_MOD_REMOVE_BAN_GROUP_NOT_FOUND': 1, - - # - # The caller does not have the required permissions for this action. - # - 'TOX_ERR_GROUP_MOD_REMOVE_BAN_PERMISSIONS': 2, - - # - # The ban entry could not be removed. This may occur if ban_id does not designate - # a valid ban entry. - # - 'TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_ACTION': 3, - - # - # The packet failed to send. - # - 'TOX_ERR_GROUP_MOD_REMOVE_BAN_FAIL_SEND': 4 -} - -TOX_GROUP_MOD_EVENT = { - - # - # A peer has been kicked from the group. - # - 'KICK': 0, - - # - # A peer has been banned from the group. - # - 'BAN': 1, - - # - # A peer as been given the observer role. - # - 'OBSERVER': 2, - - # - # A peer has been given the user role. - # - 'USER': 3, - - # - # A peer has been given the moderator role. - # - 'MODERATOR': 4, -} - -TOX_ERR_GROUP_BAN_QUERY = { - - # - # The function returned successfully. - # - 'TOX_ERR_GROUP_BAN_QUERY_OK': 0, - - # - # The group number passed did not designate a valid group. - # - 'TOX_ERR_GROUP_BAN_QUERY_GROUP_NOT_FOUND': 1, - - # - # The ban_id does not designate a valid ban list entry. - # - 'TOX_ERR_GROUP_BAN_QUERY_BAD_ID': 2, -} - - -TOX_GROUP_BAN_TYPE = { - - 'IP_PORT': 0, - - 'PUBLIC_KEY': 1, - - 'NICK': 2 -} - -TOX_PUBLIC_KEY_SIZE = 32 - -TOX_ADDRESS_SIZE = TOX_PUBLIC_KEY_SIZE + 6 - -TOX_MAX_FRIEND_REQUEST_LENGTH = 1016 - -TOX_MAX_MESSAGE_LENGTH = 1372 - -TOX_GROUP_MAX_TOPIC_LENGTH = 512 - -TOX_GROUP_MAX_PART_LENGTH = 128 - -TOX_GROUP_MAX_GROUP_NAME_LENGTH = 48 - -TOX_GROUP_MAX_PASSWORD_SIZE = 32 - -TOX_GROUP_CHAT_ID_SIZE = 32 - -TOX_GROUP_PEER_PUBLIC_KEY_SIZE = 32 - -TOX_MAX_NAME_LENGTH = 128 - -TOX_MAX_STATUS_MESSAGE_LENGTH = 1007 - -TOX_SECRET_KEY_SIZE = 32 - -TOX_FILE_ID_LENGTH = 32 - -TOX_HASH_LENGTH = 32 - -TOX_MAX_CUSTOM_PACKET_SIZE = 1373 diff --git a/toxygen/wrapper/toxencryptsave.py b/toxygen/wrapper/toxencryptsave.py deleted file mode 100644 index 58e2a0f..0000000 --- a/toxygen/wrapper/toxencryptsave.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- - -try: - from wrapper import libtox - from wrapper.toxencryptsave_enums_and_consts import * -except: - import libtox - from toxencryptsave_enums_and_consts import * - -from ctypes import (ArgumentError, byref, c_bool, c_char_p, c_int, c_size_t, - create_string_buffer) - - -class ToxEncryptSave: - - def __init__(self): - self.libtoxencryptsave = libtox.LibToxEncryptSave() - - def is_data_encrypted(self, data): - """ - Checks if given data is encrypted - """ - func = self.libtoxencryptsave.tox_is_data_encrypted - func.restype = c_bool - result = func(c_char_p(bytes(data))) - return result - - def pass_encrypt(self, data, password): - """ - Encrypts the given data with the given password. - - :return: output array - """ - out = create_string_buffer(len(data) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH) - tox_err_encryption = c_int() - self.libtoxencryptsave.tox_pass_encrypt(c_char_p(data), - c_size_t(len(data)), - c_char_p(bytes(password, 'utf-8')), - c_size_t(len(password)), - out, - byref(tox_err_encryption)) - tox_err_encryption = tox_err_encryption.value - if tox_err_encryption == TOX_ERR_ENCRYPTION['OK']: - return out[:] - elif tox_err_encryption == TOX_ERR_ENCRYPTION['NULL']: - raise ArgumentError('Some input data, or maybe the output pointer, was null.') - elif tox_err_encryption == TOX_ERR_ENCRYPTION['KEY_DERIVATION_FAILED']: - raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a' - ' lack of memory issue. The functions accepting keys do not produce this error.') - elif tox_err_encryption == TOX_ERR_ENCRYPTION['FAILED']: - raise RuntimeError('The encryption itself failed.') - - def pass_decrypt(self, data, password): - """ - Decrypts the given data with the given password. - - :return: output array - """ - out = create_string_buffer(len(data) - TOX_PASS_ENCRYPTION_EXTRA_LENGTH) - tox_err_decryption = c_int() - self.libtoxencryptsave.tox_pass_decrypt(c_char_p(bytes(data)), - c_size_t(len(data)), - c_char_p(bytes(password, 'utf-8')), - c_size_t(len(password)), - out, - byref(tox_err_decryption)) - tox_err_decryption = tox_err_decryption.value - if tox_err_decryption == TOX_ERR_DECRYPTION['OK']: - return out[:] - elif tox_err_decryption == TOX_ERR_DECRYPTION['NULL']: - raise ArgumentError('Some input data, or maybe the output pointer, was null.') - elif tox_err_decryption == TOX_ERR_DECRYPTION['INVALID_LENGTH']: - raise ArgumentError('The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes') - elif tox_err_decryption == TOX_ERR_DECRYPTION['BAD_FORMAT']: - raise ArgumentError('The input data is missing the magic number (i.e. wasn\'t created by this module, or is' - ' corrupted)') - elif tox_err_decryption == TOX_ERR_DECRYPTION['KEY_DERIVATION_FAILED']: - raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a' - ' lack of memory issue. The functions accepting keys do not produce this error.') - elif tox_err_decryption == TOX_ERR_DECRYPTION['FAILED']: - raise RuntimeError('The encrypted byte array could not be decrypted. Either the data was corrupt or the ' - 'password/key was incorrect.') diff --git a/toxygen/wrapper/toxencryptsave_enums_and_consts.py b/toxygen/wrapper/toxencryptsave_enums_and_consts.py deleted file mode 100644 index cf795f8..0000000 --- a/toxygen/wrapper/toxencryptsave_enums_and_consts.py +++ /dev/null @@ -1,29 +0,0 @@ -TOX_ERR_ENCRYPTION = { - # The function returned successfully. - 'OK': 0, - # Some input data, or maybe the output pointer, was null. - 'NULL': 1, - # The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The - # functions accepting keys do not produce this error. - 'KEY_DERIVATION_FAILED': 2, - # The encryption itself failed. - 'FAILED': 3 -} - -TOX_ERR_DECRYPTION = { - # The function returned successfully. - 'OK': 0, - # Some input data, or maybe the output pointer, was null. - 'NULL': 1, - # The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes - 'INVALID_LENGTH': 2, - # The input data is missing the magic number (i.e. wasn't created by this module, or is corrupted) - 'BAD_FORMAT': 3, - # The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The - # functions accepting keys do not produce this error. - 'KEY_DERIVATION_FAILED': 4, - # The encrypted byte array could not be decrypted. Either the data was corrupt or the password/key was incorrect. - 'FAILED': 5, -} - -TOX_PASS_ENCRYPTION_EXTRA_LENGTH = 80 diff --git a/toxygen/wrapper_tests/__init__.py b/toxygen/wrapper_tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/toxygen/wrapper_tests/socks.py b/toxygen/wrapper_tests/socks.py deleted file mode 100644 index 748fa8e..0000000 --- a/toxygen/wrapper_tests/socks.py +++ /dev/null @@ -1,391 +0,0 @@ -"""SocksiPy - Python SOCKS module. -Version 1.00 - -Copyright 2006 Dan-Haim. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -3. Neither the name of Dan Haim nor the names of his contributors may be used - to endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE. - - -This module provides a standard socket-like interface for Python -for tunneling connections through SOCKS proxies. - -""" - -""" - -Minor modifications made by Christopher Gilbert (http://motomastyle.com/) -for use in PyLoris (http://pyloris.sourceforge.net/) - -Minor modifications made by Mario Vilas (http://breakingcode.wordpress.com/) -mainly to merge bug fixes found in Sourceforge - -Minor modifications made by Eugene Dementiev (http://www.dementiev.eu/) - -""" - -import socket -import struct -import sys - -PROXY_TYPE_SOCKS4 = 1 -PROXY_TYPE_SOCKS5 = 2 -PROXY_TYPE_HTTP = 3 - -_defaultproxy = None -_orgsocket = socket.socket - -class ProxyError(Exception): pass -class GeneralProxyError(ProxyError): pass -class Socks5AuthError(ProxyError): pass -class Socks5Error(ProxyError): pass -class Socks4Error(ProxyError): pass -class HTTPError(ProxyError): pass - -_generalerrors = ("success", - "invalid data", - "not connected", - "not available", - "bad proxy type", - "bad input") - -_socks5errors = ("succeeded", - "general SOCKS server failure", - "connection not allowed by ruleset", - "Network unreachable", - "Host unreachable", - "Connection refused", - "TTL expired", - "Command not supported", - "Address type not supported", - "Unknown error") - -_socks5autherrors = ("succeeded", - "authentication is required", - "all offered authentication methods were rejected", - "unknown username or invalid password", - "unknown error") - -_socks4errors = ("request granted", - "request rejected or failed", - "request rejected because SOCKS server cannot connect to identd on the client", - "request rejected because the client program and identd report different user-ids", - "unknown error") - -def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None): - """setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) - Sets a default proxy which all further socksocket objects will use, - unless explicitly changed. - """ - global _defaultproxy - _defaultproxy = (proxytype, addr, port, rdns, username, password) - -def wrapmodule(module): - """wrapmodule(module) - Attempts to replace a module's socket library with a SOCKS socket. Must set - a default proxy using setdefaultproxy(...) first. - This will only work on modules that import socket directly into the namespace; - most of the Python Standard Library falls into this category. - """ - if _defaultproxy != None: - module.socket.socket = socksocket - else: - raise GeneralProxyError((4, "no proxy specified")) - -class socksocket(socket.socket): - """socksocket([family[, type[, proto]]]) -> socket object - Open a SOCKS enabled socket. The parameters are the same as - those of the standard socket init. In order for SOCKS to work, - you must specify family=AF_INET, type=SOCK_STREAM and proto=0. - """ - - def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None): - _orgsocket.__init__(self, family, type, proto, _sock) - if _defaultproxy != None: - self.__proxy = _defaultproxy - else: - self.__proxy = (None, None, None, None, None, None) - self.__proxysockname = None - self.__proxypeername = None - - def __recvall(self, count): - """__recvall(count) -> data - Receive EXACTLY the number of bytes requested from the socket. - Blocks until the required number of bytes have been received. - """ - data = self.recv(count) - while len(data) < count: - d = self.recv(count-len(data)) - if not d: raise GeneralProxyError((0, "connection closed unexpectedly")) - data = data + d - return data - - def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None): - """setproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) - Sets the proxy to be used. - proxytype - The type of the proxy to be used. Three types - are supported: PROXY_TYPE_SOCKS4 (including socks4a), - PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP - addr - The address of the server (IP or DNS). - port - The port of the server. Defaults to 1080 for SOCKS - servers and 8080 for HTTP proxy servers. - rdns - Should DNS queries be preformed on the remote side - (rather than the local side). The default is True. - Note: This has no effect with SOCKS4 servers. - username - Username to authenticate with to the server. - The default is no authentication. - password - Password to authenticate with to the server. - Only relevant when username is also provided. - """ - self.__proxy = (proxytype, addr, port, rdns, username, password) - - def __negotiatesocks5(self, destaddr, destport): - """__negotiatesocks5(self,destaddr,destport) - Negotiates a connection through a SOCKS5 server. - """ - # First we'll send the authentication packages we support. - if (self.__proxy[4]!=None) and (self.__proxy[5]!=None): - # The username/password details were supplied to the - # setproxy method so we support the USERNAME/PASSWORD - # authentication (in addition to the standard none). - self.sendall(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) - else: - # No username/password were entered, therefore we - # only support connections with no authentication. - self.sendall(struct.pack('BBB', 0x05, 0x01, 0x00)) - # We'll receive the server's response to determine which - # method was selected - chosenauth = self.__recvall(2) - if chosenauth[0:1] != chr(0x05).encode(): - self.close() - raise GeneralProxyError((1, _generalerrors[1])) - # Check the chosen authentication method - if chosenauth[1:2] == chr(0x00).encode(): - # No authentication is required - pass - elif chosenauth[1:2] == chr(0x02).encode(): - # Okay, we need to perform a basic username/password - # authentication. - self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5]) - authstat = self.__recvall(2) - if authstat[0:1] != chr(0x01).encode(): - # Bad response - self.close() - raise GeneralProxyError((1, _generalerrors[1])) - if authstat[1:2] != chr(0x00).encode(): - # Authentication failed - self.close() - raise Socks5AuthError((3, _socks5autherrors[3])) - # Authentication succeeded - else: - # Reaching here is always bad - self.close() - if chosenauth[1] == chr(0xFF).encode(): - raise Socks5AuthError((2, _socks5autherrors[2])) - else: - raise GeneralProxyError((1, _generalerrors[1])) - # Now we can request the actual connection - req = struct.pack('BBB', 0x05, 0x01, 0x00) - # If the given destination address is an IP address, we'll - # use the IPv4 address request even if remote resolving was specified. - try: - ipaddr = socket.inet_aton(destaddr) - req = req + chr(0x01).encode() + ipaddr - except socket.error: - # Well it's not an IP number, so it's probably a DNS name. - if self.__proxy[3]: - # Resolve remotely - ipaddr = None - if type(destaddr) != type(b''): # python3 - destaddr_bytes = destaddr.encode(encoding='idna') - else: - destaddr_bytes = destaddr - req = req + chr(0x03).encode() + chr(len(destaddr_bytes)).encode() + destaddr_bytes - else: - # Resolve locally - ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) - req = req + chr(0x01).encode() + ipaddr - req = req + struct.pack(">H", destport) - self.sendall(req) - # Get the response - resp = self.__recvall(4) - if resp[0:1] != chr(0x05).encode(): - self.close() - raise GeneralProxyError((1, _generalerrors[1])) - elif resp[1:2] != chr(0x00).encode(): - # Connection failed - self.close() - if ord(resp[1:2])<=8: - raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])])) - else: - raise Socks5Error((9, _socks5errors[9])) - # Get the bound address/port - elif resp[3:4] == chr(0x01).encode(): - boundaddr = self.__recvall(4) - elif resp[3:4] == chr(0x03).encode(): - resp = resp + self.recv(1) - boundaddr = self.__recvall(ord(resp[4:5])) - else: - self.close() - raise GeneralProxyError((1,_generalerrors[1])) - boundport = struct.unpack(">H", self.__recvall(2))[0] - self.__proxysockname = (boundaddr, boundport) - if ipaddr != None: - self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) - else: - self.__proxypeername = (destaddr, destport) - - def getproxysockname(self): - """getsockname() -> address info - Returns the bound IP address and port number at the proxy. - """ - return self.__proxysockname - - def getproxypeername(self): - """getproxypeername() -> address info - Returns the IP and port number of the proxy. - """ - return _orgsocket.getpeername(self) - - def getpeername(self): - """getpeername() -> address info - Returns the IP address and port number of the destination - machine (note: getproxypeername returns the proxy) - """ - return self.__proxypeername - - def __negotiatesocks4(self,destaddr,destport): - """__negotiatesocks4(self,destaddr,destport) - Negotiates a connection through a SOCKS4 server. - """ - # Check if the destination address provided is an IP address - rmtrslv = False - try: - ipaddr = socket.inet_aton(destaddr) - except socket.error: - # It's a DNS name. Check where it should be resolved. - if self.__proxy[3]: - ipaddr = struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01) - rmtrslv = True - else: - ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) - # Construct the request packet - req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr - # The username parameter is considered userid for SOCKS4 - if self.__proxy[4] != None: - req = req + self.__proxy[4] - req = req + chr(0x00).encode() - # DNS name if remote resolving is required - # NOTE: This is actually an extension to the SOCKS4 protocol - # called SOCKS4A and may not be supported in all cases. - if rmtrslv: - req = req + destaddr + chr(0x00).encode() - self.sendall(req) - # Get the response from the server - resp = self.__recvall(8) - if resp[0:1] != chr(0x00).encode(): - # Bad data - self.close() - raise GeneralProxyError((1,_generalerrors[1])) - if resp[1:2] != chr(0x5A).encode(): - # Server returned an error - self.close() - if ord(resp[1:2]) in (91, 92, 93): - self.close() - raise Socks4Error((ord(resp[1:2]), _socks4errors[ord(resp[1:2]) - 90])) - else: - raise Socks4Error((94, _socks4errors[4])) - # Get the bound address/port - self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0]) - if rmtrslv != None: - self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) - else: - self.__proxypeername = (destaddr, destport) - - def __negotiatehttp(self, destaddr, destport): - """__negotiatehttp(self,destaddr,destport) - Negotiates a connection through an HTTP server. - """ - # If we need to resolve locally, we do this now - if not self.__proxy[3]: - addr = socket.gethostbyname(destaddr) - else: - addr = destaddr - self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode()) - # We read the response until we get the string "\r\n\r\n" - resp = self.recv(1) - while resp.find("\r\n\r\n".encode()) == -1: - recv = self.recv(1) - if not recv: - raise GeneralProxyError((1, _generalerrors[1])) - resp = resp + recv - # We just need the first line to check if the connection - # was successful - statusline = resp.splitlines()[0].split(" ".encode(), 2) - if statusline[0] not in ("HTTP/1.0".encode(), "HTTP/1.1".encode()): - self.close() - raise GeneralProxyError((1, _generalerrors[1])) - try: - statuscode = int(statusline[1]) - except ValueError: - self.close() - raise GeneralProxyError((1, _generalerrors[1])) - if statuscode != 200: - self.close() - raise HTTPError((statuscode, statusline[2])) - self.__proxysockname = ("0.0.0.0", 0) - self.__proxypeername = (addr, destport) - - def connect(self, destpair): - """connect(self, despair) - Connects to the specified destination through a proxy. - destpar - A tuple of the IP/DNS address and the port number. - (identical to socket's connect). - To select the proxy server use setproxy(). - """ - # Do a minimal input check first - if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (type(destpair[0]) != type('')) or (type(destpair[1]) != int): - raise GeneralProxyError((5, _generalerrors[5])) - if self.__proxy[0] == PROXY_TYPE_SOCKS5: - if self.__proxy[2] != None: - portnum = int(self.__proxy[2]) - else: - portnum = 1080 - _orgsocket.connect(self, (self.__proxy[1], portnum)) - self.__negotiatesocks5(destpair[0], destpair[1]) - elif self.__proxy[0] == PROXY_TYPE_SOCKS4: - if self.__proxy[2] != None: - portnum = self.__proxy[2] - else: - portnum = 1080 - _orgsocket.connect(self,(self.__proxy[1], portnum)) - self.__negotiatesocks4(destpair[0], destpair[1]) - elif self.__proxy[0] == PROXY_TYPE_HTTP: - if self.__proxy[2] != None: - portnum = self.__proxy[2] - else: - portnum = 8080 - _orgsocket.connect(self,(self.__proxy[1], portnum)) - self.__negotiatehttp(destpair[0], destpair[1]) - elif self.__proxy[0] == None: - _orgsocket.connect(self, (destpair[0], destpair[1])) - else: - raise GeneralProxyError((4, _generalerrors[4])) diff --git a/toxygen/wrapper_tests/support_http.py b/toxygen/wrapper_tests/support_http.py deleted file mode 100644 index d60ccbf..0000000 --- a/toxygen/wrapper_tests/support_http.py +++ /dev/null @@ -1,164 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- - -import os -import sys -import logging -from io import BytesIO -import urllib -import traceback - -global LOG -LOG = logging.getLogger('app.'+'ts') - -try: - import pycurl -except ImportError: - pycurl = None -try: - import requests -except ImportError: - requests = None - -lNO_PROXY = ['localhost', '127.0.0.1'] -CONNECT_TIMEOUT = 20.0 - -def bAreWeConnected(): - # FixMe: Linux only - sFile = f"/proc/{os.getpid()}/net/route" - if not os.path.isfile(sFile): return None - i = 0 - for elt in open(sFile, "r").readlines(): - if elt.startswith('Iface'): continue - if elt.startswith('lo'): continue - i += 1 - return i > 0 - -def pick_up_proxy_from_environ(): - retval = dict() - if os.environ.get('socks_proxy', ''): - # socks_proxy takes precedence over https/http - proxy = os.environ.get('socks_proxy', '') - i = proxy.find('//') - if i >= 0: proxy = proxy[i+2:] - retval['proxy_host'] = proxy.split(':')[0] - retval['proxy_port'] = proxy.split(':')[-1] - retval['proxy_type'] = 2 - retval['udp_enabled'] = False - elif os.environ.get('https_proxy', ''): - # https takes precedence over http - proxy = os.environ.get('https_proxy', '') - i = proxy.find('//') - if i >= 0: proxy = proxy[i+2:] - retval['proxy_host'] = proxy.split(':')[0] - retval['proxy_port'] = proxy.split(':')[-1] - retval['proxy_type'] = 1 - retval['udp_enabled'] = False - elif os.environ.get('http_proxy', ''): - proxy = os.environ.get('http_proxy', '') - i = proxy.find('//') - if i >= 0: proxy = proxy[i+2:] - retval['proxy_host'] = proxy.split(':')[0] - retval['proxy_port'] = proxy.split(':')[-1] - retval['proxy_type'] = 1 - retval['udp_enabled'] = False - else: - retval['proxy_host'] = '' - retval['proxy_port'] = '' - retval['proxy_type'] = 0 - retval['udp_enabled'] = True - return retval - -def download_url(url, settings=None): - if not bAreWeConnected(): return '' - - if settings is None: - settings = pick_up_proxy_from_environ() - - if pycurl: - LOG.debug('Downloading with pycurl: ' + str(url)) - buffer = BytesIO() - c = pycurl.Curl() - c.setopt(c.URL, url) - c.setopt(c.WRITEDATA, buffer) - # Follow redirect. - c.setopt(c.FOLLOWLOCATION, True) - - # cookie jar - cjar = os.path.join(os.environ['HOME'], '.local', 'jar.cookie') - if os.path.isfile(cjar): - c.setopt(c.COOKIEFILE, cjar) - # LARGS+=( --cookie-jar --junk-session-cookies ) - - #? c.setopt(c.ALTSVC_CTRL, 16) - - c.setopt(c.NOPROXY, ','.join(lNO_PROXY)) - #? c.setopt(c.CAINFO, certifi.where()) - if settings['proxy_type'] == 2 and settings['proxy_host']: - socks_proxy = 'socks5h://'+settings['proxy_host']+':'+str(settings['proxy_port']) - settings['udp_enabled'] = False - c.setopt(c.PROXY, socks_proxy) - c.setopt(c.PROXYTYPE, pycurl.PROXYTYPE_SOCKS5_HOSTNAME) - elif settings['proxy_type'] == 1 and settings['proxy_host']: - https_proxy = 'https://'+settings['proxy_host']+':'+str(settings['proxy_port']) - c.setopt(c.PROXY, https_proxy) - elif settings['proxy_type'] == 1 and settings['proxy_host']: - http_proxy = 'http://'+settings['proxy_host']+':'+str(settings['proxy_port']) - c.setopt(c.PROXY, http_proxy) - c.setopt(c.PROTOCOLS, c.PROTO_HTTPS) - try: - c.perform() - c.close() - #? assert c.getinfo(c.RESPONSE_CODE) < 300 - result = buffer.getvalue() - # Body is a byte string. - LOG.info('nodes loaded with pycurl: ' + str(url)) - return result - except Exception as ex: - LOG.error('TOX Downloading error with pycurl: ' + str(ex)) - LOG.error('\n' + traceback.format_exc()) - # drop through - - if requests: - LOG.debug('Downloading with requests: ' + str(url)) - try: - headers = dict() - headers['Content-Type'] = 'application/json' - proxies = dict() - if settings['proxy_type'] == 2 and settings['proxy_host']: - socks_proxy = 'socks5://'+settings['proxy_host']+':'+str(settings['proxy_port']) - settings['udp_enabled'] = False - proxies['https'] = socks_proxy - elif settings['proxy_type'] == 1 and settings['proxy_host']: - https_proxy = 'https://'+settings['proxy_host']+':'+str(settings['proxy_port']) - proxies['https'] = https_proxy - elif settings['proxy_type'] == 1 and settings['proxy_host']: - http_proxy = 'http://'+settings['proxy_host']+':'+str(settings['proxy_port']) - proxies['http'] = http_proxy - req = requests.get(url, - headers=headers, - proxies=proxies, - timeout=CONNECT_TIMEOUT) - # max_retries=3 - assert req.status_code < 300 - result = req.content - LOG.info('nodes loaded with requests: ' + str(url)) - return result - except Exception as ex: - LOG.error('TOX Downloading error with requests: ' + str(ex)) - # drop through - - if not settings['proxy_type']: # no proxy - LOG.debug('Downloading with urllib no proxy: ' + str(url)) - try: - req = urllib.request.Request(url) - req.add_header('Content-Type', 'application/json') - response = urllib.request.urlopen(req) - result = response.read() - LOG.info('nodes loaded with no proxy: ' + str(url)) - return result - except Exception as ex: - LOG.error('TOX Downloading ' + str(ex)) - return '' - - return '' - diff --git a/toxygen/wrapper_tests/support_onions.py b/toxygen/wrapper_tests/support_onions.py deleted file mode 100644 index ba1d182..0000000 --- a/toxygen/wrapper_tests/support_onions.py +++ /dev/null @@ -1,572 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- - -import getpass -import os -import re -import select -import shutil -import socket -import sys -import time - -if False: - import cepa as stem - from cepa.connection import MissingPassword - from cepa.control import Controller - from cepa.util.tor_tools import is_valid_fingerprint -else: - import stem - from stem.connection import MissingPassword - from stem.control import Controller - from stem.util.tor_tools import is_valid_fingerprint - -global LOG -import logging -import warnings - -warnings.filterwarnings('ignore') -LOG = logging.getLogger() - -bHAVE_TORR = shutil.which('tor-resolve') - -yKNOWN_ONIONS = """ - - facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd # facebook - - duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad # ddg - - zkaan2xfbuxia2wpf7ofnkbz6r5zdbbvxbunvp5g2iebopbfc4iqmbad # hks -""" -# grep -B 1 '
  • 0 - -def sMapaddressResolv(target, iPort=9051, log_level=10): - if not stem: - LOG.warn('please install the stem Python package') - return '' - - try: - controller = oGetStemController(log_level=log_level) - - map_dict = {"0.0.0.0": target} - map_ret = controller.map_address(map_dict) - - return map_ret - except Exception as e: - LOG.exception(e) - return '' - -def vwait_for_controller(controller, wait_boot=10): - if bAreWeConnected() is False: - raise SystemExit("we are not connected") - percent = i = 0 - # You can call this while boostrapping - while percent < 100 and i < wait_boot: - bootstrap_status = controller.get_info("status/bootstrap-phase") - progress_percent = re.match('.* PROGRESS=([0-9]+).*', bootstrap_status) - percent = int(progress_percent.group(1)) - LOG.info(f"Bootstrapping {percent}%") - time.sleep(5) - i += 5 - -def bin_to_hex(raw_id, length=None): - if length is None: length = len(raw_id) - res = ''.join('{:02x}'.format(raw_id[i]) for i in range(length)) - return res.upper() - -def lIntroductionPoints(controller=None, lOnions=[], itimeout=120, log_level=10): - """now working !!! stem 1.8.x timeout must be huge >120 - 'Provides the descriptor for a hidden service. The **address** is the - '.onion' address of the hidden service ' - What about Services? - """ - try: - from cryptography.utils import int_from_bytes - except ImportError: - import cryptography.utils - - # guessing - not in the current cryptography but stem expects it - def int_from_bytes(**args): return int.to_bytes(*args) - cryptography.utils.int_from_bytes = int_from_bytes - # this will fai if the trick above didnt work - from stem.prereq import is_crypto_available - is_crypto_available(ed25519=True) - - from queue import Empty - - from stem import Timeout - from stem.client.datatype import LinkByFingerprint - from stem.descriptor.hidden_service import HiddenServiceDescriptorV3 - - if type(lOnions) not in [set, tuple, list]: - lOnions = list(lOnions) - if controller is None: - controller = oGetStemController(log_level=log_level) - l = [] - for elt in lOnions: - LOG.info(f"controller.get_hidden_service_descriptor {elt}") - try: - desc = controller.get_hidden_service_descriptor(elt, - await_result=True, - timeout=itimeout) - # LOG.log(40, f"{dir(desc)} get_hidden_service_descriptor") - # timeouts 20 sec - # mistakenly a HSv2 descriptor - hs_address = HiddenServiceDescriptorV3.from_str(str(desc)) # reparse as HSv3 - oInnerLayer = hs_address.decrypt(elt) - # LOG.log(40, f"{dir(oInnerLayer)}") - - # IntroductionPointV3 - n = oInnerLayer.introduction_points - if not n: - LOG.warn(f"NO introduction points for {elt}") - continue - LOG.info(f"{elt} {len(n)} introduction points") - lp = [] - for introduction_point in n: - for linkspecifier in introduction_point.link_specifiers: - if isinstance(linkspecifier, LinkByFingerprint): - # LOG.log(40, f"Getting fingerprint for {linkspecifier}") - if hasattr(linkspecifier, 'fingerprint'): - assert len(linkspecifier.value) == 20 - lp += [bin_to_hex(linkspecifier.value)] - LOG.info(f"{len(lp)} introduction points for {elt}") - l += lp - except (Empty, Timeout,) as e: # noqa - LOG.warn(f"Timed out getting introduction points for {elt}") - except stem.DescriptorUnavailable as e: - LOG.error(e) - except Exception as e: - LOG.exception(e) - return l - -def zResolveDomain(domain): - try: - ip = sTorResolve(domain) - except Exception as e: # noqa - ip = '' - if ip == '': - try: - lpair = getaddrinfo(domain, 443) - except Exception as e: - LOG.warn(f"{e}") - lpair = None - if lpair is None: - LOG.warn(f"TorResolv and getaddrinfo failed for {domain}") - return '' - ip = lpair[0] - return ip - -def sTorResolve(target, - verbose=False, - sHost='127.0.0.1', - iPort=9050, - SOCK_TIMEOUT_SECONDS=10.0, - SOCK_TIMEOUT_TRIES=3, - ): - MAX_INFO_RESPONSE_PACKET_LENGTH = 8 - if '@' in target: - LOG.warn(f"sTorResolve failed invalid hostname {target}") - return '' - target = target.strip('/') - seb = b"\x04\xf0\x00\x00\x00\x00\x00\x01\x00" - seb += bytes(target, 'US-ASCII') + b"\x00" - assert len(seb) == 10 + len(target), str(len(seb)) + repr(seb) - -# LOG.debug(f"0 Sending {len(seb)} to The TOR proxy {seb}") - - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.connect((sHost, iPort)) - - sock.settimeout(SOCK_TIMEOUT_SECONDS) - oRet = sock.sendall(seb) # noqa - - i = 0 - data = '' - while i < SOCK_TIMEOUT_TRIES: - i += 1 - time.sleep(3) - lReady = select.select([sock.fileno()], [], [], - SOCK_TIMEOUT_SECONDS) - if not lReady[0]: continue - try: - flags=socket.MSG_WAITALL - data = sock.recv(MAX_INFO_RESPONSE_PACKET_LENGTH, flags) - except socket.timeout: - LOG.warn(f"4 The TOR proxy {(sHost, iPort)}" \ - +" didnt reply in " + str(SOCK_TIMEOUT_SECONDS) + " sec." - +" #" +str(i)) - except Exception as e: - LOG.error("4 The TOR proxy " \ - +repr((sHost, iPort)) \ - +" errored with " + str(e) - +" #" +str(i)) - sock.close() - return '' - else: - if len(data) > 0: break - - if len(data) == 0: - if i > SOCK_TIMEOUT_TRIES: - sLabel = "5 No reply #" - else: - sLabel = "5 No data #" - LOG.warn(f"sTorResolve: {sLabel} {i} on {sHost}:{iPort}") - sock.close() - return '' - - assert len(data) >= 8 - packet_sf = data[1] - if packet_sf == 90: - # , "%d" % packet_sf - assert f"{packet_sf}" == "90", f"packet_sf = {packet_sf}" - return f"{data[4]}.{data[5]}.{data[6]}.{data[7]}" - else: - # 91 - LOG.warn(f"tor-resolve failed for {target} on {sHost}:{iPort}") - - os.system(f"tor-resolve -4 {target} > /tmp/e 2>/dev/null") -# os.system("strace tor-resolve -4 "+target+" 2>&1|grep '^sen\|^rec'") - - return '' - -def getaddrinfo(sHost, sPort): - # do this the explicit way = Ive seen the compact connect fail - # >>> sHost, sPort = 'l27.0.0.1', 33446 - # >>> sock.connect((sHost, sPort)) - # socket.gaierror: [Errno -2] Name or service not known - try: - lElts = socket.getaddrinfo(sHost, int(sPort), socket.AF_INET) - lElts = list(filter(lambda elt: elt[1] == socket.SOCK_DGRAM, lElts)) - assert len(lElts) == 1, repr(lElts) - lPair = lElts[0][-1] - assert len(lPair) == 2, repr(lPair) - assert type(lPair[1]) == int, repr(lPair) - except (socket.gaierror, OSError, BaseException) as e: - LOG.error(e) - return None - return lPair - -def icheck_torrc(sFile, oArgs): - l = open(sFile, 'rt').readlines() - a = {} - for elt in l: - elt = elt.strip() - if not elt or ' ' not in elt: continue - (k, v,) = elt.split(' ', 1) - a[k] = v - keys = a - - if 'HashedControlPassword' not in keys: - LOG.info('Add HashedControlPassword for security') - print('run: tor --hashcontrolpassword ') - if 'ExcludeExitNodes' in keys: - elt = 'BadNodes.ExcludeExitNodes.BadExit' - LOG.warn(f"Remove ExcludeNodes and move then to {oArgs.bad_nodes}") - print(f"move to the {elt} section as a list") - if 'GuardNodes' in keys: - elt = 'GoodNodes.GuardNodes' - LOG.warn(f"Remove GuardNodes and move then to {oArgs.good_nodes}") - print(f"move to the {elt} section as a list") - if 'ExcludeNodes' in keys: - elt = 'BadNodes.ExcludeNodes.BadExit' - LOG.warn(f"Remove ExcludeNodes and move then to {oArgs.bad_nodes}") - print(f"move to the {elt} section as a list") - if 'ControlSocket' not in keys and os.path.exists('/run/tor/control'): - LOG.info('Add ControlSocket /run/tor/control for us') - print('ControlSocket /run/tor/control GroupWritable RelaxDirModeCheck') - if 'UseMicrodescriptors' not in keys or keys['UseMicrodescriptors'] != '1': - LOG.info('Add UseMicrodescriptors 0 for us') - print('UseMicrodescriptors 0') - if 'AutomapHostsSuffixes' not in keys: - LOG.info('Add AutomapHostsSuffixes for onions') - print('AutomapHostsSuffixes .exit,.onion') - if 'AutoMapHostsOnResolve' not in keys: - LOG.info('Add AutoMapHostsOnResolve for onions') - print('AutoMapHostsOnResolve 1') - if 'VirtualAddrNetworkIPv4' not in keys: - LOG.info('Add VirtualAddrNetworkIPv4 for onions') - print('VirtualAddrNetworkIPv4 172.16.0.0/12') - return 0 - -def lExitExcluder(oArgs, iPort=9051, log_level=10): - """ - https://raw.githubusercontent.com/nusenu/noContactInfo_Exit_Excluder/main/exclude_noContactInfo_Exits.py - """ - if not stem: - LOG.warn('please install the stem Python package') - return '' - LOG.debug('lExcludeExitNodes') - - try: - controller = oGetStemController(log_level=log_level) - # generator - relays = controller.get_server_descriptors() - except Exception as e: - LOG.error(f'Failed to get relay descriptors {e}') - return None - - if controller.is_set('ExcludeExitNodes'): - LOG.info('ExcludeExitNodes is in use already.') - return None - - exit_excludelist=[] - LOG.debug("Excluded exit relays:") - for relay in relays: - if relay.exit_policy.is_exiting_allowed() and not relay.contact: - if is_valid_fingerprint(relay.fingerprint): - exit_excludelist.append(relay.fingerprint) - LOG.debug("https://metrics.torproject.org/rs.html#details/%s" % relay.fingerprint) - else: - LOG.warn('Invalid Fingerprint: %s' % relay.fingerprint) - - try: - controller.set_conf('ExcludeExitNodes', exit_excludelist) - LOG.info('Excluded a total of %s exit relays without ContactInfo from the exit position.' % len(exit_excludelist)) - except Exception as e: - LOG.exception('ExcludeExitNodes ' +str(e)) - return exit_excludelist - -if __name__ == '__main__': - target = 'duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad' - controller = oGetStemController(log_level=10) - lIntroductionPoints(controller, [target], itimeout=120) diff --git a/toxygen/wrapper_tests/support_testing.py b/toxygen/wrapper_tests/support_testing.py deleted file mode 100644 index 8e1ea48..0000000 --- a/toxygen/wrapper_tests/support_testing.py +++ /dev/null @@ -1,914 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- - -import argparse -import contextlib -import inspect -import json -import logging -import os -import re -import select -import shutil -import socket -import sys -import time -import traceback -import unittest -from ctypes import * -from random import Random -import functools - -random = Random() - -try: - import coloredlogs - if 'COLOREDLOGS_LEVEL_STYLES' not in os.environ: - os.environ['COLOREDLOGS_LEVEL_STYLES'] = 'spam=22;debug=28;verbose=34;notice=220;warning=202;success=118,bold;error=124;critical=background=red' - # https://pypi.org/project/coloredlogs/ -except ImportError as e: - coloredlogs = False -try: - import stem -except ImportError as e: - stem = False -try: - import nmap -except ImportError as e: - nmap = False - -import wrapper -from wrapper.toxcore_enums_and_consts import TOX_CONNECTION, TOX_USER_STATUS - -from wrapper_tests.support_http import bAreWeConnected -from wrapper_tests.support_onions import (is_valid_fingerprint, - lIntroductionPoints, - oGetStemController, - sMapaddressResolv, sTorResolve) - -try: - from user_data.settings import get_user_config_path -except ImportError: - get_user_config_path = None - -# LOG=util.log -global LOG -LOG = logging.getLogger() - -def LOG_ERROR(l): print('ERRORc: '+l) -def LOG_WARN(l): print('WARNc: ' +l) -def LOG_INFO(l): print('INFOc: ' +l) -def LOG_DEBUG(l): print('DEBUGc: '+l) -def LOG_TRACE(l): pass # print('TRACE+ '+l) - -try: - from trepan.api import debug - from trepan.interfaces import server as Mserver -except: -# print('trepan3 TCP server NOT available.') - pass -else: -# print('trepan3 TCP server available.') - def trepan_handler(num=None, f=None): - connection_opts={'IO': 'TCP', 'PORT': 6666} - intf = Mserver.ServerInterface(connection_opts=connection_opts) - dbg_opts = { 'interface': intf } - print(f'Starting TCP server listening on port 6666.') - debug(dbg_opts=dbg_opts) - return - -# self._audio_thread.isAlive -iTHREAD_TIMEOUT = 1 -iTHREAD_SLEEP = 1 -iTHREAD_JOINS = 8 -iNODES = 6 - -lToxSamplerates = [8000, 12000, 16000, 24000, 48000] -lToxSampleratesK = [8, 12, 16, 24, 48] -lBOOLEANS = [ - 'local_discovery_enabled', - 'udp_enabled', - 'ipv6_enabled', - 'trace_enabled', - 'compact_mode', - 'allow_inline', - 'notifications', - 'sound_notifications', - 'calls_sound', - 'hole_punching_enabled', - 'dht_announcements_enabled', - 'save_history', - 'download_nodes_list' - 'core_logging', - ] - -sDIR = os.environ.get('TMPDIR', '/tmp') -sTOX_VERSION = "1000002018" -bHAVE_NMAP = shutil.which('nmap') -bHAVE_JQ = shutil.which('jq') -bHAVE_BASH = shutil.which('bash') -bHAVE_TORR = shutil.which('tor-resolve') - -lDEAD_BS = [ - # Failed to resolve "tox3.plastiras.org" - "tox3.plastiras.org", - 'tox.kolka.tech', - # IPs that do not reverse resolve - '49.12.229.145', - "46.101.197.175", - '114.35.245.150', - '172.93.52.70', - '195.123.208.139', - '205.185.115.131', - # IPs that do not rreverse resolve - 'yggnode.cf', '188.225.9.167', - '85-143-221-42.simplecloud.ru', '85.143.221.42', - # IPs that do not ping - '104.244.74.69', 'tox.plastiras.org', - '195.123.208.139', - 'gt.sot-te.ch', '32.226.5.82', - # suspicious IPs - 'tox.abilinski.com', '172.103.164.250', '172.103.164.250.tpia.cipherkey.com', - ] - - -def assert_main_thread(): - from PyQt5 import QtCore, QtWidgets - from qtpy.QtWidgets import QApplication - - # this "instance" method is very useful! - app_thread = QtWidgets.QApplication.instance().thread() - curr_thread = QtCore.QThread.currentThread() - if app_thread != curr_thread: - raise RuntimeError('attempt to call MainWindow.append_message from non-app thread') - -@contextlib.contextmanager -def ignoreStdout(): - devnull = os.open(os.devnull, os.O_WRONLY) - old_stdout = os.dup(1) - sys.stdout.flush() - os.dup2(devnull, 1) - os.close(devnull) - try: - yield - finally: - os.dup2(old_stdout, 1) - os.close(old_stdout) - -@contextlib.contextmanager -def ignoreStderr(): - devnull = os.open(os.devnull, os.O_WRONLY) - old_stderr = os.dup(2) - sys.stderr.flush() - os.dup2(devnull, 2) - os.close(devnull) - try: - yield - finally: - os.dup2(old_stderr, 2) - os.close(old_stderr) - -def clean_booleans(oArgs): - for key in lBOOLEANS: - if not hasattr(oArgs, key): continue - val = getattr(oArgs, key) - if type(val) == bool: continue - if val in ['False', 'false', '0']: - setattr(oArgs, key, False) - else: - setattr(oArgs, key, True) - -def on_log(iTox, level, filename, line, func, message, *data): - # LOG.debug(repr((level, filename, line, func, message,))) - tox_log_cb(level, filename, line, func, message) - -def tox_log_cb(level, filename, line, func, message, *args): - """ - * @param level The severity of the log message. - * @param filename The source file from which the message originated. - * @param line The source line from which the message originated. - * @param func The function from which the message originated. - * @param message The log message. - * @param user_data The user data pointer passed to tox_new in options. - """ - if type(func) == bytes: - func = str(func, 'utf-8') - message = str(message, 'UTF-8') - filename = str(filename, 'UTF-8') - - if filename == 'network.c': - if line == 660: return - # root WARNING 3network.c#944:b'send_packet'attempted to send message with network family 10 (probably IPv6) on IPv4 socket - if line == 944: return - i = message.find('07 = GET_NODES') - if i > 0: - return - if filename == 'TCP_common.c': return - - i = message.find(' | ') - if i > 0: - message = message[:i] - # message = filename +'#' +str(line) +':'+func +' '+message - - name = 'core' - # old level is meaningless - level = 10 # LOG.level - - # LOG._log(LOG.level, f"{level}: {message}", list()) - - i = message.find('(0: OK)') - if i > 0: - level = 10 # LOG.debug - else: - i = message.find('(1: ') - if i > 0: - level = 30 # LOG.warn - else: - level = 20 # LOG.info - - o = LOG.makeRecord(filename, level, func, line, message, list(), None) - # LOG.handle(o) - LOG_TRACE(f"{level}: {func}{line} {message}") - return - - elif level == 1: - LOG.critical(f"{level}: {message}") - elif level == 2: - LOG.error(f"{level}: {message}") - elif level == 3: - LOG.warn(f"{level}: {message}") - elif level == 4: - LOG.info(f"{level}: {message}") - elif level == 5: - LOG.debug(f"{level}: {message}") - else: - LOG_TRACE(f"{level}: {message}") - -def vAddLoggerCallback(tox_options, callback=None): - if callback is None: - wrapper.tox.Tox.libtoxcore.tox_options_set_log_callback( - tox_options._options_pointer, - POINTER(None)()) - tox_options.self_logger_cb = None - return - - c_callback = CFUNCTYPE(None, c_void_p, c_int, c_char_p, c_int, c_char_p, c_char_p, c_void_p) - tox_options.self_logger_cb = c_callback(callback) - wrapper.tox.Tox.libtoxcore.tox_options_set_log_callback( - tox_options._options_pointer, - tox_options.self_logger_cb) - -def get_video_indexes(): - # Linux - return [str(l[5:]) for l in os.listdir('/dev/') if l.startswith('video')] - -def get_audio(): - with ignoreStderr(): - import pyaudio - oPyA = pyaudio.PyAudio() - - input_devices = output_devices = 0 - for i in range(oPyA.get_device_count()): - device = oPyA.get_device_info_by_index(i) - if device["maxInputChannels"]: - input_devices += 1 - if device["maxOutputChannels"]: - output_devices += 1 - # {'index': 21, 'structVersion': 2, 'name': 'default', 'hostApi': 0, 'maxInputChannels': 64, 'maxOutputChannels': 64, 'defaultLowInputLatency': 0.008707482993197279, 'defaultLowOutputLatency': 0.008707482993197279, 'defaultHighInputLatency': 0.034829931972789115, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultSampleRate': 44100.0} - audio = {'input': oPyA.get_default_input_device_info()['index'] if input_devices else -1, - 'output': oPyA.get_default_output_device_info()['index'] if output_devices else -1, - 'enabled': input_devices and output_devices} - return audio - -def oMainArgparser(_=None, iMode=0): - # 'Mode: 0=chat 1=chat+audio 2=chat+audio+video default: 0' - if not os.path.exists('/proc/sys/net/ipv6'): - bIpV6 = 'False' - else: - bIpV6 = 'True' - lIpV6Choices=[bIpV6, 'False'] - - sNodesJson = os.path.join(os.environ['HOME'], '.config', 'tox', 'DHTnodes.json') - if not os.path.exists(sNodesJson): sNodesJson = '' - - logfile = os.path.join(os.environ.get('TMPDIR', '/tmp'), 'toxygen.log') - if not os.path.exists(sNodesJson): logfile = '' - - parser = argparse.ArgumentParser(add_help=True) - parser.add_argument('--proxy_host', '--proxy-host', type=str, - # oddball - we want to use '' as a setting - default='0.0.0.0', - help='proxy host') - parser.add_argument('--proxy_port', '--proxy-port', default=0, type=int, - help='proxy port') - parser.add_argument('--proxy_type', '--proxy-type', default=0, type=int, - choices=[0,1,2], - help='proxy type 1=http, 2=socks') - parser.add_argument('--tcp_port', '--tcp-port', default=0, type=int, - help='tcp port') - parser.add_argument('--udp_enabled', type=str, default='True', - choices=['True', 'False'], - help='En/Disable udp') - parser.add_argument('--ipv6_enabled', type=str, default=bIpV6, - choices=lIpV6Choices, - help=f"En/Disable ipv6 - default {bIpV6}") - parser.add_argument('--trace_enabled',type=str, - default='True' if os.environ.get('DEBUG') else 'False', - choices=['True','False'], - help='Debugging from toxcore logger_trace or env DEBUG=1') - parser.add_argument('--download_nodes_list', type=str, default='False', - choices=['True', 'False'], - help='Download nodes list') - parser.add_argument('--nodes_json', type=str, - default=sNodesJson) - parser.add_argument('--network', type=str, - choices=['main', 'local'], - default='main') - parser.add_argument('--download_nodes_url', type=str, - default='https://nodes.tox.chat/json') - parser.add_argument('--logfile', default=logfile, - help='Filename for logging - start with + for stdout too') - parser.add_argument('--loglevel', default=logging.INFO, type=int, - # choices=[logging.info,logging.trace,logging.debug,logging.error] - help='Threshold for logging (lower is more) default: 20') - parser.add_argument('--mode', type=int, default=iMode, - choices=[0,1,2], - help='Mode: 0=chat 1=chat+audio 2=chat+audio+video default: 0') - parser.add_argument('--hole_punching_enabled',type=str, - default='False', choices=['True','False'], - help='En/Enable hole punching') - parser.add_argument('--dht_announcements_enabled',type=str, - default='True', choices=['True','False'], - help='En/Disable DHT announcements') - return parser - -def vSetupLogging(oArgs): - global LOG - logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S') - logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S' - logging._defaultFormatter.default_msec_format = '' - - add = None - kwargs = dict(level=oArgs.loglevel, - format='%(levelname)-8s %(message)s') - if oArgs.logfile: - add = oArgs.logfile.startswith('+') - sub = oArgs.logfile.startswith('-') - if add or sub: - oArgs.logfile = oArgs.logfile[1:] - kwargs['filename'] = oArgs.logfile - - if coloredlogs: - # https://pypi.org/project/coloredlogs/ - aKw = dict(level=oArgs.loglevel, - logger=LOG, - stream=sys.stdout, - fmt='%(name)s %(levelname)s %(message)s' - ) - coloredlogs.install(**aKw) - if oArgs.logfile: - oHandler = logging.FileHandler(oArgs.logfile) - LOG.addHandler(oHandler) - else: - logging.basicConfig(**kwargs) - if add: - oHandler = logging.StreamHandler(sys.stdout) - LOG.addHandler(oHandler) - - LOG.info(f"Setting loglevel to {oArgs.loglevel!s}") - - -def setup_logging(oArgs): - global LOG - if coloredlogs: - aKw = dict(level=oArgs.loglevel, - logger=LOG, - fmt='%(name)s %(levelname)s %(message)s') - if oArgs.logfile: - oFd = open(oArgs.logfile, 'wt') - setattr(oArgs, 'log_oFd', oFd) - aKw['stream'] = oFd - coloredlogs.install(**aKw) - if oArgs.logfile: - oHandler = logging.StreamHandler(stream=sys.stdout) - LOG.addHandler(oHandler) - else: - aKw = dict(level=oArgs.loglevel, - format='%(name)s %(levelname)-4s %(message)s') - if oArgs.logfile: - aKw['filename'] = oArgs.logfile - logging.basicConfig(**aKw) - - logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S') - logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S' - logging._defaultFormatter.default_msec_format = '' - - LOG.setLevel(oArgs.loglevel) -# LOG.trace = lambda l: LOG.log(0, repr(l)) - LOG.info(f"Setting loglevel to {oArgs.loglevel!s}") - -def signal_handler(num, f): - from trepan.api import debug - from trepan.interfaces import server as Mserver - connection_opts={'IO': 'TCP', 'PORT': 6666} - intf = Mserver.ServerInterface(connection_opts=connection_opts) - dbg_opts = {'interface': intf} - LOG.info('Starting TCP server listening on port 6666.') - debug(dbg_opts=dbg_opts) - return - -def merge_args_into_settings(args, settings): - if args: - if not hasattr(args, 'audio'): - LOG.warn('No audio ' +repr(args)) - settings['audio'] = getattr(args, 'audio') - if not hasattr(args, 'video'): - LOG.warn('No video ' +repr(args)) - settings['video'] = getattr(args, 'video') - for key in settings.keys(): - # proxy_type proxy_port proxy_host - not_key = 'not_' +key - if hasattr(args, key): - val = getattr(args, key) - if type(val) == bytes: - # proxy_host - ascii? - # filenames - ascii? - val = str(val, 'UTF-8') - settings[key] = val - elif hasattr(args, not_key): - val = not getattr(args, not_key) - settings[key] = val - clean_settings(settings) - return - -def clean_settings(self): - # failsafe to ensure C tox is bytes and Py settings is str - - # overrides - self['mirror_mode'] = False - # REQUIRED!! - if not os.path.exists('/proc/sys/net/ipv6'): - LOG.warn('Disabling IPV6 because /proc/sys/net/ipv6 does not exist') - self['ipv6_enabled'] = False - - if 'proxy_type' in self and self['proxy_type'] == 0: - self['proxy_host'] = '' - self['proxy_port'] = 0 - - if 'proxy_type' in self and self['proxy_type'] != 0 and \ - 'proxy_host' in self and self['proxy_host'] != '' and \ - 'proxy_port' in self and self['proxy_port'] != 0: - if 'udp_enabled' in self and self['udp_enabled']: - # We don't currently support UDP over proxy. - LOG.info("UDP enabled and proxy set: disabling UDP") - self['udp_enabled'] = False - if 'local_discovery_enabled' in self and self['local_discovery_enabled']: - LOG.info("local_discovery_enabled enabled and proxy set: disabling local_discovery_enabled") - self['local_discovery_enabled'] = False - if 'dht_announcements_enabled' in self and self['dht_announcements_enabled']: - LOG.info("dht_announcements_enabled enabled and proxy set: disabling dht_announcements_enabled") - self['dht_announcements_enabled'] = False - - if 'auto_accept_path' in self and \ - type(self['auto_accept_path']) == bytes: - self['auto_accept_path'] = str(self['auto_accept_path'], 'UTF-8') - - LOG.debug("Cleaned settings") - -def lSdSamplerates(iDev): - try: - import sounddevice as sd - except ImportError: - return [] - samplerates = (32000, 44100, 48000, 96000, ) - device = iDev - supported_samplerates = [] - for fs in samplerates: - try: - sd.check_output_settings(device=device, samplerate=fs) - except Exception as e: - # LOG.debug(f"Sample rate not supported {fs}" +' '+str(e)) - pass - else: - supported_samplerates.append(fs) - return supported_samplerates - -def _get_nodes_path(oArgs=None): - if oArgs and oArgs.nodes_json and os.path.isfile(oArgs.nodes_json): - LOG.debug("_get_nodes_path: " +oArgs.nodes_json) - default = oArgs.nodes_json - elif get_user_config_path: - default = os.path.join(get_user_config_path(), 'toxygen_nodes.json') - else: - # Windwoes - default = os.path.join(os.getenv('HOME'), '.config', 'tox', 'toxygen_nodes.json') - LOG.debug("_get_nodes_path: " +default) - return default - -DEFAULT_NODES_COUNT = 8 - -global aNODES -aNODES = {} - - -# @functools.lru_cache(maxsize=12) TypeError: unhashable type: 'Namespace' -def generate_nodes(oArgs=None, - nodes_count=DEFAULT_NODES_COUNT, - ipv='ipv4', - udp_not_tcp=True): - global aNODES - sKey = ipv - sKey += ',0' if udp_not_tcp else ',1' - if sKey in aNODES and aNODES[sKey]: - return aNODES[sKey] - sFile = _get_nodes_path(oArgs=oArgs) - assert os.path.exists(sFile), sFile - lNodes = generate_nodes_from_file(sFile, - nodes_count=nodes_count, - ipv=ipv, udp_not_tcp=udp_not_tcp) - assert lNodes - aNODES[sKey] = lNodes - return aNODES[sKey] - -aNODES_CACHE = {} -def generate_nodes_from_file(sFile, - nodes_count=DEFAULT_NODES_COUNT, - ipv='ipv4', - udp_not_tcp=True, - ): - """https://github.com/TokTok/c-toxcore/issues/469 -I had a conversation with @irungentoo on IRC about whether we really need to call tox_bootstrap() when having UDP disabled and why. The answer is yes, because in addition to TCP relays (tox_add_tcp_relay()), toxcore also needs to know addresses of UDP onion nodes in order to work correctly. The DHT, however, is not used when UDP is disabled. tox_bootstrap() function resolves the address passed to it as argument and calls onion_add_bs_node_path() and DHT_bootstrap() functions. Although calling DHT_bootstrap() is not really necessary as DHT is not used, we still need to resolve the address of the DHT node in order to populate the onion routes with onion_add_bs_node_path() call. -""" - global aNODES_CACHE - - key = ipv - key += ',0' if udp_not_tcp else ',1' - if key in aNODES_CACHE: - sorted_nodes = aNODES_CACHE[key] - else: - if not os.path.exists(sFile): - LOG.error("generate_nodes_from_file file not found " +sFile) - return [] - try: - with open(sFile, 'rt') as fl: - json_nodes = json.loads(fl.read())['nodes'] - except Exception as e: - LOG.error(f"generate_nodes_from_file error {sFile}\n{e}") - return [] - else: - LOG.debug("generate_nodes_from_file " +sFile) - - if udp_not_tcp: - nodes = [(node[ipv], node['port'], node['public_key'],) for - node in json_nodes if node[ipv] != 'NONE' \ - and node["status_udp"] in [True, "true"] - ] - else: - nodes = [] - elts = [(node[ipv], node['tcp_ports'], node['public_key'],) \ - for node in json_nodes if node[ipv] != 'NONE' \ - and node["status_tcp"] in [True, "true"] - ] - for (ipv, ports, public_key,) in elts: - for port in ports: - nodes += [(ipv, port, public_key)] - if not nodes: - LOG.warn(f'empty generate_nodes from {sFile} {json_nodes!r}') - return [] - sorted_nodes = nodes - aNODES_CACHE[key] = sorted_nodes - - random.shuffle(sorted_nodes) - if nodes_count is not None and len(sorted_nodes) > nodes_count: - sorted_nodes = sorted_nodes[-nodes_count:] - LOG.debug(f"generate_nodes_from_file {sFile} len={len(sorted_nodes)}") - return sorted_nodes - -def tox_bootstrapd_port(): - port = 33446 - sFile = '/etc/tox-bootstrapd.conf' - if os.path.exists(sFile): - with open(sFile, 'rt') as oFd: - for line in oFd.readlines(): - if line.startswith('port = '): - port = int(line[7:]) - return port - -def bootstrap_local(elts, lToxes, oArgs=None): - if os.path.exists('/run/tox-bootstrapd/tox-bootstrapd.pid'): - LOG.debug('/run/tox-bootstrapd/tox-bootstrapd.pid') - iRet = True - else: - iRet = os.system("netstat -nle4|grep -q :33") - if iRet > 0: - LOG.warn(f'bootstraping local No local DHT running') - LOG.info(f'bootstraping local') - return bootstrap_udp(elts, lToxes, oArgs) - -def lDNSClean(l): - global lDEAD_BS - # list(set(l).difference(set(lDEAD_BS))) - return [elt for elt in l if elt not in lDEAD_BS] - -def lExitExcluder(oArgs, iPort=9051): - """ - https://raw.githubusercontent.com/nusenu/noContactInfo_Exit_Excluder/main/exclude_noContactInfo_Exits.py - """ - if not stem: - LOG.warn('please install the stem Python package') - return '' - LOG.debug('lExcludeExitNodes') - - try: - controller = oGetStemController(log_level=10) - # generator - relays = controller.get_server_descriptors() - except Exception as e: - LOG.error(f'Failed to get relay descriptors {e}') - return None - - if controller.is_set('ExcludeExitNodes'): - LOG.info('ExcludeExitNodes is in use already.') - return None - - exit_excludelist=[] - LOG.debug("Excluded exit relays:") - for relay in relays: - if relay.exit_policy.is_exiting_allowed() and not relay.contact: - if is_valid_fingerprint(relay.fingerprint): - exit_excludelist.append(relay.fingerprint) - LOG.debug("https://metrics.torproject.org/rs.html#details/%s" % relay.fingerprint) - else: - LOG.warn('Invalid Fingerprint: %s' % relay.fingerprint) - - try: - controller.set_conf('ExcludeExitNodes', exit_excludelist) - LOG.info('Excluded a total of %s exit relays without ContactInfo from the exit position.' % len(exit_excludelist)) - except Exception as e: - LOG.exception('ExcludeExitNodes ' +str(e)) - return exit_excludelist - -aHOSTS = {} -@functools.lru_cache(maxsize=20) -def sDNSLookup(host): - global aHOSTS - ipv = 0 - if host in lDEAD_BS: -# LOG.warn(f"address skipped because in lDEAD_BS {host}") - return '' - if host in aHOSTS: - return aHOSTS[host] - - try: - s = host.replace('.','') - int(s) - ipv = 4 - except: - try: - s = host.replace(':','') - int(s) - ipv = 6 - except: pass - - if ipv > 0: -# LOG.debug(f"v={ipv} IP address {host}") - return host - - LOG.debug(f"sDNSLookup {host}") - ip = '' - if host.endswith('.tox') or host.endswith('.onion'): - if False and stem: - ip = sMapaddressResolv(host) - if ip: return ip - - ip = sTorResolve(host) - if ip: return ip - - if not bHAVE_TORR: - LOG.warn(f"onion address skipped because no tor-resolve {host}") - return '' - try: - sout = f"/tmp/TR{os.getpid()}.log" - i = os.system(f"tor-resolve -4 {host} > {sout}") - if not i: - LOG.warn(f"onion address skipped because tor-resolve on {host}") - return '' - ip = open(sout, 'rt').read() - if ip.endswith('failed.'): - LOG.warn(f"onion address skipped because tor-resolve failed on {host}") - return '' - LOG.debug(f"onion address tor-resolve {ip} on {host}") - return ip - except: - pass - else: - try: - ip = socket.gethostbyname(host) - LOG.debug(f"host={host} gethostbyname IP address {ip}") - if ip: - aHOSTS[host] = ip - return ip - # drop through - except: - # drop through - pass - - if ip == '': - try: - sout = f"/tmp/TR{os.getpid()}.log" - i = os.system(f"dig {host} +timeout=15|grep ^{host}|sed -e 's/.* //'> {sout}") - if not i: - LOG.warn(f"address skipped because dig failed on {host}") - return '' - ip = open(sout, 'rt').read().strip() - LOG.debug(f"address dig {ip} on {host}") - aHOSTS[host] = ip - return ip - except: - ip = host - LOG.debug(f'sDNSLookup {host} -> {ip}') - if ip and ip != host: - aHOSTS[host] = ip - return ip - -def bootstrap_udp(lelts, lToxes, oArgs=None): - lelts = lDNSClean(lelts) - socket.setdefaulttimeout(15.0) - for oTox in lToxes: - random.shuffle(lelts) - if hasattr(oTox, 'oArgs'): - oArgs = oTox.oArgs - if hasattr(oArgs, 'contents') and oArgs.contents.proxy_type != 0: - lelts = lelts[:1] - -# LOG.debug(f'bootstrap_udp DHT bootstraping {oTox.name} {len(lelts)}') - for largs in lelts: - assert len(largs) == 3 - host, port, key = largs - assert host; assert port; assert key - if host in lDEAD_BS: continue - ip = sDNSLookup(host) - if not ip: - LOG.warn(f'bootstrap_udp to host={host} port={port} did not resolve ip={ip}') - continue - - if type(port) == str: - port = int(port) - try: - assert len(key) == 64, key - # NOT ip - oRet = oTox.bootstrap(host, - port, - key) - except Exception as e: - if oArgs is None or ( - hasattr(oArgs, 'contents') and oArgs.contents.proxy_type == 0): - pass - # LOG.error(f'bootstrap_udp failed to host={host} port={port} {e}') - continue - if not oRet: - LOG.warn(f'bootstrap_udp failed to {host} : {oRet}') - elif oTox.self_get_connection_status() != TOX_CONNECTION['NONE']: - LOG.info(f'bootstrap_udp to {host} connected') - break - else: -# LOG.debug(f'bootstrap_udp to {host} not connected') - pass - -def bootstrap_tcp(lelts, lToxes, oArgs=None): - lelts = lDNSClean(lelts) - for oTox in lToxes: - if hasattr(oTox, 'oArgs'): oArgs = oTox.oArgs - random.shuffle(lelts) -# LOG.debug(f'bootstrap_tcp bootstapping {oTox.name} {len(lelts)}') - for (host, port, key,) in lelts: - assert host; assert port;assert key - if host in lDEAD_BS: continue - ip = sDNSLookup(host) - if not ip: - LOG.warn(f'bootstrap_tcp to {host} did not resolve ip={ip}') -# continue - ip = host - if host.endswith('.onion') and stem: - l = lIntroductionPoints(host) - if not l: - LOG.warn(f'bootstrap_tcp to {host} has no introduction points') - continue - if type(port) == str: - port = int(port) - try: - assert len(key) == 64, key - oRet = oTox.add_tcp_relay(ip, - port, - key) - except Exception as e: - LOG.error(f'bootstrap_tcp to {host} : ' +str(e)) - continue - if not oRet: - LOG.warn(f'bootstrap_tcp failed to {host} : {oRet}') - elif oTox.mycon_time == 1: - LOG.info(f'bootstrap_tcp to {host} not yet connected last=1') - elif oTox.mycon_status is False: - LOG.info(f'bootstrap_tcp to {host} not True' \ - +f" last={int(oTox.mycon_time)}" ) - elif oTox.self_get_connection_status() != TOX_CONNECTION['NONE']: - LOG.info(f'bootstrap_tcp to {host} connected' \ - +f" last={int(oTox.mycon_time)}" ) - break - else: - LOG.debug(f'bootstrap_tcp to {host} but not connected' \ - +f" last={int(oTox.mycon_time)}" ) - pass - -def iNmapInfoNmap(sProt, sHost, sPort, key=None, environ=None, cmd=''): - if sHost in ['-', 'NONE']: return 0 - if not nmap: return 0 - nmps = nmap.PortScanner - if sProt in ['socks', 'socks5', 'tcp4']: - prot = 'tcp' - cmd = f" -Pn -n -sT -p T:{sPort}" - else: - prot = 'udp' - cmd = f" -Pn -n -sU -p U:{sPort}" - LOG.debug(f"iNmapInfoNmap cmd={cmd}") - sys.stdout.flush() - o = nmps().scan(hosts=sHost, arguments=cmd) - aScan = o['scan'] - ip = list(aScan.keys())[0] - state = aScan[ip][prot][sPort]['state'] - LOG.info(f"iNmapInfoNmap: to {sHost} {state}") - return 0 - -def iNmapInfo(sProt, sHost, sPort, key=None, environ=None, cmd='nmap'): - if sHost in ['-', 'NONE']: return 0 - sFile = os.path.join("/tmp", f"{sHost}.{os.getpid()}.nmap") - if sProt in ['socks', 'socks5', 'tcp4']: - cmd += f" -Pn -n -sT -p T:{sPort} {sHost} | grep /tcp " - else: - cmd += f" -Pn -n -sU -p U:{sPort} {sHost} | grep /udp " - LOG.debug(f"iNmapInfo cmd={cmd}") - sys.stdout.flush() - iRet = os.system('sudo ' +cmd +f" >{sFile} 2>&1 ") - LOG.debug(f"iNmapInfo cmd={cmd} iRet={iRet}") - if iRet != 0: - return iRet - assert os.path.exists(sFile), sFile - with open(sFile, 'rt') as oFd: - l = oFd.readlines() - assert len(l) - l = [line for line in l if line and not line.startswith('WARNING:')] - s = '\n'.join([s.strip() for s in l]) - LOG.info(f"iNmapInfo: to {sHost}\n{s}") - return 0 - -def bootstrap_iNmapInfo(lElts, oArgs, protocol="tcp4", bIS_LOCAL=False, iNODES=iNODES, cmd='nmap'): - if not bIS_LOCAL and not bAreWeConnected(): - LOG.warn(f"bootstrap_iNmapInfo not local and NOT CONNECTED") - return True - if os.environ['USER'] != 'root': - LOG.warn(f"bootstrap_iNmapInfo not ROOT") - return True - - lRetval = [] - for elts in lElts[:iNODES]: - host, port, key = elts - ip = sDNSLookup(host) - if not ip: - LOG.info('bootstrap_iNmapInfo to {host} did not resolve ip={ip}') - continue - if type(port) == str: - port = int(port) - iRet = -1 - try: - if not nmap: - iRet = iNmapInfo(protocol, ip, port, key, cmd=cmd) - else: - iRet = iNmapInfoNmap(protocol, ip, port, key) - if iRet != 0: - LOG.warn('iNmapInfo to ' +repr(host) +' retval=' +str(iRet)) - lRetval += [False] - else: - LOG.debug('iNmapInfo to ' +repr(host) +' retval=' +str(iRet)) - lRetval += [True] - except Exception as e: - LOG.exception('iNmapInfo to {host} : ' +str(e) - ) - lRetval += [False] - return any(lRetval) - -def caseFactory(cases): - """We want the tests run in order.""" - if len(cases) > 1: - ordered_cases = sorted(cases, key=lambda f: inspect.findsource(f)[1]) - else: - ordered_cases = cases - return ordered_cases - -def suiteFactory(*testcases): - """We want the tests run in order.""" - linen = lambda f: getattr(tc, f).__code__.co_firstlineno - lncmp = lambda a, b: linen(a) - linen(b) - - test_suite = unittest.TestSuite() - for tc in testcases: - test_suite.addTest(unittest.makeSuite(tc, sortUsing=lncmp)) - return test_suite diff --git a/toxygen/wrapper_tests/tests_wrapper.py b/toxygen/wrapper_tests/tests_wrapper.py deleted file mode 100644 index 640916d..0000000 --- a/toxygen/wrapper_tests/tests_wrapper.py +++ /dev/null @@ -1,1885 +0,0 @@ -# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- -# -# @file tests.py -# @author Wei-Ning Huang (AZ) -# -# Copyright (C) 2013 - 2014 Wei-Ning Huang (AZ) -# All Rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -"""Originaly from https://github.com/oxij/PyTox c-toxcore-02 branch -which itself was forked from https://github.com/aitjcize/PyTox/ - -Modified to work with -""" - -import ctypes -import faulthandler -import hashlib -import logging -import os -import random -import re -import sys -import threading -import traceback -import unittest -from ctypes import * - -faulthandler.enable() - -import warnings - -warnings.filterwarnings('ignore') - -try: - from io import BytesIO - - import certifi - import pycurl -except ImportError: - pycurl = None - -try: - import coloredlogs - os.environ['COLOREDLOGS_LEVEL_STYLES'] = 'spam=22;debug=28;verbose=34;notice=220;warning=202;success=118,bold;error=124;critical=background=red' -except ImportError as e: - logging.log(logging.DEBUG, f"coloredlogs not available: {e}") - coloredlogs = None - -try: - import color_runner -except ImportError as e: - logging.log(logging.DEBUG, f"color_runner not available: {e}") - color_runner = None - -import wrapper -import wrapper.toxcore_enums_and_consts as enums -from wrapper.tox import Tox -from wrapper.toxcore_enums_and_consts import (TOX_ADDRESS_SIZE, TOX_CONNECTION, - TOX_FILE_CONTROL, - TOX_MESSAGE_TYPE, - TOX_SECRET_KEY_SIZE, - TOX_USER_STATUS) - -try: - import support_testing as ts -except ImportError: - import wrapper_tests.support_testing as ts - -try: - from tests.toxygen_tests import test_sound_notification - bIS_NOT_TOXYGEN = False -except ImportError: - bIS_NOT_TOXYGEN = True - -# from PyQt5 import QtCore -import time - -sleep = time.sleep - -global LOG -LOG = logging.getLogger('TestS') -# just print to stdout so there is no complications from logging. -def LOG_ERROR(l): print('EROR+ '+l) -def LOG_WARN(l): print('WARN+ '+l) -def LOG_INFO(l): print('INFO+ '+l) -def LOG_DEBUG(l): print('DEBUG+ '+l) -def LOG_TRACE(l): pass # print('TRAC+ '+l) - -ADDR_SIZE = 38 * 2 -CLIENT_ID_SIZE = 32 * 2 -THRESHOLD = 25 - -global oTOX_OPTIONS -oTOX_OPTIONS = {} - -bIS_LOCAL = 'new' in sys.argv or 'main' in sys.argv or 'newlocal' in sys.argv - -# Patch unittest for Python version <= 2.6 -if not hasattr(unittest, 'skip'): - def unittest_skip(reason): - def _wrap1(func): - def _wrap2(self, *args, **kwargs): - pass - return _wrap2 - return _wrap1 - unittest.skip = unittest_skip - -if not hasattr(unittest, 'expectedFailureIf'): - def unittest_expectedFailureIf(condition, reason): - def _wrap1(test_item): - def _wrap2(self, *args, **kwargs): - if condition: - test_item.__unittest_expecting_failure__ = True - pass - return _wrap2 - return _wrap1 - - unittest.expectedFailureIf = unittest_expectedFailureIf - -def expectedFailure(test_item): - test_item.__unittest_expecting_failure__ = True - return test_item - -class ToxOptions(): - def __init__(self): - self.ipv6_enabled = True - self.udp_enabled = True - self.proxy_type = 0 - self.proxy_host = '' - self.proxy_port = 0 - self.start_port = 0 - self.end_port = 0 - self.tcp_port = 0 - self.savedata_type = 0 # 1=toxsave, 2=secretkey - self.savedata_data = b'' - self.savedata_length = 0 - self.local_discovery_enabled = False - self.dht_announcements_enabled = True - self.hole_punching_enabled = False - self.experimental_thread_safety = False - -class App(): - def __init__(self): - self.mode = 0 -oAPP = App() - -class AliceTox(Tox): - - def __init__(self, opts, app=None): - - super(AliceTox, self).__init__(opts, app=app) - self._address = self.self_get_address() - self.name = 'alice' - self._opts = opts - self._app = app - -class BobTox(Tox): - - def __init__(self, opts, app=None): - super(BobTox, self).__init__(opts, app=app) - self._address = self.self_get_address() - self.name = 'bob' - self._opts = opts - self._app = app - -class BaseThread(threading.Thread): - - def __init__(self, name=None, target=None): - if name: - super().__init__(name=name, target=target) - else: - super().__init__(target=target) - self._stop_thread = False - self.name = name - - def stop_thread(self, timeout=-1): - self._stop_thread = True - if timeout < 0: - timeout = ts.iTHREAD_TIMEOUT - i = 0 - while i < ts.iTHREAD_JOINS: - self.join(timeout) - if not self.is_alive(): break - i = i + 1 - else: - LOG.warning(f"{self.name} BLOCKED") - -class ToxIterateThread(BaseThread): - - def __init__(self, tox): - super().__init__(name='ToxIterateThread') - self._tox = tox - - def run(self): - while not self._stop_thread: - self._tox.iterate() - sleep(self._tox.iteration_interval() / 1000) - -global bob, alice -bob = alice = None - -def prepare(self): - global bob, alice - def bobs_on_self_connection_status(iTox, connection_state, *args): - status = connection_state - self.bob.dht_connected = status - self.bob.mycon_time = time.time() - try: - if status != TOX_CONNECTION['NONE']: - LOG_DEBUG(f"bobs_on_self_connection_status TRUE {status}" \ - +f" last={int(self.bob.mycon_time)}" ) - self.bob.mycon_status = True - else: - LOG_DEBUG(f"bobs_on_self_connection_status FALSE {status}" \ - +f" last={int(self.bob.mycon_time)}" ) - self.bob.mycon_status = False - except Exception as e: - LOG_ERROR(f"bobs_on_self_connection_status {e}") - else: - if self.bob.self_get_connection_status() != status: - LOG_WARN(f"bobs_on_self_connection_status DISAGREE {status}") - - def alices_on_self_connection_status(iTox, connection_state, *args): - #FixMe connection_num - status = connection_state - self.alice.dht_connected = status - self.alice.mycon_time = time.time() - try: - if status != TOX_CONNECTION['NONE']: - LOG_DEBUG(f"alices_on_self_connection_status TRUE {status}" \ - +f" last={int(self.alice.mycon_time)}" ) - self.alice.mycon_status = True - else: - LOG_WARN(f"alices_on_self_connection_status FALSE {status}" \ - +f" last={int(self.alice.mycon_time)}" ) - self.alice.mycon_status = False - except Exception as e: - LOG_ERROR(f"alices_on_self_connection_status error={e}") - else: - if self.alice.self_get_connection_status() != status: - LOG_WARN(f"alices_on_self_connection_status != {status}") - self.alice.dht_connected = status - - opts = oToxygenToxOptions(oTOX_OARGS) - alice = AliceTox(opts, app=oAPP) - alice.oArgs = opts - alice.dht_connected = -1 - alice.mycon_status = False - alice.mycon_time = 1 - alice.callback_self_connection_status(alices_on_self_connection_status) - - bob = BobTox(opts, app=oAPP) - bob.oArgs = opts - bob.dht_connected = -1 - bob.mycon_status = False - bob.mycon_time = 1 - bob.callback_self_connection_status(bobs_on_self_connection_status) - if not bIS_LOCAL and not ts.bAreWeConnected(): - LOG.warning(f"doOnce not local and NOT CONNECTED") - return [bob, alice] - -class ToxSuite(unittest.TestCase): - failureException = RuntimeError - - @classmethod - def setUpClass(cls): - global oTOX_OARGS - assert oTOX_OPTIONS - assert oTOX_OARGS - - if not hasattr(cls, 'alice') and not hasattr(cls, 'bob'): - l = prepare(cls) - assert l - cls.bob, cls.alice = l - if not hasattr(cls.bob, '_main_loop'): - cls.bob._main_loop = ToxIterateThread(cls.bob) - cls.bob._main_loop.start() - LOG.debug(f"cls.bob._main_loop: ") # {threading.enumerate()} - if not hasattr(cls.alice, '_main_loop'): - cls.alice._main_loop = ToxIterateThread(cls.alice) - cls.alice._main_loop.start() - LOG.debug(f"cls.alice._main_loop: ") # {threading.enumerate()} - - cls.lUdp = ts.generate_nodes( - oArgs=oTOX_OARGS, - nodes_count=2*ts.iNODES, - ipv='ipv4', - udp_not_tcp=True) - - cls.lTcp = ts.generate_nodes( - oArgs=oTOX_OARGS, - nodes_count=2*ts.iNODES, - ipv='ipv4', - udp_not_tcp=False) - - @classmethod - def tearDownClass(cls): - cls.bob._main_loop.stop_thread() - cls.alice._main_loop.stop_thread() - if False: - cls.alice.kill() - cls.bob.kill() - del cls.bob - del cls.alice - - def setUp(self): - """ - """ - if hasattr(self, 'baid') and self.baid >= 0 and \ - self.baid in self.bob.self_get_friend_list(): - LOG.warn(f"setUp ALICE IS ALREADY IN BOBS FRIEND LIST") - elif self.bob.self_get_friend_list_size() >= 1: - LOG.warn(f"setUp BOB STILL HAS A FRIEND LIST") - - if hasattr(self, 'abid') and self.abid >= 0 and \ - self.abid in self.alice.self_get_friend_list(): - LOG.warn(f"setUp BOB IS ALREADY IN ALICES FRIEND LIST") - elif self.alice.self_get_friend_list_size() >= 1: - LOG.warn(f"setUp ALICE STILL HAS A FRIEND LIST") - - def tearDown(self): - """ - """ - if hasattr(self, 'baid') and self.baid >= 0 and \ - self.baid in self.bob.self_get_friend_list(): - LOG.warn(f"tearDown ALICE IS STILL IN BOBS FRIEND LIST") - elif self.bob.self_get_friend_list_size() >= 1: - LOG.warn(f"tearDown BOBS STILL HAS A FRIEND LIST") - - if hasattr(self, 'abid') and self.abid >= 0 and \ - self.abid in self.alice.self_get_friend_list(): - LOG.warn(f"tearDown BOB IS STILL IN ALICES FRIEND LIST") - elif self.bob.self_get_friend_list_size() >= 1: - LOG.warn(f"tearDown ALICE STILL HAS A FRIEND LIST") - - def run(self, result=None): - """ Stop after first error """ - if not result.errors: - super(ToxSuite, self).run(result) - - def get_connection_status(self): - if self.bob.mycon_time == -1 or self.alice.mycon_time == -1: - pass - # drop through - elif self.bob.dht_connected == TOX_CONNECTION['NONE']: - return False - elif self.alice.dht_connected == TOX_CONNECTION['NONE']: - return False - - # if not self.connected - if self.bob.self_get_connection_status() == TOX_CONNECTION['NONE']: - return False - if self.alice.self_get_connection_status() == TOX_CONNECTION['NONE']: - return False - return True - - def loop(self, n): - """ - t:iterate - t:iteration_interval - """ - interval = self.bob.iteration_interval() - for i in range(n): - self.alice.iterate() - self.bob.iterate() - sleep(interval / 1000.0) - - def call_bootstrap(self, num=None, lToxes=None, i=0): - if num == None: num=ts.iNODES -# LOG.debug(f"call_bootstrap network={oTOX_OARGS.network}") - if oTOX_OARGS.network in ['new', 'newlocal', 'localnew']: - ts.bootstrap_local(self.lUdp, [self.alice, self.bob]) - elif not ts.bAreWeConnected(): - LOG.warning('we are NOT CONNECTED') - else: - random.shuffle(self.lUdp) - if oTOX_OARGS.proxy_port > 0: - lElts = self.lUdp[:1] - else: - lElts = self.lUdp[:num+i] - LOG.debug(f"call_bootstrap ts.bootstrap_udp {len(lElts)}") - if lToxes is None: lToxes = [self.alice, self.bob] - ts.bootstrap_udp(lElts, lToxes) - random.shuffle(self.lTcp) - lElts = self.lTcp[:num+i] - LOG.debug(f"call_bootstrap ts.bootstrap_tcp {len(lElts)}") - ts.bootstrap_tcp(lElts, lToxes) - - def loop_until_connected(self, num=None): - """ - t:on_self_connection_status - t:self_get_connection_status - """ - i = 0 - bRet = None - while i <= THRESHOLD : - if (self.alice.mycon_status and self.bob.mycon_status): - bRet = True - break - if i % 5 == 0: - j = i//5 - self.call_bootstrap(num, lToxes=None, i=j) - s = '' - if i == 0: s = '\n' - LOG.info(s+"loop_until_connected " \ - +" #" + str(i) \ - +" BOB=" +repr(self.bob.self_get_connection_status()) \ - +" ALICE=" +repr(self.alice.self_get_connection_status()) - +f" BOBS={self.bob.mycon_status}" \ - +f" ALICES={self.alice.mycon_status}" \ - +f" last={int(self.bob.mycon_time)}" ) - if (self.alice.mycon_status and self.bob.mycon_status): - bRet = True - break - if (self.alice.self_get_connection_status() and - self.bob.self_get_connection_status()): - LOG_WARN(f"loop_until_connected disagree status() DISAGREE" \ - +f' self.bob.mycon_status={self.bob.mycon_status}' \ - +f' alice.mycon_status={self.alice.mycon_status}' \ - +f" last={int(self.bob.mycon_time)}" ) - bRet = True - break - i += 1 - self.loop(100) - else: - bRet = False - - if bRet or \ - ( self.bob.self_get_connection_status() != TOX_CONNECTION['NONE'] and \ - self.alice.self_get_connection_status() != TOX_CONNECTION['NONE'] ): - LOG.info(f"loop_until_connected returning True {i}" \ - +f" BOB={self.bob.self_get_connection_status()}" \ - +f" ALICE={self.alice.self_get_connection_status()}" \ - +f" last={int(self.bob.mycon_time)}" ) - return True - else: - LOG.warning(f"loop_until_connected returning False {i}" \ - +f" BOB={self.bob.self_get_connection_status()}" \ - +f" ALICE={self.alice.self_get_connection_status()}" \ - +f" last={int(self.bob.mycon_time)}" ) - return False - - def wait_obj_attr(self, obj, attr): - return wait_otox_attrs(self, obj, [attr]) - - def wait_objs_attr(self, objs, attr): - i = 0 - while i <= THRESHOLD: - if i % 5 == 0: - num = None - j = i//5 - self.call_bootstrap(num, objs, i=j) - LOG.debug("wait_objs_attr " +repr(objs) \ - +" for " +repr(attr) \ - +" " +str(i)) - if all([getattr(obj, attr) for obj in objs]): - return True - self.loop(100) - i += 1 - else: - LOG.error(f"wait_obj_attr i >= {THRESHOLD}") - - return all([getattr(obj, attr) for obj in objs]) - - def wait_otox_attrs(self, obj, attrs): - i = 0 - while i <= THRESHOLD: - if i % 5 == 0: - num = None - j = 0 - if obj.mycon_time == 1: - num = 4 - j = i//5 - self.call_bootstrap(num, [obj], i=j) - LOG.debug(f"wait_otox_attrs {obj.name} for {attrs} {i}" \ - +f" last={int(obj.mycon_time)}") - if all([getattr(obj, attr) is not None for attr in attrs]): - return True - self.loop(100) - i += 1 - else: - LOG.warning(f"wait_otox_attrs i >= {THRESHOLD}") - - return all([getattr(obj, attr) for attr in attrs]) - - def wait_ensure_exec(self, method, args): - i = 0 - oRet = None - while i <= THRESHOLD: - if i % 5 == 0: - j = i//5 - self.call_bootstrap(num=None, lToxes=None, i=j) - LOG.debug("wait_ensure_exec " \ - +" " +str(method) - +" " +str(i)) - try: - oRet = method(*args) - if oRet: - LOG.info(f"wait_ensure_exec oRet {oRet!r}") - return True - except ArgumentError as e: - # ArgumentError('This client is currently NOT CONNECTED to the friend.') - # dunno - LOG.warning(f"wait_ensure_exec ArgumentError {e}") - return False - except Exception as e: - LOG.warning(f"wait_ensure_exec EXCEPTION {e}") - return False - sleep(3) - i += 1 - else: - LOG.error(f"wait_ensure_exec i >= {1*THRESHOLD}") - return False - - return oRet - - def bob_add_alice_as_friend_norequest(self): - if hasattr(self, 'baid') and self.baid >= 0 and \ - self.baid in self.bob.self_get_friend_list(): - LOG.warn('Alice is already in bobs friend list') - return True - if self.bob.self_get_friend_list_size() >= 1: - LOG.warn(f'Bob has a friend list {self.bob.self_get_friend_list()}') - return True - - MSG = 'Hi, this is Bob.' - iRet = self.bob.friend_add_norequest(self.alice._address) - self.baid = self.bob.friend_by_public_key(self.alice._address) - assert self.baid >= 0, self.baid - assert self.bob.friend_exists(self.baid), "bob.friend_exists" - assert not self.bob.friend_exists(self.baid + 1) - assert self.baid in self.bob.self_get_friend_list() - assert self.bob.self_get_friend_list_size() >= 1 - return iRet >= 0 - - def alice_add_bob_as_friend_norequest(self): - if hasattr(self, 'abid') and self.abid >= 0 and \ - self.abid in self.alice.self_get_friend_list(): - LOG.warn('Alice is already in Bobs friend list') - return True - if self.alice.self_get_friend_list_size() >= 1: - LOG.warn(f'Alice has a friend list {self.alice.self_get_friend_list()}') - - MSG = 'Hi Bob, this is Alice.' - iRet = self.alice.friend_add_norequest(self.bob._address) - self.abid = self.alice.friend_by_public_key(self.bob._address) - assert self.abid >= 0, self.abid - assert self.abid in self.alice.self_get_friend_list() - assert self.alice.friend_exists(self.abid), "alice.friend_exists" - assert not self.alice.friend_exists(self.abid + 1) - assert self.alice.self_get_friend_list_size() >= 1 - return iRet >= 0 - - def both_add_as_friend_norequest(self): - assert self.bob_add_alice_as_friend_norequest() - if not hasattr(self, 'baid') or self.baid < 0: - raise AssertionError("both_add_as_friend_norequest bob, 'baid'") - - assert self.alice_add_bob_as_friend_norequest() - if not hasattr(self, 'abid') or self.abid < 0: - raise AssertionError("both_add_as_friend_norequest alice, 'abid'") - - #: Test last online - assert self.alice.friend_get_last_online(self.abid) is not None - assert self.bob.friend_get_last_online(self.baid) is not None - return True - - def bob_add_alice_as_friend(self): - """ - t:friend_add - t:on_friend_request - t:friend_by_public_key - """ - MSG = 'Alice, this is Bob.' - sSlot = 'friend_request' - - def alices_on_friend_request(iTox, - public_key, - message_data, - message_data_size, - *largs): - LOG_DEBUG(f"alices_on_friend_request: " +repr(message_data)) - try: - assert str(message_data, 'UTF-8') == MSG - LOG_INFO(f"alices_on_friend_request: friend_added = True ") - except Exception as e: - LOG_WARN(f"alices_on_friend_request: Exception {e}") - # return - setattr(self.bob, sSlot, True) - - setattr(self.bob, sSlot, None) - inum = -1 - self.alice.callback_friend_request(alices_on_friend_request) - try: - inum = self.bob.friend_add(self.alice._address, bytes(MSG, 'UTF-8')) - if not inum >= 0: - LOG.warning('bob.friend_add !>= 0 ' +repr(inum)) - if not self.wait_otox_attrs(self.bob, [sSlot]): - return False - except Exception as e: - LOG.error(f"bob.friend_add EXCEPTION {e}") - return False - finally: - self.bob.callback_friend_message(None) - - self.baid = self.bob.friend_by_public_key(self.alice._address) - assert self.baid >= 0, self.baid - assert self.bob.friend_exists(self.baid) - assert not self.bob.friend_exists(self.baid + 1) - assert self.baid in self.bob.self_get_friend_list() - assert self.bob.self_get_friend_list_size() >= 1 - return True - - def alice_add_bob_as_friend(self): - """ - t:friend_add - t:on_friend_request - t:friend_by_public_key - """ - MSG = 'Bob, this is Alice.' - sSlot = 'friend_request' - - def bobs_on_friend_request(iTox, - public_key, - message_data, - message_data_size, - *largs): - LOG_DEBUG(f"bobs_on_friend_request: " +repr(message_data)) - try: - assert str(message_data, 'UTF-8') == MSG - LOG_INFO(f"bobs_on_friend_request: friend_added = True ") - except Exception as e: - LOG_WARN(f"bobs_on_friend_request: Exception {e}") - # return - else: - setattr(self.alice, sSlot, True) - - setattr(self.alice, sSlot, None) - inum = -1 - self.bob.callback_friend_request(bobs_on_friend_request) - try: - inum = self.alice.friend_add(self.bob._address, bytes(MSG, 'UTF-8')) - if not inum >= 0: - LOG.warning('alice.friend_add !>= 0 ' +repr(inum)) - if not self.wait_obj_attr(self.alice, sSlot): - return False - except Exception as e: - LOG.error(f"alice.friend_add EXCEPTION {e}") - return False - finally: - self.bob.callback_friend_message(None) - self.abid = self.alice.friend_by_public_key(self.bob._address) - assert self.abid >= 0, self.abid - assert self.alice.friend_exists(self.abid) - assert not self.alice.friend_exists(self.abid + 1) - assert self.abid in self.alice.self_get_friend_list() - assert self.alice.self_get_friend_list_size() >= 1 - return True - - def both_add_as_friend(self): - assert self.bob_add_alice_as_friend() - assert self.alice_add_bob_as_friend() - - #: Test last online - assert self.alice.friend_get_last_online(self.abid) is not None - assert self.bob.friend_get_last_online(self.baid) is not None - - def bob_add_alice_as_friend_and_status(self): - if oTOX_OARGS.bIS_LOCAL: - assert self.bob_add_alice_as_friend_norequest() - else: - assert self.bob_add_alice_as_friend() - - #: Wait until both are online - self.bob.friend_conn_status = False - def bobs_on_friend_connection_status(iTox, friend_id, iStatus, *largs): - LOG_INFO(f"bobs_on_friend_connection_status {friend_id} ?>=0" +repr(iStatus)) - if iStatus > 0: - self.bob.friend_conn_status = True - - self.bob.friend_status = None - def bobs_on_friend_status(iTox, friend_id, iStatus, *largs): - LOG_INFO(f"bobs_on_friend_status {friend_id} ?>=0" +repr(iStatus)) - if iStatus > 0: - self.bob.friend_status = True - - self.alice.friend_conn_status = None - def alices_on_friend_connection_status(iTox, friend_id, iStatus, *largs): - LOG_INFO(f"alices_on_friend_connection_status {friend_id} ?>=0 " +repr(iStatus)) - if iStatus > 0: - self.alice.friend_conn_status = True - - self.alice.friend_status = False - def alices_on_friend_status(iTox, friend_id, iStatus, *largs): - LOG_INFO(f"alices_on_friend_status {friend_id} ?>=0 " +repr(iStatus)) - if iStatus > 0: - self.alice.friend_status = True - - self.alice.callback_friend_connection_status(alices_on_friend_connection_status) - self.alice.callback_friend_status(alices_on_friend_status) - try: - LOG.info("bob_add_alice_as_friend_and_status waiting for alice connections") - if not self.wait_otox_attrs(self.alice, - ['friend_conn_status', - 'friend_status']): - return False - - self.bob.callback_friend_connection_status(bobs_on_friend_connection_status) - self.bob.callback_friend_status(bobs_on_friend_status) - - LOG.info("bob_add_alice_as_friend_and_status waiting for bob connections") - if not self.wait_otox_attrs(self.bob, - ['friend_conn_status', - 'friend_status']): - return False - except Exception as e: - LOG.error(f"bob_add_alice_as_friend_and_status ERROR {e}") - return False - finally: - self.alice.callback_friend_connection_status(None) - self.bob.callback_friend_connection_status(None) - self.alice.callback_friend_status(None) - self.bob.callback_friend_status(None) - return True - - def friend_delete(self, fname, baid): - #: Test delete friend - assert getattr(self, fname).friend_exists(baid) - getattr(self, fname).friend_delete(baid) - self.loop(50) - assert not self.bob.friend_exists(baid) - - def warn_if_no_cb(self, alice, sSlot): - if not hasattr(alice, sSlot+'_cb') or \ - not getattr(alice, sSlot+'_cb'): - LOG.warning(f"self.bob.{sSlot}_cb NOT EXIST") - - def warn_if_cb(self, alice, sSlot): - if hasattr(self.bob, sSlot+'_cb') and \ - getattr(self.bob, sSlot+'_cb'): - LOG.warning(f"self.bob.{sSlot}_cb EXIST") - - # tests are executed in order - def test_notice_log(self): # works - notice = '/var/lib/tor/.SelekTOR/3xx/cache/9050/notice.log' - if True or os.path.exists(notice): - iRet = os.system(f"sudo sed -e '1,/.notice. Bootstrapped 100%/d' {notice}" + \ - "| grep 'Tried for 120 seconds to get a connection to :0.'") - if iRet == 0: - raise SystemExit("seconds to get a connection to :0") - else: - LOG.debug(f"checked {notice}") - - def test_tests_logging(self): # works - with self.assertLogs('foo', level='INFO') as cm: - logging.getLogger('foo').info('first message') - logging.getLogger('foo.bar').error('second message') - logging.getLogger('foo.bar.baz').debug('third message') - self.assertEqual(cm.output, ['INFO:foo:first message', - 'ERROR:foo.bar:second message']) - - def test_tests_start(self): # works - LOG.info("test_tests_start " ) - port = ts.tox_bootstrapd_port() - - assert len(self.bob._address) == 2*TOX_ADDRESS_SIZE, len(self.bob._address) - assert len(self.alice._address) == 2*TOX_ADDRESS_SIZE, \ - len(self.alice._address) - - def test_bootstrap_local_netstat(self): # works - """ - t:bootstrap - """ - if oTOX_OARGS.network not in ['new', 'newlocal', 'local']: - return - - port = ts.tox_bootstrapd_port() - if not port: - return - iStatus = os.system(f"""netstat -nle4 | grep :{port}""") - if iStatus == 0: - LOG.info(f"bootstrap_local_netstat port {port} iStatus={iStatus}") - else: - LOG.warning(f"bootstrap_local_netstat NOT {port} iStatus={iStatus}") - - @unittest.skipIf(not bIS_LOCAL, "local test") - def test_bootstrap_local(self): # works - """ - t:bootstrap - """ - # get port from /etc/tox-bootstrapd.conf 33445 - self.call_bootstrap() - # ts.bootstrap_local(self, self.lUdp) - i = 0 - iStatus = -1 - while i < 10: - i = i + 1 - iStatus = self.bob.self_get_connection_status() - if iStatus != TOX_CONNECTION['NONE']: - break - sleep(3) - else: - pass - - o1 = self.alice.self_get_dht_id() - assert len(o1) == 64 - o2 = self.bob.self_get_dht_id() - assert len(o2) == 64 - -# if o1 != o2: LOG.warning(f"bootstrap_local DHT NOT same {o1} {o2} iStatus={iStatus}") - - iStatus = self.bob.self_get_connection_status() - if iStatus != TOX_CONNECTION['NONE']: - LOG.info(f"bootstrap_local connected iStatus={iStatus}") - return True - iStatus = self.alice.self_get_connection_status() - if iStatus != TOX_CONNECTION['NONE']: - LOG.info(f"bootstrap_local connected iStatus={iStatus}") - return True - LOG.warning(f"bootstrap_local NOT CONNECTED iStatus={iStatus}") - return False - - def test_bootstrap_iNmapInfo(self): # works - if os.environ['USER'] != 'root': - return - if oTOX_OARGS.network in ['new', 'newlocal', 'localnew']: - lElts = self.lUdp - elif oTOX_OARGS.proxy_port > 0: - lElts = self.lTcp - else: - lElts = self.lUdp - lRetval = [] - random.shuffle(lElts) - # assert - ts.bootstrap_iNmapInfo(lElts, oTOX_OARGS, bIS_LOCAL, iNODES=8) - - def test_self_get_secret_key(self): # works - """ - t:self_get_secret_key - """ - # test_self_get_secret_key - CRYPTO_SECRET_KEY_SIZE = 32 - secret_key = create_string_buffer(CRYPTO_SECRET_KEY_SIZE) - oRet0 = self.alice.self_get_secret_key(secret_key) - assert oRet0, repr(oRet0) - LOG.info('test_self_get_secret_key ' +repr(oRet0)) - assert len(str(oRet0)) - del secret_key - - def test_self_get_public_keys(self): # works - """ - t:self_get_secret_key - t:self_get_public_key - """ - - LOG.info('test_self_get_public_keys self.alice.self_get_secret_key') - oRet0 = self.alice.self_get_secret_key() - assert len(oRet0) - LOG.info('test_self_get_public_keys ' +repr(oRet0)) - oRet1 = self.alice.self_get_public_key() - assert len(oRet1) - LOG.info('test_self_get_public_keys ' +repr(oRet1)) - assert oRet0 != oRet1, repr(oRet0) +' != ' +repr(oRet1) - - def test_self_name(self): # works - """ - t:self_set_name - t:self_get_name - t:self_get_name_size - """ - self.alice.self_set_name('Alice') - assert self.alice.self_get_name() == 'Alice' - assert self.alice.self_get_name_size() == len('Alice') - self.bob.self_set_name('Bob') - assert self.bob.self_get_name() == 'Bob' - assert self.bob.self_get_name_size() == len('Bob') - - @unittest.skip('loud') - @unittest.skipIf(bIS_NOT_TOXYGEN or oTOX_OARGS.mode == 0, 'not testing in toxygen') - def test_sound_notification(self): # works - """ - Plays sound notification - :param type of notification - """ - from tests.toxygen_tests import test_sound_notification - test_sound_notification(self) - - def test_address(self): # works - """ - t:self_get_address - t:self_get_nospam - t:self_set_nospam - t:self_get_keys - """ - assert len(self.alice.self_get_address()) == ADDR_SIZE - assert len(self.bob.self_get_address()) == ADDR_SIZE - - self.alice.self_set_nospam(0x12345678) - assert self.alice.self_get_nospam() == 0x12345678 - self.loop(50) - - if hasattr(self.alice, 'self_get_keys'): - pk, sk = self.alice.self_get_keys() - assert pk == self.alice.self_get_address()[:CLIENT_ID_SIZE] - - def test_status_message(self): # works - MSG = 'Happy' - self.alice.self_set_status_message(MSG) - self.loop(100) - assert self.alice.self_get_status_message() == MSG, \ - self.alice.self_get_status_message() +' is not ' +MSG - assert self.alice.self_get_status_message_size() == len(MSG) - - def test_loop_until_connected(self): # works - assert self.loop_until_connected() - - def test_self_get_udp_port(self): # works - """ - t:self_get_udp_port - """ - if hasattr(oTOX_OPTIONS, 'udp_port') and oTOX_OPTIONS.udp_port: - o = self.alice.self_get_udp_port() - LOG.info('self_get_udp_port alice ' +repr(o)) - assert o > 0 - o = self.bob.self_get_udp_port() - LOG.info('self_get_udp_port bob ' +repr(o)) - assert o > 0 - - def test_self_get_tcp_port(self): # works - """ - t:self_get_tcp_port - """ - if hasattr(oTOX_OPTIONS, 'tcp_port') and oTOX_OPTIONS.tcp_port: - # errors if tcp_port <= 0 - o = self.alice.self_get_tcp_port() - LOG.info('self_get_tcp_port ' +repr(o)) - o = self.bob.self_get_tcp_port() - LOG.info('self_get_tcp_port ' +repr(o)) - - def test_get_dht_id(self): # works - """ - t:self_get_dht_id - """ - o1 = self.alice.self_get_dht_id() - assert len(o1) == 64 - o2 = self.bob.self_get_dht_id() - assert len(o2) == 64 - - def test_bob_assert_connection_status(self): # works - if self.bob.self_get_connection_status() == TOX_CONNECTION['NONE']: - RuntimeError("ERROR: NOT CONNECTED " \ - +repr(self.bob.self_get_connection_status())) - - def test_alice_assert_connection_status(self): # works - if self.alice.self_get_connection_status() == TOX_CONNECTION['NONE']: - RuntimeError("ERROR: NOT CONNECTED " \ - +repr(self.alice.self_get_connection_status())) - - def test_bob_assert_mycon_status(self): # works - if self.bob.mycon_status == False: - RuntimeError("ERROR: NOT CONNECTED " \ - +repr(self.bob.mycon_status)) - - def test_alice_assert_mycon_status(self): # works - if self.alice.mycon_status == False: - RuntimeError("ERROR: NOT CONNECTED " \ - +repr(self.alice.mycon_status)) - - def test_bob_add_alice_as_friend_norequest(self): # works - assert len(self.bob.self_get_friend_list()) == 0 - assert self.bob_add_alice_as_friend_norequest() - #: Test last online - assert self.bob.friend_get_last_online(self.baid) is not None - self.bob.friend_delete(self.baid) - - def test_alice_add_bob_as_friend_norequest(self): # works - assert len(self.alice.self_get_friend_list()) == 0 - assert self.alice_add_bob_as_friend_norequest() - assert len(self.alice.self_get_friend_list()) != 0 - #: Test last online - assert self.alice.friend_get_last_online(self.abid) is not None - self.alice.friend_delete(self.abid) - - def test_both_add_as_friend_norequest(self): # works - assert len(self.bob.self_get_friend_list()) == 0 - assert len(self.alice.self_get_friend_list()) == 0 - self.both_add_as_friend_norequest() - - self.bob.friend_delete(self.baid) - self.alice.friend_delete(self.abid) - assert len(self.bob.self_get_friend_list()) == 0 - assert len(self.alice.self_get_friend_list()) == 0 - - def test_bob_add_alice_as_friend_and_status(self): - self.bob_add_alice_as_friend_and_status() - self.bob.friend_delete(self.baid) - - @unittest.skip('malloc_consolidate(): invalid chunk size') -# @unittest.skipIf(bIS_LOCAL, "local test") -# @expectedFailure # (bIS_LOCAL, "local test") - def test_bob_add_alice_as_friend(self): # fails - assert len(self.bob.self_get_friend_list()) == 0 - try: - assert self.bob_add_alice_as_friend() - #: Test last online - assert self.bob.friend_get_last_online(self.baid) is not None - except AssertionError as e: - #WTF? - self.bob.friend_delete(self.baid) - raise RuntimeError(f"Failed test {e}") - finally: - self.bob.friend_delete(self.baid) - assert len(self.bob.self_get_friend_list()) == 0 - - @unittest.skip('malloc_consolidate(): invalid chunk size') -# @unittest.skipIf(bIS_LOCAL, "local test") -# @expectedFailure - def test_alice_add_bob_as_friend(self): # fails - assert len(self.bob.self_get_friend_list()) == 0 - try: - assert self.alice_add_bob_as_friend() - #: Test last online - assert self.alice.friend_get_last_online(self.abid) is not None - except AssertionError as e: - raise RuntimeError(f"Failed test {e}") - except Exception as e: - LOG.error(f"test_alice_add_bob_as_friend EXCEPTION {e}") - raise - finally: - self.alice.friend_delete(self.abid) - assert len(self.alice.self_get_friend_list()) == 0 - -# @unittest.skipIf(bIS_LOCAL, "local test") - @expectedFailure - def test_both_add_as_friend(self): # works - try: - self.both_add_as_friend() - except AssertionError as e: - raise RuntimeError(f"Failed test {e}") - except Exception as e: - LOG.error(f"test_both_add_as_friend EXCEPTION {e}") - raise - finally: - self.bob.friend_delete(self.baid) - self.alice.friend_delete(self.abid) - assert len(self.bob.self_get_friend_list()) == 0 - assert len(self.alice.self_get_friend_list()) == 0 - - @unittest.skip('unfinished') - def test_bob_add_alice_as_friend_and_status(self): - assert self.bob_add_alice_as_friend_and_status() - self.bob.friend_delete(self.baid) - -#? @unittest.skip('fails') - @expectedFailure - def test_on_friend_status_message(self): # fails - """ - t:self_set_status_message - t:self_get_status_message - t:self_get_status_message_size - t:friend_set_status_message - t:friend_get_status_message - t:friend_get_status_message_size - t:on_friend_status_message - """ - MSG = 'Happy' - sSlot = 'friend_status_message' - - def bob_on_friend_status_message(iTox, friend_id, new_status_message, new_status_size, *largs): - try: - assert str(new_status_message, 'UTF-8') == MSG - assert friend_id == self.baid - except Exception as e: - LOG_ERROR(f"BOB_ON_friend_status_message EXCEPTION {e}") - else: - LOG_INFO(f"BOB_ON_friend_status_message {friend_id}" \ - +repr(new_status_message)) - setattr(self.bob, sSlot, True) - - setattr(self.bob, sSlot, None) - try: - if oTOX_OARGS.bIS_LOCAL: - assert self.bob_add_alice_as_friend_norequest() - else: - assert self.bob_add_alice_as_friend() - - self.bob.callback_friend_status_message(bob_on_friend_status_message) - self.warn_if_no_cb(self.bob, sSlot) - self.alice.self_set_status_message(MSG) - assert self.wait_otox_attrs(self.bob, [sSlot]) - - assert self.bob.friend_get_status_message(self.baid) == MSG - assert self.bob.friend_get_status_message_size(self.baid) == len(MSG) - - except AssertionError as e: - raise RuntimeError(f"Failed test {e}") - except Exception as e: - LOG.error(f"test_on_friend_status_message EXCEPTION {e}") - raise - finally: - self.alice.callback_friend_status(None) - self.bob.friend_delete(self.baid) - - @expectedFailure - def test_friend(self): # works - """ - t:friend_delete - t:friend_exists - t:friend_get_public_key - t:self_get_friend_list - t:self_get_friend_list_size - t:self_set_name - t:friend_get_name - t:friend_get_name_size - t:on_friend_name - """ - - assert len(self.bob.self_get_friend_list()) == 0 - assert len(self.alice.self_get_friend_list()) == 0 - #: Test friend request - if oTOX_OARGS.bIS_LOCAL: - assert self.bob_add_alice_as_friend_norequest() - assert self.alice_add_bob_as_friend_norequest() - else: - # no not connected error - assert self.bob_add_alice_as_friend() - assert self.alice_add_bob_as_friend() - try: - assert self.bob.friend_get_public_key(self.baid) == \ - self.alice.self_get_address()[:CLIENT_ID_SIZE] - - #: Test friend_get_public_key - assert self.alice.friend_get_public_key(self.abid) == \ - self.bob.self_get_address()[:CLIENT_ID_SIZE] - except AssertionError as e: - raise RuntimeError(f"Failed test {e}") - except Exception as e: - LOG.error(f"test_friend EXCEPTION {e}") - raise - finally: - self.bob.friend_delete(self.baid) - self.alice.friend_delete(self.abid) - -# @unittest.skip('fails') -# @unittest.skipIf(not bIS_LOCAL and not ts.bAreWeConnected(), 'NOT CONNECTED') - @expectedFailure - def test_user_status(self): - """ - t:self_get_status - t:self_set_status - t:friend_get_status - t:friend_get_status - t:on_friend_status - """ - sSlot = 'friend_status' - if oTOX_OARGS.bIS_LOCAL: - assert self.bob_add_alice_as_friend_norequest() - else: - assert self.bob_add_alice_as_friend() - - sSTATUS = TOX_USER_STATUS['NONE'] - setattr(self.bob, sSlot, None) - def bobs_on_friend_set_status(iTox, friend_id, new_status, *largs): - LOG_INFO(f"bobs_on_friend_set_status {friend_id} {new_status}") - try: - assert friend_id == self.baid - assert new_status in [TOX_USER_STATUS['BUSY'], TOX_USER_STATUS['AWAY']] - except Exception as e: - LOG_WARN(f"bobs_on_friend_set_status EXCEPTION {e}") - setattr(self.bob, sSlot, True) - - try: - if not self.get_connection_status(): - LOG.warning(f"test_user_status NOT CONNECTED self.get_connection_status") - self.loop_until_connected() - - self.bob.callback_friend_status(bobs_on_friend_set_status) - self.warn_if_no_cb(self.bob, sSlot) - sSTATUS = TOX_USER_STATUS['BUSY'] - self.alice.self_set_status(sSTATUS) - sSTATUS = TOX_USER_STATUS['AWAY'] - self.alice.self_set_status(sSTATUS) - assert self.wait_otox_attrs(self.bob, [sSlot]) - # wait_obj_attr count >= 15 for friend_status - - self.alice.self_set_status(TOX_USER_STATUS['NONE']) - assert self.alice.self_get_status() == TOX_USER_STATUS['NONE'] - assert self.bob.friend_get_status(self.baid) == TOX_USER_STATUS['NONE'] - - except AssertionError as e: - raise RuntimeError(f"Failed test {e}") - - except Exception as e: - LOG.error(f"test_user_status EXCEPTION {e}") - raise - finally: - self.bob.callback_friend_status(None) - self.warn_if_cb(self.bob, sSlot) - self.bob.friend_delete(self.baid) - - @unittest.skip('crashes') - def test_connection_status(self): - """ - t:friend_get_connection_status - t:on_friend_connection_status - """ - LOG.info("test_connection_status ") - if oTOX_OARGS.bIS_LOCAL: - assert self.bob_add_alice_as_friend_norequest() - else: - assert self.bob_add_alice_as_friend() - - sSlot = 'friend_connection_status' - setattr(self.bob, sSlot, None) - def bobs_on_friend_connection_status(iTox, friend_id, iStatus, *largs): - setattr(self.bob, sSlot, True) - LOG_INFO(f"bobs_on_friend_connection_status " +repr(iStatus)) - try: - assert friend_id == self.baid - except Exception as e: - LOG.error(f"bobs_on_friend_connection_status ERROR {e}") - - opts = oToxygenToxOptions(oTOX_OARGS) - try: - setattr(self.bob, sSlot, True) - self.bob.callback_friend_connection_status(bobs_on_friend_connection_status) - - LOG.info("test_connection_status killing alice") - self.alice.kill() #! bang - LOG.info("test_connection_status making alice") - self.alice = Tox(opts, app=oAPP) - LOG.info("test_connection_status maked alice") - - assert self.wait_otox_attrs(self.bob, [sSlot]) - except AssertionError as e: - raise - except Exception as e: - LOG.error(f"bobs_on_friend_connection_status {e}") - raise - finally: - self.bob.callback_friend_connection_status(None) - - #? assert self.bob.friend_get_connection_status(self.aid) is False - self.bob.friend_delete(self.baid) - -#? @unittest.skip('fails') - def test_friend_name(self): # fails - """ - t:self_set_name - t:friend_get_name - t:friend_get_name_size - t:on_friend_name - """ - - sSlot= 'friend_name' - #: Test friend request - - LOG.info("test_friend_name") - if oTOX_OARGS.bIS_LOCAL: - assert self.bob_add_alice_as_friend_norequest() - else: - assert self.bob_add_alice_as_friend() - - if not self.get_connection_status(): - LOG.warning(f"test_friend_message NOT CONNECTED") - self.loop_until_connected() - - #: Test friend name - NEWNAME = 'Jenny' - - def bobs_on_friend_name(iTox, fid, newname, iNameSize, *largs): - LOG_INFO(f"bobs_on_friend_name {sSlot} {fid}") - try: - assert fid == self.baid - assert str(newname, 'UTF-8') == NEWNAME - except Exception as e: - LOG.error(f"bobs_on_friend_name EXCEPTION {e}") - setattr(self.bob, sSlot, True) - - setattr(self.bob, sSlot, None) - self.bob.callback_friend_name(bobs_on_friend_name) - self.warn_if_no_cb(self.bob, sSlot) - try: - self.alice.self_set_name(NEWNAME) - assert self.wait_otox_attrs(self.bob, [sSlot]) - - assert self.bob.friend_get_name(self.baid) == NEWNAME - assert self.bob.friend_get_name_size(self.baid) == len(NEWNAME) - - except AssertionError as e: - raise RuntimeError(f"test_friend Failed test {e}") - - except Exception as e: - LOG.error(f"test_friend EXCEPTION {e}") - raise - - finally: - self.bob.callback_friend_name(None) - if hasattr(self.bob, sSlot + '_cb') and \ - getattr(self.bob, sSlot + '_cb'): - LOG.warning(sSlot + ' EXISTS') - - self.bob.friend_delete(self.baid) - - # wait_ensure_exec ArgumentError This client is currently not connected to the friend. - def test_friend_message(self): # fails - """ - t:on_friend_action - t:on_friend_message - t:friend_send_message - """ - - #: Test message - MSG = 'Hi, Bob!' - sSlot = 'friend_message' - if oTOX_OARGS.bIS_LOCAL: - assert self.both_add_as_friend_norequest() - else: - assert self.both_add_as_friend() - - if not self.get_connection_status(): - LOG.warning(f"test_friend_message NOT CONNECTED") - self.loop_until_connected() - - iRet = self.bob.friend_get_connection_status(self.baid) - if iRet == TOX_CONNECTION['NONE']: - LOG.error("bob.friend_get_connection_status") - raise RuntimeError("bob.friend_get_connection_status") - iRet = self.alice.friend_get_connection_status(self.abid) - if iRet == TOX_CONNECTION['NONE']: - LOG.error("alice.friend_get_connection_status") - raise RuntimeError("alice.friend_get_connection_status") - - def alices_on_friend_message(iTox, fid, msg_type, message, iSize, *largs): - LOG_DEBUG(f"alices_on_friend_message {fid} {message}") - try: - assert fid == self.alice.abid - assert msg_type == TOX_MESSAGE_TYPE['NORMAL'] - assert str(message, 'UTF-8') == MSG - except Exception as e: - LOG_ERROR(f"alices_on_friend_message EXCEPTION {e}") - else: - LOG_INFO(f"alices_on_friend_message {message}") - setattr(self.alice, sSlot, True) - - setattr(self.alice, sSlot, None) - try: - self.alice.callback_friend_message(alices_on_friend_message) - self.warn_if_no_cb(self.alice, sSlot) - - # dunno - both This client is currently NOT CONNECTED to the friend. - if True: - iMesId = self.bob.friend_send_message( - self.baid, - TOX_MESSAGE_TYPE['NORMAL'], - bytes(MSG, 'UTF-8')) - # ArgumentError('This client is currently NOT CONNECTED to the friend.') - else: - iMesId = self.wait_ensure_exec(self.bob.friend_send_message, - [self.baid, - TOX_MESSAGE_TYPE['NORMAL'], - bytes(MSG, 'UTF-8')]) - assert iMesId >= 0 - assert self.wait_otox_attrs(self.alice, [sSlot]) - except ArgumentError as e: - # ArgumentError('This client is currently NOT CONNECTED to the friend.') - # dunno - LOG.error(f"test_friend_message {e}") - raise - except AssertionError as e: - LOG.warning(f"test_friend_message {e}") - raise RuntimeError(f"Failed test test_friend_message {e}") - except Exception as e: - LOG.error(f"test_friend_message {e}") - raise - finally: - self.alice.callback_friend_message(None) - self.warn_if_cb(self.alice, sSlot) - self.bob.friend_delete(self.baid) - self.alice.friend_delete(self.abid) - -#? @unittest.skip('fails') - def test_friend_action(self): - """ - t:on_friend_action - t:on_friend_message - t:friend_send_message - """ - - if oTOX_OARGS.bIS_LOCAL: - assert self.both_add_as_friend_norequest() - else: - assert self.both_add_as_friend() - - if not self.get_connection_status(): - LOG.warning(f"test_friend_message NOT CONNECTED") - self.loop_until_connected() - - iRet = self.bob.friend_get_connection_status(self.baid) - if iRet == TOX_CONNECTION['NONE']: - LOG.error("bob.friend_get_connection_status") - raise RuntimeError("bob.friend_get_connection_status") - iRet = self.alice.friend_get_connection_status(self.abid) - if iRet == TOX_CONNECTION['NONE']: - LOG.error("alice.friend_get_connection_status") - raise RuntimeError("alice.friend_get_connection_status") - - BID = self.baid - #: Test action - ACTION = 'Kick' - sSlot = 'friend_read_action' - setattr(self.bob, sSlot, None) - sSlot = 'friend_read_receipt' - setattr(self.bob, sSlot, None) - def alices_on_friend_action(iTox, fid, msg_type, action, *largs): - sSlot = 'friend_read_action' - LOG_DEBUG(f"alices_on_friend_action") - try: - assert fid == self.bob.baid - assert msg_type == TOX_MESSAGE_TYPE['ACTION'] - assert action == ACTION - except Exception as e: - LOG_ERROR(f"alices_on_friend_action EXCEPTION {e}") - else: - LOG_INFO(f"alices_on_friend_action {message}") - setattr(self.bob, sSlot, True) - - sSlot = 'friend_read_action' - setattr(self.alice, sSlot, None) - sSlot = 'friend_read_receipt' - setattr(self.alice, sSlot, None) - def alices_on_read_reciept(iTox, fid, msg_id, *largs): - LOG_DEBUG(f"alices_on_read_reciept") - sSlot = 'friend_read_receipt' - try: - assert fid == BID - except Exception as e: - LOG_ERROR(f"alices_on_read_reciept {e}") - else: - LOG_INFO(f"alices_on_read_reciept {fid}") - setattr(self.alice, sSlot, True) - - sSlot = 'friend_read_receipt' - try: - sSlot = 'friend_read_action' - setattr(self.bob, sSlot, False) - sSlot = 'friend_read_receipt' - setattr(self.alice, sSlot, False) - - self.alice.callback_friend_read_receipt(alices_on_read_reciept) #was alices_on_friend_action - self.warn_if_no_cb(self.alice, sSlot) - assert self.wait_ensure_exec(self.bob.friend_send_message, - [self.baid, - TOX_MESSAGE_TYPE['ACTION'], - bytes(ACTION, 'UTF-8')]) - assert self.wait_otox_attrs(self.alice, [sSlot]) - except AssertionError as e: - raise RuntimeError(f"Failed test {e}") - except ArgumentError as e: - # ArgumentError('This client is currently NOT CONNECTED to the friend.') - # dunno - LOG.warning(f"test_friend_action {e}") - except Exception as e: - LOG.error(f"test_friend_action {e}") - raise - finally: - self.alice.callback_friend_read_receipt(None) - self.bob.friend_delete(self.baid) - self.alice.friend_delete(self.abid) - - @unittest.skip('fails') - def test_alice_typing_status(self): - """ - t:on_friend_read_receipt - t:on_friend_typing - t:self_set_typing - t:friend_get_typing - t:friend_get_last_online - """ - - sSlot = 'friend_typing' - # works - LOG.info("test_typing_status bob adding alice") - if oTOX_OARGS.bIS_LOCAL: - assert self.both_add_as_friend_norequest() - else: - assert self.both_add_as_friend() - - BID = self.baid - - #: Test typing status - def bob_on_friend_typing(iTox, fid, is_typing, *largs): - try: - assert fid == BID - assert is_typing is True - assert self.bob.friend_get_typing(fid) is True - except Exception as e: - LOG.error(f"BOB_ON_friend_typing {e}") - raise - else: - LOG_INFO(f"BOB_ON_friend_typing" + str(fid)) - setattr(self.bob, sSlot, True) - - setattr(self.bob, sSlot, None) - try: - if not self.get_connection_status(): - LOG.warning(f"test_friend_message NOT CONNECTED") - self.loop_until_connected() - - self.bob.callback_friend_typing(bob_on_friend_typing) - self.alice.self_set_typing(self.abid, True) - assert self.wait_otox_attrs(self.bob, [sSlot]) - if not hasattr(self.bob, sSlot+'_cb') or \ - not getattr(self.bob, sSlot+'_cb'): - LOG.warning(f"self.bob.{sSlot}_cb NOT EXIST") - except AssertionError as e: - raise RuntimeError(f"Failed test {e}") - except Exception as e: - LOG.error(f"test_alice_typing_status error={e}") - raise - finally: - self.bob.callback_friend_typing(None) - self.bob.friend_delete(self.baid) - self.alice.friend_delete(self.abid) - - @unittest.skip('unfinished') - def test_file_transfer(self): # unfinished - """ - t:file_send - t:file_send_chunk - t:file_control - t:file_seek - t:file_get_file_id - t:on_file_recv - t:on_file_recv_control - t:on_file_recv_chunk - t:on_file_chunk_request - """ - - if oTOX_OARGS.bIS_LOCAL: - assert self.bob_add_alice_as_friend_norequest() - else: - assert self.bob_add_alice_as_friend() - - BID = self.baid - - FRIEND_NUMBER = self.baid - FILE_NUMBER = 1 - FILE = os.urandom(1024 * 1024) - FILE_NAME = b"/tmp/test.bin" - if not os.path.exists(FILE_NAME): - with open(FILE_NAME, 'wb') as oFd: - oFd.write(FILE) - FILE_SIZE = len(FILE) - OFFSET = 567 - - m = hashlib.md5() - m.update(FILE[OFFSET:]) - FILE_DIGEST = m.hexdigest() - - CONTEXT = { 'FILE': bytes(), 'RECEIVED': 0, 'START': False, 'SENT': 0 } - - def alice_on_file_recv(iTox, fid, file_number, kind, size, filename): - LOG_DEBUG(f"ALICE_ON_file_recv fid={fid} {file_number}") - try: - assert size == FILE_SIZE - assert filename == FILE_NAME - retv = self.alice.file_seek(fid, file_number, OFFSET) - assert retv is True - self.alice.file_control(fid, file_number, TOX_FILE_CONTROL['RESUME']) - except Exception as e: - LOG_ERROR(f"ALICE_ON_file_recv {e}") - else: - LOG_INFO(f"ALICE_ON_file_recv " + str(fid)) - - def alice_on_file_recv_control(iTox, fid, file_number, control, *largs): - # TOX_FILE_CONTROL = { 'RESUME': 0, 'PAUSE': 1, 'CANCEL': 2,} - LOG_DEBUG(f"ALICE_ON_file_recv_control fid={fid} {file_number} {control}") - try: - assert FILE_NUMBER == file_number - # FixMe _FINISHED? - if False and control == TOX_FILE_CONTROL['RESUME']: - # assert CONTEXT['RECEIVED'] == FILE_SIZE - # m = hashlib.md5() - # m.update(CONTEXT['FILE']) - # assert m.hexdigest() == FILE_DIGEST - self.alice.completed = True - except Exception as e: - LOG_ERROR(f"ALICE_ON_file_recv {e}") - else: - LOG_INFO(f"ALICE_ON_file_recv " + str(fid)) - - self.alice.completed = False - def alice_on_file_recv_chunk(iTox, fid, file_number, position, iNumBytes, *largs): - LOG_DEBUG(f"ALICE_ON_file_recv_chunk {fid} {file_number}") - # FixMe - use file_number and iNumBytes to get data? - data = '' - try: - if data is None: - assert CONTEXT['RECEIVED'] == (FILE_SIZE - OFFSET) - m = hashlib.md5() - m.update(CONTEXT['FILE']) - assert m.hexdigest() == FILE_DIGEST - self.alice.completed = True - self.alice.file_control(fid, file_number, TOX_FILE_CONTROL['CANCEL']) - return - - CONTEXT['FILE'] += data - CONTEXT['RECEIVED'] += len(data) - # if CONTEXT['RECEIVED'] < FILE_SIZE: - # assert self.file_data_remaining( - # fid, file_number, 1) == FILE_SIZE - CONTEXT['RECEIVED'] - except Exception as e: - LOG_ERROR(f"ALICE_ON_file_recv_chunk {e}") - else: - LOG_INFO(f"ALICE_ON_file_recv_chunk {fid}") - - # AliceTox.on_file_send_request = on_file_send_request - # AliceTox.on_file_control = on_file_control - # AliceTox.on_file_data = on_file_data - - LOG.info(f"test_file_transfer: baid={self.baid}") - try: - self.alice.callback_file_recv(alice_on_file_recv) - self.alice.callback_file_recv_control(alice_on_file_recv_control) - self.alice.callback_file_recv_chunk(alice_on_file_recv_chunk) - - self.bob.completed = False - def bob_on_file_recv_control2(iTox, fid, file_number, control): - LOG_DEBUG(f"BOB_ON_file_recv_control2 {fid} {file_number} control={control}") - if control == TOX_FILE_CONTROL['RESUME']: - CONTEXT['START'] = True - elif control == TOX_FILE_CONTROL['CANCEL']: - self.bob.completed = True - pass - - def bob_on_file_chunk_request(iTox, fid, file_number, position, length, *largs): - LOG_DEBUG(f"BOB_ON_file_chunk_request {fid} {file_number}") - if length == 0: - return - data = FILE[position:(position + length)] - self.bob.file_send_chunk(fid, file_number, position, data) - - sSlot = 'file_recv_control' - self.bob.callback_file_recv_control(bob_on_file_recv_control2) - self.bob.callback_file_chunk_request(bob_on_file_chunk_request) - - # was FILE_ID = FILE_NAME - FILE_ID = 32*'1' # - FILE_NAME = b'test.in' - - if not self.get_connection_status(): - LOG.warning(f"test_file_transfer NOT CONNECTED") - self.loop_until_connected() - - i = 0 - iKind = 0 - while i < 2: - i += 1 - try: - FN = self.bob.file_send(self.baid, iKind, FILE_SIZE, FILE_ID, FILE_NAME) - LOG.info(f"test_file_transfer bob.file_send {FN}") - except ArgumentError as e: - LOG.debug(f"test_file_transfer bob.file_send {e} {i}") - # ctypes.ArgumentError: This client is currently not connected to the friend. - raise - else: - break - self.loop(100) - sleep(1) - else: - LOG.error(f"test_file_transfer bob.file_send 2") - raise RuntimeError(f"test_file_transfer bob.file_send {THRESHOLD // 2}") - - # UINT32_MAX - FID = self.bob.file_get_file_id(self.baid, FN) - hexFID = "".join([hex(ord(c))[2:].zfill(2) for c in FILE_NAME]) - assert FID.startswith(hexFID.upper()) - - if not self.wait_obj_attrs(self.bob, ['completed']): - LOG.warning(f"test_file_transfer Bob not completed") - return False - if not self.wait_obj_attrs(self.alice, ['completed']): - LOG.warning(f"test_file_transfer Alice not completed") - return False - return True - - except (ArgumentError, ValueError,) as e: - # ValueError: non-hexadecimal number found in fromhex() arg at position 0 - LOG_ERROR(f"test_file_transfer: {e}") - raise - - except Exception as e: - LOG_ERROR(f"test_file_transfer:: {e}") - LOG_DEBUG('\n' + traceback.format_exc()) - raise - - finally: - self.bob.friend_delete(self.baid) - self.alice.callback_file_recv(None) - self.alice.callback_file_recv_control(None) - self.alice.callback_file_recv_chunk(None) - self.bob.callback_file_recv_control(None) - self.bob.callback_file_chunk_request(None) - - LOG_INFO(f"test_file_transfer:: self.wait_objs_attr completed") - - @unittest.skip('crashes') - def test_tox_savedata(self): # works sorta - # but "{addr} != {self.alice.self_get_address()}" - """ - t:get_savedata_size - t:get_savedata - """ - # Fatal Python error: Aborted - # "/var/local/src/toxygen_wrapper/wrapper/tox.py", line 180 in kill - return - - assert self.alice.get_savedata_size() > 0 - data = self.alice.get_savedata() - assert data is not None - addr = self.alice.self_get_address() - # self._address - - try: - LOG.info("test_tox_savedata alice.kill") - # crashes - self.alice.kill() - except: - pass - - oArgs = oTOX_OARGS - opts = oToxygenToxOptions(oArgs) - opts.savedata_data = data - opts.savedata_length = len(data) - - self.alice = Tox(tox_options=opts) - if addr != self.alice.self_get_address(): - LOG.warning("test_tox_savedata " + - f"{addr} != {self.alice.self_get_address()}") - else: - LOG.info("passed test_tox_savedata") - -def vOargsToxPreamble(oArgs, Tox, ToxTest): - - ts.vSetupLogging(oArgs) - - methods = set([x for x in dir(Tox) if not x[0].isupper() - and not x[0] == '_']) - docs = "".join([getattr(ToxTest, x).__doc__ for x in dir(ToxTest) - if getattr(ToxTest, x).__doc__ is not None]) - - tested = set(re.findall(r't:(.*?)\n', docs)) - not_tested = methods.difference(tested) - - logging.info('Test Coverage: %.2f%%' % (len(tested) * 100.0 / len(methods))) - if len(not_tested): - logging.info('Not tested:\n %s' % "\n ".join(sorted(list(not_tested)))) - -### - -def iMain(oArgs): - failfast=True - - vOargsToxPreamble(oArgs, Tox, ToxSuite) - # https://stackoverflow.com/questions/35930811/how-to-sort-unittest-testcases-properly/35930812#35930812 - cases = ts.suiteFactory(*ts.caseFactory([ToxSuite])) - if color_runner: - runner = color_runner.runner.TextTestRunner(verbosity=2, failfast=failfast) - else: - runner = unittest.TextTestRunner(verbosity=2, failfast=failfast, warnings='ignore') - runner.run(cases) - -def oToxygenToxOptions(oArgs): - data = None - 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 - else: - tox_options.contents.ipv6_enabled = oArgs.ipv6_enabled - - tox_options.contents.tcp_port = int(oArgs.tcp_port) - tox_options.contents.dht_announcements_enabled = oArgs.dht_announcements_enabled - tox_options.contents.hole_punching_enabled = oArgs.hole_punching_enabled - - # overrides - tox_options.contents.local_discovery_enabled = False - tox_options.contents.experimental_thread_safety = False - # REQUIRED!! - if oArgs.ipv6_enabled and not os.path.exists('/proc/sys/net/ipv6'): - LOG.warning('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) - - if data: # load existing profile - tox_options.contents.savedata_type = enums.TOX_SAVEDATA_TYPE['TOX_SAVE'] - tox_options.contents.savedata_data = 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 - - #? tox_options.contents.log_callback = LOG - if tox_options._options_pointer: - # LOG.debug("Adding logging to tox_options._options_pointer ") - ts.vAddLoggerCallback(tox_options, ts.on_log) - else: - LOG.warning("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, - help='Path to Tox profile') - 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): - global oTOX_OARGS - if lArgs is None: lArgs = [] - oArgs = oArgparse(lArgs) - global bIS_LOCAL - bIS_LOCAL = oArgs.network in ['newlocal', 'localnew', 'local'] - oTOX_OARGS = oArgs - setattr(oTOX_OARGS, 'bIS_LOCAL', bIS_LOCAL) - bIS_LOCAL = True - setattr(oTOX_OARGS, 'bIS_LOCAL', bIS_LOCAL) - # oTOX_OPTIONS = ToxOptions() - global oTOX_OPTIONS - oTOX_OPTIONS = oToxygenToxOptions(oArgs) - if coloredlogs: - # https://pypi.org/project/coloredlogs/ - coloredlogs.install(level=oArgs.loglevel, - logger=LOG, - # %(asctime)s,%(msecs)03d %(hostname)s [%(process)d] - fmt='%(name)s %(levelname)s %(message)s' - ) - else: - logging.basicConfig(level=oArgs.loglevel) # logging.INFO - - return iMain(oArgs) - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) - -# Ran 33 tests in 51.733s