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