1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2005-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 //
6 // Any parts of this program derived from the xMule, lMule or eMule project,
7 // or contributed by third-party developers are copyrighted by their
8 // respective authors.
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
23 //
24 
25 #include "Logger.h"
26 #include "amule.h"
27 #include "Preferences.h"
28 #include <common/Macros.h>
29 #include <common/MacrosProgramSpecific.h>
30 #include <sstream>
31 #include <wx/tokenzr.h>
32 #include <wx/wfstream.h>
33 #include <wx/sstream.h>
34 #include <wx/filename.h>
35 
36 
37 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_LOGLINE)
38 
39 
40 CDebugCategory g_debugcats[] = {
41 	CDebugCategory( logGeneral,		wxT("General") ),
42 	CDebugCategory( logHasher,		wxT("Hasher") ),
43 	CDebugCategory( logClient,		wxT("ED2k Client") ),
44 	CDebugCategory( logLocalClient,		wxT("Local Client Protocol") ),
45 	CDebugCategory( logRemoteClient,	wxT("Remote Client Protocol") ),
46 	CDebugCategory( logPacketErrors,	wxT("Packet Parsing Errors") ),
47 	CDebugCategory( logCFile,		wxT("CFile") ),
48 	CDebugCategory( logFileIO,		wxT("FileIO") ),
49 	CDebugCategory( logZLib,		wxT("ZLib") ),
50 	CDebugCategory( logAICHThread,		wxT("AICH-Hasher") ),
51 	CDebugCategory( logAICHTransfer,	wxT("AICH-Transfer") ),
52 	CDebugCategory( logAICHRecovery,	wxT("AICH-Recovery") ),
53 	CDebugCategory( logListenSocket,	wxT("ListenSocket") ),
54 	CDebugCategory( logCredits,		wxT("Credits") ),
55 	CDebugCategory( logClientUDP,		wxT("ClientUDPSocket") ),
56 	CDebugCategory( logDownloadQueue,	wxT("DownloadQueue") ),
57 	CDebugCategory( logIPFilter,		wxT("IPFilter") ),
58 	CDebugCategory( logKnownFiles,		wxT("KnownFileList") ),
59 	CDebugCategory( logPartFile,		wxT("PartFiles") ),
60 	CDebugCategory( logSHAHashSet,		wxT("SHAHashSet") ),
61 	CDebugCategory( logServer,		wxT("Servers") ),
62 	CDebugCategory( logProxy,		wxT("Proxy") ),
63 	CDebugCategory( logSearch,		wxT("Searching") ),
64 	CDebugCategory( logServerUDP,		wxT("ServerUDP") ),
65 	CDebugCategory( logClientKadUDP,	wxT("Client Kademlia UDP") ),
66 	CDebugCategory( logKadSearch,		wxT("Kademlia Search") ),
67 	CDebugCategory( logKadRouting,		wxT("Kademlia Routing") ),
68 	CDebugCategory( logKadIndex,		wxT("Kademlia Indexing") ),
69 	CDebugCategory( logKadMain,		wxT("Kademlia Main Thread") ),
70 	CDebugCategory( logKadPrefs,		wxT("Kademlia Preferences") ),
71 	CDebugCategory( logPfConvert,		wxT("PartFileConvert") ),
72 	CDebugCategory( logMuleUDP,		wxT("MuleUDPSocket" ) ),
73 	CDebugCategory( logThreads,		wxT("ThreadScheduler" ) ),
74 	CDebugCategory( logUPnP,		wxT("Universal Plug and Play" ) ),
75 	CDebugCategory( logKadUdpFwTester,	wxT("Kademlia UDP Firewall Tester") ),
76 	CDebugCategory( logKadPacketTracking,	wxT("Kademlia Packet Tracking") ),
77 	CDebugCategory( logKadEntryTracking,	wxT("Kademlia Entry Tracking") ),
78 	CDebugCategory( logEC,			wxT("External Connect") ),
79 	CDebugCategory( logHTTP,		wxT("HTTP") ),
80 	CDebugCategory( logAsio,		wxT("Asio Sockets") )
81 };
82 
83 
84 const int categoryCount = itemsof(g_debugcats);
85 
86 
87 
88 #ifdef __DEBUG__
IsEnabled(DebugType type) const89 bool CLogger::IsEnabled( DebugType type ) const
90 {
91 	int index = (int)type;
92 
93 	if ( index >= 0 && index < categoryCount ) {
94 		const CDebugCategory& cat = g_debugcats[ index ];
95 		wxASSERT( type == cat.GetType() );
96 
97 		return ( cat.IsEnabled() && thePrefs::GetVerbose() );
98 	}
99 
100 	wxFAIL;
101 	return false;
102 }
103 #endif
104 
105 
SetEnabled(DebugType type,bool enabled)106 void CLogger::SetEnabled( DebugType type, bool enabled )
107 {
108 	int index = (int)type;
109 
110 	if ( index >= 0 && index < categoryCount ) {
111 		CDebugCategory& cat = g_debugcats[ index ];
112 		wxASSERT( type == cat.GetType() );
113 
114 		cat.SetEnabled( enabled );
115 	} else {
116 		wxFAIL;
117 	}
118 }
119 
120 
AddLogLine(const wxString & DEBUG_ONLY (file),int DEBUG_ONLY (line),bool critical,DebugType type,const wxString & str,bool toStdout,bool toGUI)121 void CLogger::AddLogLine(
122 	const wxString& DEBUG_ONLY(file),
123 	int DEBUG_ONLY(line),
124 	bool critical,
125 	DebugType type,
126 	const wxString &str,
127 	bool toStdout,
128 	bool toGUI)
129 {
130 	wxString msg(str);
131 // handle Debug messages
132 	if (type != logStandard) {
133 		if (!critical && !IsEnabled(type)) {
134 			return;
135 		}
136 		if (!critical && thePrefs::GetVerboseLogfile()) {
137 			// print non critical debug messages only to the logfile
138 			toGUI = false;
139 		}
140 		int index = (int)type;
141 
142 		if ( index >= 0 && index < categoryCount ) {
143 			const CDebugCategory& cat = g_debugcats[ index ];
144 			wxASSERT(type == cat.GetType());
145 
146 			msg = cat.GetName() + wxT(": ") + msg;
147 		} else {
148 			wxFAIL;
149 		}
150 	}
151 
152 #ifdef __DEBUG__
153 	if (line) {
154 		msg = file.AfterLast(wxFileName::GetPathSeparator()).AfterLast(wxT('/')) << wxT("(") << line << wxT("): ") + msg;
155 	}
156 #endif
157 
158 	if (toGUI && !wxThread::IsMain()) {
159 		// put to background
160 		CLoggingEvent Event(critical, toStdout, toGUI, msg);
161 		AddPendingEvent(Event);
162 	} else {
163 		// Try to handle events immediatly when possible (to save to file).
164 		DoLines(msg, critical, toStdout, toGUI);
165 	}
166 }
167 
168 
AddLogLine(const wxString & file,int line,bool critical,DebugType type,const std::ostringstream & msg)169 void CLogger::AddLogLine(
170 	const wxString &file,
171 	int line,
172 	bool critical,
173 	DebugType type,
174 	const std::ostringstream &msg)
175 {
176 	AddLogLine(file, line, critical, type, wxString(char2unicode(msg.str().c_str())));
177 }
178 
179 
GetDebugCategory(int index)180 const CDebugCategory& CLogger::GetDebugCategory( int index )
181 {
182 	wxASSERT( index >= 0 && index < categoryCount );
183 
184 	return g_debugcats[ index ];
185 }
186 
187 
GetDebugCategoryCount()188 unsigned int CLogger::GetDebugCategoryCount()
189 {
190 	return categoryCount;
191 }
192 
193 
OpenLogfile(const wxString & name)194 bool CLogger::OpenLogfile(const wxString & name)
195 {
196 	applog = new wxFFileOutputStream(name);
197 	bool ret = applog->Ok();
198 	if (ret) {
199 		FlushApplog();
200 		m_LogfileName = name;
201 	} else {
202 		CloseLogfile();
203 	}
204 	return ret;
205 }
206 
207 
CloseLogfile()208 void CLogger::CloseLogfile()
209 {
210 	delete applog;
211 	applog = NULL;
212 	m_LogfileName.Clear();
213 }
214 
215 
OnLoggingEvent(class CLoggingEvent & evt)216 void CLogger::OnLoggingEvent(class CLoggingEvent& evt)
217 {
218 	DoLines(evt.Message(), evt.IsCritical(), evt.ToStdout(), evt.ToGUI());
219 }
220 
221 
DoLines(const wxString & lines,bool critical,bool toStdout,bool toGUI)222 void CLogger::DoLines(const wxString & lines, bool critical, bool toStdout, bool toGUI)
223 {
224 	// Remove newspace at end
225 	wxString bufferline = lines.Strip(wxString::trailing);
226 
227 	// Create the timestamp
228 	wxString stamp = wxDateTime::Now().FormatISODate() + wxT(" ") + wxDateTime::Now().FormatISOTime()
229 #ifdef CLIENT_GUI
230 					+ wxT(" (remote-GUI): ");
231 #else
232 					+ wxT(": ");
233 #endif
234 
235 	// critical lines get a ! prepended, ordinary lines a blank
236 	// logfile-only lines get a . to prevent transmission on EC
237 	wxString prefix = !toGUI ? wxT(".") : (critical ? wxT("!") : wxT(" "));
238 
239 	if ( bufferline.IsEmpty() ) {
240 		// If it's empty we just write a blank line with no timestamp.
241 		DoLine(wxT(" \n"), toStdout, toGUI);
242 	} else {
243 		// Split multi-line messages into individual lines
244 		wxStringTokenizer tokens( bufferline, wxT("\n") );
245 		while ( tokens.HasMoreTokens() ) {
246 			wxString fullline = prefix + stamp + tokens.GetNextToken() + wxT("\n");
247 			DoLine(fullline, toStdout, toGUI);
248 		}
249 	}
250 }
251 
252 
DoLine(const wxString & line,bool toStdout,bool GUI_ONLY (toGUI))253 void CLogger::DoLine(const wxString & line, bool toStdout, bool GUI_ONLY(toGUI))
254 {
255 	{
256 		wxMutexLocker lock(m_lineLock);
257 		++m_count;
258 
259 		// write to logfile
260 		m_ApplogBuf += line;
261 		FlushApplog();
262 
263 		// write to Stdout
264 		if (m_StdoutLog || toStdout) {
265 			printf("%s", (const char*)unicode2char(line));
266 		}
267 	}
268 #ifndef AMULE_DAEMON
269 	// write to Listcontrol
270 	if (toGUI) {
271 		theApp->AddGuiLogLine(line);
272 	}
273 #endif
274 }
275 
276 
EmergencyLog(const wxString & message,bool closeLog)277 void CLogger::EmergencyLog(const wxString &message, bool closeLog)
278 {
279 	fprintf(stderr, "%s", (const char*)unicode2char(message));
280 	m_ApplogBuf += message;
281 	FlushApplog();
282 	if (closeLog && applog) {
283 		applog->Close();
284 		applog = NULL;
285 	}
286 }
287 
288 
FlushApplog()289 void CLogger::FlushApplog()
290 {
291 	if (applog) { // Wait with output until logfile is actually opened
292 		wxStringInputStream stream(m_ApplogBuf);
293 		(*applog) << stream;
294 		applog->Sync();
295 		m_ApplogBuf.Clear();
296 	}
297 }
298 
299 CLogger theLogger;
300 
BEGIN_EVENT_TABLE(CLogger,wxEvtHandler)301 BEGIN_EVENT_TABLE(CLogger, wxEvtHandler)
302 	EVT_MULE_LOGGING(CLogger::OnLoggingEvent)
303 END_EVENT_TABLE()
304 
305 
306 CLoggerTarget::CLoggerTarget()
307 {
308 }
309 
310 #if wxCHECK_VERSION(2, 9, 0)
DoLogText(const wxString & msg)311 void CLoggerTarget::DoLogText(const wxString &msg)
312 {
313 	// prevent infinite recursion
314 	static bool recursion = false;
315 	if (recursion) {
316 		return;
317 	}
318 	recursion = true;
319 
320 	// This is much simpler than manually handling all wx log-types.
321 	if (msg.StartsWith(_("ERROR: ")) || msg.StartsWith(_("WARNING: "))) {
322 		AddLogLineC(msg);
323 	} else {
324 		AddLogLineN(msg);
325 	}
326 
327 	recursion = false;
328 }
329 #else
DoLogString(const wxChar * msg,time_t)330 void CLoggerTarget::DoLogString(const wxChar* msg, time_t)
331 {
332 	// prevent infinite recursion
333 	static bool recursion = false;
334 	if (recursion) {
335 		return;
336 	}
337 	recursion = true;
338 
339 	wxCHECK_RET(msg, wxT("Log message is NULL in DoLogString!"));
340 
341 	wxString str(msg);
342 
343 	// This is much simpler than manually handling all wx log-types.
344 	// cppcheck-suppress duplicateBranch
345 	if (str.StartsWith(_("ERROR: ")) || str.StartsWith(_("WARNING: "))) {
346 		AddLogLineC(str);
347 	} else {
348 		AddLogLineN(str);
349 	}
350 
351 	recursion = false;
352 }
353 #endif
354 
CLoggerAccess()355 CLoggerAccess::CLoggerAccess()
356 {
357 	m_bufferlen = 4096;
358 	m_buffer = new wxCharBuffer(m_bufferlen);
359 	m_logfile = NULL;
360 	Reset();
361 }
362 
363 
Reset()364 void CLoggerAccess::Reset()
365 {
366 	delete m_logfile;
367 	m_logfile = new wxFFileInputStream(theLogger.GetLogfileName());
368 	m_pos = 0;
369 	m_ready = false;
370 }
371 
372 
~CLoggerAccess()373 CLoggerAccess::~CLoggerAccess()
374 {
375 	delete m_buffer;
376 	delete m_logfile;
377 }
378 
379 
380 //
381 // read a line of text from the logfile if available
382 // (can't believe there's no library function for this >:( )
383 //
HasString()384 bool CLoggerAccess::HasString()
385 {
386 	while (!m_ready) {
387 		int c = m_logfile->GetC();
388 		if (c == wxEOF) {
389 			break;
390 		}
391 		// check for buffer overrun
392 		if (m_pos == m_bufferlen) {
393 			m_bufferlen += 1024;
394 			m_buffer->extend(m_bufferlen);
395 		}
396 		m_buffer->data()[m_pos++] = c;
397 		if (c == '\n') {
398 			if (m_buffer->data()[0] == '.') {
399 				// Log-only line, skip
400 				m_pos = 0;
401 			} else {
402 				m_ready = true;
403 			}
404 		}
405 	}
406 	return m_ready;
407 }
408 
409 
GetString(wxString & s)410 bool CLoggerAccess::GetString(wxString & s)
411 {
412 	if (!HasString()) {
413 		return false;
414 	}
415 	s = wxString(m_buffer->data(), wxConvUTF8, m_pos);
416 	m_pos = 0;
417 	m_ready = false;
418 	return true;
419 }
420 
421 // Functions for EC logging
422 
423 #ifdef __DEBUG__
424 #include "ec/cpp/ECLog.h"
425 
ECLogIsEnabled()426 bool ECLogIsEnabled()
427 {
428 	return theLogger.IsEnabled(logEC);
429 }
430 
DoECLogLine(const wxString & line)431 void DoECLogLine(const wxString &line)
432 {
433 	// without file/line
434 	theLogger.AddLogLine(wxEmptyString, 0, false, logStandard, line, false, false);
435 }
436 
437 #endif /* __DEBUG__ */
438 // File_checked_for_headers
439