Upstream tarball 20080414
[amule.git] / src / ExternalConnector.cpp
blob4b9e6173fceb1e44defb9dad8baa7e7258ce982c
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2004-2008 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.
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
25 #include "ExternalConnector.h"
27 #ifdef HAVE_CONFIG_H
28 #include "config.h" // Needed for VERSION and readline detection
29 #endif
31 #include <common/ClientVersion.h>
33 #include <common/Format.h> // Needed for CFormat
35 #include <wx/tokenzr.h> // For wxStringTokenizer
37 // For readline
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) */
47 /* no readline */
48 #endif /* HAVE_LIBREADLINE */
50 // For history
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) */
59 #else
60 /* no history */
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) {
73 delete *it;
75 m_subcommands.clear();
79 CCommandTree* CCommandTree::AddCommand(CCommandTree* command)
81 command->m_parent = this;
82 const wxString& cmd = command->m_command;
83 CmdPos_t it;
84 for (it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
85 if ((*it)->m_command > cmd) {
86 break;
89 m_subcommands.insert(it, command);
90 return 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;
108 } else {
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;
112 } else {
113 return m_cmd_id;
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;
129 return cmd;
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));
140 return;
143 if (m_parent) {
144 m_app.Show(CFormat(_("Unknown extension '%s' for the '%s' command.\n")) % command % GetFullCommand());
145 } else {
146 m_app.Show(CFormat(_("Unknown command '%s'.\n")) % command);
148 } else {
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()) {
166 CmdPosConst_t it;
167 int maxlen = 0;
168 if (m_parent) {
169 m_app.Show(_("\nAvailable extensions:\n"));
170 } else {
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();
176 if (len > maxlen) {
177 maxlen = len;
181 maxlen += 4;
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"));
187 if (!m_parent) {
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()
198 : m_commands(*this)
200 m_ECClient = NULL;
201 m_KeepQuiet = false;
202 m_InputLine = NULL;
203 m_port = -1;
204 m_NeedsConfigSave = false;
205 m_Verbose = false;
206 m_configFile = NULL;
207 SetAppName(wxT("aMule"));
210 CaMuleExternalConnector::~CaMuleExternalConnector()
212 delete m_configFile;
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."),
220 /* TRANSLATORS:
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)
227 if( !m_KeepQuiet ) {
228 printf("%s", (const char *)unicode2char(s));
229 #ifdef __WXMSW__
230 fflush(stdout);
231 #endif
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)
256 wxString cmd;
257 wxStringTokenizer tokens(buffer);
258 while (tokens.HasMoreTokens()) {
259 cmd += tokens.GetNextToken() + wxT(' ');
261 cmd.Trim(false);
262 cmd.Trim(true);
263 int cmd_ID = GetIDFromString(cmd);
264 if ( cmd_ID >= 0 ) {
265 cmd_ID = ProcessCommand(cmd_ID);
267 wxString error;
268 switch (cmd_ID) {
269 case CMD_ID_HELP:
270 m_commands.PrintHelpFor(GetCmdArgs());
271 break;
272 case CMD_ERR_SYNTAX:
273 error = _("Syntax error!");
274 break;
275 case CMD_ERR_PROCESS_CMD:
276 Show(_("Error processing command - should never happen! Report bug, please\n"));
277 break;
278 case CMD_ERR_NO_PARAM:
279 error = _("This command should not have any parameters.");
280 break;
281 case CMD_ERR_MUST_HAVE_PARAM:
282 error = _("This command must have a parameter.");
283 break;
284 case CMD_ERR_INVALID_ARG:
285 error = _("Invalid argument.");
286 break;
287 case CMD_ERR_INCOMPLETE:
288 error = _("This is an incomplete command.");
289 break;
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("$ ")));
306 if (text && *text &&
307 (m_InputLine == 0 || strcmp(text,m_InputLine) != 0)) {
308 add_history (text);
310 if (m_InputLine)
311 free(m_InputLine);
312 m_InputLine = text;
313 #else
314 Show(prompt + wxT("$ "));
315 fflush(stdin);
316 fgets(buffer, buffer_size, stdin);
317 const char *text = buffer;
318 #endif /* HAVE_LIBREADLINE */
319 if ( text ) {
320 size_t len = strlen(text);
321 if (len > buffer_size - 2) {
322 len = buffer_size - 2;
324 strncpy(buffer, text, len);
325 buffer[len] = '\n';
326 buffer[len + 1] = 0;
327 } else {
328 strncpy(buffer, "quit", buffer_size);
332 void CaMuleExternalConnector::TextShell(const wxString &prompt)
334 char buffer[256];
335 wxString buf;
337 bool The_End = false;
338 do {
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) {
348 SaveConfigFile();
349 return;
352 wxString appName =
353 // Find out Application Name
354 #ifdef WEBSERVERDIR
355 wxT("amuleweb")
356 #else
357 wxT("amulecmd")
358 #endif
361 #ifdef SVNDATE
362 Show(CFormat(_("This is %s %s %s\n")) % appName % wxT(VERSION) % wxT(SVNDATE));
363 #else
364 Show(CFormat(_("This is %s %s\n")) % appName % wxT(VERSION));
365 #endif
367 // HostName, Port and Password
368 if ( m_password.IsEmpty() ) {
369 wxString pass_plain;
370 #ifndef __WXMSW__
371 pass_plain = char2unicode(getpass("Enter password for mule connection: "));
372 #else
373 //#warning This way, pass enter is not hidden on windows. Bad thing.
374 char temp_str[512];
375 fflush(stdin);
376 printf("Enter password for mule connection: \n");
377 fflush(stdout);
378 fgets(temp_str, 512, stdin);
379 temp_str[strlen(temp_str)-1] = '\0';
380 pass_plain = char2unicode(temp_str);
381 #endif
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")) {
385 m_password.Clear();
388 // Clear plain-text password
389 pass_plain = wxT("01234567890123456789");
392 if (!m_password.IsEmpty()) {
394 // Create the socket
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"));
406 } else {
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()) {
412 ShowGreet();
413 Pre_Shell();
414 TextShell(ProgName);
415 Post_Shell();
416 Show(CFormat(_("\nOk, exiting %s...\n")) % ProgName);
419 m_ECClient->DestroySocket();
420 } else {
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
467 #ifdef WEBSERVERDIR
468 "amuleweb"
469 #else
470 "amulecmd"
471 #endif
473 printf("%s %s\n", appName, (const char *)unicode2char(GetMuleVersion()));
474 return false;
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));
486 exit(1);
488 CECFileConfig aMuleConfig(aMuleConfigFile);
489 LoadAmuleConfig(aMuleConfig);
490 SaveConfigFile();
491 m_configFile->Flush();
492 exit(0);
495 LoadConfigFile();
497 if ( !parser.Found(wxT("host"), &m_host) ) {
498 if ( m_host.IsEmpty() ) {
499 m_host = wxT("localhost");
503 long port;
504 if (!parser.Found(wxT("port"), &port)) {
505 if (m_port == -1) {
506 m_port = 4712;
508 } else {
509 m_port = port;
512 wxString pass_plain;
513 if (parser.Found(wxT("password"), &pass_plain)) {
514 if (!pass_plain.IsEmpty()) {
515 m_password.Decode(MD5Sum(pass_plain).GetHash());
516 } else {
517 m_password.Clear();
521 if (parser.Found(wxT("write-config"))) {
522 m_NeedsConfigSave = true;
525 parser.Found(wxT("locale"), &m_language);
527 if (parser.Found(wxT("help"))) {
528 parser.Usage();
529 return false;
532 m_KeepQuiet = parser.Found(wxT("quiet"));
533 m_Verbose = parser.Found(wxT("verbose"));
535 return true;
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()
549 if (!m_configFile) {
550 m_configFile = new CECFileConfig(m_configFileName);
552 if (m_configFile) {
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());
565 if (!m_configFile) {
566 m_configFile = new CECFileConfig(m_configFileName);
568 if (m_configFile) {
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();
579 OnInitCommandSet();
580 InitCustomLanguages();
581 InitLocale(m_locale, StrLang2wx(m_language));
582 return retval;
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
593 public:
594 virtual wxStandardPathsBase& GetStandardPaths()
596 return s_stdPaths;
599 private:
600 static wxStandardPathsCF s_stdPaths;
603 wxStandardPathsCF CaMuleExternalConnectorTraits::s_stdPaths;
605 wxAppTraits* CaMuleExternalConnector::CreateTraits()
607 return new CaMuleExternalConnectorTraits;
610 #endif
611 // File_checked_for_headers