2 // This file is part of the aMule Project.
4 // Copyright (c) 2004-2008 aMule Team ( admin@amule.org / http://www.amule.org )
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
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.
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.
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
25 #include "ExternalConnector.h"
28 #include "config.h" // Needed for VERSION and readline detection
31 #include <common/ClientVersion.h>
33 #include <common/Format.h> // Needed for CFormat
35 #include <wx/tokenzr.h> // For wxStringTokenizer
38 #ifdef HAVE_LIBREADLINE
39 #if defined(HAVE_READLINE_READLINE_H)
40 #include <readline/readline.h> // Do_not_auto_remove
41 #elif defined(HAVE_READLINE_H)
42 #include <readline.h> // Do_not_auto_remove
43 #else /* !defined(HAVE_READLINE_H) */
44 extern "C" char *readline (const char*);
45 #endif /* !defined(HAVE_READLINE_H) */
46 #else /* !defined(HAVE_READLINE_READLINE_H) */
48 #endif /* HAVE_LIBREADLINE */
51 #ifdef HAVE_READLINE_HISTORY
52 #if defined(HAVE_READLINE_HISTORY_H)
53 #include <readline/history.h> // Do_not_auto_remove
54 #elif defined(HAVE_HISTORY_H)
55 #include <history.h> // Do_not_auto_remove
56 #else /* !defined(HAVE_HISTORY_H) */
57 extern "C" void add_history (const char*);
58 #endif /* defined(HAVE_READLINE_HISTORY_H) */
61 #endif /* HAVE_READLINE_HISTORY */
64 #include <ec/cpp/ECFileConfig.h> // Needed for CECFileConfig
66 #include <common/MD5Sum.h>
68 //-------------------------------------------------------------------
70 CCommandTree::~CCommandTree()
72 for (CmdPos_t it
= m_subcommands
.begin(); it
!= m_subcommands
.end(); ++it
) {
75 m_subcommands
.clear();
79 CCommandTree
* CCommandTree::AddCommand(CCommandTree
* command
)
81 command
->m_parent
= this;
82 const wxString
& cmd
= command
->m_command
;
84 for (it
= m_subcommands
.begin(); it
!= m_subcommands
.end(); ++it
) {
85 if ((*it
)->m_command
> cmd
) {
89 m_subcommands
.insert(it
, command
);
94 int CCommandTree::FindCommandId(const wxString
& command
, wxString
& args
, wxString
& cmdstr
) const
96 wxString cmd
= command
.BeforeFirst(wxT(' ')).Lower();
97 for (CmdPosConst_t it
= m_subcommands
.begin(); it
!= m_subcommands
.end(); ++it
) {
98 if ((*it
)->m_command
.Lower() == cmd
) {
99 args
= command
.AfterFirst(wxT(' ')).Trim(false);
100 return (*it
)->FindCommandId(args
, args
, cmdstr
);
103 cmdstr
= GetFullCommand().Lower();
104 if (m_params
== CMD_PARAM_ALWAYS
&& args
.IsEmpty()) {
105 return CMD_ERR_MUST_HAVE_PARAM
;
106 } else if (m_params
== CMD_PARAM_NEVER
&& !args
.IsEmpty()) {
107 return CMD_ERR_NO_PARAM
;
109 if ((m_cmd_id
>= 0) && (m_cmd_id
& CMD_DEPRECATED
)) {
110 m_app
.Show(wxT('\n') + m_verbose
+ wxT('\n'));
111 return m_cmd_id
& ~CMD_DEPRECATED
;
119 wxString
CCommandTree::GetFullCommand() const
121 wxString cmd
= m_command
;
123 const CCommandTree
*parent
= m_parent
;
124 while (parent
&& parent
->m_parent
) {
125 cmd
= parent
->m_command
+ wxT(" ") + cmd
;
126 parent
= parent
->m_parent
;
133 void CCommandTree::PrintHelpFor(const wxString
& command
) const
135 wxString cmd
= command
.BeforeFirst(wxT(' ')).Lower();
136 if (!cmd
.IsEmpty()) {
137 for (CmdPosConst_t it
= m_subcommands
.begin(); it
!= m_subcommands
.end(); ++it
) {
138 if ((*it
)->m_command
.Lower() == cmd
) {
139 (*it
)->PrintHelpFor(command
.AfterFirst(wxT(' ')).Trim(false));
144 m_app
.Show(CFormat(_("Unknown extension '%s' for the '%s' command.\n")) % command
% GetFullCommand());
146 m_app
.Show(CFormat(_("Unknown command '%s'.\n")) % command
);
149 wxString fullcmd
= GetFullCommand();
150 if (!fullcmd
.IsEmpty()) {
151 m_app
.Show(fullcmd
.Upper() + wxT(": ") + wxGetTranslation(m_short
) + wxT("\n"));
152 if (!m_verbose
.IsEmpty()) {
153 m_app
.Show(wxT("\n"));
154 m_app
.Show(wxGetTranslation(m_verbose
));
157 if (m_params
== CMD_PARAM_NEVER
) {
158 m_app
.Show(_("\nThis command cannot have an argument.\n"));
159 } else if (m_params
== CMD_PARAM_ALWAYS
) {
160 m_app
.Show(_("\nThis command must have an argument.\n"));
162 if (m_cmd_id
== CMD_ERR_INCOMPLETE
) {
163 m_app
.Show(_("\nThis command is incomplete, you must use one of the extensions below.\n"));
165 if (!m_subcommands
.empty()) {
169 m_app
.Show(_("\nAvailable extensions:\n"));
171 m_app
.Show(_("Available commands:\n"));
173 for (it
= m_subcommands
.begin(); it
!= m_subcommands
.end(); ++it
) {
174 if (!((*it
)->m_cmd_id
>= 0 && (*it
)->m_cmd_id
& CMD_DEPRECATED
) || m_parent
) {
175 int len
= (*it
)->m_command
.Length();
182 for (it
= m_subcommands
.begin(); it
!= m_subcommands
.end(); ++it
) {
183 if (!((*it
)->m_cmd_id
>= 0 && (*it
)->m_cmd_id
& CMD_DEPRECATED
) || m_parent
) {
184 m_app
.Show((*it
)->m_command
+ wxString(wxT(' '), maxlen
- (*it
)->m_command
.Length()) + wxGetTranslation((*it
)->m_short
) + wxT("\n"));
188 m_app
.Show(CFormat(_("\nAll commands are case insensitive.\nType '%s <command>' to get detailed info on <command>.\n")) % wxT("help"));
192 m_app
.Show(wxT("\n"));
195 //-------------------------------------------------------------------
197 CaMuleExternalConnector::CaMuleExternalConnector()
204 m_NeedsConfigSave
= false;
207 SetAppName(wxT("aMule"));
210 CaMuleExternalConnector::~CaMuleExternalConnector()
215 void CaMuleExternalConnector::OnInitCommandSet()
217 m_commands
.AddCommand(wxT("Quit"), CMD_ID_QUIT
, wxTRANSLATE("Exits from the application."), wxEmptyString
);
218 m_commands
.AddCommand(wxT("Exit"), CMD_ID_QUIT
, wxTRANSLATE("Exits from the application."), wxEmptyString
);
219 m_commands
.AddCommand(wxT("Help"), CMD_ID_HELP
, wxTRANSLATE("Show help."),
221 Do not translate the word 'help', it is a command to the program! */
222 wxTRANSLATE("To get help on a command, type 'help <command>'.\nTo get the full command list type 'help'.\n"));
225 void CaMuleExternalConnector::Show(const wxString
&s
)
228 printf("%s", (const char *)unicode2char(s
));
235 void CaMuleExternalConnector::ShowGreet()
237 wxString text
= GetGreetingTitle();
238 int len
= text
.Length();
239 Show(wxT('\n') + wxString(wxT('-'), 22 + len
) + wxT('\n'));
240 Show(wxT('|') + wxString(wxT(' '), 10) + text
+ wxString(wxT(' '), 10) + wxT('|') + wxT('\n'));
241 Show(wxString(wxT('-'), 22 + len
) + wxT('\n'));
242 // Do not merge the line below, or translators could translate "Help"
243 Show(CFormat(_("\nUse '%s' for command list\n\n")) % wxT("Help"));
246 void CaMuleExternalConnector::Process_Answer(const wxString
& answer
)
248 wxStringTokenizer
tokens(answer
, wxT("\n"));
249 while ( tokens
.HasMoreTokens() ) {
250 Show(wxT(" > ") + tokens
.GetNextToken() + wxT("\n"));
254 bool CaMuleExternalConnector::Parse_Command(const wxString
& buffer
)
257 wxStringTokenizer
tokens(buffer
);
258 while (tokens
.HasMoreTokens()) {
259 cmd
+= tokens
.GetNextToken() + wxT(' ');
263 int cmd_ID
= GetIDFromString(cmd
);
265 cmd_ID
= ProcessCommand(cmd_ID
);
270 m_commands
.PrintHelpFor(GetCmdArgs());
273 error
= _("Syntax error!");
275 case CMD_ERR_PROCESS_CMD
:
276 Show(_("Error processing command - should never happen! Report bug, please\n"));
278 case CMD_ERR_NO_PARAM
:
279 error
= _("This command should not have any parameters.");
281 case CMD_ERR_MUST_HAVE_PARAM
:
282 error
= _("This command must have a parameter.");
284 case CMD_ERR_INVALID_ARG
:
285 error
= _("Invalid argument.");
287 case CMD_ERR_INCOMPLETE
:
288 error
= _("This is an incomplete command.");
291 if (!error
.IsEmpty()) {
292 Show(error
+ wxT('\n'));
293 wxString
helpStr(wxT("help"));
294 if (!GetLastCmdStr().IsEmpty()) {
295 helpStr
<< wxT(' ') << GetLastCmdStr();
297 Show(CFormat(_("Type '%s' to get more help.\n")) % helpStr
);
299 return cmd_ID
== CMD_ID_QUIT
;
302 void CaMuleExternalConnector::GetCommand(const wxString
&prompt
, char* buffer
, size_t buffer_size
)
304 #ifdef HAVE_LIBREADLINE
305 char *text
= readline(unicode2char(prompt
+ wxT("$ ")));
307 (m_InputLine
== 0 || strcmp(text
,m_InputLine
) != 0)) {
314 Show(prompt
+ wxT("$ "));
316 fgets(buffer
, buffer_size
, stdin
);
317 const char *text
= buffer
;
318 #endif /* HAVE_LIBREADLINE */
320 size_t len
= strlen(text
);
321 if (len
> buffer_size
- 2) {
322 len
= buffer_size
- 2;
324 strncpy(buffer
, text
, len
);
328 strncpy(buffer
, "quit", buffer_size
);
332 void CaMuleExternalConnector::TextShell(const wxString
&prompt
)
337 bool The_End
= false;
339 GetCommand(prompt
, buffer
, 256);
340 buf
= char2unicode(buffer
);
341 The_End
= Parse_Command(buf
);
342 } while ((!The_End
) && (m_ECClient
->IsSocketConnected()));
345 void CaMuleExternalConnector::ConnectAndRun(const wxString
&ProgName
, const wxString
& ProgVersion
)
347 if (m_NeedsConfigSave
) {
353 // Find out Application Name
362 Show(CFormat(_("This is %s %s %s\n")) % appName
% wxT(VERSION
) % wxT(SVNDATE
));
364 Show(CFormat(_("This is %s %s\n")) % appName
% wxT(VERSION
));
367 // HostName, Port and Password
368 if ( m_password
.IsEmpty() ) {
371 pass_plain
= char2unicode(getpass("Enter password for mule connection: "));
373 //#warning This way, pass enter is not hidden on windows. Bad thing.
376 printf("Enter password for mule connection: \n");
378 fgets(temp_str
, 512, stdin
);
379 temp_str
[strlen(temp_str
)-1] = '\0';
380 pass_plain
= char2unicode(temp_str
);
382 wxCHECK2(m_password
.Decode(MD5Sum(pass_plain
).GetHash()), /* Do nothing. */ );
383 // MD5 hash for an empty string, according to rfc1321.
384 if (m_password
.Encode() == wxT("D41D8CD98F00B204E9800998ECF8427E")) {
388 // Clear plain-text password
389 pass_plain
= wxT("01234567890123456789");
392 if (!m_password
.IsEmpty()) {
395 Show(_("\nCreating client...\n"));
396 m_ECClient
= new CRemoteConnect(NULL
);
398 m_ECClient
->ConnectToCore(m_host
, m_port
, wxT("foobar"), m_password
.Encode(),
399 ProgName
, ProgVersion
);
401 m_ECClient
->WaitSocketConnect(10);
403 if (!m_ECClient
->IsSocketConnected()) {
404 // no connection => close gracefully
405 Show(_("Connection Failed. Unable to connect to the specified host\n"));
407 // Authenticate ourselves
408 // ConnectToCore() already authenticated for us.
409 //m_ECClient->ConnectionEstablished();
410 Show(m_ECClient
->GetServerReply()+wxT("\n"));
411 if (m_ECClient
->IsSocketConnected()) {
416 Show(CFormat(_("\nOk, exiting %s...\n")) % ProgName
);
419 m_ECClient
->DestroySocket();
421 Show(_("Cannot connect with an empty password.\nYou must specify a password either in config file\nor on command-line, or enter one when asked.\n\nExiting...\n"));
425 void CaMuleExternalConnector::OnInitCmdLine(wxCmdLineParser
& parser
)
427 parser
.AddSwitch(wxEmptyString
, wxT("help"),
428 _("Show this help text."),
429 wxCMD_LINE_PARAM_OPTIONAL
);
430 parser
.AddOption(wxT("h"), wxT("host"),
431 _("Host where aMule is running. (default: localhost)"),
432 wxCMD_LINE_VAL_STRING
, wxCMD_LINE_PARAM_OPTIONAL
);
433 parser
.AddOption(wxT("p"), wxT("port"),
434 _("aMule's port for External Connection. (default: 4712)"),
435 wxCMD_LINE_VAL_NUMBER
, wxCMD_LINE_PARAM_OPTIONAL
);
436 parser
.AddOption(wxT("P"), wxT("password"),
437 _("External Connection password."),
438 wxCMD_LINE_VAL_STRING
, wxCMD_LINE_PARAM_OPTIONAL
);
439 parser
.AddOption(wxT("f"), wxT("config-file"),
440 _("Read configuration from file."),
441 wxCMD_LINE_VAL_STRING
, wxCMD_LINE_PARAM_OPTIONAL
);
442 parser
.AddSwitch(wxT("q"), wxT("quiet"),
443 _("Do not print any output to stdout."),
444 wxCMD_LINE_PARAM_OPTIONAL
);
445 parser
.AddSwitch(wxT("v"), wxT("verbose"),
446 _("Be verbose - show also debug messages."),
447 wxCMD_LINE_PARAM_OPTIONAL
);
448 parser
.AddOption(wxT("l"), wxT("locale"),
449 _("Sets program locale (language)."),
450 wxCMD_LINE_VAL_STRING
, wxCMD_LINE_PARAM_OPTIONAL
);
451 parser
.AddSwitch(wxT("w"), wxT("write-config"),
452 _("Write command line options to config file."),
453 wxCMD_LINE_PARAM_OPTIONAL
);
454 parser
.AddOption(wxEmptyString
, wxT("create-config-from"),
455 _("Creates config file based on aMule's config file."),
456 wxCMD_LINE_VAL_STRING
, wxCMD_LINE_PARAM_OPTIONAL
);
457 parser
.AddSwitch(wxEmptyString
, wxT("version"),
458 _("Print program version."),
459 wxCMD_LINE_PARAM_OPTIONAL
);
462 bool CaMuleExternalConnector::OnCmdLineParsed(wxCmdLineParser
& parser
)
464 if (parser
.Found(wxT("version"))) {
465 const char *appName
=
466 // Find out Application Name
473 printf("%s %s\n", appName
, (const char *)unicode2char(GetMuleVersion()));
477 if (!parser
.Found(wxT("config-file"), &m_configFileName
)) {
478 m_configFileName
= GetConfigDir() + wxT("remote.conf");
481 wxString aMuleConfigFile
;
482 if (parser
.Found(wxT("create-config-from"), &aMuleConfigFile
)) {
483 aMuleConfigFile
= FinalizeFilename(aMuleConfigFile
);
484 if (!::wxFileExists(aMuleConfigFile
)) {
485 fprintf(stderr
, "%s\n", (const char *)unicode2char(wxT("FATAL ERROR: File does not exist: ") + aMuleConfigFile
));
488 CECFileConfig
aMuleConfig(aMuleConfigFile
);
489 LoadAmuleConfig(aMuleConfig
);
491 m_configFile
->Flush();
497 if ( !parser
.Found(wxT("host"), &m_host
) ) {
498 if ( m_host
.IsEmpty() ) {
499 m_host
= wxT("localhost");
504 if (!parser
.Found(wxT("port"), &port
)) {
513 if (parser
.Found(wxT("password"), &pass_plain
)) {
514 if (!pass_plain
.IsEmpty()) {
515 m_password
.Decode(MD5Sum(pass_plain
).GetHash());
521 if (parser
.Found(wxT("write-config"))) {
522 m_NeedsConfigSave
= true;
525 parser
.Found(wxT("locale"), &m_language
);
527 if (parser
.Found(wxT("help"))) {
532 m_KeepQuiet
= parser
.Found(wxT("quiet"));
533 m_Verbose
= parser
.Found(wxT("verbose"));
538 void CaMuleExternalConnector::LoadAmuleConfig(CECFileConfig
& cfg
)
540 m_host
= wxT("localhost");
541 m_port
= cfg
.Read(wxT("/ExternalConnect/ECPort"), -1l);
542 cfg
.ReadHash(wxT("/ExternalConnect/ECPassword"), &m_password
);
543 m_language
= cfg
.Read(wxT("/eMule/Language"), wxEmptyString
);
547 void CaMuleExternalConnector::LoadConfigFile()
550 m_configFile
= new CECFileConfig(m_configFileName
);
553 m_language
= m_configFile
->Read(wxT("/Locale"), wxEmptyString
);
554 m_host
= m_configFile
->Read(wxT("/EC/Host"), wxEmptyString
);
555 m_port
= m_configFile
->Read(wxT("/EC/Port"), -1l);
556 m_configFile
->ReadHash(wxT("/EC/Password"), &m_password
);
560 void CaMuleExternalConnector::SaveConfigFile()
562 if (!wxFileName::DirExists(GetConfigDir())) {
563 wxFileName::Mkdir(GetConfigDir());
566 m_configFile
= new CECFileConfig(m_configFileName
);
569 m_configFile
->Write(wxT("/Locale"), m_language
);
570 m_configFile
->Write(wxT("/EC/Host"), m_host
);
571 m_configFile
->Write(wxT("/EC/Port"), m_port
);
572 m_configFile
->WriteHash(wxT("/EC/Password"), m_password
);
576 bool CaMuleExternalConnector::OnInit()
578 bool retval
= wxApp::OnInit();
580 InitCustomLanguages();
581 InitLocale(m_locale
, StrLang2wx(m_language
));
586 #if !wxUSE_GUI && defined(__WXMAC__)
588 #include <wx/apptrait.h> // Do_not_auto_remove
589 #include <wx/stdpaths.h> // Do_not_auto_remove
591 class CaMuleExternalConnectorTraits
: public wxConsoleAppTraits
594 virtual wxStandardPathsBase
& GetStandardPaths()
600 static wxStandardPathsCF s_stdPaths
;
603 wxStandardPathsCF
CaMuleExternalConnectorTraits::s_stdPaths
;
605 wxAppTraits
* CaMuleExternalConnector::CreateTraits()
607 return new CaMuleExternalConnectorTraits
;
611 // File_checked_for_headers