File transmission: sending

This commit is contained in:
Андрей Владимирович 2016-03-07 00:42:30 +03:00
parent d0a0fe6189
commit f1507a1628
2 changed files with 171 additions and 30 deletions

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ctypes import * from ctypes import c_char_p, Structure, CDLL, c_bool, addressof, c_int, c_size_t, POINTER, c_uint16, c_void_p, c_uint64
from ctypes import create_string_buffer, ArgumentError, CFUNCTYPE, c_uint32, sizeof
from platform import system from platform import system
from toxcore_enums_and_consts import * from toxcore_enums_and_consts import *
@ -88,16 +89,17 @@ class Tox(object):
' badly formatted data, some data may have been loaded, and the rest is discarded.' ' badly formatted data, some data may have been loaded, and the rest is discarded.'
' Passing an invalid length parameter also causes this error.') ' Passing an invalid length parameter also causes this error.')
self.tox_self_connection_status_cb = None self.self_connection_status_cb = None
self.tox_friend_name_cb = None self.friend_name_cb = None
self.friend_status_message_cb = None self.friend_status_message_cb = None
self.tox_friend_status_cb = None self.friend_status_cb = None
self.tox_friend_connection_status_cb = None self.friend_connection_status_cb = None
self.tox_friend_request_cb = None self.friend_request_cb = None
self.tox_friend_read_receipt_cb = None self.friend_read_receipt_cb = None
self.tox_friend_typing_cb = None self.friend_typing_cb = None
self.tox_friend_message_cb = None self.friend_message_cb = None
self.tox_file_recv_control_cb = None self.file_recv_control_cb = None
self.file_chunk_request_cb = None
def __del__(self): def __del__(self):
Tox.libtoxcore.tox_kill(self._tox_pointer) Tox.libtoxcore.tox_kill(self._tox_pointer)
@ -259,9 +261,9 @@ class Tox(object):
:param user_data: pointer (c_void_p) to user data :param user_data: pointer (c_void_p) to user data
""" """
c_callback = CFUNCTYPE(None, c_void_p, c_int, c_void_p) c_callback = CFUNCTYPE(None, c_void_p, c_int, c_void_p)
self.tox_self_connection_status_cb = c_callback(callback) self.self_connection_status_cb = c_callback(callback)
Tox.libtoxcore.tox_callback_self_connection_status(self._tox_pointer, Tox.libtoxcore.tox_callback_self_connection_status(self._tox_pointer,
self.tox_self_connection_status_cb, user_data) self.self_connection_status_cb, user_data)
def iteration_interval(self): def iteration_interval(self):
""" """
@ -711,8 +713,8 @@ class Tox(object):
:param user_data: pointer (c_void_p) to user data :param user_data: pointer (c_void_p) to user data
""" """
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p) c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_char_p, c_size_t, c_void_p)
self.tox_friend_name_cb = c_callback(callback) self.friend_name_cb = c_callback(callback)
Tox.libtoxcore.tox_callback_friend_name(self._tox_pointer, self.tox_friend_name_cb, user_data) Tox.libtoxcore.tox_callback_friend_name(self._tox_pointer, self.friend_name_cb, user_data)
def friend_get_status_message_size(self, friend_number): def friend_get_status_message_size(self, friend_number):
""" """
@ -815,8 +817,8 @@ class Tox(object):
:param user_data: pointer (c_void_p) to user data :param user_data: pointer (c_void_p) to user data
""" """
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p)
self.tox_friend_status_cb = c_callback(callback) self.friend_status_cb = c_callback(callback)
Tox.libtoxcore.tox_callback_friend_status(self._tox_pointer, self.tox_friend_status_cb, c_void_p(user_data)) Tox.libtoxcore.tox_callback_friend_status(self._tox_pointer, self.friend_status_cb, c_void_p(user_data))
def friend_get_connection_status(self, friend_number): def friend_get_connection_status(self, friend_number):
""" """
@ -857,9 +859,9 @@ class Tox(object):
:param user_data: pointer (c_void_p) to user data :param user_data: pointer (c_void_p) to user data
""" """
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p) c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_void_p)
self.tox_friend_connection_status_cb = c_callback(callback) self.friend_connection_status_cb = c_callback(callback)
Tox.libtoxcore.tox_callback_friend_connection_status(self._tox_pointer, Tox.libtoxcore.tox_callback_friend_connection_status(self._tox_pointer,
self.tox_friend_connection_status_cb, c_void_p(user_data)) self.friend_connection_status_cb, c_void_p(user_data))
def friend_get_typing(self, friend_number): def friend_get_typing(self, friend_number):
""" """
@ -894,8 +896,8 @@ class Tox(object):
:param user_data: pointer (c_void_p) to user data :param user_data: pointer (c_void_p) to user data
""" """
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_void_p) c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_bool, c_void_p)
self.tox_friend_typing_cb = c_callback(callback) self.friend_typing_cb = c_callback(callback)
Tox.libtoxcore.tox_callback_friend_typing(self._tox_pointer, self.tox_friend_typing_cb, c_void_p(user_data)) Tox.libtoxcore.tox_callback_friend_typing(self._tox_pointer, self.friend_typing_cb, c_void_p(user_data))
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Sending private messages # Sending private messages
@ -974,9 +976,9 @@ class Tox(object):
:param user_data: pointer (c_void_p) to user data :param user_data: pointer (c_void_p) to user data
""" """
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p) c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_void_p)
self.tox_friend_read_receipt_cb = c_callback(callback) self.friend_read_receipt_cb = c_callback(callback)
Tox.libtoxcore.tox_callback_friend_read_receipt(self._tox_pointer, Tox.libtoxcore.tox_callback_friend_read_receipt(self._tox_pointer,
self.tox_friend_read_receipt_cb, c_void_p(user_data)) self.friend_read_receipt_cb, c_void_p(user_data))
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Receiving private messages and friend requests # Receiving private messages and friend requests
@ -996,8 +998,8 @@ class Tox(object):
:param user_data: pointer (c_void_p) to user data :param user_data: pointer (c_void_p) to user data
""" """
c_callback = CFUNCTYPE(None, c_void_p, c_char_p, c_char_p, c_size_t, c_void_p) c_callback = CFUNCTYPE(None, c_void_p, c_char_p, c_char_p, c_size_t, c_void_p)
self.tox_friend_request_cb = c_callback(callback) self.friend_request_cb = c_callback(callback)
Tox.libtoxcore.tox_callback_friend_request(self._tox_pointer, self.tox_friend_request_cb, c_void_p(user_data)) Tox.libtoxcore.tox_callback_friend_request(self._tox_pointer, self.friend_request_cb, c_void_p(user_data))
def callback_friend_message(self, callback, user_data): def callback_friend_message(self, callback, user_data):
""" """
@ -1014,8 +1016,8 @@ class Tox(object):
:param user_data: pointer (c_void_p) to user data :param user_data: pointer (c_void_p) to user data
""" """
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_char_p, c_size_t, c_void_p) c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_int, c_char_p, c_size_t, c_void_p)
self.tox_friend_message_cb = c_callback(callback) self.friend_message_cb = c_callback(callback)
Tox.libtoxcore.tox_callback_friend_message(self._tox_pointer, self.tox_friend_message_cb, c_void_p(user_data)) Tox.libtoxcore.tox_callback_friend_message(self._tox_pointer, self.friend_message_cb, c_void_p(user_data))
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# File transmission: common between sending and receiving # File transmission: common between sending and receiving
@ -1091,9 +1093,9 @@ class Tox(object):
:param user_data: pointer (c_void_p) to user data :param user_data: pointer (c_void_p) to user data
""" """
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p) c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint32, c_int, c_void_p)
self.tox_file_recv_control_cb = c_callback(callback) self.file_recv_control_cb = c_callback(callback)
Tox.libtoxcore.tox_callback_file_recv_control(self._tox_pointer, Tox.libtoxcore.tox_callback_file_recv_control(self._tox_pointer,
self.tox_file_recv_control_cb, user_data) self.file_recv_control_cb, user_data)
def file_seek(self, friend_number, file_number, position): def file_seek(self, friend_number, file_number, position):
""" """
@ -1151,9 +1153,148 @@ class Tox(object):
raise ArgumentError('No file transfer with the given file number was found for the given friend.') raise ArgumentError('No file transfer with the given file number was found for the given friend.')
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# TODO File transmission: sending # 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.
"""
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)), addressof(tox_err_file_send))
tox_err_file_send = tox_err_file_send.value
if tox_err_file_send == TOX_ERR_FILE_SEND['OK']:
return result
elif tox_err_file_send == TOX_ERR_FILE_SEND['NULL']:
raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_FOUND']:
raise ArgumentError('The friend_number passed did not designate a valid friend.')
elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_CONNECTED']:
raise RuntimeError('This client is currently not connected to the friend.')
elif tox_err_file_send == TOX_ERR_FILE_SEND['NAME_TOO_LONG']:
raise ArgumentError('Filename length exceeded TOX_MAX_FILENAME_LENGTH bytes.')
elif tox_err_file_send == TOX_ERR_FILE_SEND['TOO_MANY']:
raise RuntimeError('Too many ongoing transfers. The maximum number of concurrent file transfers is 256 per'
'friend per direction (sending and receiving).')
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.
"""
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)),
addressof(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 ArgumentError('Packet queue is full.')
elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['WRONG_POSITION']:
raise ArgumentError('Position parameter was wrong.')
def callback_file_chunk_request(self, callback, user_data):
"""
Set the callback for the `file_chunk_request` event. Pass NULL 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
:param user_data: pointer (c_void_p) to user data
"""
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, user_data)
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# TODO File transmission: receiving # TODO File transmission: receiving
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------

View file

@ -160,7 +160,7 @@ TOX_ERR_FILE_SEND = {
'TOO_MANY': 5, 'TOO_MANY': 5,
} }
CHUNK = { TOX_ERR_FILE_SEND_CHUNK = {
'OK': 0, 'OK': 0,
'NULL': 1, 'NULL': 1,
'FRIEND_NOT_FOUND': 2, 'FRIEND_NOT_FOUND': 2,