1//
2// This file is part of the aMule Project.
3//
4// Copyright (c) 2004-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 "ExternalConnector.h"
26
27#ifdef HAVE_CONFIG_H
28	#include "config.h"	// Needed for VERSION and readline detection
29#endif
30
31#include <common/ClientVersion.h>
32#include <common/Format.h>		// Needed for CFormat
33#include <wx/tokenzr.h>		// For wxStringTokenizer
34
35// For readline
36#ifdef HAVE_LIBREADLINE
37	#if defined(HAVE_READLINE_READLINE_H)
38		#include <readline/readline.h>  // Do_not_auto_remove
39	#elif defined(HAVE_READLINE_H)
40		#include <readline.h> // Do_not_auto_remove
41	#else /* !defined(HAVE_READLINE_H) */
42		extern "C" char *readline (const char*);
43	#endif /* !defined(HAVE_READLINE_H) */
44#else /* !defined(HAVE_READLINE_READLINE_H) */
45	/* no readline */
46#endif /* HAVE_LIBREADLINE */
47
48// For history
49#ifdef HAVE_READLINE_HISTORY
50	#if defined(HAVE_READLINE_HISTORY_H)
51		#include <readline/history.h> // Do_not_auto_remove
52	#elif defined(HAVE_HISTORY_H)
53		#include <history.h> // Do_not_auto_remove
54	#else /* !defined(HAVE_HISTORY_H) */
55		extern "C" void add_history (const char*);
56	#endif /* defined(HAVE_READLINE_HISTORY_H) */
57#else
58	/* no history */
59#endif /* HAVE_READLINE_HISTORY */
60
61
62#include <ec/cpp/ECFileConfig.h>	// Needed for CECFileConfig
63#include <common/MD5Sum.h>
64
65#ifdef _MSC_VER  // silly warnings about deprecated functions
66#pragma warning(disable:4996)
67#endif
68
69//-------------------------------------------------------------------
70
71CCommandTree::~CCommandTree()
72{
73	DeleteContents(m_subcommands);
74}
75
76
77CCommandTree* CCommandTree::AddCommand(CCommandTree* command)
78{
79	command->m_parent = this;
80	const wxString& cmd = command->m_command;
81	CmdPos_t it;
82	for (it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
83		if ((*it)->m_command > cmd) {
84			break;
85		}
86	}
87	m_subcommands.insert(it, command);
88	return command;
89}
90
91
92int CCommandTree::FindCommandId(const wxString& command, wxString& args, wxString& cmdstr) const
93{
94	wxString cmd = command.BeforeFirst(wxT(' ')).Lower();
95	for (CmdPosConst_t it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
96		if ((*it)->m_command.Lower() == cmd) {
97			args = command.AfterFirst(wxT(' ')).Trim(false);
98			return (*it)->FindCommandId(args, args, cmdstr);
99		}
100	}
101	cmdstr = GetFullCommand().Lower();
102	if (m_params == CMD_PARAM_ALWAYS && args.IsEmpty()) {
103		return CMD_ERR_MUST_HAVE_PARAM;
104	} else if (m_params == CMD_PARAM_NEVER && !args.IsEmpty()) {
105		return CMD_ERR_NO_PARAM;
106	} else {
107		if ((m_cmd_id >= 0) && (m_cmd_id & CMD_DEPRECATED)) {
108			m_app.Show(wxT('\n') + m_verbose + wxT('\n'));
109			return m_cmd_id & ~CMD_DEPRECATED;
110		} else {
111			return m_cmd_id;
112		}
113	}
114}
115
116
117wxString CCommandTree::GetFullCommand() const
118{
119	wxString cmd = m_command;
120
121	const CCommandTree *parent = m_parent;
122	while (parent && parent->m_parent) {
123		cmd = parent->m_command + wxT(" ") + cmd;
124		parent = parent->m_parent;
125	}
126
127	return cmd;
128}
129
130
131void CCommandTree::PrintHelpFor(const wxString& command) const
132{
133	wxString cmd = command.BeforeFirst(wxT(' ')).Lower();
134	if (!cmd.IsEmpty()) {
135		for (CmdPosConst_t it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
136			if ((*it)->m_command.Lower() == cmd) {
137				(*it)->PrintHelpFor(command.AfterFirst(wxT(' ')).Trim(false));
138				return;
139			}
140		}
141		if (m_parent) {
142			m_app.Show(CFormat(_("Unknown extension '%s' for the '%s' command.\n")) % command % GetFullCommand());
143		} else {
144			m_app.Show(CFormat(_("Unknown command '%s'.\n")) % command);
145		}
146	} else {
147		wxString fullcmd = GetFullCommand();
148		if (!fullcmd.IsEmpty()) {
149			m_app.Show(fullcmd.Upper() + wxT(": ") + wxGetTranslation(m_short) + wxT("\n"));
150			if (!m_verbose.IsEmpty()) {
151				m_app.Show(wxT("\n"));
152				m_app.Show(wxGetTranslation(m_verbose));
153			}
154		}
155		if (m_params == CMD_PARAM_NEVER) {
156			m_app.Show(_("\nThis command cannot have an argument.\n"));
157		} else if (m_params == CMD_PARAM_ALWAYS) {
158			m_app.Show(_("\nThis command must have an argument.\n"));
159		}
160		if (m_cmd_id == CMD_ERR_INCOMPLETE) {
161			m_app.Show(_("\nThis command is incomplete, you must use one of the extensions below.\n"));
162		}
163		if (!m_subcommands.empty()) {
164			CmdPosConst_t it;
165			int maxlen = 0;
166			if (m_parent) {
167				m_app.Show(_("\nAvailable extensions:\n"));
168			} else {
169				m_app.Show(_("Available commands:\n"));
170			}
171			for (it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
172				if (!((*it)->m_cmd_id >= 0 && (*it)->m_cmd_id & CMD_DEPRECATED) || m_parent) {
173					int len = (*it)->m_command.Length();
174					if (len > maxlen) {
175						maxlen = len;
176					}
177				}
178			}
179			maxlen += 4;
180			for (it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
181				if (!((*it)->m_cmd_id >= 0 && (*it)->m_cmd_id & CMD_DEPRECATED) || m_parent) {
182					m_app.Show((*it)->m_command + wxString(wxT(' '), maxlen - (*it)->m_command.Length()) + wxGetTranslation((*it)->m_short) + wxT("\n"));
183				}
184			}
185			if (!m_parent) {
186				m_app.Show(CFormat(_("\nAll commands are case insensitive.\nType '%s <command>' to get detailed info on <command>.\n")) % wxT("help"));
187			}
188		}
189	}
190	m_app.Show(wxT("\n"));
191}
192
193//-------------------------------------------------------------------
194
195CaMuleExternalConnector::CaMuleExternalConnector()
196	: m_configFile(NULL),
197	  m_port(-1),
198	  m_KeepQuiet(false),
199	  m_Verbose(false),
200	  m_interactive(false),
201	  m_commands(*this),
202	  m_ECClient(NULL),
203	  m_InputLine(NULL),
204	  m_NeedsConfigSave(false),
205	  m_locale(NULL),
206	  m_strFullVersion(NULL),
207	  m_strOSDescription(NULL)
208{
209	SetAppName(wxT("aMule"));	// Do not change!
210}
211
212CaMuleExternalConnector::~CaMuleExternalConnector()
213{
214	delete m_configFile;
215	delete m_locale;
216	free(m_strFullVersion);
217	free(m_strOSDescription);
218}
219
220void CaMuleExternalConnector::OnInitCommandSet()
221{
222	m_commands.AddCommand(wxT("Quit"), CMD_ID_QUIT, wxTRANSLATE("Exits from the application."), wxEmptyString);
223	m_commands.AddCommand(wxT("Exit"), CMD_ID_QUIT, wxTRANSLATE("Exits from the application."), wxEmptyString);
224	m_commands.AddCommand(wxT("Help"), CMD_ID_HELP, wxTRANSLATE("Show help."),
225			      /* TRANSLATORS:
226				 Do not translate the word 'help', it is a command to the program! */
227			      wxTRANSLATE("To get help on a command, type 'help <command>'.\nTo get the full command list type 'help'.\n"));
228}
229
230void CaMuleExternalConnector::Show(const wxString &s)
231{
232	if( !m_KeepQuiet ) {
233		printf("%s", (const char *)unicode2char(s));
234#ifdef __WXMSW__
235		fflush(stdout);
236#endif
237	}
238}
239
240void CaMuleExternalConnector::ShowGreet()
241{
242	wxString text = GetGreetingTitle();
243	int len = text.Length();
244	Show(wxT('\n') + wxString(wxT('-'), 22 + len) + wxT('\n'));
245	Show(wxT('|') + wxString(wxT(' '), 10) + text + wxString(wxT(' '), 10) + wxT('|') + wxT('\n'));
246	Show(wxString(wxT('-'), 22 + len) + wxT('\n'));
247	// Do not merge the line below, or translators could translate "Help"
248	Show(CFormat(_("\nUse '%s' for command list\n\n")) % wxT("Help"));
249}
250
251void CaMuleExternalConnector::Process_Answer(const wxString& answer)
252{
253	wxStringTokenizer tokens(answer, wxT("\n"));
254	while ( tokens.HasMoreTokens() ) {
255		Show(wxT(" > ") + tokens.GetNextToken() + wxT("\n"));
256	}
257}
258
259bool CaMuleExternalConnector::Parse_Command(const wxString& buffer)
260{
261	wxString cmd;
262	wxStringTokenizer tokens(buffer);
263	while (tokens.HasMoreTokens()) {
264		cmd += tokens.GetNextToken() + wxT(' ');
265	}
266	cmd.Trim(false);
267	cmd.Trim(true);
268	int cmd_ID = GetIDFromString(cmd);
269	if ( cmd_ID >= 0 ) {
270		cmd_ID = ProcessCommand(cmd_ID);
271	}
272	wxString error;
273	switch (cmd_ID) {
274		case CMD_ID_HELP:
275			m_commands.PrintHelpFor(GetCmdArgs());
276			break;
277		case CMD_ERR_SYNTAX:
278			error = _("Syntax error!");
279			break;
280		case CMD_ERR_PROCESS_CMD:
281			Show(_("Error processing command - should never happen! Report bug, please\n"));
282			break;
283		case CMD_ERR_NO_PARAM:
284			error = _("This command should not have any parameters.");
285			break;
286		case CMD_ERR_MUST_HAVE_PARAM:
287			error = _("This command must have a parameter.");
288			break;
289		case CMD_ERR_INVALID_ARG:
290			error = _("Invalid argument.");
291			break;
292		case CMD_ERR_INCOMPLETE:
293			error = _("This is an incomplete command.");
294			break;
295	}
296	if (!error.IsEmpty()) {
297		Show(error + wxT('\n'));
298		wxString helpStr(wxT("help"));
299		if (!GetLastCmdStr().IsEmpty()) {
300			helpStr << wxT(' ') << GetLastCmdStr();
301		}
302		Show(CFormat(_("Type '%s' to get more help.\n")) % helpStr);
303	}
304	return cmd_ID == CMD_ID_QUIT;
305}
306
307void CaMuleExternalConnector::GetCommand(const wxString &prompt, char* buffer, size_t buffer_size)
308{
309#ifdef HAVE_LIBREADLINE
310		char *text = readline(unicode2char(prompt + wxT("$ ")));
311		if (text && *text &&
312		    (m_InputLine == 0 || strcmp(text,m_InputLine) != 0)) {
313		  add_history (text);
314		}
315		if (m_InputLine)
316		  free(m_InputLine);
317		m_InputLine = text;
318#else
319		Show(prompt + wxT("$ "));
320		const char *text = fgets(buffer, buffer_size, stdin);	// == buffer if ok, NULL if eof
321#endif /* HAVE_LIBREADLINE */
322		if ( text ) {
323			size_t len = strlen(text);
324			if (len > buffer_size - 1) {
325				len = buffer_size - 1;
326			}
327			if (buffer != text) {
328				strncpy(buffer, text, len);
329			}
330			buffer[len] = 0;
331		} else {
332			strncpy(buffer, "quit", buffer_size);
333		}
334}
335
336void CaMuleExternalConnector::TextShell(const wxString &prompt)
337{
338	char buffer[2048];
339	wxString buf;
340
341	bool The_End = false;
342	do {
343		GetCommand(prompt, buffer, sizeof buffer);
344		buf = char2unicode(buffer);
345		The_End = Parse_Command(buf);
346	} while ((!The_End) && (m_ECClient->IsSocketConnected()));
347}
348
349void CaMuleExternalConnector::ConnectAndRun(const wxString &ProgName, const wxString& ProgVersion)
350{
351	if (m_NeedsConfigSave) {
352		SaveConfigFile();
353		return;
354	}
355
356	#ifdef SVNDATE
357		Show(CFormat(_("This is %s %s %s\n")) % wxString::FromAscii(m_appname) % wxT(VERSION) % wxT(SVNDATE));
358	#else
359		Show(CFormat(_("This is %s %s\n")) % wxString::FromAscii(m_appname) % wxT(VERSION));
360	#endif
361
362	// HostName, Port and Password
363	if ( m_password.IsEmpty() ) {
364		wxString pass_plain;
365		#ifndef __WXMSW__
366			pass_plain = char2unicode(getpass("Enter password for mule connection: "));
367		#else
368			//#warning This way, pass enter is not hidden on windows. Bad thing.
369			char temp_str[512];
370			fflush(stdin);
371			printf("Enter password for mule connection: \n");
372			fflush(stdout);
373			fgets(temp_str, 512, stdin);
374			temp_str[strlen(temp_str)-1] = '\0';
375			pass_plain = char2unicode(temp_str);
376		#endif
377		wxCHECK2(m_password.Decode(MD5Sum(pass_plain).GetHash()), /* Do nothing. */ );
378		// MD5 hash for an empty string, according to rfc1321.
379		if (m_password.Encode() == wxT("D41D8CD98F00B204E9800998ECF8427E")) {
380			m_password.Clear();
381		}
382
383		// Clear plain-text password
384		pass_plain		= wxT("01234567890123456789");
385	}
386
387	if (!m_password.IsEmpty()) {
388
389		// Create the socket
390		Show(_("\nCreating client...\n"));
391		m_ECClient = new CRemoteConnect(NULL);
392		m_ECClient->SetCapabilities(m_ZLIB, true, false);	// ZLIB, UTF8 numbers, notification
393
394		// ConnectToCore is blocking since m_ECClient was initialized with NULL
395		if (!m_ECClient->ConnectToCore(m_host, m_port, wxT("foobar"), m_password.Encode(), ProgName, ProgVersion)) {
396			// no connection => close gracefully
397			if (!m_ECClient->GetServerReply().IsEmpty()) {
398					Show(CFormat(wxT("%s\n")) % m_ECClient->GetServerReply());
399			}
400			Show(CFormat(_("Connection Failed. Unable to connect to %s:%d\n")) % m_host % m_port);
401		} else {
402			// Authenticate ourselves
403			// ConnectToCore() already authenticated for us.
404			//m_ECClient->ConnectionEstablished();
405			Show(m_ECClient->GetServerReply()+wxT("\n"));
406			if (m_ECClient->IsSocketConnected()) {
407				if (m_interactive) {
408					ShowGreet();
409				}
410				Pre_Shell();
411				TextShell(ProgName);
412				Post_Shell();
413				if (m_interactive) {
414					Show(CFormat(_("\nOk, exiting %s...\n")) % ProgName);
415				}
416			}
417		}
418		m_ECClient->DestroySocket();
419	} else {
420		Show(_("Cannot connect with an empty password.\nYou must specify a password either in config file\nor on command-line, or enter one when asked.\n\nExiting...\n"));
421	}
422}
423
424void CaMuleExternalConnector::OnInitCmdLine(wxCmdLineParser& parser, const char* appname)
425{
426	m_appname = appname;
427
428	parser.AddSwitch(wxEmptyString, wxT("help"),
429		_("Show this help text."),
430		wxCMD_LINE_PARAM_OPTIONAL);
431	parser.AddOption(wxT("h"), wxT("host"),
432		_("Host where aMule is running. (default: localhost)"),
433		wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
434	parser.AddOption(wxT("p"), wxT("port"),
435		_("aMule's port for External Connection. (default: 4712)"),
436		wxCMD_LINE_VAL_NUMBER, wxCMD_LINE_PARAM_OPTIONAL);
437	parser.AddOption(wxT("P"), wxT("password"),
438		_("External Connection password."),
439		wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
440	parser.AddOption(wxT("f"), wxT("config-file"),
441		_("Read configuration from file."),
442		wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
443	parser.AddSwitch(wxT("q"), wxT("quiet"),
444		_("Do not print any output to stdout."),
445		wxCMD_LINE_PARAM_OPTIONAL);
446	parser.AddSwitch(wxT("v"), wxT("verbose"),
447		_("Be verbose - show also debug messages."),
448		wxCMD_LINE_PARAM_OPTIONAL);
449	parser.AddOption(wxT("l"), wxT("locale"),
450		_("Sets program locale (language)."),
451		wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
452	parser.AddSwitch(wxT("w"), wxT("write-config"),
453		_("Write command line options to config file."),
454		wxCMD_LINE_PARAM_OPTIONAL);
455	parser.AddOption(wxEmptyString, wxT("create-config-from"),
456		_("Creates config file based on aMule's config file."),
457		wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
458	parser.AddSwitch(wxEmptyString, wxT("version"),
459		_("Print program version."),
460		wxCMD_LINE_PARAM_OPTIONAL);
461}
462
463bool CaMuleExternalConnector::OnCmdLineParsed(wxCmdLineParser& parser)
464{
465	if (parser.Found(wxT("version"))) {
466		printf("%s %s\n", m_appname, (const char *)unicode2char(GetMuleVersion()));
467		return false;
468	}
469
470	if (!parser.Found(wxT("config-file"), &m_configFileName)) {
471		m_configFileName = GetConfigDir() + wxT("remote.conf");
472	}
473
474	wxString aMuleConfigFile;
475	if (parser.Found(wxT("create-config-from"), &aMuleConfigFile)) {
476		aMuleConfigFile = FinalizeFilename(aMuleConfigFile);
477		if (!::wxFileExists(aMuleConfigFile)) {
478			fprintf(stderr, "%s\n", (const char *)unicode2char(wxT("FATAL ERROR: File does not exist: ") + aMuleConfigFile));
479			exit(1);
480		}
481		CECFileConfig aMuleConfig(aMuleConfigFile);
482		LoadAmuleConfig(aMuleConfig);
483		SaveConfigFile();
484		m_configFile->Flush();
485		exit(0);
486	}
487
488	LoadConfigFile();
489
490	if ( !parser.Found(wxT("host"), &m_host) ) {
491		if ( m_host.IsEmpty() ) {
492			m_host = wxT("localhost");
493		}
494	}
495
496	long port;
497	if (parser.Found(wxT("port"), &port)) {
498		m_port = port;
499	}
500
501	wxString pass_plain;
502	if (parser.Found(wxT("password"), &pass_plain)) {
503		if (!pass_plain.IsEmpty()) {
504			m_password.Decode(MD5Sum(pass_plain).GetHash());
505		} else {
506			m_password.Clear();
507		}
508	}
509
510	if (parser.Found(wxT("write-config"))) {
511		m_NeedsConfigSave = true;
512	}
513
514	parser.Found(wxT("locale"), &m_language);
515
516	if (parser.Found(wxT("help"))) {
517		parser.Usage();
518		return false;
519	}
520
521	m_KeepQuiet = parser.Found(wxT("quiet"));
522	m_Verbose = parser.Found(wxT("verbose"));
523
524	return true;
525}
526
527void CaMuleExternalConnector::LoadAmuleConfig(CECFileConfig& cfg)
528{
529	m_host = wxT("localhost");
530	m_port = cfg.Read(wxT("/ExternalConnect/ECPort"), 4712l);
531	cfg.ReadHash(wxT("/ExternalConnect/ECPassword"), &m_password);
532	m_language = cfg.Read(wxT("/eMule/Language"), wxEmptyString);
533}
534
535
536void CaMuleExternalConnector::LoadConfigFile()
537{
538	if (!m_configFile) {
539		m_configFile = new CECFileConfig(m_configFileName);
540	}
541	if (m_configFile) {
542		m_language = m_configFile->Read(wxT("/Locale"), wxEmptyString);
543		m_host = m_configFile->Read(wxT("/EC/Host"), wxEmptyString);
544		m_port = m_configFile->Read(wxT("/EC/Port"), 4712l);
545		m_configFile->ReadHash(wxT("/EC/Password"), &m_password);
546		m_ZLIB = m_configFile->Read(wxT("/EC/ZLIB"), 1l) != 0;
547	}
548}
549
550void CaMuleExternalConnector::SaveConfigFile()
551{
552	if (!wxFileName::DirExists(GetConfigDir())) {
553		wxFileName::Mkdir(GetConfigDir());
554	}
555	if (!m_configFile) {
556		m_configFile = new CECFileConfig(m_configFileName);
557	}
558	if (m_configFile) {
559		m_configFile->Write(wxT("/Locale"), m_language);
560		m_configFile->Write(wxT("/EC/Host"), m_host);
561		m_configFile->Write(wxT("/EC/Port"), m_port);
562		m_configFile->WriteHash(wxT("/EC/Password"), m_password);
563	}
564}
565
566bool CaMuleExternalConnector::OnInit()
567{
568#ifndef __WXMSW__
569	#if wxUSE_ON_FATAL_EXCEPTION
570		// catch fatal exceptions
571		wxHandleFatalExceptions(true);
572	#endif
573#endif
574
575	m_strFullVersion = strdup((const char *)unicode2char(GetMuleVersion()));
576	m_strOSDescription = strdup((const char *)unicode2char(wxGetOsDescription()));
577
578	// Handle uncaught exceptions
579	InstallMuleExceptionHandler();
580
581	bool retval = wxApp::OnInit();
582	OnInitCommandSet();
583	InitCustomLanguages();
584	SetLocale(m_language);
585	return retval;
586}
587
588wxString CaMuleExternalConnector::SetLocale(const wxString& language)
589{
590	if (!language.IsEmpty()) {
591		m_language = language;
592		if (m_locale) {
593			delete m_locale;
594		}
595		m_locale = new wxLocale;
596		InitLocale(*m_locale, StrLang2wx(language));
597	}
598
599	return m_locale == NULL ? wxString() : m_locale->GetCanonicalName();
600}
601
602#if !wxUSE_GUI && defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0)
603
604#include <wx/apptrait.h> // Do_not_auto_remove
605#include <wx/stdpaths.h> // Do_not_auto_remove
606
607class CaMuleExternalConnectorTraits : public wxConsoleAppTraits
608{
609public:
610	virtual wxStandardPathsBase& GetStandardPaths()
611	{
612		return s_stdPaths;
613	}
614
615private:
616	static wxStandardPathsCF s_stdPaths;
617};
618
619wxStandardPathsCF CaMuleExternalConnectorTraits::s_stdPaths;
620
621wxAppTraits* CaMuleExternalConnector::CreateTraits()
622{
623	return new CaMuleExternalConnectorTraits;
624}
625
626#endif
627
628#if wxUSE_ON_FATAL_EXCEPTION
629// Gracefully handle fatal exceptions and print backtrace if possible
630void CaMuleExternalConnector::OnFatalException()
631{
632	/* Print the backtrace */
633	fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
634	fprintf(stderr, "A fatal error has occurred and %s has crashed.\n", m_appname);
635	fprintf(stderr, "Please assist us in fixing this problem by posting the backtrace below in our\n");
636	fprintf(stderr, "'aMule Crashes' forum and include as much information as possible regarding the\n");
637	fprintf(stderr, "circumstances of this crash. The forum is located here:\n");
638	fprintf(stderr, "    http://forum.amule.org/index.php?board=67.0\n");
639	fprintf(stderr, "If possible, please try to generate a real backtrace of this crash:\n");
640	fprintf(stderr, "    http://wiki.amule.org/index.php/Backtraces\n\n");
641	fprintf(stderr, "----------------------------=| BACKTRACE FOLLOWS: |=----------------------------\n");
642	fprintf(stderr, "Current version is: %s %s\n", m_appname, m_strFullVersion);
643	fprintf(stderr, "Running on: %s\n\n", m_strOSDescription);
644
645	print_backtrace(1); // 1 == skip this function.
646
647	fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
648}
649#endif
650
651#ifdef __WXDEBUG__
652void CaMuleExternalConnector::OnAssertFailure(const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg)
653{
654#if !defined wxUSE_STACKWALKER || !wxUSE_STACKWALKER
655	wxString errmsg = CFormat( wxT("%s:%s:%d: Assertion '%s' failed. %s") ) % file % func % line % cond % ( msg ? msg : wxT("") );
656
657	fprintf(stderr, "Assertion failed: %s\n", (const char*)unicode2char(errmsg));
658
659	// Skip the function-calls directly related to the assert call.
660	fprintf(stderr, "\nBacktrace follows:\n");
661	print_backtrace(3);
662	fprintf(stderr, "\n");
663#else
664	wxApp::OnAssertFailure(file, line, func, cond, msg);
665#endif
666}
667#endif
668// File_checked_for_headers
669