Skip to content

Commit

Permalink
VENOM-302: Implement file transfer
Browse files Browse the repository at this point in the history
  • Loading branch information
naxuroqa committed Apr 8, 2018
1 parent 5a52934 commit ea82d50
Show file tree
Hide file tree
Showing 11 changed files with 323 additions and 139 deletions.
179 changes: 123 additions & 56 deletions src/core/FileTransfer.vala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* FileTransfer.vala
*
* Copyright (C) 2018 Venom authors and contributors
* Copyright (C) 2018 Venom authors and contributors
*
* This file is part of Venom.
*
Expand All @@ -25,121 +25,188 @@ namespace Venom {
PAUSED,
RUNNING,
CANCEL,
FINISHED
FINISHED,
FAILED
}

public enum FileTransferDirection {
INCOMING,
OUTGOING
}

public errordomain FileTransferError {
READ,
WRITE,
INIT,
OVERFLOW
}

public interface FileTransfer : GLib.Object {
public signal void state_changed();
public signal void progress_changed();

public abstract bool is_avatar();
public abstract string get_description();
public abstract uint64 get_transmitted_size();
public abstract FileTransferDirection get_direction();
public abstract uint32 get_friend_number();
public abstract uint32 get_file_number();
public abstract uint64 get_file_size();
public abstract unowned uint8[]? get_file_data();
public abstract void set_file_data(uint64 offset, uint8[] data);
public abstract uint64 get_transmitted_size();
public abstract FileTransferState get_state();
public abstract void set_state(FileTransferState state);
public abstract string? get_file_path();
public abstract bool is_avatar();
public abstract unowned uint8[] ? get_avatar_buffer();

public abstract uint32 get_friend_number();
public abstract uint32 get_file_number();
public abstract void init_file(File file) throws Error;

public abstract void write_data(uint8[] data) throws Error;
public abstract uint8[] read_data(uint64 length) throws Error;

public abstract string? get_file_name();
public abstract string? get_file_path();
public abstract void set_state(FileTransferState state);
}

