summaryrefslogtreecommitdiffstats
path: root/pica-client/skynet.h
blob: 64f0874069bacf6e76f644ec8bf6b6f5121245fc (plain) (blame)
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/*
	(c) Copyright  2012 - 2018 Anton Sviridenko
	https://picapica.im

	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, version 3.

	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, see <https://www.gnu.org/licenses/>.
*/
#ifndef SKYNET_H
#define SKYNET_H

#include "nodes.h"
#include "../PICA_client.h"
#include "../PICA_proto.h"
#include "accounts.h"
#include <QObject>
#include <QMap>
#include <QVector>
#include <QPair>


class SkyNet : public QObject
{
	Q_OBJECT
public:
	SkyNet();
	~SkyNet();
	void Join(const Accounts::AccountRecord &accrec);
	void Exit();

	void SendMessage(QByteArray to, QString msg);
	void SendFile(QByteArray to, QString filepath);
	void AcceptFile(QByteArray from, QString filepath);
	void DenyFile(QByteArray from);
	void PauseFile(QByteArray peer_id, bool pause_sending);
	void ResumeFile(QByteArray peer_id, bool resume_sending);
	void CancelFile(QByteArray peer_id, bool cancel_sending);

	void StartCall(QByteArray to);
	void AcceptCall(QByteArray from);
	void RejectCall(QByteArray from);
	void HangupCall(QByteArray with);

signals:
	void MessageReceived(QByteArray from, QString msg);
	void UnableToDeliver(QByteArray to, QString msg);
	void Delivered(QByteArray to);
	void BecameSelfAware();
	void LostSelfAwareness();
	void PeerCertificateReceived(QByteArray peer_id, QString cert_pem, bool *verified);
	void CertificateForged(QByteArray peer_id, QString received_cert, QString stored_cert);
	void StatusMsg(QString msg, bool is_critical);
	void ContactsUpdated();
	void IncomingFileRequestReceived(QByteArray peer_id, quint64 file_size, QString filename);
	void OutgoingFileRequestAccepted(QByteArray peer_id);
	void OutgoingFileRequestDenied(QByteArray peer_id);
	void FileProgress(QByteArray peer_id, quint64 bytes_sent, quint64 bytes_received);
	void IncomingFilePaused(QByteArray peer_id);
	void IncomingFileCancelled(QByteArray peer_id);
	void IncomingFileIoError(QByteArray peer_id);
	void IncomingFileResumed(QByteArray peer_id);
	void OutgoingFilePaused(QByteArray peer_id);
	void OutgoingFileCancelled(QByteArray peer_id);
	void OutgoingFileIoError(QByteArray peer_id);
	void OutgoingFileResumed(QByteArray peer_id);
	void OutgoingFileFinished(QByteArray peer_id);
	void IncomingFileFinished(QByteArray peer_id);
	void MultiloginMessageReceived(quint64 timestamp, QString node_addr, quint16 node_port);
	void ConnectionStatusUpdated(QByteArray peer_id, QString status);

	void c2cClosed(QByteArray peer_id);

	void CallFailed(QString reason);
	void IncomingCall(QByteArray from);
	void CallAccepted(QByteArray by);
	void CallRejected(QByteArray by);
	void CallHungup(QByteArray by);
private:
	// Constructed in init(), once this SkyNet instance is running on its
	// own network thread and that thread's dedicated QSqlDatabase
	// connection has been opened - see init() in skynet.cpp.
	Nodes *nodes;
	QList<QPair<struct PICA_c2n *, Nodes::NodeRecord> > connecting_nodes;
	QList<struct PICA_c2n *> connected_nodes_to_close;
	bool self_aware;
	bool call_waiting_c2c_connect;
	QByteArray call_waiting_to;
	QString status;
	struct PICA_c2n *active_nodelink;
	struct PICA_acc *acc;
	struct PICA_listener *listener;
	Accounts::AccountRecord skynet_account;

	QMap<QByteArray, QList<QString> > msgqueues;
	QMap<QByteArray, QList<QString> > sndfilequeues;
	QMap<QByteArray, quint32> c2c_reconnect_timeouts;
	int nodelink_reconnect_timer_id;
	int event_loop_timer_id;
	int file_transfer_timer_id;
	int c2c_reconnect_timer_id;

	void timerEvent(QTimerEvent * e);

	void reconnect_c2c();
	void update_c2c_reconnect_timeout(QByteArray peer_id);
	void reset_c2c_reconnect_timeout(QByteArray peer_id);
	void flush_queues(QByteArray to);
	struct PICA_c2c *find_active_chan(QByteArray peer_id);
	QList<QByteArray> filter_existing_chans(QList<QByteArray> peer_ids);
	bool open_account();
	void active_filetransfers_up();
	void active_filetransfers_down();
	void active_filetransfers_reset();
	quint32 active_filetransfers;

	void continue_start_call();
	// check if there is a call waiting to be initiated with peer_id
	bool is_call_waiting_for(const QByteArray &peer_id);

	void emit_MessageReceived(QByteArray from, QString msg);
	void emit_UnableToDeliver(QByteArray to, QString msg);
	void emit_Delivered(QByteArray to);
	void emit_PeerCertificateReceived(QByteArray peer_id, QString cert_pem, bool *verified);
	void emit_IncomingFileRequestReceived(QByteArray peer_id, quint64 file_size, QString filename);
	void emit_OutgoingFileRequestAccepted(QByteArray peer_id);
	void emit_OutgoingFileRequestDenied(QByteArray peer_id);
	void emit_FileProgress(QByteArray peer_id, quint64 bytes_sent, quint64 bytes_received);

