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