1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
using Gee;
using Gtk;
using Dino.Entities;
using Xmpp;
namespace Dino.Plugins.HttpFiles {
public class FileProvider : Dino.FileProvider, Object {
private StreamInteractor stream_interactor;
private Dino.Database dino_db;
private static Regex http_url_regex = /^https?:\/\/([^\s#]*)$/; // Spaces are invalid in URLs and we can't use fragments for downloads
private static Regex omemo_url_regex = /^aesgcm:\/\/(.*)#(([A-Fa-f0-9]{2}){48}|([A-Fa-f0-9]{2}){44})$/;
public FileProvider(StreamInteractor stream_interactor, Dino.Database dino_db) {
this.stream_interactor = stream_interactor;
this.dino_db = dino_db;
stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new ReceivedMessageListener(this));
}
private class ReceivedMessageListener : MessageListener {
public string[] after_actions_const = new string[]{ "STORE" };
public override string action_group { get { return ""; } }
public override string[] after_actions { get { return after_actions_const; } }
private FileProvider outer;
private StreamInteractor stream_interactor;
public ReceivedMessageListener(FileProvider outer) {
this.outer = outer;
this.stream_interactor = outer.stream_interactor;
}
public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
string? oob_url = Xmpp.Xep.OutOfBandData.get_url_from_message(stanza);
bool normal_file = oob_url != null && oob_url == message.body && FileProvider.http_url_regex.match(message.body);
bool omemo_file = FileProvider.omemo_url_regex.match(message.body);
if (normal_file || omemo_file) {
yield outer.on_file_message(message, conversation);
}
return false;
}
}
private async void on_file_message(Entities.Message message, Conversation conversation) {
// Hide message
ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item(conversation, 1, message.id);
if (content_item != null) {
stream_interactor.get_module(ContentItemStore.IDENTITY).set_item_hide(content_item, true);
}
var additional_info = message.id.to_string();
var receive_data = new HttpFileReceiveData();
receive_data.url = message.body;
var file_meta = new HttpFileMeta();
file_meta.file_name = extract_file_name_from_url(message.body);
file_meta.message = message;
file_incoming(additional_info, message.from, message.time, message.local_time, conversation, receive_data, file_meta);
}
public async FileMeta get_meta_info(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError {
HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData;
if (http_receive_data == null) return file_meta;
var session = new Soup.Session();
var head_message = new Soup.Message("HEAD", http_receive_data.url);
if (head_message != null) {
try {
yield session.send_async(head_message, null);
} catch (Error e) {
throw new FileReceiveError.GET_METADATA_FAILED("HEAD request failed");
}
string? content_type = null, content_length = null;
head_message.response_headers.foreach((name, val) => {
if (name == "Content-Type") content_type = val;
if (name == "Content-Length") content_length = val;
});
file_meta.mime_type = content_type;
if (content_length != null) {
file_meta.size = int.parse(content_length);
}
}
return file_meta;
}
public async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError {
HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData;
if (http_receive_data == null) assert(false);
try {
var session = new Soup.Session();
Soup.Request request = session.request(http_receive_data.url);
return yield request.send_async(null);
} catch (Error e) {
throw new FileReceiveError.DOWNLOAD_FAILED("Downloading file error: %s".printf(e.message));
}
}
public FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError {
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
if (conversation == null) throw new FileReceiveError.GET_METADATA_FAILED("No conversation");
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation);
if (message == null) throw new FileReceiveError.GET_METADATA_FAILED("No message");
var file_meta = new HttpFileMeta();
file_meta.size = file_transfer.size;
file_meta.mime_type = file_transfer.mime_type;
file_meta.file_name = extract_file_name_from_url(message.body);
file_meta.message = message;
return file_meta;
}
public FileReceiveData? get_file_receive_data(FileTransfer file_transfer) {
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
if (conversation == null) return null;
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation);
if (message == null) return null;
var receive_data = new HttpFileReceiveData();
receive_data.url = message.body;
return receive_data;
}
private string extract_file_name_from_url(string url) {
string ret = Uri.unescape_string(url.substring(url.last_index_of("/") + 1));
if (ret.contains("#")) {
ret = ret.substring(0, ret.last_index_of("#"));
}
return ret;
}
public int get_id() { return 0; }
}
}
|