public class FileTransferImpl : FileTransfer, GLib.Object {
private uint8[] file_data;
private uint64 transmitted_size;
private ILogger logger;
private string description;
private bool _is_avatar;
private FileTransferState state;
private FileTransferDirection direction;
private uint32 friend_number;
private uint32 file_number;
private uint64 file_size;
private uint64 transmitted_size;
private FileTransferState state;

private bool _is_avatar;
private uint8[] avatar_buffer;

private FileTransferImpl(ILogger logger, uint32 friend_number, uint32 file_number) {
this.logger = logger;
private string file_name;
private File file;

private FileTransferImpl(FileTransferDirection direction, uint32 friend_number, uint32 file_number, uint64 file_size) {
this.friend_number = friend_number;
this.file_number = file_number;
}
this.direction = direction;
this.file_size = file_size;

public FileTransferImpl.File(ILogger logger, uint32 friend_number, uint32 file_number, uint64 file_size, string filename) {
this(logger, friend_number, file_number);
this.description = filename;
transmitted_size = 0;
state = FileTransferState.INIT;
transmitted_size = 0;
}

public FileTransferImpl.File(FileTransferDirection direction, uint32 friend_number, uint32 file_number, uint64 file_size, string file_name) {
this(direction, friend_number, file_number, file_size);
this.file_name = file_name;
_is_avatar = false;
}

public FileTransferImpl.Avatar(ILogger logger, uint32 friend_number, uint32 file_number, uint64 file_size, string description) {
this(logger, friend_number, file_number);
this.description = description;
file_data = new uint8[file_size];
transmitted_size = 0;
state = FileTransferState.INIT;
public FileTransferImpl.Avatar(FileTransferDirection direction, uint32 friend_number, uint32 file_number, uint64 file_size) {
this(direction, friend_number, file_number, file_size);
avatar_buffer = new uint8[file_size];
_is_avatar = true;
}

public void init_file(File file) throws Error {
this.file = file;
if (direction == INCOMING) {
file.create(FileCreateFlags.NONE);
}
}

private static void copy_with_offset(uint8[] dest, uint8[] src, uint64 offset) {
unowned uint8[] dest_ptr = dest[offset : dest.length];
GLib.Memory.copy(dest_ptr, src, src.length);
}

public virtual bool is_avatar() {
public bool is_avatar() {
return _is_avatar;
}

public virtual string get_description() {
return description;
}

public virtual uint64 get_transmitted_size() {
public uint64 get_transmitted_size() {
return transmitted_size;
}

public virtual uint64 get_file_size() {
return file_data.length;
public uint64 get_file_size() {
return file_size;
}

public virtual void set_file_data(uint64 offset, uint8[] data) {
if (!is_avatar()) {
//FIXME implement this
transmitted_size += data.length;
progress_changed();
return;
public void write_data(uint8[] data) throws Error {
if (data.length + transmitted_size > file_size) {
set_state(FileTransferState.FAILED);
throw new FileTransferError.OVERFLOW("Appending too much data, discarding data");
}

if (data.length + offset > file_data.length) {
logger.e("set_data overflow");
return;
if (_is_avatar) {
copy_with_offset(avatar_buffer, data, transmitted_size);
} else {
if (file == null) {
set_state(FileTransferState.FAILED);
throw new FileTransferError.INIT("File is not initialized");
}
try {
file.append_to(GLib.FileCreateFlags.NONE).write(data);
} catch (Error e) {
set_state(FileTransferState.FAILED);
throw new FileTransferError.WRITE("Writing to file failed: " + e.message);
}
}
copy_with_offset(file_data, data, offset);

transmitted_size += data.length;
progress_changed();
}

public virtual string? get_file_path() {
return ".";
public uint8[] read_data(uint64 length) throws Error {
if (length + transmitted_size > file_size) {
set_state(FileTransferState.FAILED);
throw new FileTransferError.OVERFLOW("Appending too much data, discarding data");
}
if (file == null) {
set_state(FileTransferState.FAILED);
throw new FileTransferError.INIT("File is not initialized");
}
var buf = new uint8[length];
try {
var istream = file.read();
istream.seek((int64) transmitted_size, SeekType.SET);
istream.read(buf);
} catch (Error e) {
set_state(FileTransferState.FAILED);
throw new FileTransferError.WRITE("Writing to file failed: " + e.message);
}

transmitted_size += length;
progress_changed();
return buf;
}

public virtual unowned uint8[]? get_file_data() {
return file_data;
public unowned uint8[] ? get_avatar_buffer() {
return avatar_buffer;
}

public virtual FileTransferState get_state() {
public FileTransferState get_state() {
return state;
}

public virtual void set_state(FileTransferState state) {
public void set_state(FileTransferState state) {
this.state = state;
state_changed();
}

public virtual uint32 get_friend_number() {
public uint32 get_friend_number() {
return friend_number;
}

public virtual uint32 get_file_number() {
public uint32 get_file_number() {
return file_number;
}

public FileTransferDirection get_direction() {
return direction;
}

public string? get_file_name() {
return file_name;
}

public string? get_file_path() {
if (file == null) {
return null;
}
return file.get_path();
}
}
}
4 changes: 2 additions & 2 deletions src/core/R.vala
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ namespace Venom {

public static Gdk.Pixbuf ? pixbuf_from_resource(string res) {
try {
return new Gdk.Pixbuf.from_resource(R.constants.icons_prefix() + res
+ R.constants.icons_suffix());
var resource_path = R.constants.icons_prefix() + res + R.constants.icons_suffix();
return new Gdk.Pixbuf.from_resource_at_scale(resource_path, 96, 96, true);
} catch (Error e) {
}
return null;
Expand Down
45 changes: 9 additions & 36 deletions src/core/Tools.vala
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,7 @@

namespace Venom {
public class Tools {
// find our data dir (on linux most likely /usr/share or /usr/local/share)
public static string find_data_dir() {
//System data directory
foreach (string s in GLib.Environment.get_system_data_dirs()) {
string dir = Path.build_filename(s, "venom");
File f = File.new_for_path(dir);
if (f.query_exists()) {
return dir;
}
}
// Check for common directories on portable versions
string[] portable_directories = {
Path.build_filename(GLib.Environment.get_user_data_dir(), "venom"),
Path.build_filename("share", "venom"),
Path.build_filename("..", "share", "venom")
};
for (int i = 0; i < portable_directories.length; ++i) {
File f = File.new_for_path(portable_directories[i]);
if (f.query_exists()) {
return portable_directories[i];
}
}

// Assume that our current pwd is our data dir
return "";
}
private Tools() {}

public static void create_path_for_file(string filename, int mode) {
string pathname = Path.get_dirname(filename);
Expand Down Expand Up @@ -116,16 +91,14 @@ namespace Venom {
}
}

public static string markup_uris(string text) {
string escaped_text = Markup.escape_text(text);
string ret;
try {
ret = Tools.uri_regex.replace(escaped_text, -1, 0, "<a href=\"\\g<u>\">\\g<u></a>");
} catch (GLib.RegexError e) {
// Logger.log(LogLevel.ERROR, "Error when doing uri markup: " + e.message);
return text;
}
return ret;
public static uint64 get_file_size(GLib.File file) throws Error {
var is = file.read();
is.seek(0, SeekType.END);
return is.tell();
}

public static string markup_uris(string text) throws Error {
return Tools.uri_regex.replace(text, -1, 0, "<a href=\"\\g<u>\">\\g<u></a>");
}

private static GLib.Regex _action_regex;
Expand Down
24 changes: 17 additions & 7 deletions src/testing/mocks/MockFiletransfer.vala
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,19 @@ namespace Mock {
public uint64 get_file_size() {
return mock().actual_call(this, "get_file_size").get_int();
}
public unowned uint8[] ? get_file_data() {
mock().actual_call(this, "get_file_data");
public unowned uint8[] ? get_avatar_buffer() {
mock().actual_call(this, "get_avatar_buffer");
return null;
}
public void set_file_data(uint64 offset, uint8[] data) {
var args = Arguments.builder()
.uint64(offset)
.create();
mock().actual_call(this, "set_file_data", args);
public void write_data(uint8[] data) throws Error {
mock().actual_call(this, "write_data").get_throws();
}
public uint8[] read_data(uint64 length) throws Error{
mock().actual_call(this, "read_data").get_throws();
return (uint8[]) null;
}
public void init_file(File file) throws Error {
mock().actual_call(this, "init_file").get_throws();
}
public FileTransferState get_state() {
return (FileTransferState) mock().actual_call(this, "get_state").get_int();
Expand All @@ -54,6 +58,9 @@ namespace Mock {
.create();
mock().actual_call(this, "set_state", args);
}
public string? get_file_name() {
return mock().actual_call(this, "get_file_name").get_string();
}
public string? get_file_path() {
return mock().actual_call(this, "get_file_path").get_string();
}
Expand All @@ -63,5 +70,8 @@ namespace Mock {
public uint32 get_file_number() {
return mock().actual_call(this, "get_file_number").get_int();
}
public FileTransferDirection get_direction() {
return (FileTransferDirection) mock().actual_call(this, "get_direction").get_int();
}
}
}

0 comments on commit ea82d50

Please sign in to comment.