1 /*
2 * This file is part of nzbget. See <http://nzbget.net>.
3 *
4 * Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
5 * Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22 #include "nzbget.h"
23 #include "Options.h"
24 #include "WorkState.h"
25 #include "Frontend.h"
26 #include "Log.h"
27 #include "Connection.h"
28 #include "MessageBase.h"
29 #include "RemoteClient.h"
30 #include "Util.h"
31 #include "StatMeter.h"
32
Frontend()33 Frontend::Frontend()
34 {
35 debug("Creating Frontend");
36
37 m_workStateObserver.m_owner = this;
38 g_WorkState->Attach(&m_workStateObserver);
39
40 m_updateInterval = g_Options->GetUpdateInterval();
41 }
42
Stop()43 void Frontend::Stop()
44 {
45 Thread::Stop();
46
47 m_waitCond.NotifyAll();
48 }
49
WorkStateUpdate(Subject * caller,void * aspect)50 void Frontend::WorkStateUpdate(Subject* caller, void* aspect)
51 {
52 m_waitCond.NotifyAll();
53 }
54
PrepareData()55 bool Frontend::PrepareData()
56 {
57 if (IsRemoteMode())
58 {
59 if (IsStopped())
60 {
61 return false;
62 }
63 if (!RequestMessages() || ((m_summary || m_fileList) && !RequestFileList()))
64 {
65 const char* controlIp = !strcmp(g_Options->GetControlIp(), "0.0.0.0") ? "127.0.0.1" : g_Options->GetControlIp();
66 printf("\nUnable to send request to nzbget-server at %s (port %i) \n", controlIp, g_Options->GetControlPort());
67 Stop();
68 return false;
69 }
70 }
71 else
72 {
73 if (m_summary)
74 {
75 m_currentDownloadSpeed = g_StatMeter->CalcCurrentDownloadSpeed();
76 m_pauseDownload = g_WorkState->GetPauseDownload();
77 m_downloadLimit = g_WorkState->GetSpeedLimit();
78 m_threadCount = Thread::GetThreadCount();
79 g_StatMeter->CalcTotalStat(&m_upTimeSec, &m_dnTimeSec, &m_allBytes, &m_standBy);
80
81 GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
82 m_postJobCount = 0;
83 for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
84 {
85 m_postJobCount += nzbInfo->GetPostInfo() ? 1 : 0;
86 }
87 downloadQueue->CalcRemainingSize(&m_remainingSize, nullptr);
88 }
89 }
90 return true;
91 }
92
FreeData()93 void Frontend::FreeData()
94 {
95 if (IsRemoteMode())
96 {
97 m_remoteMessages.clear();
98 DownloadQueue::Guard()->GetQueue()->clear();
99 }
100 }
101
GuardMessages()102 GuardedMessageList Frontend::GuardMessages()
103 {
104 if (IsRemoteMode())
105 {
106 return GuardedMessageList(&m_remoteMessages, nullptr);
107 }
108 else
109 {
110 return g_Log->GuardMessages();
111 }
112 }
113
IsRemoteMode()114 bool Frontend::IsRemoteMode()
115 {
116 return g_Options->GetRemoteClientMode();
117 }
118
ServerPauseUnpause(bool pause)119 void Frontend::ServerPauseUnpause(bool pause)
120 {
121 if (IsRemoteMode())
122 {
123 RequestPauseUnpause(pause);
124 }
125 else
126 {
127 g_WorkState->SetResumeTime(0);
128 g_WorkState->SetPauseDownload(pause);
129 }
130 }
131
ServerSetDownloadRate(int rate)132 void Frontend::ServerSetDownloadRate(int rate)
133 {
134 if (IsRemoteMode())
135 {
136 RequestSetDownloadRate(rate);
137 }
138 else
139 {
140 g_WorkState->SetSpeedLimit(rate);
141 }
142 }
143
ServerEditQueue(DownloadQueue::EEditAction action,int offset,int id)144 bool Frontend::ServerEditQueue(DownloadQueue::EEditAction action, int offset, int id)
145 {
146 if (IsRemoteMode())
147 {
148 return RequestEditQueue(action, offset, id);
149 }
150 else
151 {
152 return DownloadQueue::Guard()->EditEntry(id, action, CString::FormatStr("%i", offset));
153 }
154 }
155
InitMessageBase(SNzbRequestBase * messageBase,int request,int size)156 void Frontend::InitMessageBase(SNzbRequestBase* messageBase, int request, int size)
157 {
158 messageBase->m_signature = htonl(NZBMESSAGE_SIGNATURE);
159 messageBase->m_type = htonl(request);
160 messageBase->m_structSize = htonl(size);
161
162 strncpy(messageBase->m_username, g_Options->GetControlUsername(), NZBREQUESTPASSWORDSIZE - 1);
163 messageBase->m_username[NZBREQUESTPASSWORDSIZE - 1] = '\0';
164
165 strncpy(messageBase->m_password, g_Options->GetControlPassword(), NZBREQUESTPASSWORDSIZE);
166 messageBase->m_password[NZBREQUESTPASSWORDSIZE - 1] = '\0';
167 }
168
RequestMessages()169 bool Frontend::RequestMessages()
170 {
171 const char* controlIp = !strcmp(g_Options->GetControlIp(), "0.0.0.0") ? "127.0.0.1" : g_Options->GetControlIp();
172 Connection connection(controlIp, g_Options->GetControlPort(), false);
173
174 bool OK = connection.Connect();
175 if (!OK)
176 {
177 return false;
178 }
179
180 SNzbLogRequest LogRequest;
181 InitMessageBase(&LogRequest.m_messageBase, rrLog, sizeof(LogRequest));
182 LogRequest.m_lines = htonl(m_neededLogEntries);
183 if (m_neededLogEntries == 0)
184 {
185 LogRequest.m_idFrom = htonl(m_neededLogFirstId > 0 ? m_neededLogFirstId : 1);
186 }
187 else
188 {
189 LogRequest.m_idFrom = 0;
190 }
191
192 if (!connection.Send((char*)(&LogRequest), sizeof(LogRequest)))
193 {
194 return false;
195 }
196
197 // Now listen for the returned log
198 SNzbLogResponse LogResponse;
199 bool read = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
200 if (!read ||
201 (int)ntohl(LogResponse.m_messageBase.m_signature) != (int)NZBMESSAGE_SIGNATURE ||
202 ntohl(LogResponse.m_messageBase.m_structSize) != sizeof(LogResponse))
203 {
204 return false;
205 }
206
207 CharBuffer buf;
208 if (ntohl(LogResponse.m_trailingDataLength) > 0)
209 {
210 buf.Reserve(ntohl(LogResponse.m_trailingDataLength));
211 if (!connection.Recv(buf, buf.Size()))
212 {
213 return false;
214 }
215 }
216
217 connection.Disconnect();
218
219 if (ntohl(LogResponse.m_trailingDataLength) > 0)
220 {
221 char* bufPtr = (char*)buf;
222 for (uint32 i = 0; i < ntohl(LogResponse.m_nrTrailingEntries); i++)
223 {
224 SNzbLogResponseEntry* logAnswer = (SNzbLogResponseEntry*) bufPtr;
225
226 char* text = bufPtr + sizeof(SNzbLogResponseEntry);
227
228 m_remoteMessages.emplace_back(ntohl(logAnswer->m_id), (Message::EKind)ntohl(logAnswer->m_kind), ntohl(logAnswer->m_time), text);
229
230 bufPtr += sizeof(SNzbLogResponseEntry) + ntohl(logAnswer->m_textLen);
231 }
232 }
233
234 return true;
235 }
236
RequestFileList()237 bool Frontend::RequestFileList()
238 {
239 const char* controlIp = !strcmp(g_Options->GetControlIp(), "0.0.0.0") ? "127.0.0.1" : g_Options->GetControlIp();
240 Connection connection(controlIp, g_Options->GetControlPort(), false);
241
242 bool OK = connection.Connect();
243 if (!OK)
244 {
245 return false;
246 }
247
248 SNzbListRequest ListRequest;
249 InitMessageBase(&ListRequest.m_messageBase, rrList, sizeof(ListRequest));
250 ListRequest.m_fileList = htonl(m_fileList);
251 ListRequest.m_serverState = htonl(m_summary);
252
253 if (!connection.Send((char*)(&ListRequest), sizeof(ListRequest)))
254 {
255 return false;
256 }
257
258 // Now listen for the returned list
259 SNzbListResponse ListResponse;
260 bool read = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
261 if (!read ||
262 (int)ntohl(ListResponse.m_messageBase.m_signature) != (int)NZBMESSAGE_SIGNATURE ||
263 ntohl(ListResponse.m_messageBase.m_structSize) != sizeof(ListResponse))
264 {
265 return false;
266 }
267
268 CharBuffer buf;
269 if (ntohl(ListResponse.m_trailingDataLength) > 0)
270 {
271 buf.Reserve(ntohl(ListResponse.m_trailingDataLength));
272 if (!connection.Recv(buf, buf.Size()))
273 {
274 return false;
275 }
276 }
277
278 connection.Disconnect();
279
280 if (m_summary)
281 {
282 m_pauseDownload = ntohl(ListResponse.m_downloadPaused);
283 m_remainingSize = Util::JoinInt64(ntohl(ListResponse.m_remainingSizeHi), ntohl(ListResponse.m_remainingSizeLo));
284 m_currentDownloadSpeed = ntohl(ListResponse.m_downloadRate);
285 m_downloadLimit = ntohl(ListResponse.m_downloadLimit);
286 m_threadCount = ntohl(ListResponse.m_threadCount);
287 m_postJobCount = ntohl(ListResponse.m_postJobCount);
288 m_upTimeSec = ntohl(ListResponse.m_upTimeSec);
289 m_dnTimeSec = ntohl(ListResponse.m_downloadTimeSec);
290 m_standBy = ntohl(ListResponse.m_downloadStandBy);
291 m_allBytes = Util::JoinInt64(ntohl(ListResponse.m_downloadedBytesHi), ntohl(ListResponse.m_downloadedBytesLo));
292 }
293
294 if (m_fileList && ntohl(ListResponse.m_trailingDataLength) > 0)
295 {
296 RemoteClient client;
297 client.SetVerbose(false);
298
299 client.BuildFileList(&ListResponse, buf, DownloadQueue::Guard());
300 }
301
302 return true;
303 }
304
RequestPauseUnpause(bool pause)305 bool Frontend::RequestPauseUnpause(bool pause)
306 {
307 RemoteClient client;
308 client.SetVerbose(false);
309 return client.RequestServerPauseUnpause(pause, rpDownload);
310 }
311
RequestSetDownloadRate(int rate)312 bool Frontend::RequestSetDownloadRate(int rate)
313 {
314 RemoteClient client;
315 client.SetVerbose(false);
316 return client.RequestServerSetDownloadRate(rate);
317 }
318
RequestEditQueue(DownloadQueue::EEditAction action,int offset,int id)319 bool Frontend::RequestEditQueue(DownloadQueue::EEditAction action, int offset, int id)
320 {
321 RemoteClient client;
322 client.SetVerbose(false);
323 IdList ids = { id };
324 return client.RequestServerEditQueue(action, offset, nullptr, &ids, nullptr, rmId);
325 }
326
Wait(int milliseconds)327 void Frontend::Wait(int milliseconds)
328 {
329 if (g_WorkState->GetPauseFrontend())
330 {
331 Guard guard(m_waitMutex);
332 m_waitCond.WaitFor(m_waitMutex, 2000);
333 }
334 else
335 {
336 Util::Sleep(milliseconds);
337 }
338 }
339