Upstream tarball 10137
[amule.git] / src / amule.cpp
blobf36cf76c4e289d16c1e38073d46b054e763c885d
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();
218 CamuleApp::~CamuleApp()
220 // Closing the log-file as the very last thing, since
221 // wxWidgets log-events are saved in it as well.
222 theLogger.CloseLogfile();
224 free(strFullMuleVersion);
225 free(strOSDescription);
228 int CamuleApp::OnExit()
230 if (m_app_state!=APP_STATE_STARTING) {
231 AddLogLineMS(false, _("Now, exiting main app..."));
234 // From wxWidgets docs, wxConfigBase:
235 // ...
236 // Note that you must delete this object (usually in wxApp::OnExit)
237 // in order to avoid memory leaks, wxWidgets won't do it automatically.
239 // As it happens, you may even further simplify the procedure described
240 // above: you may forget about calling Set(). When Get() is called and
241 // there is no current object, it will create one using Create() function.
242 // To disable this behaviour DontCreateOnDemand() is provided.
243 delete wxConfigBase::Set((wxConfigBase *)NULL);
245 // Save credits
246 clientcredits->SaveList();
248 // Kill amuleweb if running
249 if (webserver_pid) {
250 AddLogLineNS(CFormat(_("Terminating amuleweb instance with pid `%ld' ... ")) % webserver_pid);
251 wxKillError rc;
252 if (wxKill(webserver_pid, wxSIGTERM, &rc) == -1) {
253 AddLogLineNS(CFormat(_("Killing amuleweb instance with pid `%ld' ... ")) % webserver_pid);
254 if (wxKill(webserver_pid, wxSIGKILL, &rc) == -1) {
255 AddLogLineNS(_("Failed"));
260 if (m_app_state!=APP_STATE_STARTING) {
261 AddLogLineMS(false, _("aMule OnExit: Terminating core."));
264 delete serverlist;
265 serverlist = NULL;
267 delete searchlist;
268 searchlist = NULL;
270 delete clientcredits;
271 clientcredits = NULL;
273 delete friendlist;
274 friendlist = NULL;
276 // Destroying CDownloadQueue calls destructor for CPartFile
277 // calling CSharedFileList::SafeAddKFile occasionally.
278 delete sharedfiles;
279 sharedfiles = NULL;
281 delete serverconnect;
282 serverconnect = NULL;
284 delete listensocket;
285 listensocket = NULL;
287 delete clientudp;
288 clientudp = NULL;
290 delete knownfiles;
291 knownfiles = NULL;
293 delete canceledfiles;
294 canceledfiles = NULL;
296 delete clientlist;
297 clientlist = NULL;
299 delete uploadqueue;
300 uploadqueue = NULL;
302 delete downloadqueue;
303 downloadqueue = NULL;
305 delete ipfilter;
306 ipfilter = NULL;
308 #ifdef ENABLE_UPNP
309 delete m_upnp;
310 m_upnp = NULL;
311 #endif
313 delete ECServerHandler;
314 ECServerHandler = NULL;
316 delete m_statistics;
317 m_statistics = NULL;
319 delete glob_prefs;
320 glob_prefs = NULL;
321 CPreferences::EraseItemList();
323 delete uploadBandwidthThrottler;
324 uploadBandwidthThrottler = NULL;
326 if (m_app_state!=APP_STATE_STARTING) {
327 AddLogLineNS(_("aMule shutdown completed."));
330 #if wxUSE_MEMORY_TRACING
331 AddLogLineNS(_("Memory debug results for aMule exit:"));
332 // Log mem debug mesages to wxLogStderr
333 wxLog* oldLog = wxLog::SetActiveTarget(new wxLogStderr);
334 //AddLogLineNS(wxT("**************Classes**************");
335 //wxDebugContext::PrintClasses();
336 //AddLogLineNS(wxT("***************Dump***************");
337 //wxDebugContext::Dump();
338 AddLogLineNS(wxT("***************Stats**************"));
339 wxDebugContext::PrintStatistics(true);
341 // Set back to wxLogGui
342 delete wxLog::SetActiveTarget(oldLog);
343 #endif
345 StopTickTimer();
347 // Return 0 for succesful program termination
348 return AMULE_APP_BASE::OnExit();
352 int CamuleApp::InitGui(bool, wxString &)
354 return 0;
359 // Application initialization
361 bool CamuleApp::OnInit()
363 #if wxUSE_MEMORY_TRACING
364 // any text before call of Localize_mule needs not to be translated.
365 AddLogLineMS(false, wxT("Checkpoint set on app init for memory debug"));
366 wxDebugContext::SetCheckpoint();
367 #endif
369 // Forward wxLog events to CLogger
370 wxLog::SetActiveTarget(new CLoggerTarget);
372 m_localip = StringHosttoUint32(::wxGetFullHostName());
374 #ifndef __WXMSW__
375 // get rid of sigpipe
376 signal(SIGPIPE, SIG_IGN);
377 #else
378 // Handle CTRL-Break
379 signal(SIGBREAK, OnShutdownSignal);
380 #endif
381 // Handle sigint and sigterm
382 signal(SIGINT, OnShutdownSignal);
383 signal(SIGTERM, OnShutdownSignal);
385 #ifdef __WXMAC__
386 // For listctrl's to behave on Mac
387 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
388 #endif
390 // Handle uncaught exceptions
391 InstallMuleExceptionHandler();
393 if (!InitCommon(AMULE_APP_BASE::argc, AMULE_APP_BASE::argv)) {
394 return false;
397 glob_prefs = new CPreferences();
399 CPath outDir;
400 if (CheckMuleDirectory(wxT("temp"), thePrefs::GetTempDir(), ConfigDir + wxT("Temp"), outDir)) {
401 thePrefs::SetTempDir(outDir);
402 } else {
403 return false;
406 if (CheckMuleDirectory(wxT("incoming"), thePrefs::GetIncomingDir(), ConfigDir + wxT("Incoming"), outDir)) {
407 thePrefs::SetIncomingDir(outDir);
408 } else {
409 return false;
412 // Some sanity check
413 if (!thePrefs::UseTrayIcon()) {
414 thePrefs::SetMinToTray(false);
417 // Build the filenames for the two OS files
418 SetOSFiles(thePrefs::GetOSDir().GetRaw());
420 #ifdef ENABLE_NLS
421 // Load localization settings
422 Localize_mule();
423 #endif
425 // Configure EC for amuled when invoked with ec-config
426 if (ec_config) {
427 AddLogLineMS(false, _("\nEC configuration"));
428 thePrefs::SetECPass(GetPassword());
429 thePrefs::EnableExternalConnections(true);
430 AddLogLineMS(false, _("Password set and external connections enabled."));
433 #ifndef __WXMSW__
434 if (getuid() == 0) {
435 wxString msg =
436 wxT("Warning! You are running aMule as root.\n")
437 wxT("Doing so is not recommended for security reasons,\n")
438 wxT("and you are advised to run aMule as an normal\n")
439 wxT("user instead.");
441 ShowAlert(msg, _("WARNING"), wxCENTRE | wxOK | wxICON_ERROR);
443 fprintf(stderr, "\n--------------------------------------------------\n");
444 fprintf(stderr, "%s", (const char*)unicode2UTF8(msg));
445 fprintf(stderr, "\n--------------------------------------------------\n\n");
447 #endif
449 // Display notification on new version or first run
450 wxTextFile vfile( ConfigDir + wxT("lastversion") );
451 wxString newMule(wxT( VERSION ));
453 if ( !wxFileExists( vfile.GetName() ) ) {
454 vfile.Create();
457 if ( vfile.Open() ) {
458 // Check if this version has been run before
459 bool found = false;
460 for ( size_t i = 0; i < vfile.GetLineCount(); i++ ) {
461 // Check if this version has been run before
462 if ( vfile.GetLine(i) == newMule ) {
463 found = true;
464 break;
468 // We havent run this version before?
469 if ( !found ) {
470 // Insert new at top to provide faster searches
471 vfile.InsertLine( newMule, 0 );
473 Trigger_New_version( newMule );
476 // Keep at most 10 entires
477 while ( vfile.GetLineCount() > 10 )
478 vfile.RemoveLine( vfile.GetLineCount() - 1 );
480 vfile.Write();
481 vfile.Close();
484 // Check if we have the old style locale config
485 wxString langId = thePrefs::GetLanguageID();
486 if (!langId.IsEmpty() && (langId.GetChar(0) >= '0' && langId.GetChar(0) <= '9')) {
487 wxString info(_("Your locale has been changed to System Default due to a configuration change. Sorry."));
488 thePrefs::SetLanguageID(wxLang2Str(wxLANGUAGE_DEFAULT));
489 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
492 m_statistics = new CStatistics();
494 clientlist = new CClientList();
495 friendlist = new CFriendList();
496 searchlist = new CSearchList();
497 knownfiles = new CKnownFileList();
498 canceledfiles = new CCanceledFileList;
499 serverlist = new CServerList();
501 sharedfiles = new CSharedFileList(knownfiles);
502 clientcredits = new CClientCreditsList();
504 // bugfix - do this before creating the uploadqueue
505 downloadqueue = new CDownloadQueue();
506 uploadqueue = new CUploadQueue();
507 ipfilter = new CIPFilter();
509 // Creates all needed listening sockets
510 wxString msg;
511 if (!ReinitializeNetwork(&msg)) {
512 AddLogLineMS(false, wxT("\n"));
513 AddLogLineMS(false, msg);
516 // Test if there's any new version
517 if (thePrefs::GetCheckNewVersion()) {
518 // We use the thread base because I don't want a dialog to pop up.
519 CHTTPDownloadThread* version_check =
520 new CHTTPDownloadThread(wxT("http://amule.sourceforge.net/lastversion"),
521 theApp->ConfigDir + wxT("last_version_check"), theApp->ConfigDir + wxT("last_version"), HTTP_VersionCheck, false);
522 version_check->Create();
523 version_check->Run();
526 // Create main dialog, or fork to background (daemon).
527 InitGui(m_geometryEnabled, m_geometryString);
529 #ifdef AMULE_DAEMON
530 // Need to refresh wxSingleInstanceChecker after the daemon fork() !
531 if (enable_daemon_fork) {
532 RefreshSingleInstanceChecker();
533 // No need to check IsAnotherRunning() - we've done it before.
535 #endif
537 // Has to be created after the call to InitGui, as fork
538 // (when using posix threads) only replicates the mainthread,
539 // and the UBT constructor creates a thread.
540 uploadBandwidthThrottler = new UploadBandwidthThrottler();
542 // Start performing background tasks
543 // This will start loading the IP filter. It will start right away.
544 // Log is confusing, because log entries from background will only be printed
545 // once foreground becomes idle, and that will only be after loading
546 // of the partfiles has finished.
547 CThreadScheduler::Start();
549 // These must be initialized after the gui is loaded.
550 if (thePrefs::GetNetworkED2K()) {
551 serverlist->Init();
553 downloadqueue->LoadMetFiles(thePrefs::GetTempDir());
554 sharedfiles->Reload();
556 // Ensure that the up/down ratio is used
557 CPreferences::CheckUlDlRatio();
559 // The user can start pressing buttons like mad if he feels like it.
560 m_app_state = APP_STATE_RUNNING;
562 // Kry - Load the sources seeds on app init
563 if (thePrefs::GetSrcSeedsOn() && ipfilter->IsReady()) {
564 downloadqueue->LoadSourceSeeds();
567 if (!serverlist->GetServerCount() && thePrefs::GetNetworkED2K()) {
568 // There are no servers and ED2K active -> ask for download.
569 // As we cannot ask in amuled, we just update there
570 // Kry TODO: Store server.met URL on preferences and use it here and in GUI.
571 #ifndef AMULE_DAEMON
572 if (wxYES == wxMessageBox(
573 wxString(
574 _("You don't have any server in the server list.\nDo you want aMule to download a new list now?")),
575 wxString(_("Server list download")),
576 wxYES_NO,
577 static_cast<wxWindow*>(theApp->amuledlg)))
578 #endif
580 // workaround amuled crash
581 #ifndef AMULE_DAEMON
582 serverlist->UpdateServerMetFromURL(
583 wxT("http://gruk.org/server.met.gz"));
584 #endif
589 // Autoconnect if that option is enabled
590 if (thePrefs::DoAutoConnect() && (thePrefs::GetNetworkED2K() || thePrefs::GetNetworkKademlia())) {
591 if (ipfilter->IsReady()) {
592 // If it's not ready it will connect later, so don't print it now.
593 AddLogLineC(_("Connecting"));
595 if (thePrefs::GetNetworkED2K()) {
596 theApp->serverconnect->ConnectToAnyServer();
599 StartKad();
603 // Enable GeoIP
604 #ifdef ENABLE_IP2COUNTRY
605 theApp->amuledlg->EnableIP2Country();
606 #endif
608 // Run webserver?
609 if (thePrefs::GetWSIsEnabled()) {
610 wxString aMuleConfigFile = ConfigDir + m_configFile;
611 wxString amulewebPath = thePrefs::GetWSPath();
613 #if defined(__WXMAC__) && !defined(AMULE_DAEMON)
614 // For the Mac GUI application, look for amuleweb in the bundle
615 CFURLRef amulewebUrl = CFBundleCopyAuxiliaryExecutableURL(
616 CFBundleGetMainBundle(), CFSTR("amuleweb"));
618 if (amulewebUrl) {
619 CFURLRef absoluteUrl = CFURLCopyAbsoluteURL(amulewebUrl);
620 CFRelease(amulewebUrl);
622 if (absoluteUrl) {
623 CFStringRef amulewebCfstr = CFURLCopyFileSystemPath(absoluteUrl, kCFURLPOSIXPathStyle);
624 CFRelease(absoluteUrl);
625 #if wxCHECK_VERSION(2, 9, 0)
626 amulewebPath = wxCFStringRef(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
627 #else
628 amulewebPath = wxMacCFStringHolder(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
629 #endif
632 #endif
634 #ifdef __WXMSW__
635 # define QUOTE wxT("\"")
636 #else
637 # define QUOTE wxT("\'")
638 #endif
640 wxString cmd =
641 QUOTE +
642 amulewebPath +
643 QUOTE wxT(" ") QUOTE wxT("--amule-config-file=") +
644 aMuleConfigFile +
645 QUOTE;
646 CTerminationProcessAmuleweb *p = new CTerminationProcessAmuleweb(cmd, &webserver_pid);
647 webserver_pid = wxExecute(cmd, wxEXEC_ASYNC, p);
648 bool webserver_ok = webserver_pid > 0;
649 if (webserver_ok) {
650 AddLogLineM(true, CFormat(_("web server running on pid %d")) % webserver_pid);
651 } else {
652 delete p;
653 ShowAlert(_(
654 "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"),
655 _("ERROR"), wxOK | wxICON_ERROR);
659 return true;
662 bool CamuleApp::ReinitializeNetwork(wxString* msg)
664 bool ok = true;
665 static bool firstTime = true;
667 if (!firstTime) {
668 // TODO: Destroy previously created sockets
670 firstTime = false;
672 // Some sanity checks first
673 if (thePrefs::ECPort() == thePrefs::GetPort()) {
674 // Select a random usable port in the range 1025 ... 2^16 - 1
675 uint16 port = thePrefs::ECPort();
676 while ( port < 1024 || port == thePrefs::GetPort() ) {
677 port = (uint16)rand();
679 thePrefs::SetECPort( port );
681 wxString err =
682 wxT("Network configuration failed! You cannot use the same port\n")
683 wxT("for the main TCP port and the External Connections port.\n")
684 wxT("The EC port has been changed to avoid conflict, see the\n")
685 wxT("preferences for the new value.\n");
686 *msg << err;
688 AddLogLineM( false, wxEmptyString );
689 AddLogLineM( true, err );
690 AddLogLineM( false, wxEmptyString );
692 ok = false;
695 if (thePrefs::GetUDPPort() == thePrefs::GetPort() + 3) {
696 // Select a random usable value in the range 1025 ... 2^16 - 1
697 uint16 port = thePrefs::GetUDPPort();
698 while ( port < 1024 || port == thePrefs::GetPort() + 3 ) {
699 port = (uint16)rand();
701 thePrefs::SetUDPPort( port );
703 wxString err =
704 wxT("Network configuration failed! You set your UDP port to\n")
705 wxT("the value of the main TCP port plus 3.\n")
706 wxT("This port has been reserved for the Server-UDP port. The\n")
707 wxT("port value has been changed to avoid conflict, see the\n")
708 wxT("preferences for the new value\n");
709 *msg << err;
711 AddLogLineM( false, wxEmptyString );
712 AddLogLineM( true, err );
713 AddLogLineM( false, wxEmptyString );
715 ok = false;
718 // Create the address where we are going to listen
719 // TODO: read this from configuration file
720 amuleIPV4Address myaddr[4];
722 // Create the External Connections Socket.
723 // Default is 4712.
724 // Get ready to handle connections from apps like amulecmd
725 if (thePrefs::GetECAddress().IsEmpty() || !myaddr[0].Hostname(thePrefs::GetECAddress())) {
726 myaddr[0].AnyAddress();
728 myaddr[0].Service(thePrefs::ECPort());
729 ECServerHandler = new ExternalConn(myaddr[0], msg);
731 // Create the UDP socket TCP+3.
732 // Used for source asking on servers.
733 if (thePrefs::GetAddress().IsEmpty()) {
734 myaddr[1].AnyAddress();
735 } else if (!myaddr[1].Hostname(thePrefs::GetAddress())) {
736 myaddr[1].AnyAddress();
737 AddLogLineM(true, CFormat(_("Could not bind ports to the specified address: %s"))
738 % thePrefs::GetAddress());
741 wxString ip = myaddr[1].IPAddress();
742 myaddr[1].Service(thePrefs::GetPort()+3);
743 serverconnect = new CServerConnect(serverlist, myaddr[1]);
744 *msg << CFormat( wxT("*** Server UDP socket (TCP+3) at %s:%u\n") )
745 % ip % ((unsigned int)thePrefs::GetPort() + 3u);
747 // Create the ListenSocket (aMule TCP socket).
748 // Used for Client Port / Connections from other clients,
749 // Client to Client Source Exchange.
750 // Default is 4662.
751 myaddr[2] = myaddr[1];
752 myaddr[2].Service(thePrefs::GetPort());
753 listensocket = new CListenSocket(myaddr[2]);
754 *msg << CFormat( wxT("*** TCP socket (TCP) listening on %s:%u\n") )
755 % ip % (unsigned int)(thePrefs::GetPort());
756 // This command just sets a flag to control maximum number of connections.
757 // Notify(true) has already been called to the ListenSocket, so events may
758 // be already comming in.
759 if (listensocket->Ok()) {
760 listensocket->StartListening();
761 } else {
762 // If we wern't able to start listening, we need to warn the user
763 wxString err;
764 err = CFormat(_("Port %u is not available. You will be LOWID\n")) %
765 (unsigned int)(thePrefs::GetPort());
766 *msg << err;
767 AddLogLineM(true, err);
768 err.Clear();
769 err = CFormat(
770 _("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.")) %
771 (unsigned int)(thePrefs::GetPort());
772 ShowAlert(err, _("ERROR"), wxOK | wxICON_ERROR);
775 // Create the UDP socket.
776 // Used for extended eMule protocol, Queue Rating, File Reask Ping.
777 // Also used for Kademlia.
778 // Default is port 4672.
779 myaddr[3] = myaddr[1];
780 myaddr[3].Service(thePrefs::GetUDPPort());
781 clientudp = new CClientUDPSocket(myaddr[3], thePrefs::GetProxyData());
782 if (!thePrefs::IsUDPDisabled()) {
783 *msg << CFormat( wxT("*** Client UDP socket (extended eMule) at %s:%u") )
784 % ip % thePrefs::GetUDPPort();
785 } else {
786 *msg << wxT("*** Client UDP socket (extended eMule) disabled on preferences");
789 #ifdef ENABLE_UPNP
790 if (thePrefs::GetUPnPEnabled()) {
791 try {
792 m_upnpMappings[0] = CUPnPPortMapping(
793 myaddr[0].Service(),
794 "TCP",
795 thePrefs::GetUPnPECEnabled(),
796 "aMule TCP External Connections Socket");
797 m_upnpMappings[1] = CUPnPPortMapping(
798 myaddr[1].Service(),
799 "UDP",
800 thePrefs::GetUPnPEnabled(),
801 "aMule UDP socket (TCP+3)");
802 m_upnpMappings[2] = CUPnPPortMapping(
803 myaddr[2].Service(),
804 "TCP",
805 thePrefs::GetUPnPEnabled(),
806 "aMule TCP Listen Socket");
807 m_upnpMappings[3] = CUPnPPortMapping(
808 myaddr[3].Service(),
809 "UDP",
810 thePrefs::GetUPnPEnabled(),
811 "aMule UDP Extended eMule Socket");
812 m_upnp = new CUPnPControlPoint(thePrefs::GetUPnPTCPPort());
813 m_upnp->AddPortMappings(m_upnpMappings);
814 } catch(CUPnPException &e) {
815 wxString error_msg;
816 error_msg << e.what();
817 AddLogLineM(true, error_msg);
818 fprintf(stderr, "%s\n", (const char *)unicode2char(error_msg));
821 #endif
823 return ok;
826 /* Original implementation by Bouc7 of the eMule Project.
827 aMule Signature idea was designed by BigBob and implemented
828 by Un-Thesis, with design inputs and suggestions from bothie.
830 void CamuleApp::OnlineSig(bool zero /* reset stats (used on shutdown) */)
832 // Do not do anything if online signature is disabled in Preferences
833 if (!thePrefs::IsOnlineSignatureEnabled() || m_emulesig_path.IsEmpty()) {
834 // We do not need to check m_amulesig_path because if m_emulesig_path is empty,
835 // that means m_amulesig_path is empty too.
836 return;
839 // Remove old signature files
840 if ( wxFileExists( m_emulesig_path ) ) { wxRemoveFile( m_emulesig_path ); }
841 if ( wxFileExists( m_amulesig_path ) ) { wxRemoveFile( m_amulesig_path ); }
844 wxTextFile amulesig_out;
845 wxTextFile emulesig_out;
847 // Open both files if needed
848 if ( !emulesig_out.Create( m_emulesig_path) ) {
849 AddLogLineM(true, _("Failed to create OnlineSig File"));
850 // Will never try again.
851 m_amulesig_path.Clear();
852 m_emulesig_path.Clear();
853 return;
856 if ( !amulesig_out.Create(m_amulesig_path) ) {
857 AddLogLineM(true, _("Failed to create aMule OnlineSig File"));
858 // Will never try again.
859 m_amulesig_path.Clear();
860 m_emulesig_path.Clear();
861 return;
864 wxString emulesig_string;
865 wxString temp;
867 if (zero) {
868 emulesig_string = wxT("0\xA0.0|0.0|0");
869 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0\n0\n0.0\n0.0\n0\n0"));
870 } else {
871 if (IsConnectedED2K()) {
873 temp = wxString::Format(wxT("%d"),serverconnect->GetCurrentServer()->GetPort());
875 // We are online
876 emulesig_string =
877 // Connected
878 wxT("1|")
879 //Server name
880 + serverconnect->GetCurrentServer()->GetListName()
881 + wxT("|")
882 // IP and port of the server
883 + serverconnect->GetCurrentServer()->GetFullIP()
884 + wxT("|")
885 + temp;
888 // Now for amule sig
890 // Connected. State 1, full info
891 amulesig_out.AddLine(wxT("1"));
892 // Server Name
893 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetListName());
894 // Server IP
895 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetFullIP());
896 // Server Port
897 amulesig_out.AddLine(temp);
899 if (serverconnect->IsLowID()) {
900 amulesig_out.AddLine(wxT("L"));
901 } else {
902 amulesig_out.AddLine(wxT("H"));
905 } else if (serverconnect->IsConnecting()) {
906 emulesig_string = wxT("0");
908 // Connecting. State 2, No info.
909 amulesig_out.AddLine(wxT("2\n0\n0\n0\n0"));
910 } else {
911 // Not connected to a server
912 emulesig_string = wxT("0");
914 // Not connected, state 0, no info
915 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0"));
917 if (IsConnectedKad()) {
918 if(Kademlia::CKademlia::IsFirewalled()) {
919 // Connected. Firewalled. State 1.
920 amulesig_out.AddLine(wxT("1"));
921 } else {
922 // Connected. State 2.
923 amulesig_out.AddLine(wxT("2"));
925 } else {
926 // Not connected.State 0.
927 amulesig_out.AddLine(wxT("0"));
929 emulesig_string += wxT("\xA");
931 // Datarate for downloads
932 temp = wxString::Format(wxT("%.1f"), theStats::GetDownloadRate() / 1024.0);
934 emulesig_string += temp + wxT("|");
935 amulesig_out.AddLine(temp);
937 // Datarate for uploads
938 temp = wxString::Format(wxT("%.1f"), theStats::GetUploadRate() / 1024.0);
940 emulesig_string += temp + wxT("|");
941 amulesig_out.AddLine(temp);
943 // Number of users waiting for upload
944 temp = wxString::Format(wxT("%d"), theStats::GetWaitingUserCount());
946 emulesig_string += temp;
947 amulesig_out.AddLine(temp);
949 // Number of shared files (not on eMule)
950 amulesig_out.AddLine(wxString::Format(wxT("%d"), theStats::GetSharedFileCount()));
953 // eMule signature finished here. Write the line to the wxTextFile.
954 emulesig_out.AddLine(emulesig_string);
956 // Now for aMule signature extras
958 // Nick on the network
959 amulesig_out.AddLine(thePrefs::GetUserNick());
961 // Total received in bytes
962 amulesig_out.AddLine( CFormat( wxT("%llu") ) % (theStats::GetSessionReceivedBytes() + thePrefs::GetTotalDownloaded()) );
964 // Total sent in bytes
965 amulesig_out.AddLine( CFormat( wxT("%llu") ) % (theStats::GetSessionSentBytes() + thePrefs::GetTotalUploaded()) );
967 // amule version
968 #ifdef SVNDATE
969 amulesig_out.AddLine(wxT(VERSION) wxT(" ") wxT(SVNDATE));
970 #else
971 amulesig_out.AddLine(wxT(VERSION));
972 #endif
974 if (zero) {
975 amulesig_out.AddLine(wxT("0"));
976 amulesig_out.AddLine(wxT("0"));
977 amulesig_out.AddLine(wxT("0"));
978 } else {
979 // Total received bytes in session
980 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
981 theStats::GetSessionReceivedBytes() );
983 // Total sent bytes in session
984 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
985 theStats::GetSessionSentBytes() );
987 // Uptime
988 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetUptimeSeconds());
991 // Flush the files
992 emulesig_out.Write();
993 amulesig_out.Write();
994 } //End Added By Bouc7
997 // Gracefully handle fatal exceptions and print backtrace if possible
998 void CamuleApp::OnFatalException()
1000 /* Print the backtrace */
1001 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
1002 fprintf(stderr, "A fatal error has occurred and aMule has crashed.\n");
1003 fprintf(stderr, "Please assist us in fixing this problem by posting the backtrace below in our\n");
1004 fprintf(stderr, "'aMule Crashes' forum and include as much information as possible regarding the\n");
1005 fprintf(stderr, "circumstances of this crash. The forum is located here:\n");
1006 fprintf(stderr, " http://forum.amule.org/index.php?board=67.0\n");
1007 fprintf(stderr, "If possible, please try to generate a real backtrace of this crash:\n");
1008 fprintf(stderr, " http://wiki.amule.org/index.php/Backtraces\n\n");
1009 fprintf(stderr, "----------------------------=| BACKTRACE FOLLOWS: |=----------------------------\n");
1010 fprintf(stderr, "Current version is: %s\n", strFullMuleVersion);
1011 fprintf(stderr, "Running on: %s\n\n", strOSDescription);
1013 print_backtrace(1); // 1 == skip this function.
1015 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
1019 // Sets the localization of aMule
1020 void CamuleApp::Localize_mule()
1022 InitCustomLanguages();
1023 InitLocale(m_locale, StrLang2wx(thePrefs::GetLanguageID()));
1024 if (!m_locale.IsOk()) {
1025 AddLogLineM(false,_("The selected locale seems not to be installed on your box. (Note: I'll try to set it anyway)"));
1030 // Displays information related to important changes in aMule.
1031 // Is called when the user runs a new version of aMule
1032 void CamuleApp::Trigger_New_version(wxString new_version)
1034 wxString info = wxT(" --- ") + CFormat(_("This is the first time you run aMule %s")) % new_version + wxT(" ---\n\n");
1035 if (new_version == wxT("SVN")) {
1036 info += _("This version is a testing version, updated daily, and\n");
1037 info += _("we give no warranty it won't break anything, burn your house,\n");
1038 info += _("or kill your dog. But it *should* be safe to use anyway.\n");
1041 // General info
1042 info += wxT("\n");
1043 info += _("More information, support and new releases can found at our homepage,\n");
1044 info += _("at www.aMule.org, or in our IRC channel #aMule at irc.freenode.net.\n");
1045 info += wxT("\n");
1046 info += _("Feel free to report any bugs to http://forum.amule.org");
1048 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
1052 void CamuleApp::SetOSFiles(const wxString new_path)
1054 if ( thePrefs::IsOnlineSignatureEnabled() ) {
1055 if ( ::wxDirExists(new_path) ) {
1056 m_emulesig_path = JoinPaths(new_path, wxT("onlinesig.dat"));
1057 m_amulesig_path = JoinPaths(new_path, wxT("amulesig.dat"));
1058 } else {
1059 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);
1060 m_emulesig_path.Clear();
1061 m_amulesig_path.Clear();
1063 } else {
1064 m_emulesig_path.Clear();
1065 m_amulesig_path.Clear();
1070 #ifdef __WXDEBUG__
1071 #ifndef wxUSE_STACKWALKER
1072 #define wxUSE_STACKWALKER 0
1073 #endif
1074 void CamuleApp::OnAssertFailure(const wxChar* file, int line,
1075 const wxChar* func, const wxChar* cond, const wxChar* msg)
1077 if (!wxUSE_STACKWALKER || !wxThread::IsMain() || !IsRunning()) {
1078 wxString errmsg = CFormat( wxT("%s:%s:%d: Assertion '%s' failed. %s") )
1079 % file % func % line % cond % ( msg ? msg : wxT("") );
1081 fprintf(stderr, "Assertion failed: %s\n", (const char*)unicode2char(errmsg));
1083 // Skip the function-calls directly related to the assert call.
1084 fprintf(stderr, "\nBacktrace follows:\n");
1085 print_backtrace(3);
1086 fprintf(stderr, "\n");
1089 if (wxThread::IsMain() && IsRunning()) {
1090 AMULE_APP_BASE::OnAssertFailure(file, line, func, cond, msg);
1091 } else {
1092 // Abort, allows gdb to catch the assertion
1093 raise( SIGABRT );
1096 #endif
1099 void CamuleApp::OnUDPDnsDone(CMuleInternalEvent& evt)
1101 CServerUDPSocket* socket =(CServerUDPSocket*)evt.GetClientData();
1102 socket->OnHostnameResolved(evt.GetExtraLong());
1106 void CamuleApp::OnSourceDnsDone(CMuleInternalEvent& evt)
1108 downloadqueue->OnHostnameResolved(evt.GetExtraLong());
1112 void CamuleApp::OnServerDnsDone(CMuleInternalEvent& evt)
1114 AddLogLineMS(false, _("Server hostname notified"));
1115 serverconnect->OnServerHostnameResolved(evt.GetClientData(), evt.GetExtraLong());
1119 void CamuleApp::OnTCPTimer(CTimerEvent& WXUNUSED(evt))
1121 if(!IsRunning()) {
1122 return;
1124 serverconnect->StopConnectionTry();
1125 if (IsConnectedED2K() ) {
1126 return;
1128 serverconnect->ConnectToAnyServer();
1132 void CamuleApp::OnCoreTimer(CTimerEvent& WXUNUSED(evt))
1134 // Former TimerProc section
1135 static uint64 msPrev1, msPrev5, msPrevSave, msPrevHist, msPrevOS, msPrevKnownMet;
1136 uint64 msCur = theStats::GetUptimeMillis();
1137 TheTime = msCur / 1000;
1139 if (!IsRunning()) {
1140 return;
1143 #ifndef AMULE_DAEMON
1144 // Check if we should terminate the app
1145 if ( g_shutdownSignal ) {
1146 wxWindow* top = GetTopWindow();
1148 if ( top ) {
1149 top->Close(true);
1150 } else {
1151 // No top-window, have to force termination.
1152 wxExit();
1155 #endif
1157 // There is a theoretical chance that the core time function can recurse:
1158 // if an event function gets blocked on a mutex (communicating with the
1159 // UploadBandwidthThrottler) wx spawns a new event loop and processes more events.
1160 // If CPU load gets high a new core timer event could be generated before the last
1161 // one was finished and so recursion could occur, which would be bad.
1162 // Detect this and do an early return then.
1163 static bool recurse = false;
1164 if (recurse) {
1165 return;
1167 recurse = true;
1169 uploadqueue->Process();
1170 downloadqueue->Process();
1171 //theApp->clientcredits->Process();
1172 theStats::CalculateRates();
1174 if (msCur-msPrevHist > 1000) {
1175 // unlike the other loop counters in this function this one will sometimes
1176 // produce two calls in quick succession (if there was a gap of more than one
1177 // second between calls to TimerProc) - this is intentional! This way the
1178 // history list keeps an average of one node per second and gets thinned out
1179 // correctly as time progresses.
1180 msPrevHist += 1000;
1182 m_statistics->RecordHistory();
1187 if (msCur-msPrev1 > 1000) { // approximately every second
1188 msPrev1 = msCur;
1189 clientcredits->Process();
1190 clientlist->Process();
1192 // Publish files to server if needed.
1193 sharedfiles->Process();
1195 if( Kademlia::CKademlia::IsRunning() ) {
1196 Kademlia::CKademlia::Process();
1197 if(Kademlia::CKademlia::GetPrefs()->HasLostConnection()) {
1198 StopKad();
1199 clientudp->Close();
1200 clientudp->Open();
1201 if (thePrefs::Reconnect()) {
1202 StartKad();
1207 if( serverconnect->IsConnecting() && !serverconnect->IsSingleConnect() ) {
1208 serverconnect->TryAnotherConnectionrequest();
1210 if (serverconnect->IsConnecting()) {
1211 serverconnect->CheckForTimeout();
1213 listensocket->UpdateConnectionsStatus();
1218 if (msCur-msPrev5 > 5000) { // every 5 seconds
1219 msPrev5 = msCur;
1220 listensocket->Process();
1223 if (msCur-msPrevSave >= 60000) {
1224 msPrevSave = msCur;
1225 wxString buffer;
1227 // Save total upload/download to preferences
1228 wxConfigBase* cfg = wxConfigBase::Get();
1229 buffer = CFormat(wxT("%llu")) % (theStats::GetSessionReceivedBytes() + thePrefs::GetTotalDownloaded());
1230 cfg->Write(wxT("/Statistics/TotalDownloadedBytes"), buffer);
1232 buffer = CFormat(wxT("%llu")) % (theStats::GetSessionSentBytes() + thePrefs::GetTotalUploaded());
1233 cfg->Write(wxT("/Statistics/TotalUploadedBytes"), buffer);
1235 // Write changes to file
1236 cfg->Flush();
1240 // Special
1241 if (msCur - msPrevOS >= thePrefs::GetOSUpdate() * 1000ull) {
1242 OnlineSig(); // Added By Bouc7
1243 msPrevOS = msCur;
1246 if (msCur - msPrevKnownMet >= 30*60*1000/*There must be a prefs option for this*/) {
1247 // Save Shared Files data
1248 knownfiles->Save();
1249 msPrevKnownMet = msCur;
1253 // Recomended by lugdunummaster himself - from emule 0.30c
1254 serverconnect->KeepConnectionAlive();
1256 // Disarm recursion protection
1257 recurse = false;
1261 void CamuleApp::OnFinishedHashing(CHashingEvent& evt)
1263 wxCHECK_RET(evt.GetResult(), wxT("No result of hashing"));
1265 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1266 CKnownFile* result = evt.GetResult();
1268 if (owner) {
1269 // Check if the partfile still exists, as it might have
1270 // been deleted in the mean time.
1271 if (downloadqueue->IsPartFile(owner)) {
1272 // This cast must not be done before the IsPartFile
1273 // call, as dynamic_cast will barf on dangling pointers.
1274 dynamic_cast<CPartFile*>(owner)->PartFileHashFinished(result);
1276 } else {
1277 static int filecount;
1278 static uint64 bytecount;
1280 if (knownfiles->SafeAddKFile(result)) {
1281 AddDebugLogLineM(false, logKnownFiles,
1282 CFormat(wxT("Safe adding file to sharedlist: %s")) % result->GetFileName());
1283 sharedfiles->SafeAddKFile(result);
1285 filecount++;
1286 bytecount += result->GetFileSize();
1287 // If we have added 30 files or files with a total size of ~300mb
1288 if ( ( filecount == 30 ) || ( bytecount >= 314572800 ) ) {
1289 AddDebugLogLineM(false, logKnownFiles, wxT("Failsafe for crash on file hashing creation"));
1290 if ( m_app_state != APP_STATE_SHUTTINGDOWN ) {
1291 knownfiles->Save();
1292 filecount = 0;
1293 bytecount = 0;
1296 } else {
1297 AddDebugLogLineM(false, logKnownFiles,
1298 CFormat(wxT("File not added to sharedlist: %s")) % result->GetFileName());
1299 delete result;
1305 void CamuleApp::OnFinishedAICHHashing(CHashingEvent& evt)
1307 wxCHECK_RET(evt.GetResult(), wxT("No result of AICH-hashing"));
1309 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1310 CScopedPtr<CKnownFile> result(evt.GetResult());
1312 // Check that the owner is still valid
1313 if (knownfiles->IsKnownFile(owner)) {
1314 if (result->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE) {
1315 CAICHHashSet* oldSet = owner->GetAICHHashset();
1316 CAICHHashSet* newSet = result->GetAICHHashset();
1318 owner->SetAICHHashset(newSet);
1319 newSet->SetOwner(owner);
1321 result->SetAICHHashset(oldSet);
1322 oldSet->SetOwner(result.get());
1328 void CamuleApp::OnFinishedCompletion(CCompletionEvent& evt)
1330 CPartFile* completed = const_cast<CPartFile*>(evt.GetOwner());
1331 wxCHECK_RET(completed, wxT("Completion event sent for unspecified file"));
1332 wxASSERT_MSG(downloadqueue->IsPartFile(completed), wxT("CCompletionEvent for unknown partfile."));
1334 completed->CompleteFileEnded(evt.ErrorOccured(), evt.GetFullPath());
1335 if (evt.ErrorOccured()) {
1336 CUserEvents::ProcessEvent(CUserEvents::ErrorOnCompletion, completed);
1339 // Check if we should execute an script/app/whatever.
1340 CUserEvents::ProcessEvent(CUserEvents::DownloadCompleted, completed);
1343 void CamuleApp::OnFinishedAllocation(CAllocFinishedEvent& evt)
1345 CPartFile *file = evt.GetFile();
1346 wxCHECK_RET(file, wxT("Allocation finished event sent for unspecified file"));
1347 wxASSERT_MSG(downloadqueue->IsPartFile(file), wxT("CAllocFinishedEvent for unknown partfile"));
1349 file->SetPartFileStatus(PS_EMPTY);
1351 if (evt.Succeeded()) {
1352 if (evt.IsPaused()) {
1353 file->StopFile();
1354 } else {
1355 file->ResumeFile();
1357 } else {
1358 AddLogLineM(false, CFormat(_("Disk space preallocation for file '%s' failed: %s")) % file->GetFileName() % wxString(UTF82unicode(std::strerror(evt.GetResult()))));
1359 file->StopFile();
1362 file->AllocationFinished();
1365 void CamuleApp::OnNotifyEvent(CMuleGUIEvent& evt)
1367 #if defined(AMULE_DAEMON)
1368 evt.Notify();
1369 #else
1370 if (theApp->amuledlg) {
1371 evt.Notify();
1373 #endif
1377 void CamuleApp::ShutDown()
1379 // Log
1380 AddDebugLogLineM(false, logGeneral, wxT("CamuleApp::ShutDown() has started."));
1382 // Signal the hashing thread to terminate
1383 m_app_state = APP_STATE_SHUTTINGDOWN;
1385 StopKad();
1387 // Kry - Save the sources seeds on app exit
1388 if (thePrefs::GetSrcSeedsOn()) {
1389 downloadqueue->SaveSourceSeeds();
1392 OnlineSig(true); // Added By Bouc7
1394 // Exit HTTP downloads
1395 CHTTPDownloadThread::StopAll();
1397 // Exit thread scheduler and upload thread
1398 CThreadScheduler::Terminate();
1400 AddDebugLogLineN(logGeneral, wxT("Terminate upload thread."));
1401 uploadBandwidthThrottler->EndThread();
1403 // Close sockets to avoid new clients coming in
1404 if (listensocket) {
1405 listensocket->Close();
1406 listensocket->KillAllSockets();
1409 if (serverconnect) {
1410 serverconnect->Disconnect();
1413 ECServerHandler->KillAllSockets();
1415 #ifdef ENABLE_UPNP
1416 if (thePrefs::GetUPnPEnabled()) {
1417 if (m_upnp) {
1418 m_upnp->DeletePortMappings(m_upnpMappings);
1421 #endif
1423 // saving data & stuff
1424 if (knownfiles) {
1425 knownfiles->Save();
1428 thePrefs::Add2TotalDownloaded(theStats::GetSessionReceivedBytes());
1429 thePrefs::Add2TotalUploaded(theStats::GetSessionSentBytes());
1431 if (glob_prefs) {
1432 glob_prefs->Save();
1435 CPath configFileName = CPath(ConfigDir + m_configFile);
1436 CPath::BackupFile(configFileName, wxT(".bak"));
1438 if (clientlist) {
1439 clientlist->DeleteAll();
1442 // Log
1443 AddDebugLogLineM(false, logGeneral, wxT("CamuleApp::ShutDown() has ended."));
1447 bool CamuleApp::AddServer(CServer *srv, bool fromUser)
1449 if ( serverlist->AddServer(srv, fromUser) ) {
1450 Notify_ServerAdd(srv);
1451 return true;
1453 return false;
1457 uint32 CamuleApp::GetPublicIP(bool ignorelocal) const
1459 if (m_dwPublicIP == 0) {
1460 if (Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::GetIPAddress() ) {
1461 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetIPAddress());
1462 } else {
1463 return ignorelocal ? 0 : m_localip;
1467 return m_dwPublicIP;
1471 void CamuleApp::SetPublicIP(const uint32 dwIP)
1473 wxASSERT((dwIP == 0) || !IsLowID(dwIP));
1475 if (dwIP != 0 && dwIP != m_dwPublicIP && serverlist != NULL) {
1476 m_dwPublicIP = dwIP;
1477 serverlist->CheckForExpiredUDPKeys();
1478 } else {
1479 m_dwPublicIP = dwIP;
1484 wxString CamuleApp::GetLog(bool reset)
1486 wxFile logfile;
1487 logfile.Open(ConfigDir + wxT("logfile"));
1488 if ( !logfile.IsOpened() ) {
1489 return _("ERROR: can't open logfile");
1491 int len = logfile.Length();
1492 if ( len == 0 ) {
1493 return _("WARNING: logfile is empty. Something is wrong.");
1495 char *tmp_buffer = new char[len + sizeof(wxChar)];
1496 logfile.Read(tmp_buffer, len);
1497 memset(tmp_buffer + len, 0, sizeof(wxChar));
1499 // try to guess file format
1500 wxString str;
1501 if (tmp_buffer[0] && tmp_buffer[1]) {
1502 str = wxString(UTF82unicode(tmp_buffer));
1503 } else {
1504 str = wxWCharBuffer((wchar_t *)tmp_buffer);
1507 delete [] tmp_buffer;
1508 if ( reset ) {
1509 theLogger.CloseLogfile();
1510 if (theLogger.OpenLogfile(ConfigDir + wxT("logfile"))) {
1511 AddLogLineM(false, _("Log has been reset"));
1514 return str;
1518 wxString CamuleApp::GetServerLog(bool reset)
1520 wxString ret = server_msg;
1521 if ( reset ) {
1522 server_msg.Clear();
1524 return ret;
1527 wxString CamuleApp::GetDebugLog(bool reset)
1529 return GetLog(reset);
1533 void CamuleApp::AddServerMessageLine(wxString &msg)
1535 server_msg += msg + wxT("\n");
1536 AddLogLineM(false, CFormat(_("ServerMessage: %s")) % msg);
1541 void CamuleApp::OnFinishedHTTPDownload(CMuleInternalEvent& event)
1543 switch (event.GetInt()) {
1544 case HTTP_IPFilter:
1545 ipfilter->DownloadFinished(event.GetExtraLong());
1546 break;
1547 case HTTP_ServerMet:
1548 serverlist->DownloadFinished(event.GetExtraLong());
1549 break;
1550 case HTTP_ServerMetAuto:
1551 serverlist->AutoDownloadFinished(event.GetExtraLong());
1552 break;
1553 case HTTP_VersionCheck:
1554 CheckNewVersion(event.GetExtraLong());
1555 break;
1556 case HTTP_NodesDat:
1557 if (event.GetExtraLong() == HTTP_Success) {
1559 wxString file = ConfigDir + wxT("nodes.dat");
1560 if (wxFileExists(file)) {
1561 wxRemoveFile(file);
1564 if ( Kademlia::CKademlia::IsRunning() ) {
1565 Kademlia::CKademlia::Stop();
1568 wxRenameFile(file + wxT(".download"),file);
1570 Kademlia::CKademlia::Start();
1571 theApp->ShowConnectionState();
1573 } else if (event.GetExtraLong() == HTTP_Skipped) {
1574 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("nodes.dat"));
1575 } else {
1576 AddLogLineC(_("Failed to download the nodes list."));
1578 break;
1579 #ifdef ENABLE_IP2COUNTRY
1580 case HTTP_GeoIP:
1581 theApp->amuledlg->IP2CountryDownloadFinished(event.GetExtraLong());
1582 // If we updated, the dialog is already up. Redraw it to show the flags.
1583 theApp->amuledlg->Refresh();
1584 break;
1585 #endif
1589 void CamuleApp::CheckNewVersion(uint32 result)
1591 if (result == HTTP_Success) {
1592 wxString filename = ConfigDir + wxT("last_version_check");
1593 wxTextFile file;
1595 if (!file.Open(filename)) {
1596 AddLogLineM(true, _("Failed to open the downloaded version check file") );
1597 return;
1598 } else if (!file.GetLineCount()) {
1599 AddLogLineM(true, _("Corrupted version check file"));
1600 } else {
1601 wxString versionLine = file.GetFirstLine();
1602 wxStringTokenizer tkz(versionLine, wxT("."));
1604 AddDebugLogLineM(false, logGeneral, wxString(wxT("Running: ")) + wxT(VERSION) + wxT(", Version check: ") + versionLine);
1606 long fields[] = {0, 0, 0};
1607 for (int i = 0; i < 3; ++i) {
1608 if (!tkz.HasMoreTokens()) {
1609 AddLogLineM(true, _("Corrupted version check file"));
1610 return;
1611 } else {
1612 wxString token = tkz.GetNextToken();
1614 if (!token.ToLong(&fields[i])) {
1615 AddLogLineM(true, _("Corrupted version check file"));
1616 return;
1621 long curVer = make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE);
1622 long newVer = make_full_ed2k_version(fields[0], fields[1], fields[2]);
1624 if (curVer < newVer) {
1625 AddLogLineM(true, _("You are using an outdated version of aMule!"));
1626 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]));
1627 AddLogLineM(false, _("The latest version can always be found at http://www.amule.org"));
1628 #ifdef AMULE_DAEMON
1629 AddLogLineMS(true, CFormat(_("WARNING: Your aMuled version is outdated: %i.%i.%i < %li.%li.%li"))
1630 % VERSION_MJR % VERSION_MIN % VERSION_UPDATE % fields[0] % fields[1] % fields[2]);
1631 #endif
1632 } else {
1633 AddLogLineM(false, _("Your copy of aMule is up to date."));
1637 file.Close();
1638 wxRemoveFile(filename);
1639 } else {
1640 AddLogLineM(true, _("Failed to download the version check file"));
1646 bool CamuleApp::IsConnected() const
1648 return (IsConnectedED2K() || IsConnectedKad());
1652 bool CamuleApp::IsConnectedED2K() const
1654 return serverconnect && serverconnect->IsConnected();
1658 bool CamuleApp::IsConnectedKad() const
1660 return Kademlia::CKademlia::IsConnected();
1664 bool CamuleApp::IsFirewalled() const
1666 if (theApp->IsConnectedED2K() && !theApp->serverconnect->IsLowID()) {
1667 return false; // we have an eD2K HighID -> not firewalled
1670 return IsFirewalledKad(); // If kad says ok, it's ok.
1673 bool CamuleApp::IsFirewalledKad() const
1675 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1676 || Kademlia::CKademlia::IsFirewalled();
1679 bool CamuleApp::IsFirewalledKadUDP() const
1681 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1682 || Kademlia::CUDPFirewallTester::IsFirewalledUDP(true);
1685 bool CamuleApp::IsKadRunning() const
1687 return Kademlia::CKademlia::IsRunning();
1690 // Kad stats
1691 uint32 CamuleApp::GetKadUsers() const
1693 return Kademlia::CKademlia::GetKademliaUsers();
1696 uint32 CamuleApp::GetKadFiles() const
1698 return Kademlia::CKademlia::GetKademliaFiles();
1701 uint32 CamuleApp::GetKadIndexedSources() const
1703 return Kademlia::CKademlia::GetIndexed()->m_totalIndexSource;
1706 uint32 CamuleApp::GetKadIndexedKeywords() const
1708 return Kademlia::CKademlia::GetIndexed()->m_totalIndexKeyword;
1711 uint32 CamuleApp::GetKadIndexedNotes() const
1713 return Kademlia::CKademlia::GetIndexed()->m_totalIndexNotes;
1716 uint32 CamuleApp::GetKadIndexedLoad() const
1718 return Kademlia::CKademlia::GetIndexed()->m_totalIndexLoad;
1722 // True IP of machine
1723 uint32 CamuleApp::GetKadIPAdress() const
1725 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetPrefs()->GetIPAddress());
1728 // Buddy status
1729 uint8 CamuleApp::GetBuddyStatus() const
1731 return clientlist->GetBuddyStatus();
1734 uint32 CamuleApp::GetBuddyIP() const
1736 return clientlist->GetBuddy()->GetIP();
1739 uint32 CamuleApp::GetBuddyPort() const
1741 return clientlist->GetBuddy()->GetUDPPort();
1744 bool CamuleApp::CanDoCallback(CUpDownClient *client)
1746 if (Kademlia::CKademlia::IsConnected()) {
1747 if (IsConnectedED2K()) {
1748 if (serverconnect->IsLowID()) {
1749 if (Kademlia::CKademlia::IsFirewalled()) {
1750 //Both Connected - Both Firewalled
1751 return false;
1752 } else {
1753 if (client->GetServerIP() == theApp->serverconnect->GetCurrentServer()->GetIP() &&
1754 client->GetServerPort() == theApp->serverconnect->GetCurrentServer()->GetPort()) {
1755 // Both Connected - Server lowID, Kad Open - Client on same server
1756 // We prevent a callback to the server as this breaks the protocol
1757 // and will get you banned.
1758 return false;
1759 } else {
1760 // Both Connected - Server lowID, Kad Open - Client on remote server
1761 return true;
1764 } else {
1765 //Both Connected - Server HighID, Kad don't care
1766 return true;
1768 } else {
1769 if (Kademlia::CKademlia::IsFirewalled()) {
1770 //Only Kad Connected - Kad Firewalled
1771 return false;
1772 } else {
1773 //Only Kad Conected - Kad Open
1774 return true;
1777 } else {
1778 if (IsConnectedED2K()) {
1779 if (serverconnect->IsLowID()) {
1780 //Only Server Connected - Server LowID
1781 return false;
1782 } else {
1783 //Only Server Connected - Server HighID
1784 return true;
1786 } else {
1787 //We are not connected at all!
1788 return false;
1793 void CamuleApp::ShowUserCount() {
1794 uint32 totaluser = 0, totalfile = 0;
1796 theApp->serverlist->GetUserFileStatus( totaluser, totalfile );
1798 wxString buffer;
1800 static const wxString s_singlenetstatusformat = _("Users: %s | Files: %s");
1801 static const wxString s_bothnetstatusformat = _("Users: E: %s K: %s | Files: E: %s K: %s");
1803 if (thePrefs::GetNetworkED2K() && thePrefs::GetNetworkKademlia()) {
1804 buffer = CFormat(s_bothnetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(totalfile) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1805 } else if (thePrefs::GetNetworkED2K()) {
1806 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(totalfile);
1807 } else if (thePrefs::GetNetworkKademlia()) {
1808 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1809 } else {
1810 buffer = _("No networks selected");
1813 Notify_ShowUserCount(buffer);
1817 void CamuleApp::ListenSocketHandler(wxSocketEvent& event)
1819 wxCHECK_RET(listensocket, wxT("Connection-event for NULL'd listen-socket"));
1820 wxCHECK_RET(event.GetSocketEvent() == wxSOCKET_CONNECTION,
1821 wxT("Invalid event received for listen-socket"));
1823 if (m_app_state == APP_STATE_RUNNING) {
1824 listensocket->OnAccept(0);
1825 } else if (m_app_state == APP_STATE_STARTING) {
1826 // When starting up, connection may be made before we are able
1827 // to handle them. However, if these are ignored, no futher
1828 // connection-events will be triggered, so we have to accept it.
1829 wxSocketBase* socket = listensocket->Accept(false);
1831 wxCHECK_RET(socket, wxT("NULL returned by Accept() during startup"));
1833 socket->Destroy();
1838 void CamuleApp::ShowConnectionState()
1840 static uint8 old_state = (1<<7); // This flag doesn't exist
1842 uint8 state = 0;
1844 if (theApp->serverconnect->IsConnected()) {
1845 state |= CONNECTED_ED2K;
1848 if (Kademlia::CKademlia::IsRunning()) {
1849 if (Kademlia::CKademlia::IsConnected()) {
1850 if (!Kademlia::CKademlia::IsFirewalled()) {
1851 state |= CONNECTED_KAD_OK;
1852 } else {
1853 state |= CONNECTED_KAD_FIREWALLED;
1855 } else {
1856 state |= CONNECTED_KAD_NOT;
1860 Notify_ShowConnState(state);
1862 if (old_state != state) {
1863 // Get the changed value
1864 int changed_flags = old_state ^ state;
1866 if (changed_flags & CONNECTED_ED2K) {
1867 // ED2K status changed
1868 wxString connected_server;
1869 CServer* ed2k_server = theApp->serverconnect->GetCurrentServer();
1870 if (ed2k_server) {
1871 connected_server = ed2k_server->GetListName();
1873 if (state & CONNECTED_ED2K) {
1874 // We connected to some server
1875 const wxString id = theApp->serverconnect->IsLowID() ? _("with LowID") : _("with HighID");
1877 AddLogLineM(true, CFormat(_("Connected to %s %s")) % connected_server % id);
1878 } else {
1879 if ( theApp->serverconnect->IsConnecting() ) {
1880 AddLogLineM(true, CFormat(_("Connecting to %s")) % connected_server);
1881 } else {
1882 AddLogLineM(true, _("Disconnected from eD2k"));
1887 if (changed_flags & CONNECTED_KAD_NOT) {
1888 if (state & CONNECTED_KAD_NOT) {
1889 AddLogLineM(true, _("Kad started."));
1890 } else {
1891 AddLogLineM(true, _("Kad stopped."));
1895 if (changed_flags & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1896 if (state & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1897 if (state & CONNECTED_KAD_OK) {
1898 AddLogLineM(true, _("Connected to Kad (ok)"));
1899 } else {
1900 AddLogLineM(true, _("Connected to Kad (firewalled)"));
1902 } else {
1903 AddLogLineM(true, _("Disconnected from Kad"));
1907 old_state = state;
1909 theApp->downloadqueue->OnConnectionState(IsConnected());
1912 ShowUserCount();
1913 Notify_ShowConnState(state);
1917 void CamuleApp::UDPSocketHandler(wxSocketEvent& event)
1919 CMuleUDPSocket* socket = (CMuleUDPSocket*)(event.GetClientData());
1920 wxCHECK_RET(socket, wxT("No socket owner specified."));
1922 if (IsOnShutDown() || thePrefs::IsUDPDisabled()) return;
1924 if (!IsRunning()) {
1925 if (event.GetSocketEvent() == wxSOCKET_INPUT) {
1926 // Back to the queue!
1927 theApp->AddPendingEvent(event);
1928 return;
1932 switch (event.GetSocketEvent()) {
1933 case wxSOCKET_INPUT:
1934 socket->OnReceive(0);
1935 break;
1937 case wxSOCKET_OUTPUT:
1938 socket->OnSend(0);
1939 break;
1941 case wxSOCKET_LOST:
1942 socket->OnDisconnected(0);
1943 break;
1945 default:
1946 wxFAIL;
1947 break;
1952 void CamuleApp::OnUnhandledException()
1954 // Call the generic exception-handler.
1955 fprintf(stderr, "\taMule Version: %s\n", (const char*)unicode2char(GetFullMuleVersion()));
1956 ::OnUnhandledException();
1959 void CamuleApp::StartKad()
1961 if (!Kademlia::CKademlia::IsRunning() && thePrefs::GetNetworkKademlia()) {
1962 // Kad makes no sense without the Client-UDP socket.
1963 if (!thePrefs::IsUDPDisabled()) {
1964 if (ipfilter->IsReady()) {
1965 Kademlia::CKademlia::Start();
1966 } else {
1967 ipfilter->StartKADWhenReady();
1969 } else {
1970 AddLogLineM(true,_("Kad network cannot be used if UDP port is disabled on preferences, not starting."));
1972 } else if (!thePrefs::GetNetworkKademlia()) {
1973 AddLogLineM(true,_("Kad network disabled on preferences, not connecting."));
1977 void CamuleApp::StopKad()
1979 // Stop Kad if it's running
1980 if (Kademlia::CKademlia::IsRunning()) {
1981 Kademlia::CKademlia::Stop();
1986 void CamuleApp::BootstrapKad(uint32 ip, uint16 port)
1988 if (!Kademlia::CKademlia::IsRunning()) {
1989 Kademlia::CKademlia::Start();
1990 theApp->ShowConnectionState();
1993 Kademlia::CKademlia::Bootstrap(ip, port, true);
1997 void CamuleApp::UpdateNotesDat(const wxString& url)
1999 wxString strTempFilename(theApp->ConfigDir + wxT("nodes.dat.download"));
2001 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(url, strTempFilename, theApp->ConfigDir + wxT("nodes.dat"), HTTP_NodesDat);
2002 downloader->Create();
2003 downloader->Run();
2007 void CamuleApp::DisconnectED2K()
2009 // Stop ED2K if it's running
2010 if (IsConnectedED2K()) {
2011 serverconnect->Disconnect();
2015 bool CamuleApp::CryptoAvailable() const
2017 return clientcredits && clientcredits->CryptoAvailable();
2020 uint32 CamuleApp::GetED2KID() const {
2021 return serverconnect ? serverconnect->GetClientID() : 0;
2024 uint32 CamuleApp::GetID() const {
2025 uint32 ID;
2027 if( Kademlia::CKademlia::IsConnected() && !Kademlia::CKademlia::IsFirewalled() ) {
2028 // We trust Kad above ED2K
2029 ID = ENDIAN_NTOHL(Kademlia::CKademlia::GetIPAddress());
2030 } else if( theApp->serverconnect->IsConnected() ) {
2031 ID = theApp->serverconnect->GetClientID();
2032 } else if ( Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::IsFirewalled() ) {
2033 // A firewalled Kad client get's a "1"
2034 ID = 1;
2035 } else {
2036 ID = 0;
2039 return ID;
2042 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD)
2043 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SOURCE_DNS_DONE)
2044 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_UDP_DNS_DONE)
2045 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SERVER_DNS_DONE)
2046 // File_checked_for_headers