	void emit_IncomingFilePaused(QByteArray peer_id);
	void emit_IncomingFileCancelled(QByteArray peer_id);
	void emit_IncomingFileIoError(QByteArray peer_id);
	void emit_IncomingFileResumed(QByteArray peer_id);
	void emit_OutgoingFilePaused(QByteArray peer_id);
	void emit_OutgoingFileCancelled(QByteArray peer_id);
	void emit_OutgoingFileIoError(QByteArray peer_id);
	void emit_OutgoingFileResumed(QByteArray peer_id);

	void emit_OutgoingFileFinished(QByteArray peer_id);
	void emit_IncomingFileFinished(QByteArray peer_id);
	void emit_MultiloginMessageReceived(quint64 timestamp, QString node_addr, quint16 node_port);

	void emit_c2cClosed(QByteArray peer_id);
	void emit_ConnectionStatusUpdated(QByteArray peer_id, QString status);

	void emit_CallFailed(QString reason);
	void emit_IncomingCall(QByteArray from);
	void emit_CallAccepted(QByteArray by);
	void emit_CallRejected(QByteArray by);
	void emit_CallHungup(QByteArray by);

	static void newmsg_cb(const unsigned char *peer_id, const char* msgbuf, unsigned int nb, int type);

	static void msgok_cb(const unsigned char *peer_id);

	static void c2c_established_cb(const unsigned char *peer_id, const char *ciphersuitename);

	static void c2c_failed(const unsigned char *peer_id);

	static int accept_cb(const unsigned char *caller_id);

	static void notfound_cb(const unsigned char *callee_id);

	static void c2c_closed_cb(const unsigned char *peer_id, int reason);

	static void nodelist_cb(int type, void *addr_bin, const char *addr_str, unsigned int port);

	static int peer_cert_verify_cb(const unsigned char *peer_id, const char *cert_pem, unsigned int nb);

	static int accept_file_cb(const unsigned char  *peer_id, uint64_t file_size, const char *filename, unsigned int filename_size);

	static void accepted_file_cb(const unsigned char *peer_id);

	static void denied_file_cb(const unsigned char *peer_id);

	static void file_progress(const unsigned char *peer_id, uint64_t sent, uint64_t received);

	static void file_control(const unsigned char *peer_id, unsigned int sender_cmd, unsigned int receiver_cmd);

	static void file_finished(const unsigned char *peer_id, int sending);

	static void c2n_established_cb(struct PICA_c2n *c2n);

	static void c2n_failed_cb(struct PICA_c2n *c2n, int error);

	static void c2n_closed_cb(struct PICA_c2n *c2n, int error);

	static void listener_error_cb(struct PICA_listener *lst, int errorcode);

	static void multilogin_cb(uint64_t timestamp, void *addr_bin, const char *addr_str, uint16_t port);

	static void direct_c2c_established_cb(const unsigned char *peer_id, const char *ciphersuitename);

	static void incoming_call_cb(const unsigned char *peer_id);

	static void call_picked_up_cb(const unsigned char *peer_id);

	static void call_rejected_cb(const unsigned char *peer_id);

	static void call_hangup_cb(const unsigned char *peer_id);

	static void call_audio_params_cb(const unsigned char *peer_id, const char *codec, uint16_t sample_rate);

	static void call_video_params_cb(const unsigned char *peer_id, const char *codec, uint16_t width, uint16_t height);

	static void call_audio_packet_cb(const unsigned char *peer_id, uint16_t size, const char *pkt_data);

	static void call_video_packet_cb(const unsigned char *peer_id, uint16_t size, const char *pkt_data);

private slots:
	void nodelink_activated(PICA_c2n *c2n);
	void nodelink_closed(PICA_c2n *c2n, int error);
	void nodelink_failed(PICA_c2n *c2n, int error);
	void multilogin_event(quint64 timestamp, QString node_addr, quint16 node_port);

	void node_status_changed(Nodes::NodeRecord nr, bool alive);
	void verify_peer_cert(QByteArray peer_id, QString cert_pem, bool *verified);

	// Connected to the network thread's QThread::started() signal. Runs
	// once, on the network thread itself, right before that thread's
	// event loop starts. Anything that needs to be tied to the network
	// thread rather than whatever thread constructed this SkyNet
	// instance - the event-loop timers and this object's own QSqlDatabase
	// connection - is set up here instead of in the constructor.
	void init();

	// QObject::moveToThread() only succeeds when called from the thread
	// the object is currently affiliated with - it cannot be "pulled" in
	// from a different thread. So reclaiming this object for the main
	// thread at shutdown has to happen via a call executed here, on the
	// network thread itself (see main.cpp, invoked with
	// Qt::BlockingQueuedConnection before the network thread is stopped),
	// rather than by calling moveToThread() directly from main().
	void moveBackToMainThread();

	// Actual implementations behind the public action methods below.
	// The public methods only forward here via QMetaObject::invokeMethod()
	// with Qt::AutoConnection, so callers on any thread are automatically
	// marshaled onto whichever thread this SkyNet instance lives on -
	// direct call if already on that thread, queued otherwise.
	void do_Join(const Accounts::AccountRecord &accrec);
	void do_Exit();

	void do_SendMessage(QByteArray to, QString msg);
	void do_SendFile(QByteArray to, QString filepath);
	void do_AcceptFile(QByteArray from, QString filepath);
	void do_DenyFile(QByteArray from);
	void do_PauseFile(QByteArray peer_id, bool pause_sending);
	void do_ResumeFile(QByteArray peer_id, bool resume_sending);
	void do_CancelFile(QByteArray peer_id, bool cancel_sending);

	void do_StartCall(QByteArray to);
	void do_AcceptCall(QByteArray from);
	void do_RejectCall(QByteArray from);
	void do_HangupCall(QByteArray with);

};

#endif // SKYNET_H