Removed a bad assert
[amule.git] / src / amule.cpp
blobb595feb950c31527cbe0728e0dfa098b4f807239
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "amule.h" // Interface declarations.
29 #include <csignal>
30 #include <cstring>
31 #include <wx/process.h>
32 #include <wx/sstream.h>
34 #ifdef HAVE_CONFIG_H
35 #include "config.h" // Needed for HAVE_GETRLIMIT, HAVE_SETRLIMIT,
36 // HAVE_SYS_RESOURCE_H, HAVE_SYS_STATVFS_H, VERSION
37 // and ENABLE_NLS
38 #endif
40 #include <common/ClientVersion.h>
42 #include <wx/cmdline.h> // Needed for wxCmdLineParser
43 #include <wx/config.h> // Do_not_auto_remove (win32)
44 #include <wx/fileconf.h>
45 #include <wx/tokenzr.h>
46 #include <wx/wfstream.h>
49 #include <common/Format.h> // Needed for CFormat
50 #include "kademlia/kademlia/Kademlia.h"
51 #include "kademlia/kademlia/Prefs.h"
52 #include "kademlia/kademlia/UDPFirewallTester.h"
53 #include "CanceledFileList.h"
54 #include "ClientCreditsList.h" // Needed for CClientCreditsList
55 #include "ClientList.h" // Needed for CClientList
56 #include "ClientUDPSocket.h" // Needed for CClientUDPSocket & CMuleUDPSocket
57 #include "ExternalConn.h" // Needed for ExternalConn & MuleConnection
58 #include <common/FileFunctions.h> // Needed for CDirIterator
59 #include "FriendList.h" // Needed for CFriendList
60 #include "HTTPDownload.h" // Needed for CHTTPDownloadThread
61 #include "InternalEvents.h" // Needed for CMuleInternalEvent
62 #include "IPFilter.h" // Needed for CIPFilter
63 #include "KnownFileList.h" // Needed for CKnownFileList
64 #include "ListenSocket.h" // Needed for CListenSocket
65 #include "Logger.h" // Needed for CLogger // Do_not_auto_remove
66 #include "MagnetURI.h" // Needed for CMagnetURI
67 #include "OtherFunctions.h"
68 #include "PartFile.h" // Needed for CPartFile
69 #include "Preferences.h" // Needed for CPreferences
70 #include "SearchList.h" // Needed for CSearchList
71 #include "Server.h" // Needed for GetListName
72 #include "ServerList.h" // Needed for CServerList
73 #include "ServerConnect.h" // Needed for CServerConnect
74 #include "ServerUDPSocket.h" // Needed for CServerUDPSocket
75 #include "Statistics.h" // Needed for CStatistics
76 #include "TerminationProcessAmuleweb.h" // Needed for CTerminationProcessAmuleweb
77 #include "ThreadTasks.h"
78 #include "updownclient.h" // Needed for CUpDownClient
79 #include "UploadQueue.h" // Needed for CUploadQueue
80 #include "UploadBandwidthThrottler.h"
81 #include "UserEvents.h"
82 #include "ScopedPtr.h"
84 #ifdef ENABLE_UPNP
85 #include "UPnPBase.h" // Needed for UPnP
86 #endif
88 #ifdef __WXMAC__
89 #include <wx/sysopt.h> // Do_not_auto_remove
90 #endif
92 #ifndef AMULE_DAEMON
93 #ifdef __WXMAC__
94 #include <CoreFoundation/CFBundle.h> // Do_not_auto_remove
95 #if wxCHECK_VERSION(2, 9, 0)
96 #include <wx/osx/core/cfstring.h> // Do_not_auto_remove
97 #else
98 #include <wx/mac/corefoundation/cfstring.h> // Do_not_auto_remove
99 #endif
100 #endif
101 #include <wx/msgdlg.h>
103 #include "amuleDlg.h"
104 #endif
107 #ifdef HAVE_SYS_RESOURCE_H
108 #include <sys/resource.h>
109 #endif
111 #ifdef HAVE_SYS_STATVFS_H
112 #include <sys/statvfs.h> // Do_not_auto_remove
113 #endif
116 #ifdef __GLIBC__
117 # define RLIMIT_RESOURCE __rlimit_resource
118 #else
119 # define RLIMIT_RESOURCE int
120 #endif
122 #ifdef AMULE_DAEMON
123 CamuleDaemonApp *theApp;
124 #else
125 CamuleGuiApp *theApp;
126 #endif
128 static void UnlimitResource(RLIMIT_RESOURCE resType)
130 #if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
131 struct rlimit rl;
132 getrlimit(resType, &rl);
133 rl.rlim_cur = rl.rlim_max;
134 setrlimit(resType, &rl);
135 #endif
139 static void SetResourceLimits()
141 #ifdef HAVE_SYS_RESOURCE_H
142 UnlimitResource(RLIMIT_DATA);
143 #ifndef __UCLIBC__
144 UnlimitResource(RLIMIT_FSIZE);
145 #endif
146 UnlimitResource(RLIMIT_NOFILE);
147 #ifdef RLIMIT_RSS
148 UnlimitResource(RLIMIT_RSS);
149 #endif
150 #endif
153 // We store the received signal in order to avoid race-conditions
154 // in the signal handler.
155 bool g_shutdownSignal = false;
157 void OnShutdownSignal( int /* sig */ )
159 signal(SIGINT, SIG_DFL);
160 signal(SIGTERM, SIG_DFL);
162 g_shutdownSignal = true;
164 #ifdef AMULE_DAEMON
165 theApp->ExitMainLoop();
166 #endif
170 CamuleApp::CamuleApp()
172 // Madcat - Initialize timer as the VERY FIRST thing to avoid any issues later.
173 // Kry - I love to init the vars on init, even before timer.
174 StartTickTimer();
176 // Initialization
177 m_app_state = APP_STATE_STARTING;
179 theApp = &wxGetApp();
181 clientlist = NULL;
182 searchlist = NULL;
183 knownfiles = NULL;
184 canceledfiles = NULL;
185 serverlist = NULL;
186 serverconnect = NULL;
187 sharedfiles = NULL;
188 listensocket = NULL;
189 clientudp = NULL;
190 clientcredits = NULL;
191 friendlist = NULL;
192 downloadqueue = NULL;
193 uploadqueue = NULL;
194 ipfilter = NULL;
195 ECServerHandler = NULL;
196 glob_prefs = NULL;
197 m_statistics = NULL;
198 uploadBandwidthThrottler = NULL;
199 #ifdef ENABLE_UPNP
200 m_upnp = NULL;
201 m_upnpMappings.resize(4);
202 #endif
203 core_timer = NULL;
205 m_localip = 0;
206 m_dwPublicIP = 0;
207 webserver_pid = 0;
209 enable_daemon_fork = false;
211 strFullMuleVersion = NULL;
212 strOSDescription = NULL;
214 // Apprently needed for *BSD
215 SetResourceLimits();
217 #ifdef _MSC_VER
218 _CrtSetDbgFlag(0); // Disable useless memleak debugging
219 #endif
222 CamuleApp::~CamuleApp()
224 // Closing the log-file as the very last thing, since
225 // wxWidgets log-events are saved in it as well.
226 theLogger.CloseLogfile();
228 if (strFullMuleVersion) {
229 free(strFullMuleVersion);
231 if (strOSDescription) {
232 free(strOSDescription);
236 int CamuleApp::OnExit()
238 if (m_app_state!=APP_STATE_STARTING) {
239 AddLogLineMS(false, _("Now, exiting main app..."));
242 // From wxWidgets docs, wxConfigBase:
243 // ...
244 // Note that you must delete this object (usually in wxApp::OnExit)
245 // in order to avoid memory leaks, wxWidgets won't do it automatically.
247 // As it happens, you may even further simplify the procedure described
248 // above: you may forget about calling Set(). When Get() is called and
249 // there is no current object, it will create one using Create() function.
250 // To disable this behaviour DontCreateOnDemand() is provided.
251 delete wxConfigBase::Set((wxConfigBase *)NULL);
253 // Save credits
254 clientcredits->SaveList();
256 // Kill amuleweb if running
257 if (webserver_pid) {
258 AddLogLineNS(CFormat(_("Terminating amuleweb instance with pid `%ld' ... ")) % webserver_pid);
259 wxKillError rc;
260 if (wxKill(webserver_pid, wxSIGTERM, &rc) == -1) {
261 AddLogLineNS(CFormat(_("Killing amuleweb instance with pid `%ld' ... ")) % webserver_pid);
262 if (wxKill(webserver_pid, wxSIGKILL, &rc) == -1) {
263 AddLogLineNS(_("Failed"));
268 if (m_app_state!=APP_STATE_STARTING) {
269 AddLogLineMS(false, _("aMule OnExit: Terminating core."));
272 delete serverlist;
273 serverlist = NULL;
275 delete searchlist;
276 searchlist = NULL;
278 delete clientcredits;
279 clientcredits = NULL;
281 delete friendlist;
282 friendlist = NULL;
284 // Destroying CDownloadQueue calls destructor for CPartFile
285 // calling CSharedFileList::SafeAddKFile occasionally.
286 delete sharedfiles;
287 sharedfiles = NULL;
289 delete serverconnect;
290 serverconnect = NULL;
292 delete listensocket;
293 listensocket = NULL;
295 delete clientudp;
296 clientudp = NULL;
298 delete knownfiles;
299 knownfiles = NULL;
301 delete canceledfiles;
302 canceledfiles = NULL;
304 delete clientlist;
305 clientlist = NULL;
307 delete uploadqueue;
308 uploadqueue = NULL;
310 delete downloadqueue;
311 downloadqueue = NULL;
313 delete ipfilter;
314 ipfilter = NULL;
316 #ifdef ENABLE_UPNP
317 delete m_upnp;
318 m_upnp = NULL;
319 #endif
321 delete ECServerHandler;
322 ECServerHandler = NULL;
324 delete m_statistics;
325 m_statistics = NULL;
327 delete glob_prefs;
328 glob_prefs = NULL;
329 CPreferences::EraseItemList();
331 delete uploadBandwidthThrottler;
332 uploadBandwidthThrottler = NULL;
334 if (m_app_state!=APP_STATE_STARTING) {
335 AddLogLineNS(_("aMule shutdown completed."));
338 #if wxUSE_MEMORY_TRACING
339 AddLogLineNS(_("Memory debug results for aMule exit:"));
340 // Log mem debug mesages to wxLogStderr
341 wxLog* oldLog = wxLog::SetActiveTarget(new wxLogStderr);
342 //AddLogLineNS(wxT("**************Classes**************");
343 //wxDebugContext::PrintClasses();
344 //AddLogLineNS(wxT("***************Dump***************");
345 //wxDebugContext::Dump();
346 AddLogLineNS(wxT("***************Stats**************"));
347 wxDebugContext::PrintStatistics(true);
349 // Set back to wxLogGui
350 delete wxLog::SetActiveTarget(oldLog);
351 #endif
353 StopTickTimer();
355 // Return 0 for succesful program termination
356 return AMULE_APP_BASE::OnExit();
360 int CamuleApp::InitGui(bool, wxString &)
362 return 0;
367 // Application initialization
369 bool CamuleApp::OnInit()
371 #if wxUSE_MEMORY_TRACING
372 // any text before call of Localize_mule needs not to be translated.
373 AddLogLineMS(false, wxT("Checkpoint set on app init for memory debug"));
374 wxDebugContext::SetCheckpoint();
375 #endif
377 // Forward wxLog events to CLogger
378 wxLog::SetActiveTarget(new CLoggerTarget);
380 m_localip = StringHosttoUint32(::wxGetFullHostName());
382 #ifndef __WXMSW__
383 // get rid of sigpipe
384 signal(SIGPIPE, SIG_IGN);
385 #else
386 // Handle CTRL-Break
387 signal(SIGBREAK, OnShutdownSignal);
388 #endif
389 // Handle sigint and sigterm
390 signal(SIGINT, OnShutdownSignal);
391 signal(SIGTERM, OnShutdownSignal);
393 #ifdef __WXMAC__
394 // For listctrl's to behave on Mac
395 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
396 #endif
398 // Handle uncaught exceptions
399 InstallMuleExceptionHandler();
401 if (!InitCommon(AMULE_APP_BASE::argc, AMULE_APP_BASE::argv)) {
402 return false;
405 glob_prefs = new CPreferences();
407 CPath outDir;
408 if (CheckMuleDirectory(wxT("temp"), thePrefs::GetTempDir(), ConfigDir + wxT("Temp"), outDir)) {
409 thePrefs::SetTempDir(outDir);
410 } else {
411 return false;
414 if (CheckMuleDirectory(wxT("incoming"), thePrefs::GetIncomingDir(), ConfigDir + wxT("Incoming"), outDir)) {
415 thePrefs::SetIncomingDir(outDir);
416 } else {
417 return false;
420 // Some sanity check
421 if (!thePrefs::UseTrayIcon()) {
422 thePrefs::SetMinToTray(false);
425 // Build the filenames for the two OS files
426 SetOSFiles(thePrefs::GetOSDir().GetRaw());
428 #ifdef ENABLE_NLS
429 // Load localization settings
430 Localize_mule();
431 #endif
433 // Configure EC for amuled when invoked with ec-config
434 if (ec_config) {
435 AddLogLineMS(false, _("\nEC configuration"));
436 thePrefs::SetECPass(GetPassword());
437 thePrefs::EnableExternalConnections(true);
438 AddLogLineMS(false, _("Password set and external connections enabled."));
441 #ifndef __WXMSW__
442 if (getuid() == 0) {
443 wxString msg =
444 wxT("Warning! You are running aMule as root.\n")
445 wxT("Doing so is not recommended for security reasons,\n")
446 wxT("and you are advised to run aMule as an normal\n")
447 wxT("user instead.");
449 ShowAlert(msg, _("WARNING"), wxCENTRE | wxOK | wxICON_ERROR);
451 fprintf(stderr, "\n--------------------------------------------------\n");
452 fprintf(stderr, "%s", (const char*)unicode2UTF8(msg));
453 fprintf(stderr, "\n--------------------------------------------------\n\n");
455 #endif
457 // Display notification on new version or first run
458 wxTextFile vfile( ConfigDir + wxT("lastversion") );
459 wxString newMule(wxT( VERSION ));
461 if ( !wxFileExists( vfile.GetName() ) ) {
462 vfile.Create();
465 if ( vfile.Open() ) {
466 // Check if this version has been run before
467 bool found = false;
468 for ( size_t i = 0; i < vfile.GetLineCount(); i++ ) {
469 // Check if this version has been run before
470 if ( vfile.GetLine(i) == newMule ) {
471 found = true;
472 break;
476 // We havent run this version before?
477 if ( !found ) {
478 // Insert new at top to provide faster searches
479 vfile.InsertLine( newMule, 0 );
481 Trigger_New_version( newMule );
484 // Keep at most 10 entires
485 while ( vfile.GetLineCount() > 10 )
486 vfile.RemoveLine( vfile.GetLineCount() - 1 );
488 vfile.Write();
489 vfile.Close();
492 // Check if we have the old style locale config
493 wxString langId = thePrefs::GetLanguageID();
494 if (!langId.IsEmpty() && (langId.GetChar(0) >= '0' && langId.GetChar(0) <= '9')) {
495 wxString info(_("Your locale has been changed to System Default due to a configuration change. Sorry."));
496 thePrefs::SetLanguageID(wxLang2Str(wxLANGUAGE_DEFAULT));
497 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
500 m_statistics = new CStatistics();
502 clientlist = new CClientList();
503 friendlist = new CFriendList();
504 searchlist = new CSearchList();
505 knownfiles = new CKnownFileList();
506 canceledfiles = new CCanceledFileList;
507 serverlist = new CServerList();
509 sharedfiles = new CSharedFileList(knownfiles);
510 clientcredits = new CClientCreditsList();
512 // bugfix - do this before creating the uploadqueue
513 downloadqueue = new CDownloadQueue();
514 uploadqueue = new CUploadQueue();
515 ipfilter = new CIPFilter();
517 // Creates all needed listening sockets
518 wxString msg;
519 if (!ReinitializeNetwork(&msg)) {
520 AddLogLineMS(false, wxT("\n"));
521 AddLogLineMS(false, msg);
524 // Test if there's any new version
525 if (thePrefs::GetCheckNewVersion()) {
526 // We use the thread base because I don't want a dialog to pop up.
527 CHTTPDownloadThread* version_check =
528 new CHTTPDownloadThread(wxT("http://amule.sourceforge.net/lastversion"),
529 theApp->ConfigDir + wxT("last_version_check"), theApp->ConfigDir + wxT("last_version"), HTTP_VersionCheck, false);
530 version_check->Create();
531 version_check->Run();
534 // Create main dialog, or fork to background (daemon).
535 InitGui(m_geometryEnabled, m_geometryString);
537 #ifdef AMULE_DAEMON
538 // Need to refresh wxSingleInstanceChecker after the daemon fork() !
539 if (enable_daemon_fork) {
540 RefreshSingleInstanceChecker();
541 // No need to check IsAnotherRunning() - we've done it before.
543 #endif
545 // Has to be created after the call to InitGui, as fork
546 // (when using posix threads) only replicates the mainthread,
547 // and the UBT constructor creates a thread.
548 uploadBandwidthThrottler = new UploadBandwidthThrottler();
550 // Start performing background tasks
551 // This will start loading the IP filter. It will start right away.
552 // Log is confusing, because log entries from background will only be printed
553 // once foreground becomes idle, and that will only be after loading
554 // of the partfiles has finished.
555 CThreadScheduler::Start();
557 // These must be initialized after the gui is loaded.
558 if (thePrefs::GetNetworkED2K()) {
559 serverlist->Init();
561 downloadqueue->LoadMetFiles(thePrefs::GetTempDir());
562 sharedfiles->Reload();
564 // Ensure that the up/down ratio is used
565 CPreferences::CheckUlDlRatio();
567 // The user can start pressing buttons like mad if he feels like it.
568 m_app_state = APP_STATE_RUNNING;
570 if (!serverlist->GetServerCount() && thePrefs::GetNetworkED2K()) {
571 // There are no servers and ED2K active -> ask for download.
572 // As we cannot ask in amuled, we just update there
573 // Kry TODO: Store server.met URL on preferences and use it here and in GUI.
574 #ifndef AMULE_DAEMON
575 if (wxYES == wxMessageBox(
576 wxString(
577 _("You don't have any server in the server list.\nDo you want aMule to download a new list now?")),
578 wxString(_("Server list download")),
579 wxYES_NO,
580 static_cast<wxWindow*>(theApp->amuledlg)))
581 #endif
583 // workaround amuled crash
584 #ifndef AMULE_DAEMON
585 serverlist->UpdateServerMetFromURL(
586 wxT("http://gruk.org/server.met.gz"));
587 #endif
592 // Autoconnect if that option is enabled
593 if (thePrefs::DoAutoConnect()) {
594 // IP filter is still loading and will be finished on event.
595 // Tell it to autoconnect.
596 if (thePrefs::GetNetworkED2K()) {
597 ipfilter->ConnectToAnyServerWhenReady();
599 if (thePrefs::GetNetworkKademlia()) {
600 ipfilter->StartKADWhenReady();
604 // Enable GeoIP
605 #ifdef ENABLE_IP2COUNTRY
606 theApp->amuledlg->EnableIP2Country();
607 #endif
609 // Run webserver?
610 if (thePrefs::GetWSIsEnabled()) {
611 wxString aMuleConfigFile = ConfigDir + m_configFile;
612 wxString amulewebPath = thePrefs::GetWSPath();
614 #if defined(__WXMAC__) && !defined(AMULE_DAEMON)
615 // For the Mac GUI application, look for amuleweb in the bundle
616 CFURLRef amulewebUrl = CFBundleCopyAuxiliaryExecutableURL(
617 CFBundleGetMainBundle(), CFSTR("amuleweb"));
619 if (amulewebUrl) {
620 CFURLRef absoluteUrl = CFURLCopyAbsoluteURL(amulewebUrl);
621 CFRelease(amulewebUrl);
623 if (absoluteUrl) {
624 CFStringRef amulewebCfstr = CFURLCopyFileSystemPath(absoluteUrl, kCFURLPOSIXPathStyle);
625 CFRelease(absoluteUrl);
626 #if wxCHECK_VERSION(2, 9, 0)
627 amulewebPath = wxCFStringRef(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
628 #else
629 amulewebPath = wxMacCFStringHolder(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
630 #endif
633 #endif
635 #ifdef __WXMSW__
636 # define QUOTE wxT("\"")
637 #else
638 # define QUOTE wxT("\'")
639 #endif
641 wxString cmd =
642 QUOTE +
643 amulewebPath +
644 QUOTE wxT(" ") QUOTE wxT("--amule-config-file=") +
645 aMuleConfigFile +
646 QUOTE;
647 CTerminationProcessAmuleweb *p = new CTerminationProcessAmuleweb(cmd, &webserver_pid);
648 webserver_pid = wxExecute(cmd, wxEXEC_ASYNC, p);
649 bool webserver_ok = webserver_pid > 0;
650 if (webserver_ok) {
651 AddLogLineM(true, CFormat(_("web server running on pid %d")) % webserver_pid);
652 } else {
653 delete p;
654 ShowAlert(_(
655 "You requested to run web server on startup, but the amuleweb binary cannot be run. Please install the package containing aMule web server, or compile aMule using --enable-webserver and run make install"),
656 _("ERROR"), wxOK | wxICON_ERROR);
660 return true;
663 bool CamuleApp::ReinitializeNetwork(wxString* msg)
665 bool ok = true;
666 static bool firstTime = true;
668 if (!firstTime) {
669 // TODO: Destroy previously created sockets
671 firstTime = false;
673 // Some sanity checks first
674 if (thePrefs::ECPort() == thePrefs::GetPort()) {
675 // Select a random usable port in the range 1025 ... 2^16 - 1
676 uint16 port = thePrefs::ECPort();
677 while ( port < 1024 || port == thePrefs::GetPort() ) {
678 port = (uint16)rand();
680 thePrefs::SetECPort( port );
682 wxString err =
683 wxT("Network configuration failed! You cannot use the same port\n")
684 wxT("for the main TCP port and the External Connections port.\n")
685 wxT("The EC port has been changed to avoid conflict, see the\n")
686 wxT("preferences for the new value.\n");
687 *msg << err;
689 AddLogLineM( false, wxEmptyString );
690 AddLogLineM( true, err );
691 AddLogLineM( false, wxEmptyString );
693 ok = false;
696 if (thePrefs::GetUDPPort() == thePrefs::GetPort() + 3) {
697 // Select a random usable value in the range 1025 ... 2^16 - 1
698 uint16 port = thePrefs::GetUDPPort();
699 while ( port < 1024 || port == thePrefs::GetPort() + 3 ) {
700 port = (uint16)rand();
702 thePrefs::SetUDPPort( port );
704 wxString err =
705 wxT("Network configuration failed! You set your UDP port to\n")
706 wxT("the value of the main TCP port plus 3.\n")
707 wxT("This port has been reserved for the Server-UDP port. The\n")
708 wxT("port value has been changed to avoid conflict, see the\n")
709 wxT("preferences for the new value\n");
710 *msg << err;
712 AddLogLineM( false, wxEmptyString );
713 AddLogLineM( true, err );
714 AddLogLineM( false, wxEmptyString );
716 ok = false;
719 // Create the address where we are going to listen
720 // TODO: read this from configuration file
721 amuleIPV4Address myaddr[4];
723 // Create the External Connections Socket.
724 // Default is 4712.
725 // Get ready to handle connections from apps like amulecmd
726 if (thePrefs::GetECAddress().IsEmpty() || !myaddr[0].Hostname(thePrefs::GetECAddress())) {
727 myaddr[0].AnyAddress();
729 myaddr[0].Service(thePrefs::ECPort());
730 ECServerHandler = new ExternalConn(myaddr[0], msg);
732 // Create the UDP socket TCP+3.
733 // Used for source asking on servers.
734 if (thePrefs::GetAddress().IsEmpty()) {
735 myaddr[1].AnyAddress();
736 } else if (!myaddr[1].Hostname(thePrefs::GetAddress())) {
737 myaddr[1].AnyAddress();
738 AddLogLineM(true, CFormat(_("Could not bind ports to the specified address: %s"))
739 % thePrefs::GetAddress());
742 wxString ip = myaddr[1].IPAddress();
743 myaddr[1].Service(thePrefs::GetPort()+3);
744 serverconnect = new CServerConnect(serverlist, myaddr[1]);
745 *msg << CFormat( wxT("*** Server UDP socket (TCP+3) at %s:%u\n") )
746 % ip % ((unsigned int)thePrefs::GetPort() + 3u);
748 // Create the ListenSocket (aMule TCP socket).
749 // Used for Client Port / Connections from other clients,
750 // Client to Client Source Exchange.
751 // Default is 4662.
752 myaddr[2] = myaddr[1];
753 myaddr[2].Service(thePrefs::GetPort());
754 listensocket = new CListenSocket(myaddr[2]);
755 *msg << CFormat( wxT("*** TCP socket (TCP) listening on %s:%u\n") )
756 % ip % (unsigned int)(thePrefs::GetPort());
757 // This command just sets a flag to control maximum number of connections.
758 // Notify(true) has already been called to the ListenSocket, so events may
759 // be already comming in.
760 if (listensocket->Ok()) {
761 listensocket->StartListening();
762 } else {
763 // If we wern't able to start listening, we need to warn the user
764 wxString err;
765 err = CFormat(_("Port %u is not available. You will be LOWID\n")) %
766 (unsigned int)(thePrefs::GetPort());
767 *msg << err;
768 AddLogLineM(true, err);
769 err.Clear();
770 err = CFormat(
771 _("Port %u is not available!\n\nThis means that you will be LOWID.\n\nCheck your network to make sure the port is open for output and input.")) %
772 (unsigned int)(thePrefs::GetPort());
773 ShowAlert(err, _("ERROR"), wxOK | wxICON_ERROR);
776 // Create the UDP socket.
777 // Used for extended eMule protocol, Queue Rating, File Reask Ping.
778 // Also used for Kademlia.
779 // Default is port 4672.
780 myaddr[3] = myaddr[1];
781 myaddr[3].Service(thePrefs::GetUDPPort());
782 clientudp = new CClientUDPSocket(myaddr[3], thePrefs::GetProxyData());
783 if (!thePrefs::IsUDPDisabled()) {
784 *msg << CFormat( wxT("*** Client UDP socket (extended eMule) at %s:%u") )
785 % ip % thePrefs::GetUDPPort();
786 } else {
787 *msg << wxT("*** Client UDP socket (extended eMule) disabled on preferences");
790 #ifdef ENABLE_UPNP
791 if (thePrefs::GetUPnPEnabled()) {
792 try {
793 m_upnpMappings[0] = CUPnPPortMapping(
794 myaddr[0].Service(),
795 "TCP",
796 thePrefs::GetUPnPECEnabled(),
797 "aMule TCP External Connections Socket");
798 m_upnpMappings[1] = CUPnPPortMapping(
799 myaddr[1].Service(),
800 "UDP",
801 thePrefs::GetUPnPEnabled(),
802 "aMule UDP socket (TCP+3)");
803 m_upnpMappings[2] = CUPnPPortMapping(
804 myaddr[2].Service(),
805 "TCP",
806 thePrefs::GetUPnPEnabled(),
807 "aMule TCP Listen Socket");
808 m_upnpMappings[3] = CUPnPPortMapping(
809 myaddr[3].Service(),
810 "UDP",
811 thePrefs::GetUPnPEnabled(),
812 "aMule UDP Extended eMule Socket");
813 m_upnp = new CUPnPControlPoint(thePrefs::GetUPnPTCPPort());
814 m_upnp->AddPortMappings(m_upnpMappings);
815 } catch(CUPnPException &e) {
816 wxString error_msg;
817 error_msg << e.what();
818 AddLogLineM(true, error_msg);
819 fprintf(stderr, "%s\n", (const char *)unicode2char(error_msg));
822 #endif
824 return ok;
827 /* Original implementation by Bouc7 of the eMule Project.
828 aMule Signature idea was designed by BigBob and implemented
829 by Un-Thesis, with design inputs and suggestions from bothie.
831 void CamuleApp::OnlineSig(bool zero /* reset stats (used on shutdown) */)
833 // Do not do anything if online signature is disabled in Preferences
834 if (!thePrefs::IsOnlineSignatureEnabled() || m_emulesig_path.IsEmpty()) {
835 // We do not need to check m_amulesig_path because if m_emulesig_path is empty,
836 // that means m_amulesig_path is empty too.
837 return;
840 // Remove old signature files
841 if ( wxFileExists( m_emulesig_path ) ) { wxRemoveFile( m_emulesig_path ); }
842 if ( wxFileExists( m_amulesig_path ) ) { wxRemoveFile( m_amulesig_path ); }
845 wxTextFile amulesig_out;
846 wxTextFile emulesig_out;
848 // Open both files if needed
849 if ( !emulesig_out.Create( m_emulesig_path) ) {
850 AddLogLineM(true, _("Failed to create OnlineSig File"));
851 // Will never try again.
852 m_amulesig_path.Clear();
853 m_emulesig_path.Clear();
854 return;
857 if ( !amulesig_out.Create(m_amulesig_path) ) {
858 AddLogLineM(true, _("Failed to create aMule OnlineSig File"));
859 // Will never try again.
860 m_amulesig_path.Clear();
861 m_emulesig_path.Clear();
862 return;
865 wxString emulesig_string;
866 wxString temp;
868 if (zero) {
869 emulesig_string = wxT("0\xA0.0|0.0|0");
870 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0\n0\n0.0\n0.0\n0\n0"));
871 } else {
872 if (IsConnectedED2K()) {
874 temp = wxString::Format(wxT("%d"),serverconnect->GetCurrentServer()->GetPort());
876 // We are online
877 emulesig_string =
878 // Connected
879 wxT("1|")
880 //Server name
881 + serverconnect->GetCurrentServer()->GetListName()
882 + wxT("|")
883 // IP and port of the server
884 + serverconnect->GetCurrentServer()->GetFullIP()
885 + wxT("|")
886 + temp;
889 // Now for amule sig
891 // Connected. State 1, full info
892 amulesig_out.AddLine(wxT("1"));
893 // Server Name
894 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetListName());
895 // Server IP
896 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetFullIP());
897 // Server Port
898 amulesig_out.AddLine(temp);
900 if (serverconnect->IsLowID()) {
901 amulesig_out.AddLine(wxT("L"));
902 } else {
903 amulesig_out.AddLine(wxT("H"));
906 } else if (serverconnect->IsConnecting()) {
907 emulesig_string = wxT("0");
909 // Connecting. State 2, No info.
910 amulesig_out.AddLine(wxT("2\n0\n0\n0\n0"));
911 } else {
912 // Not connected to a server
913 emulesig_string = wxT("0");
915 // Not connected, state 0, no info
916 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0"));
918 if (IsConnectedKad()) {
919 if(Kademlia::CKademlia::IsFirewalled()) {
920 // Connected. Firewalled. State 1.
921 amulesig_out.AddLine(wxT("1"));
922 } else {
923 // Connected. State 2.
924 amulesig_out.AddLine(wxT("2"));
926 } else {
927 // Not connected.State 0.
928 amulesig_out.AddLine(wxT("0"));
930 emulesig_string += wxT("\xA");
932 // Datarate for downloads
933 temp = wxString::Format(wxT("%.1f"), theStats::GetDownloadRate() / 1024.0);
935 emulesig_string += temp + wxT("|");
936 amulesig_out.AddLine(temp);
938 // Datarate for uploads
939 temp = wxString::Format(wxT("%.1f"), theStats::GetUploadRate() / 1024.0);
941 emulesig_string += temp + wxT("|");
942 amulesig_out.AddLine(temp);
944 // Number of users waiting for upload
945 temp = wxString::Format(wxT("%d"), theStats::GetWaitingUserCount());
947 emulesig_string += temp;
948 amulesig_out.AddLine(temp);
950 // Number of shared files (not on eMule)
951 amulesig_out.AddLine(wxString::Format(wxT("%d"), theStats::GetSharedFileCount()));
954 // eMule signature finished here. Write the line to the wxTextFile.
955 emulesig_out.AddLine(emulesig_string);
957 // Now for aMule signature extras
959 // Nick on the network
960 amulesig_out.AddLine(thePrefs::GetUserNick());
962 // Total received in bytes
963 amulesig_out.AddLine( CFormat( wxT("%llu") ) % (theStats::GetSessionReceivedBytes() + thePrefs::GetTotalDownloaded()) );
965 // Total sent in bytes
966 amulesig_out.AddLine( CFormat( wxT("%llu") ) % (theStats::GetSessionSentBytes() + thePrefs::GetTotalUploaded()) );
968 // amule version
969 #ifdef SVNDATE
970 amulesig_out.AddLine(wxT(VERSION) wxT(" ") wxT(SVNDATE));
971 #else
972 amulesig_out.AddLine(wxT(VERSION));
973 #endif
975 if (zero) {
976 amulesig_out.AddLine(wxT("0"));
977 amulesig_out.AddLine(wxT("0"));
978 amulesig_out.AddLine(wxT("0"));
979 } else {
980 // Total received bytes in session
981 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
982 theStats::GetSessionReceivedBytes() );
984 // Total sent bytes in session
985 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
986 theStats::GetSessionSentBytes() );
988 // Uptime
989 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetUptimeSeconds());
992 // Flush the files
993 emulesig_out.Write();
994 amulesig_out.Write();
995 } //End Added By Bouc7
998 // Gracefully handle fatal exceptions and print backtrace if possible
999 void CamuleApp::OnFatalException()
1001 /* Print the backtrace */
1002 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
1003 fprintf(stderr, "A fatal error has occurred and aMule has crashed.\n");
1004 fprintf(stderr, "Please assist us in fixing this problem by posting the backtrace below in our\n");
1005 fprintf(stderr, "'aMule Crashes' forum and include as much information as possible regarding the\n");
1006 fprintf(stderr, "circumstances of this crash. The forum is located here:\n");
1007 fprintf(stderr, " http://forum.amule.org/index.php?board=67.0\n");
1008 fprintf(stderr, "If possible, please try to generate a real backtrace of this crash:\n");
1009 fprintf(stderr, " http://wiki.amule.org/index.php/Backtraces\n\n");
1010 fprintf(stderr, "----------------------------=| BACKTRACE FOLLOWS: |=----------------------------\n");
1011 fprintf(stderr, "Current version is: %s\n", strFullMuleVersion);
1012 fprintf(stderr, "Running on: %s\n\n", strOSDescription);
1014 print_backtrace(1); // 1 == skip this function.
1016 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
1020 // Sets the localization of aMule
1021 void CamuleApp::Localize_mule()
1023 InitCustomLanguages();
1024 InitLocale(m_locale, StrLang2wx(thePrefs::GetLanguageID()));
1025 if (!m_locale.IsOk()) {
1026 AddLogLineM(false,_("The selected locale seems not to be installed on your box. (Note: I'll try to set it anyway)"));
1031 // Displays information related to important changes in aMule.
1032 // Is called when the user runs a new version of aMule
1033 void CamuleApp::Trigger_New_version(wxString new_version)
1035 wxString info = wxT(" --- ") + CFormat(_("This is the first time you run aMule %s")) % new_version + wxT(" ---\n\n");
1036 if (new_version == wxT("SVN")) {
1037 info += _("This version is a testing version, updated daily, and\n");
1038 info += _("we give no warranty it won't break anything, burn your house,\n");
1039 info += _("or kill your dog. But it *should* be safe to use anyway.\n");
1042 // General info
1043 info += wxT("\n");
1044 info += _("More information, support and new releases can found at our homepage,\n");
1045 info += _("at www.aMule.org, or in our IRC channel #aMule at irc.freenode.net.\n");
1046 info += wxT("\n");
1047 info += _("Feel free to report any bugs to http://forum.amule.org");
1049 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
1053 void CamuleApp::SetOSFiles(const wxString new_path)
1055 if ( thePrefs::IsOnlineSignatureEnabled() ) {
1056 if ( ::wxDirExists(new_path) ) {
1057 m_emulesig_path = JoinPaths(new_path, wxT("onlinesig.dat"));
1058 m_amulesig_path = JoinPaths(new_path, wxT("amulesig.dat"));
1059 } else {
1060 ShowAlert(_("The folder for Online Signature files you specified is INVALID!\n OnlineSignature will be DISABLED until you fix it on preferences."), _("ERROR"), wxOK | wxICON_ERROR);
1061 m_emulesig_path.Clear();
1062 m_amulesig_path.Clear();
1064 } else {
1065 m_emulesig_path.Clear();
1066 m_amulesig_path.Clear();
1071 #ifdef __WXDEBUG__
1072 #ifndef wxUSE_STACKWALKER
1073 #define wxUSE_STACKWALKER 0
1074 #endif
1075 void CamuleApp::OnAssertFailure(const wxChar* file, int line,
1076 const wxChar* func, const wxChar* cond, const wxChar* msg)
1078 if (!wxUSE_STACKWALKER || !wxThread::IsMain() || !IsRunning()) {
1079 wxString errmsg = CFormat( wxT("%s:%s:%d: Assertion '%s' failed. %s") )
1080 % file % func % line % cond % ( msg ? msg : wxT("") );
1082 fprintf(stderr, "Assertion failed: %s\n", (const char*)unicode2char(errmsg));
1084 // Skip the function-calls directly related to the assert call.
1085 fprintf(stderr, "\nBacktrace follows:\n");
1086 print_backtrace(3);
1087 fprintf(stderr, "\n");
1090 if (wxThread::IsMain() && IsRunning()) {
1091 AMULE_APP_BASE::OnAssertFailure(file, line, func, cond, msg);
1092 } else {
1093 // Abort, allows gdb to catch the assertion
1094 raise( SIGABRT );
1097 #endif
1100 void CamuleApp::OnUDPDnsDone(CMuleInternalEvent& evt)
1102 CServerUDPSocket* socket =(CServerUDPSocket*)evt.GetClientData();
1103 socket->OnHostnameResolved(evt.GetExtraLong());
1107 void CamuleApp::OnSourceDnsDone(CMuleInternalEvent& evt)
1109 downloadqueue->OnHostnameResolved(evt.GetExtraLong());
1113 void CamuleApp::OnServerDnsDone(CMuleInternalEvent& evt)
1115 AddLogLineMS(false, _("Server hostname notified"));
1116 serverconnect->OnServerHostnameResolved(evt.GetClientData(), evt.GetExtraLong());
1120 void CamuleApp::OnTCPTimer(CTimerEvent& WXUNUSED(evt))
1122 if(!IsRunning()) {
1123 return;
1125 serverconnect->StopConnectionTry();
1126 if (IsConnectedED2K() ) {
1127 return;
1129 serverconnect->ConnectToAnyServer();
1133 void CamuleApp::OnCoreTimer(CTimerEvent& WXUNUSED(evt))
1135 // Former TimerProc section
1136 static uint64 msPrev1, msPrev5, msPrevSave, msPrevHist, msPrevOS, msPrevKnownMet;
1137 uint64 msCur = theStats::GetUptimeMillis();
1138 TheTime = msCur / 1000;
1140 if (!IsRunning()) {
1141 return;
1144 #ifndef AMULE_DAEMON
1145 // Check if we should terminate the app
1146 if ( g_shutdownSignal ) {
1147 wxWindow* top = GetTopWindow();
1149 if ( top ) {
1150 top->Close(true);
1151 } else {
1152 // No top-window, have to force termination.
1153 wxExit();
1156 #endif
1158 // There is a theoretical chance that the core time function can recurse:
1159 // if an event function gets blocked on a mutex (communicating with the
1160 // UploadBandwidthThrottler) wx spawns a new event loop and processes more events.
1161 // If CPU load gets high a new core timer event could be generated before the last
1162 // one was finished and so recursion could occur, which would be bad.
1163 // Detect this and do an early return then.
1164 static bool recurse = false;
1165 if (recurse) {
1166 return;
1168 recurse = true;
1170 uploadqueue->Process();
1171 downloadqueue->Process();
1172 //theApp->clientcredits->Process();
1173 theStats::CalculateRates();
1175 if (msCur-msPrevHist > 1000) {
1176 // unlike the other loop counters in this function this one will sometimes
1177 // produce two calls in quick succession (if there was a gap of more than one
1178 // second between calls to TimerProc) - this is intentional! This way the
1179 // history list keeps an average of one node per second and gets thinned out
1180 // correctly as time progresses.
1181 msPrevHist += 1000;
1183 m_statistics->RecordHistory();
1188 if (msCur-msPrev1 > 1000) { // approximately every second
1189 msPrev1 = msCur;
1190 clientcredits->Process();
1191 clientlist->Process();
1193 // Publish files to server if needed.
1194 sharedfiles->Process();
1196 if( Kademlia::CKademlia::IsRunning() ) {
1197 Kademlia::CKademlia::Process();
1198 if(Kademlia::CKademlia::GetPrefs()->HasLostConnection()) {
1199 StopKad();
1200 clientudp->Close();
1201 clientudp->Open();
1202 if (thePrefs::Reconnect()) {
1203 StartKad();
1208 if( serverconnect->IsConnecting() && !serverconnect->IsSingleConnect() ) {
1209 serverconnect->TryAnotherConnectionrequest();
1211 if (serverconnect->IsConnecting()) {
1212 serverconnect->CheckForTimeout();
1214 listensocket->UpdateConnectionsStatus();
1219 if (msCur-msPrev5 > 5000) { // every 5 seconds
1220 msPrev5 = msCur;
1221 listensocket->Process();
1224 if (msCur-msPrevSave >= 60000) {
1225 msPrevSave = msCur;
1226 wxString buffer;
1228 // Save total upload/download to preferences
1229 wxConfigBase* cfg = wxConfigBase::Get();
1230 buffer = CFormat(wxT("%llu")) % (theStats::GetSessionReceivedBytes() + thePrefs::GetTotalDownloaded());
1231 cfg->Write(wxT("/Statistics/TotalDownloadedBytes"), buffer);
1233 buffer = CFormat(wxT("%llu")) % (theStats::GetSessionSentBytes() + thePrefs::GetTotalUploaded());
1234 cfg->Write(wxT("/Statistics/TotalUploadedBytes"), buffer);
1236 // Write changes to file
1237 cfg->Flush();
1241 // Special
1242 if (msCur - msPrevOS >= thePrefs::GetOSUpdate() * 1000ull) {
1243 OnlineSig(); // Added By Bouc7
1244 msPrevOS = msCur;
1247 if (msCur - msPrevKnownMet >= 30*60*1000/*There must be a prefs option for this*/) {
1248 // Save Shared Files data
1249 knownfiles->Save();
1250 msPrevKnownMet = msCur;
1254 // Recomended by lugdunummaster himself - from emule 0.30c
1255 serverconnect->KeepConnectionAlive();
1257 // Disarm recursion protection
1258 recurse = false;
1262 void CamuleApp::OnFinishedHashing(CHashingEvent& evt)
1264 wxCHECK_RET(evt.GetResult(), wxT("No result of hashing"));
1266 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1267 CKnownFile* result = evt.GetResult();
1269 if (owner) {
1270 // Check if the partfile still exists, as it might have
1271 // been deleted in the mean time.
1272 if (downloadqueue->IsPartFile(owner)) {
1273 // This cast must not be done before the IsPartFile
1274 // call, as dynamic_cast will barf on dangling pointers.
1275 dynamic_cast<CPartFile*>(owner)->PartFileHashFinished(result);
1277 } else {
1278 static int filecount;
1279 static uint64 bytecount;
1281 if (knownfiles->SafeAddKFile(result)) {
1282 AddDebugLogLineM(false, logKnownFiles,
1283 CFormat(wxT("Safe adding file to sharedlist: %s")) % result->GetFileName());
1284 sharedfiles->SafeAddKFile(result);
1286 filecount++;
1287 bytecount += result->GetFileSize();
1288 // If we have added 30 files or files with a total size of ~300mb
1289 if ( ( filecount == 30 ) || ( bytecount >= 314572800 ) ) {
1290 AddDebugLogLineM(false, logKnownFiles, wxT("Failsafe for crash on file hashing creation"));
1291 if ( m_app_state != APP_STATE_SHUTTINGDOWN ) {
1292 knownfiles->Save();
1293 filecount = 0;
1294 bytecount = 0;
1297 } else {
1298 AddDebugLogLineM(false, logKnownFiles,
1299 CFormat(wxT("File not added to sharedlist: %s")) % result->GetFileName());
1300 delete result;
1306 void CamuleApp::OnFinishedAICHHashing(CHashingEvent& evt)
1308 wxCHECK_RET(evt.GetResult(), wxT("No result of AICH-hashing"));
1310 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1311 CScopedPtr<CKnownFile> result(evt.GetResult());
1313 // Check that the owner is still valid
1314 if (knownfiles->IsKnownFile(owner)) {
1315 if (result->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE) {
1316 CAICHHashSet* oldSet = owner->GetAICHHashset();
1317 CAICHHashSet* newSet = result->GetAICHHashset();
1319 owner->SetAICHHashset(newSet);
1320 newSet->SetOwner(owner);
1322 result->SetAICHHashset(oldSet);
1323 oldSet->SetOwner(result.get());
1329 void CamuleApp::OnFinishedCompletion(CCompletionEvent& evt)
1331 CPartFile* completed = const_cast<CPartFile*>(evt.GetOwner());
1332 wxCHECK_RET(completed, wxT("Completion event sent for unspecified file"));
1333 wxASSERT_MSG(downloadqueue->IsPartFile(completed), wxT("CCompletionEvent for unknown partfile."));
1335 completed->CompleteFileEnded(evt.ErrorOccured(), evt.GetFullPath());
1336 if (evt.ErrorOccured()) {
1337 CUserEvents::ProcessEvent(CUserEvents::ErrorOnCompletion, completed);
1340 // Check if we should execute an script/app/whatever.
1341 CUserEvents::ProcessEvent(CUserEvents::DownloadCompleted, completed);
1344 void CamuleApp::OnFinishedAllocation(CAllocFinishedEvent& evt)
1346 CPartFile *file = evt.GetFile();
1347 wxCHECK_RET(file, wxT("Allocation finished event sent for unspecified file"));
1348 wxASSERT_MSG(downloadqueue->IsPartFile(file), wxT("CAllocFinishedEvent for unknown partfile"));
1350 file->SetPartFileStatus(PS_EMPTY);
1352 if (evt.Succeeded()) {
1353 if (evt.IsPaused()) {
1354 file->StopFile();
1355 } else {
1356 file->ResumeFile();
1358 } else {
1359 AddLogLineM(false, CFormat(_("Disk space preallocation for file '%s' failed: %s")) % file->GetFileName() % wxString(UTF82unicode(std::strerror(evt.GetResult()))));
1360 file->StopFile();
1363 file->AllocationFinished();
1366 void CamuleApp::OnNotifyEvent(CMuleGUIEvent& evt)
1368 #if defined(AMULE_DAEMON)
1369 evt.Notify();
1370 #else
1371 if (theApp->amuledlg) {
1372 evt.Notify();
1374 #endif
1378 void CamuleApp::ShutDown()
1380 // Log
1381 AddDebugLogLineM(false, logGeneral, wxT("CamuleApp::ShutDown() has started."));
1383 // Signal the hashing thread to terminate
1384 m_app_state = APP_STATE_SHUTTINGDOWN;
1386 StopKad();
1388 // Kry - Save the sources seeds on app exit
1389 if (thePrefs::GetSrcSeedsOn()) {
1390 downloadqueue->SaveSourceSeeds();
1393 OnlineSig(true); // Added By Bouc7
1395 // Exit HTTP downloads
1396 CHTTPDownloadThread::StopAll();
1398 // Exit thread scheduler and upload thread
1399 CThreadScheduler::Terminate();
1401 AddDebugLogLineN(logGeneral, wxT("Terminate upload thread."));
1402 uploadBandwidthThrottler->EndThread();
1404 // Close sockets to avoid new clients coming in
1405 if (listensocket) {
1406 listensocket->Close();
1407 listensocket->KillAllSockets();
1410 if (serverconnect) {
1411 serverconnect->Disconnect();
1414 ECServerHandler->KillAllSockets();
1416 #ifdef ENABLE_UPNP
1417 if (thePrefs::GetUPnPEnabled()) {
1418 if (m_upnp) {
1419 m_upnp->DeletePortMappings(m_upnpMappings);
1422 #endif
1424 // saving data & stuff
1425 if (knownfiles) {
1426 knownfiles->Save();
1429 thePrefs::Add2TotalDownloaded(theStats::GetSessionReceivedBytes());
1430 thePrefs::Add2TotalUploaded(theStats::GetSessionSentBytes());
1432 if (glob_prefs) {
1433 glob_prefs->Save();
1436 CPath configFileName = CPath(ConfigDir + m_configFile);
1437 CPath::BackupFile(configFileName, wxT(".bak"));
1439 if (clientlist) {
1440 clientlist->DeleteAll();
1443 // Log
1444 AddDebugLogLineM(false, logGeneral, wxT("CamuleApp::ShutDown() has ended."));
1448 bool CamuleApp::AddServer(CServer *srv, bool fromUser)
1450 if ( serverlist->AddServer(srv, fromUser) ) {
1451 Notify_ServerAdd(srv);
1452 return true;
1454 return false;
1458 uint32 CamuleApp::GetPublicIP(bool ignorelocal) const
1460 if (m_dwPublicIP == 0) {
1461 if (Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::GetIPAddress() ) {
1462 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetIPAddress());
1463 } else {
1464 return ignorelocal ? 0 : m_localip;
1468 return m_dwPublicIP;
1472 void CamuleApp::SetPublicIP(const uint32 dwIP)
1474 wxASSERT((dwIP == 0) || !IsLowID(dwIP));
1476 if (dwIP != 0 && dwIP != m_dwPublicIP && serverlist != NULL) {
1477 m_dwPublicIP = dwIP;
1478 serverlist->CheckForExpiredUDPKeys();
1479 } else {
1480 m_dwPublicIP = dwIP;
1485 wxString CamuleApp::GetLog(bool reset)
1487 wxFile logfile;
1488 logfile.Open(ConfigDir + wxT("logfile"));
1489 if ( !logfile.IsOpened() ) {
1490 return _("ERROR: can't open logfile");
1492 int len = logfile.Length();
1493 if ( len == 0 ) {
1494 return _("WARNING: logfile is empty. Something is wrong.");
1496 char *tmp_buffer = new char[len + sizeof(wxChar)];
1497 logfile.Read(tmp_buffer, len);
1498 memset(tmp_buffer + len, 0, sizeof(wxChar));
1500 // try to guess file format
1501 wxString str;
1502 if (tmp_buffer[0] && tmp_buffer[1]) {
1503 str = wxString(UTF82unicode(tmp_buffer));
1504 } else {
1505 str = wxWCharBuffer((wchar_t *)tmp_buffer);
1508 delete [] tmp_buffer;
1509 if ( reset ) {
1510 theLogger.CloseLogfile();
1511 if (theLogger.OpenLogfile(ConfigDir + wxT("logfile"))) {
1512 AddLogLineM(false, _("Log has been reset"));
1515 return str;
1519 wxString CamuleApp::GetServerLog(bool reset)
1521 wxString ret = server_msg;
1522 if ( reset ) {
1523 server_msg.Clear();
1525 return ret;
1528 wxString CamuleApp::GetDebugLog(bool reset)
1530 return GetLog(reset);
1534 void CamuleApp::AddServerMessageLine(wxString &msg)
1536 server_msg += msg + wxT("\n");
1537 AddLogLineM(false, CFormat(_("ServerMessage: %s")) % msg);
1542 void CamuleApp::OnFinishedHTTPDownload(CMuleInternalEvent& event)
1544 switch (event.GetInt()) {
1545 case HTTP_IPFilter:
1546 ipfilter->DownloadFinished(event.GetExtraLong());
1547 break;
1548 case HTTP_ServerMet:
1549 serverlist->DownloadFinished(event.GetExtraLong());
1550 break;
1551 case HTTP_ServerMetAuto:
1552 serverlist->AutoDownloadFinished(event.GetExtraLong());
1553 break;
1554 case HTTP_VersionCheck:
1555 CheckNewVersion(event.GetExtraLong());
1556 break;
1557 case HTTP_NodesDat:
1558 if (event.GetExtraLong() == HTTP_Success) {
1560 wxString file = ConfigDir + wxT("nodes.dat");
1561 if (wxFileExists(file)) {
1562 wxRemoveFile(file);
1565 if ( Kademlia::CKademlia::IsRunning() ) {
1566 Kademlia::CKademlia::Stop();
1569 wxRenameFile(file + wxT(".download"),file);
1571 Kademlia::CKademlia::Start();
1572 theApp->ShowConnectionState();
1574 } else if (event.GetExtraLong() == HTTP_Skipped) {
1575 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("nodes.dat"));
1576 } else {
1577 AddLogLineC(_("Failed to download the nodes list."));
1579 break;
1580 #ifdef ENABLE_IP2COUNTRY
1581 case HTTP_GeoIP:
1582 theApp->amuledlg->IP2CountryDownloadFinished(event.GetExtraLong());
1583 // If we updated, the dialog is already up. Redraw it to show the flags.
1584 theApp->amuledlg->Refresh();
1585 break;
1586 #endif
1590 void CamuleApp::CheckNewVersion(uint32 result)
1592 if (result == HTTP_Success) {
1593 wxString filename = ConfigDir + wxT("last_version_check");
1594 wxTextFile file;
1596 if (!file.Open(filename)) {
1597 AddLogLineM(true, _("Failed to open the downloaded version check file") );
1598 return;
1599 } else if (!file.GetLineCount()) {
1600 AddLogLineM(true, _("Corrupted version check file"));
1601 } else {
1602 wxString versionLine = file.GetFirstLine();
1603 wxStringTokenizer tkz(versionLine, wxT("."));
1605 AddDebugLogLineM(false, logGeneral, wxString(wxT("Running: ")) + wxT(VERSION) + wxT(", Version check: ") + versionLine);
1607 long fields[] = {0, 0, 0};
1608 for (int i = 0; i < 3; ++i) {
1609 if (!tkz.HasMoreTokens()) {
1610 AddLogLineM(true, _("Corrupted version check file"));
1611 return;
1612 } else {
1613 wxString token = tkz.GetNextToken();
1615 if (!token.ToLong(&fields[i])) {
1616 AddLogLineM(true, _("Corrupted version check file"));
1617 return;
1622 long curVer = make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE);
1623 long newVer = make_full_ed2k_version(fields[0], fields[1], fields[2]);
1625 if (curVer < newVer) {
1626 AddLogLineM(true, _("You are using an outdated version of aMule!"));
1627 AddLogLineM(false, wxString::Format(_("Your aMule version is %i.%i.%i and the latest version is %li.%li.%li"), VERSION_MJR, VERSION_MIN, VERSION_UPDATE, fields[0], fields[1], fields[2]));
1628 AddLogLineM(false, _("The latest version can always be found at http://www.amule.org"));
1629 #ifdef AMULE_DAEMON
1630 AddLogLineMS(true, CFormat(_("WARNING: Your aMuled version is outdated: %i.%i.%i < %li.%li.%li"))
1631 % VERSION_MJR % VERSION_MIN % VERSION_UPDATE % fields[0] % fields[1] % fields[2]);
1632 #endif
1633 } else {
1634 AddLogLineM(false, _("Your copy of aMule is up to date."));
1638 file.Close();
1639 wxRemoveFile(filename);
1640 } else {
1641 AddLogLineM(true, _("Failed to download the version check file"));
1647 bool CamuleApp::IsConnected() const
1649 return (IsConnectedED2K() || IsConnectedKad());
1653 bool CamuleApp::IsConnectedED2K() const
1655 return serverconnect && serverconnect->IsConnected();
1659 bool CamuleApp::IsConnectedKad() const
1661 return Kademlia::CKademlia::IsConnected();
1665 bool CamuleApp::IsFirewalled() const
1667 if (theApp->IsConnectedED2K() && !theApp->serverconnect->IsLowID()) {
1668 return false; // we have an eD2K HighID -> not firewalled
1671 return IsFirewalledKad(); // If kad says ok, it's ok.
1674 bool CamuleApp::IsFirewalledKad() const
1676 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1677 || Kademlia::CKademlia::IsFirewalled();
1680 bool CamuleApp::IsFirewalledKadUDP() const
1682 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1683 || Kademlia::CUDPFirewallTester::IsFirewalledUDP(true);
1686 bool CamuleApp::IsKadRunning() const
1688 return Kademlia::CKademlia::IsRunning();
1691 // Kad stats
1692 uint32 CamuleApp::GetKadUsers() const
1694 return Kademlia::CKademlia::GetKademliaUsers();
1697 uint32 CamuleApp::GetKadFiles() const
1699 return Kademlia::CKademlia::GetKademliaFiles();
1702 uint32 CamuleApp::GetKadIndexedSources() const
1704 return Kademlia::CKademlia::GetIndexed()->m_totalIndexSource;
1707 uint32 CamuleApp::GetKadIndexedKeywords() const
1709 return Kademlia::CKademlia::GetIndexed()->m_totalIndexKeyword;
1712 uint32 CamuleApp::GetKadIndexedNotes() const
1714 return Kademlia::CKademlia::GetIndexed()->m_totalIndexNotes;
1717 uint32 CamuleApp::GetKadIndexedLoad() const
1719 return Kademlia::CKademlia::GetIndexed()->m_totalIndexLoad;
1723 // True IP of machine
1724 uint32 CamuleApp::GetKadIPAdress() const
1726 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetPrefs()->GetIPAddress());
1729 // Buddy status
1730 uint8 CamuleApp::GetBuddyStatus() const
1732 return clientlist->GetBuddyStatus();
1735 uint32 CamuleApp::GetBuddyIP() const
1737 return clientlist->GetBuddy()->GetIP();
1740 uint32 CamuleApp::GetBuddyPort() const
1742 return clientlist->GetBuddy()->GetUDPPort();
1745 bool CamuleApp::CanDoCallback(CUpDownClient *client)
1747 if (Kademlia::CKademlia::IsConnected()) {
1748 if (IsConnectedED2K()) {
1749 if (serverconnect->IsLowID()) {
1750 if (Kademlia::CKademlia::IsFirewalled()) {
1751 //Both Connected - Both Firewalled
1752 return false;
1753 } else {
1754 if (client->GetServerIP() == theApp->serverconnect->GetCurrentServer()->GetIP() &&
1755 client->GetServerPort() == theApp->serverconnect->GetCurrentServer()->GetPort()) {
1756 // Both Connected - Server lowID, Kad Open - Client on same server
1757 // We prevent a callback to the server as this breaks the protocol
1758 // and will get you banned.
1759 return false;
1760 } else {
1761 // Both Connected - Server lowID, Kad Open - Client on remote server
1762 return true;
1765 } else {
1766 //Both Connected - Server HighID, Kad don't care
1767 return true;
1769 } else {
1770 if (Kademlia::CKademlia::IsFirewalled()) {
1771 //Only Kad Connected - Kad Firewalled
1772 return false;
1773 } else {
1774 //Only Kad Conected - Kad Open
1775 return true;
1778 } else {
1779 if (IsConnectedED2K()) {
1780 if (serverconnect->IsLowID()) {
1781 //Only Server Connected - Server LowID
1782 return false;
1783 } else {
1784 //Only Server Connected - Server HighID
1785 return true;
1787 } else {
1788 //We are not connected at all!
1789 return false;
1794 void CamuleApp::ShowUserCount() {
1795 uint32 totaluser = 0, totalfile = 0;
1797 theApp->serverlist->GetUserFileStatus( totaluser, totalfile );
1799 wxString buffer;
1801 static const wxString s_singlenetstatusformat = _("Users: %s | Files: %s");
1802 static const wxString s_bothnetstatusformat = _("Users: E: %s K: %s | Files: E: %s K: %s");
1804 if (thePrefs::GetNetworkED2K() && thePrefs::GetNetworkKademlia()) {
1805 buffer = CFormat(s_bothnetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(totalfile) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1806 } else if (thePrefs::GetNetworkED2K()) {
1807 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(totalfile);
1808 } else if (thePrefs::GetNetworkKademlia()) {
1809 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1810 } else {
1811 buffer = _("No networks selected");
1814 Notify_ShowUserCount(buffer);
1818 void CamuleApp::ListenSocketHandler(wxSocketEvent& event)
1820 wxCHECK_RET(listensocket, wxT("Connection-event for NULL'd listen-socket"));
1821 wxCHECK_RET(event.GetSocketEvent() == wxSOCKET_CONNECTION,
1822 wxT("Invalid event received for listen-socket"));
1824 if (m_app_state == APP_STATE_RUNNING) {
1825 listensocket->OnAccept(0);
1826 } else if (m_app_state == APP_STATE_STARTING) {
1827 // When starting up, connection may be made before we are able
1828 // to handle them. However, if these are ignored, no futher
1829 // connection-events will be triggered, so we have to accept it.
1830 wxSocketBase* socket = listensocket->Accept(false);
1832 wxCHECK_RET(socket, wxT("NULL returned by Accept() during startup"));
1834 socket->Destroy();
1839 void CamuleApp::ShowConnectionState()
1841 static uint8 old_state = (1<<7); // This flag doesn't exist
1843 uint8 state = 0;
1845 if (theApp->serverconnect->IsConnected()) {
1846 state |= CONNECTED_ED2K;
1849 if (Kademlia::CKademlia::IsRunning()) {
1850 if (Kademlia::CKademlia::IsConnected()) {
1851 if (!Kademlia::CKademlia::IsFirewalled()) {
1852 state |= CONNECTED_KAD_OK;
1853 } else {
1854 state |= CONNECTED_KAD_FIREWALLED;
1856 } else {
1857 state |= CONNECTED_KAD_NOT;
1861 Notify_ShowConnState(state);
1863 if (old_state != state) {
1864 // Get the changed value
1865 int changed_flags = old_state ^ state;
1867 if (changed_flags & CONNECTED_ED2K) {
1868 // ED2K status changed
1869 wxString connected_server;
1870 CServer* ed2k_server = theApp->serverconnect->GetCurrentServer();
1871 if (ed2k_server) {
1872 connected_server = ed2k_server->GetListName();
1874 if (state & CONNECTED_ED2K) {
1875 // We connected to some server
1876 const wxString id = theApp->serverconnect->IsLowID() ? _("with LowID") : _("with HighID");
1878 AddLogLineM(true, CFormat(_("Connected to %s %s")) % connected_server % id);
1879 } else {
1880 if ( theApp->serverconnect->IsConnecting() ) {
1881 AddLogLineM(true, CFormat(_("Connecting to %s")) % connected_server);
1882 } else {
1883 AddLogLineM(true, _("Disconnected from eD2k"));
1888 if (changed_flags & CONNECTED_KAD_NOT) {
1889 if (state & CONNECTED_KAD_NOT) {
1890 AddLogLineM(true, _("Kad started."));
1891 } else {
1892 AddLogLineM(true, _("Kad stopped."));
1896 if (changed_flags & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1897 if (state & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1898 if (state & CONNECTED_KAD_OK) {
1899 AddLogLineM(true, _("Connected to Kad (ok)"));
1900 } else {
1901 AddLogLineM(true, _("Connected to Kad (firewalled)"));
1903 } else {
1904 AddLogLineM(true, _("Disconnected from Kad"));
1908 old_state = state;
1910 theApp->downloadqueue->OnConnectionState(IsConnected());
1913 ShowUserCount();
1914 Notify_ShowConnState(state);
1918 void CamuleApp::UDPSocketHandler(wxSocketEvent& event)
1920 CMuleUDPSocket* socket = (CMuleUDPSocket*)(event.GetClientData());
1921 wxCHECK_RET(socket, wxT("No socket owner specified."));
1923 if (IsOnShutDown() || thePrefs::IsUDPDisabled()) return;
1925 if (!IsRunning()) {
1926 if (event.GetSocketEvent() == wxSOCKET_INPUT) {
1927 // Back to the queue!
1928 theApp->AddPendingEvent(event);
1929 return;
1933 switch (event.GetSocketEvent()) {
1934 case wxSOCKET_INPUT:
1935 socket->OnReceive(0);
1936 break;
1938 case wxSOCKET_OUTPUT:
1939 socket->OnSend(0);
1940 break;
1942 case wxSOCKET_LOST:
1943 socket->OnDisconnected(0);
1944 break;
1946 default:
1947 wxFAIL;
1948 break;
1953 void CamuleApp::OnUnhandledException()
1955 // Call the generic exception-handler.
1956 fprintf(stderr, "\taMule Version: %s\n", (const char*)unicode2char(GetFullMuleVersion()));
1957 ::OnUnhandledException();
1960 void CamuleApp::StartKad()
1962 if (!Kademlia::CKademlia::IsRunning() && thePrefs::GetNetworkKademlia()) {
1963 // Kad makes no sense without the Client-UDP socket.
1964 if (!thePrefs::IsUDPDisabled()) {
1965 if (ipfilter->IsReady()) {
1966 Kademlia::CKademlia::Start();
1967 } else {
1968 ipfilter->StartKADWhenReady();
1970 } else {
1971 AddLogLineM(true,_("Kad network cannot be used if UDP port is disabled on preferences, not starting."));
1973 } else if (!thePrefs::GetNetworkKademlia()) {
1974 AddLogLineM(true,_("Kad network disabled on preferences, not connecting."));
1978 void CamuleApp::StopKad()
1980 // Stop Kad if it's running
1981 if (Kademlia::CKademlia::IsRunning()) {
1982 Kademlia::CKademlia::Stop();
1987 void CamuleApp::BootstrapKad(uint32 ip, uint16 port)
1989 if (!Kademlia::CKademlia::IsRunning()) {
1990 Kademlia::CKademlia::Start();
1991 theApp->ShowConnectionState();
1994 Kademlia::CKademlia::Bootstrap(ip, port, true);
1998 void CamuleApp::UpdateNotesDat(const wxString& url)
2000 wxString strTempFilename(theApp->ConfigDir + wxT("nodes.dat.download"));
2002 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(url, strTempFilename, theApp->ConfigDir + wxT("nodes.dat"), HTTP_NodesDat);
2003 downloader->Create();
2004 downloader->Run();
2008 void CamuleApp::DisconnectED2K()
2010 // Stop ED2K if it's running
2011 if (IsConnectedED2K()) {
2012 serverconnect->Disconnect();
2016 bool CamuleApp::CryptoAvailable() const
2018 return clientcredits && clientcredits->CryptoAvailable();
2021 uint32 CamuleApp::GetED2KID() const {
2022 return serverconnect ? serverconnect->GetClientID() : 0;
2025 uint32 CamuleApp::GetID() const {
2026 uint32 ID;
2028 if( Kademlia::CKademlia::IsConnected() && !Kademlia::CKademlia::IsFirewalled() ) {
2029 // We trust Kad above ED2K
2030 ID = ENDIAN_NTOHL(Kademlia::CKademlia::GetIPAddress());
2031 } else if( theApp->serverconnect->IsConnected() ) {
2032 ID = theApp->serverconnect->GetClientID();
2033 } else if ( Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::IsFirewalled() ) {
2034 // A firewalled Kad client get's a "1"
2035 ID = 1;
2036 } else {
2037 ID = 0;
2040 return ID;
2043 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD)
2044 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SOURCE_DNS_DONE)
2045 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_UDP_DNS_DONE)
2046 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SERVER_DNS_DONE)
2047 // File_checked_for_headers