Upstream tarball 20080603
[amule.git] / src / ExternalConnector.cpp
blobc9922f5a183c277f88f568b400ed807f26a17f16
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>
32 #include <common/Format.h> // Needed for CFormat
33 #include <wx/tokenzr.h> // For wxStringTokenizer
35 // For readline
36 #ifdef HAVE_LIBREADLINE
37 #if defined(HAVE_READLINE_READLINE_H)
38 #include <readline/readline.h> // Do_not_auto_remove
39 #elif defined(HAVE_READLINE_H)
40 #include <readline.h> // Do_not_auto_remove
41 #else /* !defined(HAVE_READLINE_H) */
42 extern "C" char *readline (const char*);
43 #endif /* !defined(HAVE_READLINE_H) */
44 #else /* !defined(HAVE_READLINE_READLINE_H) */
45 /* no readline */
46 #endif /* HAVE_LIBREADLINE */
48 // For history
49 #ifdef HAVE_READLINE_HISTORY
50 #if defined(HAVE_READLINE_HISTORY_H)
51 #include <readline/history.h> // Do_not_auto_remove
52 #elif defined(HAVE_HISTORY_H)
53 #include <history.h> // Do_not_auto_remove
54 #else /* !defined(HAVE_HISTORY_H) */
55 extern "C" void add_history (const char*);
56 #endif /* defined(HAVE_READLINE_HISTORY_H) */
57 #else
58 /* no history */
59 #endif /* HAVE_READLINE_HISTORY */
62 #include <ec/cpp/ECFileConfig.h> // Needed for CECFileConfig
63 #include <common/MD5Sum.h>
65 //-------------------------------------------------------------------
67 CCommandTree::~CCommandTree()
69 for (CmdPos_t it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
70 delete *it;
72 m_subcommands.clear();
76 CCommandTree* CCommandTree::AddCommand(CCommandTree* command)
78 command->m_parent = this;
79 const wxString& cmd = command->m_command;
80 CmdPos_t it;
81 for (it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
82 if ((*it)->m_command > cmd) {
83 break;
86 m_subcommands.insert(it, command);
87 return command;
91 int CCommandTree::FindCommandId(const wxString& command, wxString& args, wxString& cmdstr) const
93 wxString cmd = command.BeforeFirst(wxT(' ')).Lower();
94 for (CmdPosConst_t it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
95 if ((*it)->m_command.Lower() == cmd) {
96 args = command.AfterFirst(wxT(' ')).Trim(false);
97 return (*it)->FindCommandId(args, args, cmdstr);
100 cmdstr = GetFullCommand().Lower();
101 if (m_params == CMD_PARAM_ALWAYS && args.IsEmpty()) {
102 return CMD_ERR_MUST_HAVE_PARAM;
103 } else if (m_params == CMD_PARAM_NEVER && !args.IsEmpty()) {
104 return CMD_ERR_NO_PARAM;
105 } else {
106 if ((m_cmd_id >= 0) && (m_cmd_id & CMD_DEPRECATED)) {
107 m_app.Show(wxT('\n') + m_verbose + wxT('\n'));
108 return m_cmd_id & ~CMD_DEPRECATED;
109 } else {
110 return m_cmd_id;
116 wxString CCommandTree::GetFullCommand() const
118 wxString cmd = m_command;
120 const CCommandTree *parent = m_parent;
121 while (parent && parent->m_parent) {
122 cmd = parent->m_command + wxT(" ") + cmd;
123 parent = parent->m_parent;
126 return cmd;
130 void CCommandTree::PrintHelpFor(const wxString& command) const
132 wxString cmd = command.BeforeFirst(wxT(' ')).Lower();
133 if (!cmd.IsEmpty()) {
134 for (CmdPosConst_t it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
135 if ((*it)->m_command.Lower() == cmd) {
136 (*it)->PrintHelpFor(command.AfterFirst(wxT(' ')).Trim(false));
137 return;
140 if (m_parent) {
141 m_app.Show(CFormat(_("Unknown extension '%s' for the '%s' command.\n")) % command % GetFullCommand());
142 } else {
143 m_app.Show(CFormat(_("Unknown command '%s'.\n")) % command);
145 } else {
146 wxString fullcmd = GetFullCommand();
147 if (!fullcmd.IsEmpty()) {
148 m_app.Show(fullcmd.Upper() + wxT(": ") + wxGetTranslation(m_short) + wxT("\n"));
149 if (!m_verbose.IsEmpty()) {
150 m_app.Show(wxT("\n"));
151 m_app.Show(wxGetTranslation(m_verbose));
154 if (m_params == CMD_PARAM_NEVER) {
155 m_app.Show(_("\nThis command cannot have an argument.\n"));
156 } else if (m_params == CMD_PARAM_ALWAYS) {
157 m_app.Show(_("\nThis command must have an argument.\n"));
159 if (m_cmd_id == CMD_ERR_INCOMPLETE) {
160 m_app.Show(_("\nThis command is incomplete, you must use one of the extensions below.\n"));
162 if (!m_subcommands.empty()) {
163 CmdPosConst_t it;
164 int maxlen = 0;
165 if (m_parent) {
166 m_app.Show(_("\nAvailable extensions:\n"));
167 } else {
168 m_app.Show(_("Available commands:\n"));
170 for (it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
171 if (!((*it)->m_cmd_id >= 0 && (*it)->m_cmd_id & CMD_DEPRECATED) || m_parent) {
172 int len = (*it)->m_command.Length();
173 if (len > maxlen) {
174 maxlen = len;
178 maxlen += 4;
179 for (it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
180 if (!((*it)->m_cmd_id >= 0 && (*it)->m_cmd_id & CMD_DEPRECATED) || m_parent) {
181 m_app.Show((*it)->m_command + wxString(wxT(' '), maxlen - (*it)->m_command.Length()) + wxGetTranslation((*it)->m_short) + wxT("\n"));
184 if (!m_parent) {
185 m_app.Show(CFormat(_("\nAll commands are case insensitive.\nType '%s <command>' to get detailed info on <command>.\n")) % wxT("help"));
189 m_app.Show(wxT("\n"));
192 //-------------------------------------------------------------------
194 CaMuleExternalConnector::CaMuleExternalConnector()
195 : m_configFile(NULL),
196 m_port(-1),
197 m_KeepQuiet(false),
198 m_Verbose(false),
199 m_commands(*this),
200 m_ECClient(NULL),
201 m_InputLine(NULL),
202 m_NeedsConfigSave(false),
203 m_locale(NULL)
205 SetAppName(wxT("aMule"));
208 CaMuleExternalConnector::~CaMuleExternalConnector()
210 delete m_configFile;
211 delete m_locale;
214 void CaMuleExternalConnector::OnInitCommandSet()
216 m_commands.AddCommand(wxT("Quit"), CMD_ID_QUIT, wxTRANSLATE("Exits from the application."), wxEmptyString);
217 m_commands.AddCommand(wxT("Exit"), CMD_ID_QUIT, wxTRANSLATE("Exits from the application."), wxEmptyString);
218 m_commands.AddCommand(wxT("Help"), CMD_ID_HELP, wxTRANSLATE("Show help."),
219 /* TRANSLATORS:
220 Do not translate the word 'help', it is a command to the program! */
221 wxTRANSLATE("To get help on a command, type 'help <command>'.\nTo get the full command list type 'help'.\n"));
224 void CaMuleExternalConnector::Show(const wxString &s)
226 if( !m_KeepQuiet ) {
227 printf("%s", (const char *)unicode2char(s));
228 #ifdef __WXMSW__
229 fflush(stdout);
230 #endif
234 void CaMuleExternalConnector::ShowGreet()
236 wxString text = GetGreetingTitle();
237 int len = text.Length();
238 Show(wxT('\n') + wxString(wxT('-'), 22 + len) + wxT('\n'));
239 Show(wxT('|') + wxString(wxT(' '), 10) + text + wxString(wxT(' '), 10) + wxT('|') + wxT('\n'));
240 Show(wxString(wxT('-'), 22 + len) + wxT('\n'));
241 // Do not merge the line below, or translators could translate "Help"
242 Show(CFormat(_("\nUse '%s' for command list\n\n")) % wxT("Help"));
245 void CaMuleExternalConnector::Process_Answer(const wxString& answer)
247 wxStringTokenizer tokens(answer, wxT("\n"));
248 while ( tokens.HasMoreTokens() ) {
249 Show(wxT(" > ") + tokens.GetNextToken() + wxT("\n"));
253 bool CaMuleExternalConnector::Parse_Command(const wxString& buffer)
255 wxString cmd;
256 wxStringTokenizer tokens(buffer);
257 while (tokens.HasMoreTokens()) {
258 cmd += tokens.GetNextToken() + wxT(' ');
260 cmd.Trim(false);
261 cmd.Trim(true);
262 int cmd_ID = GetIDFromString(cmd);
263 if ( cmd_ID >= 0 ) {
264 cmd_ID = ProcessCommand(cmd_ID);
266 wxString error;
267 switch (cmd_ID) {
268 case CMD_ID_HELP:
269 m_commands.PrintHelpFor(GetCmdArgs());
270 break;
271 case CMD_ERR_SYNTAX:
272 error = _("Syntax error!");
273 break;
274 case CMD_ERR_PROCESS_CMD:
275 Show(_("Error processing command - should never happen! Report bug, please\n"));
276 break;
277 case CMD_ERR_NO_PARAM:
278 error = _("This command should not have any parameters.");
279 break;
280 case CMD_ERR_MUST_HAVE_PARAM:
281 error = _("This command must have a parameter.");
282 break;
283 case CMD_ERR_INVALID_ARG:
284 error = _("Invalid argument.");
285 break;
286 case CMD_ERR_INCOMPLETE:
287 error = _("This is an incomplete command.");
288 break;
290 if (!error.IsEmpty()) {
291 Show(error + wxT('\n'));
292 wxString helpStr(wxT("help"));
293 if (!GetLastCmdStr().IsEmpty()) {
294 helpStr << wxT(' ') << GetLastCmdStr();
296 Show(CFormat(_("Type '%s' to get more help.\n")) % helpStr);
298 return cmd_ID == CMD_ID_QUIT;
301 void CaMuleExternalConnector::GetCommand(const wxString &prompt, char* buffer, size_t buffer_size)
303 #ifdef HAVE_LIBREADLINE
304 char *text = readline(unicode2char(prompt + wxT("$ ")));
305 if (text && *text &&
306 (m_InputLine == 0 || strcmp(text,m_InputLine) != 0)) {
307 add_history (text);
309 if (m_InputLine)
310 free(m_InputLine);
311 m_InputLine = text;
312 #else
313 Show(prompt + wxT("$ "));
314 fflush(stdin);
315 fgets(buffer, buffer_size, stdin);
316 const char *text = buffer;
317 #endif /* HAVE_LIBREADLINE */
318 if ( text ) {
319 size_t len = strlen(text);
320 if (len > buffer_size - 2) {
321 len = buffer_size - 2;
323 strncpy(buffer, text, len);
324 buffer[len] = '\n';
325 buffer[len + 1] = 0;
326 } else {
327 strncpy(buffer, "quit", buffer_size);
331 void CaMuleExternalConnector::TextShell(const wxString &prompt)
333 char buffer[256];
334 wxString buf;
336 bool The_End = false;
337 do {
338 GetCommand(prompt, buffer, 256);
339 buf = char2unicode(buffer);
340 The_End = Parse_Command(buf);
341 } while ((!The_End) && (m_ECClient->IsSocketConnected()));
344 void CaMuleExternalConnector::ConnectAndRun(const wxString &ProgName, const wxString& ProgVersion)
346 if (m_NeedsConfigSave) {
347 SaveConfigFile();
348 return;
351 wxString appName =
352 // Find out Application Name
353 #ifdef WEBSERVERDIR
354 wxT("amuleweb")
355 #else
356 wxT("amulecmd")
357 #endif
360 #ifdef SVNDATE
361 Show(CFormat(_("This is %s %s %s\n")) % appName % wxT(VERSION) % wxT(SVNDATE));
362 #else
363 Show(CFormat(_("This is %s %s\n")) % appName % wxT(VERSION));
364 #endif
366 // HostName, Port and Password
367 if ( m_password.IsEmpty() ) {
368 wxString pass_plain;
369 #ifndef __WXMSW__
370 pass_plain = char2unicode(getpass("Enter password for mule connection: "));
371 #else
372 //#warning This way, pass enter is not hidden on windows. Bad thing.
373 char temp_str[512];
374 fflush(stdin);
375 printf("Enter password for mule connection: \n");
376 fflush(stdout);
377 fgets(temp_str, 512, stdin);
378 temp_str[strlen(temp_str)-1] = '\0';
379 pass_plain = char2unicode(temp_str);
380 #endif
381 wxCHECK2(m_password.Decode(MD5Sum(pass_plain).GetHash()), /* Do nothing. */ );
382 // MD5 hash for an empty string, according to rfc1321.
383 if (m_password.Encode() == wxT("D41D8CD98F00B204E9800998ECF8427E")) {
384 m_password.Clear();
387 // Clear plain-text password
388 pass_plain = wxT("01234567890123456789");
391 if (!m_password.IsEmpty()) {
393 // Create the socket
394 Show(_("\nCreating client...\n"));
395 m_ECClient = new CRemoteConnect(NULL);
397 m_ECClient->ConnectToCore(m_host, m_port, wxT("foobar"), m_password.Encode(),
398 ProgName, ProgVersion);
400 m_ECClient->WaitSocketConnect(10);
402 if (!m_ECClient->IsSocketConnected()) {
403 // no connection => close gracefully
404 Show(_("Connection Failed. Unable to connect to the specified host\n"));
405 } else {
406 // Authenticate ourselves
407 // ConnectToCore() already authenticated for us.
408 //m_ECClient->ConnectionEstablished();
409 Show(m_ECClient->GetServerReply()+wxT("\n"));
410 if (m_ECClient->IsSocketConnected()) {
411 ShowGreet();
412 Pre_Shell();
413 TextShell(ProgName);
414 Post_Shell();
415 Show(CFormat(_("\nOk, exiting %s...\n")) % ProgName);
418 m_ECClient->DestroySocket();
419 } else {
420 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"));
424 void CaMuleExternalConnector::OnInitCmdLine(wxCmdLineParser& parser)
426 parser.AddSwitch(wxEmptyString, wxT("help"),
427 _("Show this help text."),
428 wxCMD_LINE_PARAM_OPTIONAL);
429 parser.AddOption(wxT("h"), wxT("host"),
430 _("Host where aMule is running. (default: localhost)"),
431 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
432 parser.AddOption(wxT("p"), wxT("port"),
433 _("aMule's port for External Connection. (default: 4712)"),
434 wxCMD_LINE_VAL_NUMBER, wxCMD_LINE_PARAM_OPTIONAL);
435 parser.AddOption(wxT("P"), wxT("password"),
436 _("External Connection password."),
437 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
438 parser.AddOption(wxT("f"), wxT("config-file"),
439 _("Read configuration from file."),
440 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
441 parser.AddSwitch(wxT("q"), wxT("quiet"),
442 _("Do not print any output to stdout."),
443 wxCMD_LINE_PARAM_OPTIONAL);
444 parser.AddSwitch(wxT("v"), wxT("verbose"),
445 _("Be verbose - show also debug messages."),
446 wxCMD_LINE_PARAM_OPTIONAL);
447 parser.AddOption(wxT("l"), wxT("locale"),
448 _("Sets program locale (language)."),
449 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
450 parser.AddSwitch(wxT("w"), wxT("write-config"),
451 _("Write command line options to config file."),
452 wxCMD_LINE_PARAM_OPTIONAL);
453 parser.AddOption(wxEmptyString, wxT("create-config-from"),
454 _("Creates config file based on aMule's config file."),
455 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
456 parser.AddSwitch(wxEmptyString, wxT("version"),
457 _("Print program version."),
458 wxCMD_LINE_PARAM_OPTIONAL);
461 bool CaMuleExternalConnector::OnCmdLineParsed(wxCmdLineParser& parser)
463 if (parser.Found(wxT("version"))) {
464 const char *appName =
465 // Find out Application Name
466 #ifdef WEBSERVERDIR
467 "amuleweb"
468 #else
469 "amulecmd"
470 #endif
472 printf("%s %s\n", appName, (const char *)unicode2char(GetMuleVersion()));
473 return false;
476 if (!parser.Found(wxT("config-file"), &m_configFileName)) {
477 m_configFileName = GetConfigDir() + wxT("remote.conf");
480 wxString aMuleConfigFile;
481 if (parser.Found(wxT("create-config-from"), &aMuleConfigFile)) {
482 aMuleConfigFile = FinalizeFilename(aMuleConfigFile);
483 if (!::wxFileExists(aMuleConfigFile)) {
484 fprintf(stderr, "%s\n", (const char *)unicode2char(wxT("FATAL ERROR: File does not exist: ") + aMuleConfigFile));
485 exit(1);
487 CECFileConfig aMuleConfig(aMuleConfigFile);
488 LoadAmuleConfig(aMuleConfig);
489 SaveConfigFile();
490 m_configFile->Flush();
491 exit(0);
494 LoadConfigFile();
496 if ( !parser.Found(wxT("host"), &m_host) ) {
497 if ( m_host.IsEmpty() ) {
498 m_host = wxT("localhost");
502 long port;
503 if (!parser.Found(wxT("port"), &port)) {
504 if (m_port == -1) {
505 m_port = 4712;
507 } else {
508 m_port = port;
511 wxString pass_plain;
512 if (parser.Found(wxT("password"), &pass_plain)) {
513 if (!pass_plain.IsEmpty()) {
514 m_password.Decode(MD5Sum(pass_plain).GetHash());
515 } else {
516 m_password.Clear();
520 if (parser.Found(wxT("write-config"))) {
521 m_NeedsConfigSave = true;
524 parser.Found(wxT("locale"), &m_language);
526 if (parser.Found(wxT("help"))) {
527 parser.Usage();
528 return false;
531 m_KeepQuiet = parser.Found(wxT("quiet"));
532 m_Verbose = parser.Found(wxT("verbose"));
534 return true;
537 void CaMuleExternalConnector::LoadAmuleConfig(CECFileConfig& cfg)
539 m_host = wxT("localhost");
540 m_port = cfg.Read(wxT("/ExternalConnect/ECPort"), -1l);
541 cfg.ReadHash(wxT("/ExternalConnect/ECPassword"), &m_password);
542 m_language = cfg.Read(wxT("/eMule/Language"), wxEmptyString);
546 void CaMuleExternalConnector::LoadConfigFile()
548 if (!m_configFile) {
549 m_configFile = new CECFileConfig(m_configFileName);
551 if (m_configFile) {
552 m_language = m_configFile->Read(wxT("/Locale"), wxEmptyString);
553 m_host = m_configFile->Read(wxT("/EC/Host"), wxEmptyString);
554 m_port = m_configFile->Read(wxT("/EC/Port"), -1l);
555 m_configFile->ReadHash(wxT("/EC/Password"), &m_password);
559 void CaMuleExternalConnector::SaveConfigFile()
561 if (!wxFileName::DirExists(GetConfigDir())) {
562 wxFileName::Mkdir(GetConfigDir());
564 if (!m_configFile) {
565 m_configFile = new CECFileConfig(m_configFileName);
567 if (m_configFile) {
568 m_configFile->Write(wxT("/Locale"), m_language);
569 m_configFile->Write(wxT("/EC/Host"), m_host);
570 m_configFile->Write(wxT("/EC/Port"), m_port);
571 m_configFile->WriteHash(wxT("/EC/Password"), m_password);
575 bool CaMuleExternalConnector::OnInit()
577 bool retval = wxApp::OnInit();
578 OnInitCommandSet();
579 InitCustomLanguages();
580 SetLocale(m_language);
581 return retval;
584 wxString CaMuleExternalConnector::SetLocale(const wxString& language)
586 if (!language.IsEmpty()) {
587 m_language = language;
588 if (m_locale) {
589 delete m_locale;
591 m_locale = new wxLocale;
592 InitLocale(*m_locale, StrLang2wx(language));
595 return m_locale == NULL ? wxString() : m_locale->GetCanonicalName();
598 #if !wxUSE_GUI && defined(__WXMAC__)
600 #include <wx/apptrait.h> // Do_not_auto_remove
601 #include <wx/stdpaths.h> // Do_not_auto_remove
603 class CaMuleExternalConnectorTraits : public wxConsoleAppTraits
605 public:
606 virtual wxStandardPathsBase& GetStandardPaths()
608 return s_stdPaths;
611 private:
612 static wxStandardPathsCF s_stdPaths;
615 wxStandardPathsCF CaMuleExternalConnectorTraits::s_stdPaths;
617 wxAppTraits* CaMuleExternalConnector::CreateTraits()
619 return new CaMuleExternalConnectorTraits;
622 #endif
623 // File_checked_for_headers