This repository has been archived by the owner on Jan 28, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TelnetSession.cpp
318 lines (300 loc) · 9.32 KB
/
TelnetSession.cpp
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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
#include "TelnetSession.hpp"
#include <chrono>
TelnetSession::TelnetSession(SocketTcp &c) :
client(c.get()),
running(true),
mutex(),
telnetThread(new std::thread(&TelnetSession::run, this)),
waitingThreads(),
playerExecutions() {
}
void TelnetSession::run() {
client.makeNonBlocking();
Epoll efd{};
efd.addEvent(client);
while (running) {
std::vector <epoll_event> events = efd.wait(MAX_EVENTS_TELNET, INF);
for (epoll_event &event : events) {
if (!checkTelnetEvent(event)) {
}
}
}
for (std::thread *t: waitingThreads) {
t->join();
}
}
bool TelnetSession::checkStart(const std::string &command) {
if (!Utility::startsWith(command, START)) {
return false;
}
boost::smatch result;
const static boost::regex startPattern(START + R"(\s+(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\d+)\s+(\l{2,3})\s*)");
try {
if (boost::regex_match(command, result, startPattern)) {
std::string computer = result[1];
std::string host = result[2];
std::string path = result[3];
std::string file = result[5];
unsigned mPort = static_cast<unsigned>(std::stoi(result[6]));
std::string metadata = result[7];
if (metadata != NO && metadata != YES) {
static const std::string INVALID_START_METADATA = "ERROR metadata option must be " + YES + " or " + NO + "\n";
sendClient(INVALID_START_METADATA);
return true;
}
std::string parameters;
for (unsigned i = 2; i <= 7; i++) {
parameters += result[i] + " ";
}
launchPlayer(computer, parameters, mPort);
return true;
}
}
catch (...) {
}
static const std::string INVALID_START =
"ERROR in " + START + " command. Usage: " + START + " computer host path r-port file m-port metadata\n";
sendClient(INVALID_START);
return true;
}
bool TelnetSession::checkAt(const std::string &command) {
if (!Utility::startsWith(command, AT)) {
return false;
}
boost::smatch result;
const static boost::regex atPattern(
AT + R"(\s+(\d{2})\.(\d{2})\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\d+)\s+(\l{2,3})\s*)");
try {
if (boost::regex_match(command, result, atPattern)) {
int hh = std::stoi(result[1]);
int mm = std::stoi(result[2]);
if (hh > 23) {
static const std::string INVALID_AT_HH =
"ERROR in " + AT + " command. HH is too large\n";
sendClient(INVALID_AT_HH);
return true;
}
if (mm > 59) {
static const std::string INVALID_AT_MM =
"ERROR in " + AT + " command. MM is too large\n";
sendClient(INVALID_AT_MM);
return true;
}
int m = std::stoi(result[3]);
std::string computer = result[4];
std::string host = result[5];
std::string path = result[6];
unsigned mPort = static_cast<unsigned>(std::stoi(result[9]));
std::string metadata = result[10];
if (metadata != NO && metadata != YES) {
static const std::string INVALID_AT_METADATA = "ERROR metadata option must be " + YES + " or " + NO + "\n";
sendClient(INVALID_AT_METADATA);
return true;
}
std::string parameters;
for (unsigned i = 5; i <= 10; i++) {
parameters += result[i] + " ";
}
waitingThreads.push_back(
new std::thread(&TelnetSession::waitForStart, this, hh * 60 + mm, m, computer, parameters, mPort));
return true;
}
}
catch (...) {
}
static const std::string INVALID_AT =
"ERROR in " + AT + " command. Usage: " + AT + " HH.MM M computer host path r-port file m-port metadata\n";
sendClient(INVALID_AT);
return true;
}
bool TelnetSession::checkPlay(const std::string &command) {
if (!Utility::startsWith(command, PLAY)) {
return false;
}
boost::smatch result;
const static boost::regex playPattern(PLAY + R"(\s+(\d+)\s*)");
try {
if (boost::regex_match(command, result, playPattern)) {
unsigned id = std::stoi(result[1]);
if (!checkId(id)) {
static const std::string INVALID_PLAY_ID =
"ERROR in " + PLAY + " command. Invalid id.\n";
sendClient(INVALID_PLAY_ID);
return true;
}
sendCommand(id, PLAY);
const std::string OK_PLAY = "OK " + std::to_string(id) + "\n";
sendClient(OK_PLAY);
return true;
}
}
catch (...) {
}
static const std::string INVALID_PLAY =
"ERROR in " + PLAY + " command. Usage: " + PLAY + " id\n";
sendClient(INVALID_PLAY);
return true;
}
bool TelnetSession::checkPause(const std::string &command) {
if (!Utility::startsWith(command, PAUSE)) {
return false;
}
boost::smatch result;
const static boost::regex pausePattern(PAUSE + R"(\s+(\d+)\s*)");
try {
if (boost::regex_match(command, result, pausePattern)) {
unsigned id = std::stoi(result[1]);
if (!checkId(id)) {
static const std::string INVALID_PAUSE_ID =
"ERROR in " + PAUSE + " command. Invalid id.\n";
sendClient(INVALID_PAUSE_ID);
return true;
}
sendCommand(id, PAUSE);
const std::string OK_PAUSE = "OK " + std::to_string(id) + "\n";
sendClient(OK_PAUSE);
return true;
}
}
catch (...) {
}
static const std::string INVALID_PAUSE =
"ERROR in " + PAUSE + " command. Usage: " + PAUSE + " id\n";
sendClient(INVALID_PAUSE);
return true;
}
bool TelnetSession::checkQuit(const std::string &command) {
if (!Utility::startsWith(command, QUIT)) {
return false;
}
boost::smatch result;
const static boost::regex quitPattern(QUIT + R"(\s+(\d+)\s*)");
try {
if (boost::regex_match(command, result, quitPattern)) {
unsigned id = std::stoi(result[1]);
if (!checkId(id)) {
static const std::string INVALID_QUIT_ID =
"ERROR in " + QUIT + " command. Invalid id.\n";
sendClient(INVALID_QUIT_ID);
return true;
}
quitPlayerExecution(id);
const std::string OK_QUIT = "OK " + std::to_string(id) + "\n";
sendClient(OK_QUIT);
return true;
}
}
catch (...) {
}
static const std::string INVALID_QUIT =
"ERROR in " + QUIT + " command. Usage: " + QUIT + " id\n";
sendClient(INVALID_QUIT);
return true;
}
bool TelnetSession::checkTitle(const std::string &command) {
if (!Utility::startsWith(command, TITLE)) {
return false;
}
boost::smatch result;
const static boost::regex titlePattern(TITLE + R"(\s+(\d+)\s*)");
try {
if (boost::regex_match(command, result, titlePattern)) {
unsigned id = std::stoi(result[1]);
if (!checkId(id)) {
static const std::string INVALID_TITLE_ID =
"ERROR in " + TITLE + " command. Invalid id.\n";
sendClient(INVALID_TITLE_ID);
return true;
}
sendCommand(id, TITLE);
return true;
}
}
catch (...) {
}
static const std::string INVALID_TITLE =
"ERROR in " + TITLE + " command. Usage: " + TITLE + " id\n";
sendClient(INVALID_TITLE);
return true;
}
bool TelnetSession::checkCommand(const std::string &c) {
return checkAt(c) || checkPause(c) || checkPlay(c) || checkQuit(c) || checkStart(c) || checkTitle(c);
}
bool TelnetSession::checkTelnetEvent(epoll_event &event) {
if (event.data.fd == client.get()) {
std::string msg;
try {
msg = client.receive();
} catch (ClosedConnectionException) {
running = false;
return true;
}
if (!checkCommand(msg)) {
static const std::string INVALID_COMMAND = "ERROR Invalid command. Available: AT START PLAY PAUSE QUIT TITLE\n";
sendClient(INVALID_COMMAND);
}
return true;
}
return false;
}
TelnetSession::~TelnetSession() {
for (std::thread *t: waitingThreads) {
delete t;
}
delete telnetThread;
}
void TelnetSession::waitForStart(int begin, int m, std::string c, std::string p, unsigned mPort) {
const std::chrono::milliseconds timeToWait(MAX_TIME_MS);
while (running && Utility::currentMinutes() != begin) {
std::this_thread::sleep_for(timeToWait);
}
if (!running) {
return;
}
unsigned id = launchPlayer(c, p, mPort);
waitForEnd((begin + m) % (24 * 60), id);
}
void TelnetSession::waitForEnd(int end, unsigned id) {
const std::chrono::milliseconds timeToWait(MAX_TIME_MS);
while (running && Utility::currentMinutes() != end) {
std::this_thread::sleep_for(timeToWait);
}
quitPlayerExecution(id);
}
unsigned TelnetSession::launchPlayer(const std::string &computer, const std::string ¶meters, unsigned mPort) {
mutex.lock();
unsigned id = playerExecutions.size();
playerExecutions.push_back(new PlayerExecution(computer, parameters, mPort, mutex, client, id));
mutex.unlock();
return id;
}
void TelnetSession::sendClient(const std::string &msg) {
mutex.lock();
client.Send(msg);
mutex.unlock();
}
bool TelnetSession::checkId(unsigned id) {
mutex.lock();
bool result = id < playerExecutions.size() && playerExecutions[id] != nullptr;
mutex.unlock();
return result;
}
void TelnetSession::quitPlayerExecution(unsigned id) {
if (checkId(id)) {
mutex.lock();
PlayerExecution *toRemove = playerExecutions[id];
playerExecutions[id] = nullptr;
mutex.unlock();
toRemove->quit();
toRemove->thread.join();
delete toRemove;
}
}
void TelnetSession::sendCommand(unsigned id, const std::string &s) {
if (checkId(id)) {
mutex.lock();
PlayerExecution *receiver = playerExecutions[id];
receiver->sendCommand(s);
mutex.unlock();
}
}