1// 2// This file is part of the aMule Project. 3// 4// Copyright (c) 2005-2011 aMule Team ( admin@amule.org / http://www.amule.org ) 5// 6// Any parts of this program derived from the xMule, lMule or eMule project, 7// or contributed by third-party developers are copyrighted by their 8// respective authors. 9// 10// This program is free software; you can redistribute it and/or modify 11// it under the terms of the GNU General Public License as published by 12// the Free Software Foundation; either version 2 of the License, or 13// (at your option) any later version. 14// 15// This program is distributed in the hope that it will be useful, 16// but WITHOUT ANY WARRANTY; without even the implied warranty of 17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18// GNU General Public License for more details. 19// 20// You should have received a copy of the GNU General Public License 21// along with this program; if not, write to the Free Software 22// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 23// 24 25#ifndef AMULE_REMOTE_GUI_H 26#define AMULE_REMOTE_GUI_H 27 28 29#include <ec/cpp/RemoteConnect.h> // Needed for CRemoteConnect 30 31 32#include "Statistics.h" 33#include "Preferences.h" 34#include "Statistics.h" 35#include "RLE.h" 36#include "SearchList.h" // Needed for CSearchFile 37 38 39class CED2KFileLink; 40class CServer; 41class CKnownFile; 42class CSearchFile; 43class CPartFile; 44class CClientRef; 45class CStatistics; 46class CPath; 47 48class wxEvtHandler; 49class wxTimer; 50class wxTimerEvent; 51 52#include <wx/dialog.h> 53 54class CEConnectDlg : public wxDialog { 55 wxString host; 56 int port; 57 58 wxString pwd_hash; 59 wxString login, passwd; 60 bool m_save_user_pass; 61 62 DECLARE_EVENT_TABLE() 63public: 64 CEConnectDlg(); 65 66 void OnOK(wxCommandEvent& event); 67 68 wxString Host() { return host; } 69 int Port() { return port; } 70 71 wxString Login() { return login; } 72 wxString PassHash(); 73 bool SaveUserPass() { return m_save_user_pass; } 74}; 75 76DECLARE_LOCAL_EVENT_TYPE(wxEVT_EC_INIT_DONE, wxEVT_USER_FIRST + 1001) 77 78class wxECInitDoneEvent : public wxEvent { 79public: 80 wxECInitDoneEvent() : wxEvent(-1, wxEVT_EC_INIT_DONE) 81 { 82 } 83 84 wxEvent *Clone(void) const 85 { 86 return new wxECInitDoneEvent(*this); 87 } 88}; 89 90class CPreferencesRem : public CPreferences, public CECPacketHandlerBase { 91 CRemoteConnect *m_conn; 92 uint32 m_exchange_send_selected_prefs; 93 uint32 m_exchange_recv_selected_prefs; 94 95 virtual void HandlePacket(const CECPacket *packet); 96public: 97 CPreferencesRem(CRemoteConnect *); 98 99 bool CreateCategory(Category_Struct *& category, const wxString& name, const CPath& path, 100 const wxString& comment, uint32 color, uint8 prio); 101 bool UpdateCategory(uint8 cat, const wxString& name, const CPath& path, 102 const wxString& comment, uint32 color, uint8 prio); 103 104 void RemoveCat(uint8 cat); 105 106 bool LoadRemote(); 107 void SendToRemote(); 108}; 109 110// 111// T - type if item in container 112// I - type of id of item 113// G - type of tag used to create/update items 114// 115template <class T, class I, class G = CECTag> 116class CRemoteContainer : public CECPacketHandlerBase { 117protected: 118 enum { 119 IDLE, // no request in the air 120 STATUS_REQ_SENT, // sent request for item status 121 FULL_REQ_SENT // sent request for full info 122 } m_state; 123 124 CRemoteConnect *m_conn; 125 126 std::list<T *> m_items; 127 std::map<I, T *> m_items_hash; 128 129 // .size() is O(N) operation in stl 130 int m_item_count; 131 132 // use incremental tags algorithm 133 bool m_inc_tags; 134 135 // command that will be used in full request 136 int m_full_req_cmd, m_full_req_tag; 137 138 virtual void HandlePacket(const CECPacket *packet) 139 { 140 switch(this->m_state) { 141 case IDLE: wxFAIL; // not expecting anything 142 case STATUS_REQ_SENT: 143 // if derived class choose not to proceed, return - but with good status 144 this->m_state = IDLE; 145 if ( this->Phase1Done(packet) ) { 146 if (this->m_inc_tags) { 147 // Incremental tags: new items always carry full info. 148 ProcessUpdate(packet, NULL, m_full_req_tag); 149 } else { 150 // Non-incremental tags: we might get partial info on new items. 151 // Collect all this items in a tag, and then request full info about them. 152 CECPacket req_full(this->m_full_req_cmd); 153 154 ProcessUpdate(packet, &req_full, m_full_req_tag); 155 156 // Phase 3: request full info about files we don't have yet 157 if ( req_full.HasChildTags() ) { 158 m_conn->SendRequest(this, &req_full); 159 this->m_state = FULL_REQ_SENT; 160 } 161 } 162 } 163 break; 164 case FULL_REQ_SENT: 165 ProcessFull(packet); 166 m_state = IDLE; 167 break; 168 } 169 } 170public: 171 CRemoteContainer(CRemoteConnect *conn, bool inc_tags) 172 { 173 m_state = IDLE; 174 175 m_conn = conn; 176 m_item_count = 0; 177 m_inc_tags = inc_tags; 178 } 179 180 virtual ~CRemoteContainer() 181 { 182 } 183 184 typedef typename std::list<T *>::iterator iterator; 185 iterator begin() { return m_items.begin(); } 186 iterator end() { return m_items.end(); } 187 188 uint32 GetCount() 189 { 190 return m_item_count; 191 } 192 193 void AddItem(T *item) 194 { 195 m_items.push_back(item); 196 m_items_hash[GetItemID(item)] = item; 197 m_item_count++; 198 } 199 200 T *GetByID(I id) 201 { 202 // avoid creating nodes 203 return m_items_hash.count(id) ? m_items_hash[id] : NULL; 204 } 205 206 void Flush() 207 { 208 m_items.clear(); 209 m_items_hash.clear(); 210 m_item_count = 0; 211 } 212 213 // 214 // Flush & reload 215 // 216 /* 217 We usually don't keep outdated code as comments, but this blocking implementation 218 shows the overall procedure well. It had to be scattered for the event driven implementation. 219 220 bool FullReload(int cmd) 221 { 222 CECPacket req(cmd); 223 CScopedPtr<const CECPacket> reply(this->m_conn->SendRecvPacket(&req)); 224 if ( !reply.get() ) { 225 return false; 226 } 227 for(typename std::list<T *>::iterator j = this->m_items.begin(); j != this->m_items.end(); j++) { 228 this->DeleteItem(*j); 229 } 230 231 Flush(); 232 233 ProcessFull(reply.get()); 234 235 return true; 236 } 237 */ 238 void FullReload(int cmd) 239 { 240 if ( this->m_state != IDLE ) { 241 return; 242 } 243 244 for(typename std::list<T *>::iterator j = this->m_items.begin(); j != this->m_items.end(); j++) { 245 this->DeleteItem(*j); 246 } 247 248 Flush(); 249 250 CECPacket req(cmd); 251 this->m_conn->SendRequest(this, &req); 252 this->m_state = FULL_REQ_SENT; 253 this->m_full_req_cmd = cmd; 254 } 255 256 // 257 // Following are like basicly same code as in webserver. Eventually it must 258 // be same class 259 // 260 void DoRequery(int cmd, int tag) 261 { 262 if ( this->m_state != IDLE ) { 263 return; 264 } 265 CECPacket req_sts(cmd, m_inc_tags ? EC_DETAIL_INC_UPDATE : EC_DETAIL_UPDATE); 266 this->m_conn->SendRequest(this, &req_sts); 267 this->m_state = STATUS_REQ_SENT; 268 this->m_full_req_cmd = cmd; 269 this->m_full_req_tag = tag; 270 } 271 /* 272 We usually don't keep outdated code as comments, but this blocking implementation 273 shows the overall procedure well. It had to be scattered for the event driven implementation. 274 275 bool DoRequery(int cmd, int tag) 276 { 277 CECPacket req_sts(cmd, m_inc_tags ? EC_DETAIL_INC_UPDATE : EC_DETAIL_UPDATE); 278 279 // 280 // Phase 1: request status 281 CScopedPtr<const CECPacket> reply(this->m_conn->SendRecvPacket(&req_sts)); 282 if ( !reply.get() ) { 283 return false; 284 } 285 286 if ( !this->Phase1Done(reply.get()) ) { 287 // if derived class choose not to proceed, return - but with good status 288 return true; 289 } 290 // 291 // Phase 2: update status, mark new files for subsequent query 292 CECPacket req_full(cmd); 293 294 ProcessUpdate(reply.get(), &req_full, tag); 295 296 reply.reset(); 297 298 if ( !m_inc_tags ) { 299 // Phase 3: request full info about files we don't have yet 300 if ( req_full.GetTagCount() ) { 301 reply.reset(this->m_conn->SendRecvPacket(&req_full)); 302 if ( !reply.get() ) { 303 return false; 304 } 305 ProcessFull(reply.get()); 306 } 307 } 308 return true; 309 } 310 */ 311 312 void ProcessFull(const CECPacket *reply) 313 { 314 for (CECPacket::const_iterator it = reply->begin(); it != reply->end(); it++) { 315 G *tag = (G *) & *it; 316 // initialize item data from EC tag 317 AddItem(CreateItem(tag)); 318 } 319 } 320 321 void RemoveItem(iterator & it) 322 { 323 I item_id = GetItemID(*it); 324 // reduce count 325 m_item_count--; 326 // remove from map 327 m_items_hash.erase(item_id); 328 // item may contain data that need to be freed externally, before 329 // dtor is called and memory freed 330 DeleteItem(*it); 331 332 m_items.erase(it); 333 } 334 335 virtual void ProcessUpdate(const CECTag *reply, CECPacket *full_req, int req_type) 336 { 337 std::set<I> core_files; 338 for (CECPacket::const_iterator it = reply->begin(); it != reply->end(); it++) { 339 G *tag = (G *) & *it; 340 if ( tag->GetTagName() != req_type ) { 341 continue; 342 } 343 344 core_files.insert(tag->ID()); 345 if ( m_items_hash.count(tag->ID()) ) { 346 // Item already known: update it 347 T *item = m_items_hash[tag->ID()]; 348 ProcessItemUpdate(tag, item); 349 } else { 350 // New item 351 if (full_req) { 352 // Non-incremental mode: we have only partial info 353 // so we need to request full info before we can use the item 354 full_req->AddTag(CECTag(req_type, tag->ID())); 355 } else { 356 // Incremental mode: new items always carry full info, 357 // so we can add it right away 358 AddItem(CreateItem(tag)); 359 } 360 } 361 } 362 for(iterator it = begin(); it != end();) { 363 iterator it2 = it++; 364 if ( core_files.count(GetItemID(*it2)) == 0 ) { 365 RemoveItem(it2); 366 } 367 } 368 } 369 370 virtual T *CreateItem(G *) 371 { 372 return 0; 373 } 374 virtual void DeleteItem(T *) 375 { 376 } 377 virtual I GetItemID(T *) 378 { 379 return I(); 380 } 381 virtual void ProcessItemUpdate(G *, T *) 382 { 383 } 384 385 virtual bool Phase1Done(const CECPacket *) 386 { 387 return true; 388 } 389}; 390 391class CServerConnectRem { 392 CRemoteConnect *m_Conn; 393 uint32 m_ID; 394 395 CServer *m_CurrServer; 396 397public: 398 void HandlePacket(const CECPacket *packet); 399 400 CServerConnectRem(CRemoteConnect *); 401 402 bool IsConnected() { return (m_ID != 0) && (m_ID != 0xffffffff); } 403 bool IsConnecting() { return m_ID == 0xffffffff; } 404 bool IsLowID() { return m_ID < 16777216; } 405 uint32 GetClientID() { return m_ID; } 406 CServer *GetCurrentServer() { return m_CurrServer; } 407 408 // 409 // Actions 410 // 411 void ConnectToServer(CServer* server); 412 void ConnectToAnyServer(); 413 void StopConnectionTry(); 414 void Disconnect(); 415}; 416 417class CServerListRem : public CRemoteContainer<CServer, uint32, CEC_Server_Tag> { 418 uint32 m_TotalUser, m_TotalFile; 419 420 virtual void HandlePacket(const CECPacket *packet); 421public: 422 CServerListRem(CRemoteConnect *); 423 void GetUserFileStatus(uint32 &total_user, uint32 &total_file) 424 { 425 total_user = m_TotalUser; 426 total_file = m_TotalFile; 427 } 428 429 void UpdateUserFileStatus(CServer *server); 430 431 CServer* GetServerByAddress(const wxString& address, uint16 port) const; 432 CServer* GetServerByIPTCP(uint32 nIP, uint16 nPort) const; 433 434 // 435 // Actions 436 // 437 void RemoveServer(CServer* server); 438 void UpdateServerMetFromURL(wxString url); 439 void SetStaticServer(CServer* server, bool isStatic); 440 void SetServerPrio(CServer* server, uint32 prio); 441 void SaveServerMet() {} // not needed here 442 void FilterServers() {} // not needed here 443 444 // 445 // template 446 // 447 CServer *CreateItem(CEC_Server_Tag *); 448 void DeleteItem(CServer *); 449 uint32 GetItemID(CServer *); 450 void ProcessItemUpdate(CEC_Server_Tag *, CServer *); 451}; 452 453class CUpDownClientListRem : public CRemoteContainer<CClientRef, uint32, CEC_UpDownClient_Tag> { 454public: 455 CUpDownClientListRem(CRemoteConnect *); 456 457 void FilterQueues() {} // not needed here 458 // 459 // template 460 // 461 CClientRef *CreateItem(CEC_UpDownClient_Tag *); 462 void DeleteItem(CClientRef *); 463 uint32 GetItemID(CClientRef *); 464 void ProcessItemUpdate(CEC_UpDownClient_Tag *, CClientRef *); 465}; 466 467class CDownQueueRem : public std::map<uint32, CPartFile*> { 468 CRemoteConnect *m_conn; 469public: 470 CDownQueueRem(CRemoteConnect * conn) { m_conn = conn; } 471 472 CPartFile* GetFileByID(uint32 id); 473 474 // 475 // User actions 476 // 477 void Prio(CPartFile *file, uint8 prio); 478 void AutoPrio(CPartFile *file, bool flag); 479 void Category(CPartFile *file, uint8 cat); 480 481 void SendFileCommand(CPartFile *file, ec_tagname_t cmd); 482 // 483 // Actions 484 // 485 void StopUDPRequests() {} 486 void AddFileLinkToDownload(CED2KFileLink*, uint8); 487 bool AddLink(const wxString &link, uint8 category = 0); 488 void UnsetCompletedFilesExist(); 489 void ResetCatParts(int cat); 490 void AddSearchToDownload(CSearchFile* toadd, uint8 category); 491 void ClearCompleted(const ListOfUInts32 & ecids); 492}; 493 494class CSharedFilesRem : public std::map<uint32, CKnownFile*> { 495 CRemoteConnect *m_conn; 496public: 497 CSharedFilesRem(CRemoteConnect * conn); 498 499 CKnownFile *GetFileByID(uint32 id); 500 501 void SetFilePrio(CKnownFile *file, uint8 prio); 502 503 // 504 // Actions 505 // 506 void Reload(bool sendtoserver = true, bool firstload = false); 507 bool RenameFile(CKnownFile* file, const CPath& newName); 508 void SetFileCommentRating(CKnownFile* file, const wxString& newComment, int8 newRating); 509 void CopyFileList(std::vector<CKnownFile*>& out_list) const; 510}; 511 512class CKnownFilesRem : public CRemoteContainer<CKnownFile, uint32, CEC_SharedFile_Tag> { 513 CKnownFile * CreateKnownFile(CEC_SharedFile_Tag *tag, CKnownFile *file = NULL); 514 CPartFile * CreatePartFile(CEC_PartFile_Tag *tag); 515 516 bool m_initialUpdate; // improved handling for first data transfer 517public: 518 CKnownFilesRem(CRemoteConnect * conn); 519 520 CKnownFile *FindKnownFileByID(uint32 id) { return GetByID(id); } 521 522 uint16 requested; 523 uint32 transferred; 524 uint16 accepted; 525 526 // 527 // template 528 // 529 CKnownFile *CreateItem(CEC_SharedFile_Tag *) { wxFAIL; return NULL; } // unused, required by template 530 void DeleteItem(CKnownFile *); 531 uint32 GetItemID(CKnownFile *); 532 void ProcessItemUpdate(CEC_SharedFile_Tag *, CKnownFile *); 533 bool Phase1Done(const CECPacket *) { return true; } 534 void ProcessUpdate(const CECTag *reply, CECPacket *full_req, int req_type); 535 536 void ProcessItemUpdatePartfile(CEC_PartFile_Tag *, CPartFile *); 537}; 538 539class CIPFilterRem { 540 CRemoteConnect *m_conn; 541public: 542 CIPFilterRem(CRemoteConnect *conn); 543 544 // 545 // Actions 546 // 547 void Reload(); 548 void Update(wxString strURL = wxEmptyString); 549 bool IsReady() const { return true; } 550}; 551 552class CSearchListRem : public CRemoteContainer<CSearchFile, uint32, CEC_SearchFile_Tag> { 553 virtual void HandlePacket(const CECPacket *); 554public: 555 CSearchListRem(CRemoteConnect *); 556 557 int m_curr_search; 558 typedef std::map<long, CSearchResultList> ResultMap; 559 ResultMap m_results; 560 561 const CSearchResultList& GetSearchResults(long nSearchID); 562 void RemoveResults(long nSearchID); 563 const CSearchResultList& GetSearchResults(long nSearchID) const; 564 // 565 // Actions 566 // 567 568 wxString StartNewSearch(uint32* nSearchID, SearchType search_type, 569 const CSearchList::CSearchParams& params); 570 571 void StopSearch(bool globalOnly = false); 572 573 // 574 // template 575 // 576 CSearchFile *CreateItem(CEC_SearchFile_Tag *); 577 void DeleteItem(CSearchFile *); 578 uint32 GetItemID(CSearchFile *); 579 void ProcessItemUpdate(CEC_SearchFile_Tag *, CSearchFile *); 580 bool Phase1Done(const CECPacket *); 581}; 582 583class CFriendListRem : public CRemoteContainer<CFriend, uint32, CEC_Friend_Tag> { 584 virtual void HandlePacket(const CECPacket *); 585public: 586 CFriendListRem(CRemoteConnect *); 587 588 void AddFriend(const CClientRef& toadd); 589 void AddFriend(const CMD4Hash& userhash, uint32 lastUsedIP, uint32 lastUsedPort, const wxString& name); 590 void RemoveFriend(CFriend* toremove); 591 void RequestSharedFileList(CFriend* Friend); 592 void RequestSharedFileList(CClientRef& client); 593 void SetFriendSlot(CFriend* Friend, bool new_state); 594 595 // 596 // template 597 // 598 CFriend *CreateItem(CEC_Friend_Tag *); 599 void DeleteItem(CFriend *); 600 uint32 GetItemID(CFriend *); 601 void ProcessItemUpdate(CEC_Friend_Tag *, CFriend *); 602}; 603 604class CStatsUpdaterRem : public CECPacketHandlerBase { 605 virtual void HandlePacket(const CECPacket *); 606public: 607 CStatsUpdaterRem() {} 608}; 609 610class CStatTreeRem : public CECPacketHandlerBase { 611 virtual void HandlePacket(const CECPacket *); 612 CRemoteConnect *m_conn; 613public: 614 CStatTreeRem(CRemoteConnect * conn) { m_conn = conn; } 615 void DoRequery(); 616}; 617 618class CListenSocketRem { 619 uint32 m_peak_connections; 620public: 621 uint32 GetPeakConnections() { return m_peak_connections; } 622}; 623 624class CamuleRemoteGuiApp : public wxApp, public CamuleGuiBase, public CamuleAppCommon { 625 wxTimer* poll_timer; 626 627 virtual int InitGui(bool geometry_enable, wxString &geometry_string); 628 629 bool OnInit(); 630 631 int OnExit(); 632 633 void OnPollTimer(wxTimerEvent& evt); 634 635 void OnECConnection(wxEvent& event); 636 void OnECInitDone(wxEvent& event); 637 void OnNotifyEvent(CMuleGUIEvent& evt); 638 void OnFinishedHTTPDownload(CMuleInternalEvent& event); 639 640 CStatsUpdaterRem m_stats_updater; 641public: 642 643 void Startup(); 644 645 bool ShowConnectionDialog(); 646 647 class CRemoteConnect *m_connect; 648 649 CEConnectDlg *dialog; 650 651 bool CopyTextToClipboard(wxString strText); 652 653 virtual int ShowAlert(wxString msg, wxString title, int flags); 654 655 void ShutDown(wxCloseEvent &evt); 656 657 CPreferencesRem *glob_prefs; 658 659 // 660 // Provide access to core data thru EC 661 CServerConnectRem *serverconnect; 662 CServerListRem *serverlist; 663 CDownQueueRem *downloadqueue; 664 CSharedFilesRem *sharedfiles; 665 CKnownFilesRem *knownfiles; 666 CUpDownClientListRem *clientlist; 667 CIPFilterRem *ipfilter; 668 CSearchListRem *searchlist; 669 CFriendListRem *friendlist; 670 CListenSocketRem *listensocket; 671 CStatTreeRem * stattree; 672 673 CStatistics *m_statistics; 674 675 bool AddServer(CServer *srv, bool fromUser = false); 676 677 uint32 GetPublicIP(); 678 679 wxString GetLog(bool reset = false); 680 wxString GetServerLog(bool reset = false); 681 682 void AddServerMessageLine(wxString &msg); 683 void AddRemoteLogLine(const wxString& line); 684 685 void SetOSFiles(wxString ) { /* onlinesig is created on remote side */ } 686 687 bool IsConnected() const { return IsConnectedED2K() || IsConnectedKad(); } 688 bool IsFirewalled() const; 689 bool IsConnectedED2K() const; 690 bool IsConnectedKad() const 691 { 692 return ((m_ConnState & CONNECTED_KAD_OK) 693 || (m_ConnState & CONNECTED_KAD_FIREWALLED)); 694 } 695 bool IsFirewalledKad() const { return (m_ConnState & CONNECTED_KAD_FIREWALLED) != 0; } 696 697 bool IsKadRunning() const { return ((m_ConnState & CONNECTED_KAD_OK) 698 || (m_ConnState & CONNECTED_KAD_FIREWALLED) 699 || (m_ConnState & CONNECTED_KAD_NOT)); } 700 701 // Check Kad state (UDP) 702 bool IsFirewalledKadUDP() const { return theStats::IsFirewalledKadUDP(); } 703 bool IsKadRunningInLanMode() const { return theStats::IsKadRunningInLanMode(); } 704 // Kad stats 705 uint32 GetKadUsers() const { return theStats::GetKadUsers(); } 706 uint32 GetKadFiles() const { return theStats::GetKadFiles(); } 707 uint32 GetKadIndexedSources() const { return theStats::GetKadIndexedSources(); } 708 uint32 GetKadIndexedKeywords() const{ return theStats::GetKadIndexedKeywords(); } 709 uint32 GetKadIndexedNotes() const { return theStats::GetKadIndexedNotes(); } 710 uint32 GetKadIndexedLoad() const { return theStats::GetKadIndexedLoad(); } 711 // True IP of machine 712 uint32 GetKadIPAdress() const { return theStats::GetKadIPAdress(); } 713 // Buddy status 714 uint8 GetBuddyStatus() const { return theStats::GetBuddyStatus(); } 715 uint32 GetBuddyIP() const { return theStats::GetBuddyIP(); } 716 uint32 GetBuddyPort() const { return theStats::GetBuddyPort(); } 717 718 void StartKad(); 719 void StopKad(); 720 721 /** Bootstraps kad from the specified IP (must be in hostorder). */ 722 void BootstrapKad(uint32 ip, uint16 port); 723 /** Updates the nodes.dat file from the specified url. */ 724 void UpdateNotesDat(const wxString& str); 725 726 void DisconnectED2K(); 727 728 bool CryptoAvailable() const; 729 730 uint32 GetED2KID() const; 731 uint32 GetID() const; 732 void ShowUserCount(); 733 734 uint8 m_ConnState; 735 uint32 m_clientID; 736 737 wxLocale m_locale; 738 // This KnownFile collects all currently uploading clients for display in the upload list control 739 CKnownFile * m_allUploadingKnownFile; 740 741 DECLARE_EVENT_TABLE() 742}; 743 744DECLARE_APP(CamuleRemoteGuiApp) 745 746extern CamuleRemoteGuiApp *theApp; 747 748#endif /* AMULE_REMOTE_GUI_H */ 749 750// File_checked_for_headers 751