diff --git a/src/comunicationsmanagerfilesockets.cpp b/src/comunicationsmanagerfilesockets.cpp index c1ea2a74..966037eb 100644 --- a/src/comunicationsmanagerfilesockets.cpp +++ b/src/comunicationsmanagerfilesockets.cpp @@ -306,13 +306,13 @@ void ComunicationsManagerFileSockets::returnAndClosePetition(CmdPetition *inf, O connectedsocket = accept(((CmdPetitionPosixSockets *)inf)->outSocket, (struct sockaddr*)&cliAddr, &cliLength); if (fcntl(connectedsocket, F_SETFD, FD_CLOEXEC) == -1) { - LOG_err << "ERROR setting CLOEXEC to socket: " << errno; + cerr << "ERROR setting CLOEXEC to socket: " << errno << endl; } ((CmdPetitionPosixSockets *)inf)->acceptedOutSocket = connectedsocket; //So that it gets closed in destructor } if (connectedsocket == -1) { - LOG_fatal << "Return and close: Unable to accept on outsocket " << ((CmdPetitionPosixSockets *)inf)->outSocket << " error: " << errno; + cerr << "Return and close: Unable to accept on outsocket " << ((CmdPetitionPosixSockets *)inf)->outSocket << " error: " << errno << endl; delete inf; return; } @@ -322,12 +322,12 @@ void ComunicationsManagerFileSockets::returnAndClosePetition(CmdPetition *inf, O int n = send(connectedsocket, (void*)&outCode, sizeof( outCode ), MSG_NOSIGNAL); if (n < 0) { - LOG_err << "ERROR writing output Code to socket: " << errno; + cerr << "ERROR writing output Code to socket: " << errno << endl; } n = send(connectedsocket, sout.data(), max(1,(int)sout.size()), MSG_NOSIGNAL); // for some reason without the max recv never quits in the client for empty responses if (n < 0) { - LOG_err << "ERROR writing to socket: " << errno; + cerr << "ERROR writing to socket: " << errno << endl; } delete inf; diff --git a/src/comunicationsmanagernamedpipes.cpp b/src/comunicationsmanagernamedpipes.cpp index a9be8e1a..edc0f0b7 100644 --- a/src/comunicationsmanagernamedpipes.cpp +++ b/src/comunicationsmanagernamedpipes.cpp @@ -393,7 +393,7 @@ void ComunicationsManagerNamedPipes::sendPartialOutput(CmdPetition *inf, char *s DWORD n; if (!WriteFile(outNamedPipe,(const char*)&outCode, sizeof(outCode), &n, NULL)) { - LOG_err << "ERROR writing output Code to namedPipe: " << ERRNO; + cerr << "ERROR writing output Code to namedPipe: " << ERRNO << endl; if (errno == ERROR_NO_DATA) { std::cerr << "WARNING: Client disconnected, the rest of the output will be discarded" << endl; @@ -405,12 +405,12 @@ void ComunicationsManagerNamedPipes::sendPartialOutput(CmdPetition *inf, char *s size_t thesize = size > 1 ? size : 1; // client does not like empty responses if (!WriteFile(outNamedPipe,(const char*)&thesize, sizeof(thesize), &n, NULL)) { - LOG_err << "ERROR writing output Code to namedPipe: " << ERRNO; + cerr << "ERROR writing output Code to namedPipe: " << ERRNO<< endl; return; } if (!WriteFile(outNamedPipe,s, thesize, &n, NULL)) { - LOG_err << "ERROR writing to namedPipe: " << ERRNO; + cerr << "ERROR writing to namedPipe: " << ERRNO << endl; } } @@ -475,7 +475,7 @@ CmdPetition * ComunicationsManagerNamedPipes::getPetition() DWORD total_available_bytes; if (FALSE == PeekNamedPipe(pipeGeneral,0,0,0,&total_available_bytes,0)) { - LOG_err << "Failed to PeekNamedPipe. errno: L" << ERRNO; + cerr << "Failed to PeekNamedPipe. errno: L" << ERRNO << endl; break; } if (total_available_bytes == 0) @@ -495,7 +495,7 @@ CmdPetition * ComunicationsManagerNamedPipes::getPetition() if (!readok) { - LOG_err << "Failed to read petition from named pipe. errno: L" << ERRNO; + cerr << "Failed to read petition from named pipe. errno: L" << ERRNO << endl; inf->line = strdup("ERROR"); return inf; } @@ -508,21 +508,21 @@ CmdPetition * ComunicationsManagerNamedPipes::getPetition() inf->outNamedPipe = create_new_namedPipe(&namedPipe_id); if (!namedPipeValid(inf->outNamedPipe) || !namedPipe_id) { - LOG_fatal << "ERROR creating output namedPipe at getPetition"; + cerr << "ERROR creating output namedPipe at getPetition" << endl; inf->line = strdup("ERROR"); return inf; } if(!WriteFile(pipeGeneral,(const char*)&namedPipe_id, sizeof( namedPipe_id ), &n, NULL)) { - LOG_fatal << "ERROR writing to namedPipe at getPetition: ERRNO = " << ERRNO; + cerr << "ERROR writing to namedPipe at getPetition: ERRNO = " << ERRNO << endl; inf->line = strdup("ERROR"); return inf; } if (!DisconnectNamedPipe(pipeGeneral) ) { - LOG_fatal << " Error disconnecting from general pip. errno: " << ERRNO; + cerr << " Error disconnecting from general pipe. errno: " << ERRNO << endl; } inf->line = strdup(receivedutf8.c_str()); diff --git a/src/listeners.cpp b/src/listeners.cpp index a4040b76..251b84c0 100644 --- a/src/listeners.cpp +++ b/src/listeners.cpp @@ -23,6 +23,11 @@ using namespace mega; using namespace std; +#ifndef SSTR +#define SSTR( x ) static_cast< const std::ostringstream & >( \ + ( std::ostringstream() << std::dec << x ) ).str() +#endif + #ifdef ENABLE_CHAT void MegaCmdGlobalListener::onChatsUpdate(MegaApi*, MegaTextChatList*) { @@ -168,6 +173,18 @@ void MegaCmdGlobalListener::onEvent(MegaApi *api, MegaEvent *event) LOG_err << "Received event account blocked: " << event->getText(); sandboxCMD->reasonblocked = event->getText(); } + else if (event->getType() == MegaEvent::EVENT_CONNECTIVITY_CHANGED) + { + LOG_debug << "Received event connectivity changed: " << event->getNumber(); + if (event->getNumber() == RETRY_CONNECTIVITY) + { + broadcastMessage(SSTR(event->getNumber()), "connectivity:"); + } + else if (event->getNumber() == RETRY_NONE) + { + broadcastMessage(SSTR(event->getNumber()), "connectivity:"); + } + } else if (event->getType() == MegaEvent::EVENT_STORAGE) { if (event->getNumber() == MegaApi::STORAGE_STATE_CHANGE) diff --git a/src/megacmd.cpp b/src/megacmd.cpp index 64443901..1b88d803 100644 --- a/src/megacmd.cpp +++ b/src/megacmd.cpp @@ -185,6 +185,7 @@ string newpasswd; bool doExit = false; bool consoleFailed = false; bool stopcheckingforUpdaters = false; +bool loginended = false; string dynamicprompt = "MEGA CMD> "; @@ -304,12 +305,12 @@ void informStateListener(string message, int clientID) cm->informStateListenerByClientId(s, clientID); } -void broadcastMessage(string message) +void broadcastMessage(string message, const char *suffix) { string s; if (message.size()) { - s += "message:"; + s += suffix; s+=message; } cm->informStateListeners(s); @@ -3508,6 +3509,21 @@ void * retryConnections(void *pointer) return NULL; } +void * startSession(void *pointer) +{ + bool *loginended = (bool *) pointer; + if (!ConfigurationManager::session.empty()) + { + loginInAtStartup = true; + stringstream logLine; + logLine << "login " << ConfigurationManager::session; + LOG_debug << "Executing ... " << logLine.str().substr(0,9) << "..."; + process_line((char*)logLine.str().c_str()); + loginInAtStartup = false; + } + *loginended = true; + return NULL; +} void startcheckingForUpdates() { @@ -3787,6 +3803,22 @@ void megacmd() s+=(char)0x1F; } + if (!loginended) + { + s += "message:"; + s += "--------------------------------------------------\n" + "-- MEGAcmd Server has not finished login in. --\n" + "-- Please ensure your connection is OK --\n" + "--------------------------------------------------\n"; + + s+=(char)0x1F; + } + + // communicate connectivity status + s += "connectivity:"; + s += SSTR(api->isWaiting()); + s += (char)0x1F; + // communicate status info s+= "prompt:"; s+=dynamicprompt; @@ -4541,14 +4573,15 @@ int main(int argc, char* argv[]) printWelcomeMsg(); - if (!ConfigurationManager::session.empty()) + MegaThread *tlogin = new MegaThread(); + tlogin->start(startSession, &loginended); + + // give it a while to try to login before answering petitions + long long millisecondstowait = 30000; + while (!loginended && millisecondstowait > 0) { - loginInAtStartup = true; - stringstream logLine; - logLine << "login " << ConfigurationManager::session; - LOG_debug << "Executing ... " << logLine.str().substr(0,9) << "..."; - process_line((char*)logLine.str().c_str()); - loginInAtStartup = false; + sleepMilliSeconds(100); + millisecondstowait -= 100; } megacmd(); diff --git a/src/megacmd.h b/src/megacmd.h index 6a91bdd8..f2df9af5 100644 --- a/src/megacmd.h +++ b/src/megacmd.h @@ -110,7 +110,7 @@ enum confirmresponse void changeprompt(const char *newprompt); void informStateListener(std::string message, int clientID); -void broadcastMessage(std::string message); +void broadcastMessage(std::string message, const char *suffix = "message:"); mega::MegaApi* getFreeApiFolder(); void freeApiFolder(mega::MegaApi *apiFolder); diff --git a/src/megacmdcommonutils.h b/src/megacmdcommonutils.h index f5f148f3..2168aab6 100644 --- a/src/megacmdcommonutils.h +++ b/src/megacmdcommonutils.h @@ -96,6 +96,8 @@ int toInteger(std::string what, int failValue = -1); std::string joinStrings(const std::vector& vec, const char* delim = " ", bool quoted=true); +unsigned int getstringutf8size(const std::string &str); + std::string getFixLengthString(const std::string origin, unsigned int size, const char delimm=' ', bool alignedright = false); std::string getRightAlignedString(const std::string origin, unsigned int minsize); diff --git a/src/megacmdshell/megacmdshell.cpp b/src/megacmdshell/megacmdshell.cpp index 9d314554..1c51ef87 100644 --- a/src/megacmdshell/megacmdshell.cpp +++ b/src/megacmdshell/megacmdshell.cpp @@ -19,6 +19,8 @@ #include "megacmdshellcommunicationsnamedpipes.h" #include "../megacmdcommonutils.h" +#include "megaapi.h" + #define USE_VARARGS #define PREFER_STDARG @@ -178,7 +180,9 @@ bool requirepromptinstall = true; bool procesingline = false; +static string promptpreffix; static char dynamicprompt[PROMPT_MAX_SIZE]; +static bool readyforprompt = false; static char* line; @@ -201,8 +205,96 @@ MegaCmdShellCommunications *comms; MUTEX_CLASS mutexPrompt(false); +bool processing = false; +bool queryinguser = false; + void printWelcomeMsg(unsigned int width = 0); +#ifdef NO_READLINE +std::string toUtf8String(const std::wstring& ws, UINT codepage) +{ + std::string s; + s.resize((ws.size() + 1) * 4); + int nchars = WideCharToMultiByte(codepage, 0, ws.data(), int(ws.size()), (LPSTR)s.data(), int(s.size()), NULL, NULL); + s.resize(nchars); + return s; +} +#endif + +string warnstatus; +MegaThread *warnerThread = NULL; +const int MARQUEE_WARN_WIDTH = 20; +void setwarnstatus(const char * s) +{ + warnstatus = ""; + if (strlen(s)) + { + warnstatus.append(MARQUEE_WARN_WIDTH-1, ' '); + warnstatus.append(s); + } +} + +void *warner(void * pointer) +{ +#ifdef WIN32 + HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO sbi; +#endif + unsigned int i = 0; + while (!doExit) + { + int width = getNumberOfCols(80); + width -= (MARQUEE_WARN_WIDTH); + + i++; + if (i >= warnstatus.size()) + { + i = 0; + } + + if (warnstatus.size()) + { + string subwarn = warnstatus.substr(i,MARQUEE_WARN_WIDTH); + subwarn.append(MARQUEE_WARN_WIDTH - getstringutf8size(subwarn), ' '); +#ifndef NO_READLINE + char *rlline = rl_copy_text(0, rl_end); + string srline(rlline); + free(rlline); +#endif + +#ifdef WIN32 + BOOL ok = GetConsoleScreenBufferInfo(hOutput, &sbi); + if (ok) + { + ok = SetConsoleCursorPosition(hOutput, {SHORT(width-1), sbi.dwCursorPosition.Y }); + if (ok) + { + OUTSTREAM << "[ " << subwarn << " ]" << flush ; + ok = SetConsoleCursorPosition(hOutput, sbi.dwCursorPosition ); + } + } +#else + string fullprompt; + + if (!processing || queryinguser) + { + if (rl_prompt) fullprompt.append(rl_prompt); + } + OUTSTREAM << "\r" << fullprompt << srline << setw(width-getstringutf8size(fullprompt) - rl_end) << right << "[ " << subwarn << " ]" << flush ; + OUTSTREAM << "\r" << fullprompt << srline.substr(0,rl_point) << flush ; +#endif + } + #ifdef _WIN32 + Sleep(100); + #else + usleep(100000); + #endif + } + return NULL; +} + +bool firstpromptreceived = false; + void statechangehandle(string statestring) { char statedelim[2]={(char)0x1F,'\0'}; @@ -216,6 +308,7 @@ void statechangehandle(string statestring) nextstatedelimitpos = statestring.find(statedelim); if (newstate.compare(0, strlen("prompt:"), "prompt:") == 0) { + firstpromptreceived = true; changeprompt(newstate.substr(strlen("prompt:")).c_str(),true); } else if (newstate.compare(0, strlen("endtransfer:"), "endtransfer:") == 0) @@ -272,6 +365,40 @@ void statechangehandle(string statestring) { clientID = newstate.substr(strlen("clientID:")).c_str(); } + else if (newstate.compare(0, strlen("connectivity:"), "connectivity:") == 0) + { + int connectivitystatus = atoi(newstate.substr(strlen("connectivity:")).c_str()); + switch (connectivitystatus) + { + case MegaApi::RETRY_CONNECTIVITY: + setwarnstatus("SERVER SEEMS OFFLINE: commands that require connectivity might halt until connectivity is restored"); + break; + case MegaApi::RETRY_SERVERS_BUSY: + setwarnstatus("SERVER SEEMS BUSY: commands that require connectivity might take a while to complete"); + break; + case MegaApi::RETRY_RATE_LIMIT: + setwarnstatus("SERVER RATE limit reached: commands that require connectivity might take a while to complete"); + break; + case MegaApi::RETRY_LOCAL_LOCK: + setwarnstatus("SERVER SEEMS LOCKED: commands that require connectivity might halt until unlocked"); + break; + case MegaApi::RETRY_ESID: + setwarnstatus("SERVER SEEMS TO have a bad session ID: commands that requ ire connectivity might not to complete"); + break; + case MegaApi::RETRY_NONE: + setwarnstatus(""); + break; + default: + setwarnstatus("SERVER CONNECTIVY STATUS UNEXPECTED: commands that require connectivity might halt"); + break; + } + + if (connectivitystatus!= MegaApi::RETRY_NONE && !warnerThread) + { + warnerThread = new MegaThread(); //TODO: memleak + warnerThread->start(warner,NULL); + } + } else if (newstate.compare(0, strlen("progress:"), "progress:") == 0) { string rest = newstate.substr(strlen("progress:")); @@ -449,6 +576,14 @@ prompttype getprompt() { return prompt; } +#ifdef NO_READLINE +void doUpdateConsolePrompt(const char *newprompt) +{ + string fullprompt = promptpreffix; + fullprompt.append(newprompt); + console->updateInputPrompt(fullprompt); +} +#endif void setprompt(prompttype p, string arg) { @@ -478,7 +613,7 @@ void setprompt(prompttype p, string arg) if (p != COMMAND) { pw_buf_pos = 0; - console->updateInputPrompt(arg.empty() ? prompts[p] : arg); + doUpdateConsolePrompt(arg.empty() ? prompts[p] : arg.c_str()); } #endif } @@ -617,16 +752,29 @@ void install_rl_handler(const char *theprompt) rl_callback_handler_install(theprompt, store_line); #endif } + +void doInstallRLHandler() +{ + string fullprompt = promptpreffix; + fullprompt.append(*dynamicprompt ? dynamicprompt : prompts[COMMAND]); + install_rl_handler(fullprompt.c_str()); +} #endif -void changeprompt(const char *newprompt, bool redisplay) +void changeprompt(const char *newprompt, bool redisplay, const char *newpreffix) { MutexGuard g(mutexPrompt); if (*dynamicprompt) { - if (!strcmp(newprompt,dynamicprompt)) + if (!strcmp(newprompt,dynamicprompt) && (!newpreffix || !promptpreffix.compare(newpreffix))) + { return; //same prompt. do nth + } + } + if (newpreffix) + { + promptpreffix = newpreffix; } strncpy(dynamicprompt, newprompt, sizeof( dynamicprompt )); @@ -642,9 +790,12 @@ void changeprompt(const char *newprompt, bool redisplay) } #ifdef NO_READLINE - console->updateInputPrompt(newprompt); + if (readyforprompt) + { + doUpdateConsolePrompt(newprompt); + } #else - if (redisplay) + if (redisplay && readyforprompt) { // save line int saved_point = rl_point; @@ -658,7 +809,7 @@ void changeprompt(const char *newprompt, bool redisplay) rl_crlf(); } - install_rl_handler(*dynamicprompt ? dynamicprompt : prompts[COMMAND]); + doInstallRLHandler(); // restore line if (saved_line) @@ -1696,6 +1847,7 @@ void process_line(const char * line) // main loop #ifndef NO_READLINE + void readloop() { time_t lasttimeretrycons = 0; @@ -1734,9 +1886,10 @@ void readloop() mutexPrompt.lock(); if (requirepromptinstall) { - install_rl_handler(*dynamicprompt ? dynamicprompt : prompts[COMMAND]); + doInstallRLHandler(); handlerinstalled = false; + readyforprompt = true; // display prompt if (saved_line) @@ -1817,7 +1970,9 @@ void readloop() alreadyFinished = false; percentDowloaded = 0.0; mutexPrompt.lock(); + processing = true; process_line(line); + processing = false; requirepromptinstall = true; mutexPrompt.unlock(); @@ -1852,7 +2007,14 @@ void readloop() comms->registerForStateChanges(statechangehandle); //give it a while to communicate the state - sleepMilliSeconds(700); + int millistowait = 5000; + while (millistowait >0 && !firstpromptreceived) + { + sleepMilliSeconds(100); + millistowait-=100; + } + sleepMilliSeconds(300); // a little bit more to finish communicating all initial state + #if defined(_WIN32) && defined(USE_PORT_COMMS) // due to a failure in reconnecting to the socket, if the server was initiated in while registeringForStateChanges @@ -1869,7 +2031,8 @@ void readloop() { if (prompt == COMMAND) { - console->updateInputPrompt(*dynamicprompt ? dynamicprompt : prompts[COMMAND]); + doUpdateConsolePrompt(*dynamicprompt ? dynamicprompt : prompts[COMMAND]); + readyforprompt = true; } // command editing loop - exits when a line is submitted @@ -2064,11 +2227,12 @@ void mycompletefunct(char **c, int num_matches, int max_length) std::string readresponse(const char* question) { string response; + queryinguser = true; response = readline(question); rl_set_prompt(""); rl_replace_line("", 0); - rl_callback_handler_remove(); //To fix broken readline (e.g: upper key wouldnt work) + queryinguser = false; return response; } @@ -2076,12 +2240,12 @@ std::string readresponse(const char* question) std::string readresponse(const char* question) { COUT << question << flush; - console->updateInputPrompt(question); + doUpdateConsolePrompt(question); for (;;) { if (char* line = console->checkForCompletedInputLine()) { - console->updateInputPrompt(""); + doUpdateConsolePrompt(""); string response(line); free(line); return response; diff --git a/src/megacmdshell/megacmdshell.h b/src/megacmdshell/megacmdshell.h index 7a322bc2..b56c84cf 100644 --- a/src/megacmdshell/megacmdshell.h +++ b/src/megacmdshell/megacmdshell.h @@ -40,7 +40,7 @@ void restoreprompt(); void printprogress(long long completed, long long total, const char *title = "TRANSFERRING"); -void changeprompt(const char *newprompt, bool redisplay = false); +void changeprompt(const char *newprompt, bool redisplay = false, const char *newpreffix = NULL); const char * getUsageStr(const char *command); diff --git a/src/megacmdshell/megacmdshellcommunications.cpp b/src/megacmdshell/megacmdshellcommunications.cpp index 738735c6..b1eaade8 100644 --- a/src/megacmdshell/megacmdshellcommunications.cpp +++ b/src/megacmdshell/megacmdshellcommunications.cpp @@ -909,6 +909,7 @@ int MegaCmdShellCommunications::registerForStateChanges(void (*statechangehandle return -1; } + OUTSTREAM << "Connecting to server ...." << flush; #ifdef _WIN32 wstring wcommand=L"registerstatelistener"; int n = send(thesock,(char*)wcommand.data(),int(wcslen(wcommand.c_str())*sizeof(wchar_t)), MSG_NOSIGNAL); @@ -934,6 +935,9 @@ int MegaCmdShellCommunications::registerForStateChanges(void (*statechangehandle return -1; } + OUTSTREAM << "\r \r" << flush; + + if (listenerThread != NULL) { stopListener = true; diff --git a/src/megacmdshell/megacmdshellcommunicationsnamedpipes.cpp b/src/megacmdshell/megacmdshellcommunicationsnamedpipes.cpp index a0d5ceff..ae852b14 100644 --- a/src/megacmdshell/megacmdshellcommunicationsnamedpipes.cpp +++ b/src/megacmdshell/megacmdshellcommunicationsnamedpipes.cpp @@ -836,6 +836,7 @@ int MegaCmdShellCommunicationsNamedPipes::registerForStateChanges(void (*statech wstring wcommand=L"registerstatelistener"; DWORD n; + OUTSTREAM << "Connecting to server ...." << flush; if (!WriteFile(theNamedPipe,(char *)wcommand.data(),DWORD(wcslen(wcommand.c_str())*sizeof(wchar_t)), &n, NULL)) { cerr << "ERROR writing command to namedPipe: " << ERRNO << endl; @@ -850,6 +851,8 @@ int MegaCmdShellCommunicationsNamedPipes::registerForStateChanges(void (*statech return -1; } + OUTSTREAM << "\r \r" << flush; + if (listenerThread != NULL) { stopListener = true; diff --git a/src/megacmdutils.cpp b/src/megacmdutils.cpp index b3e73734..3921fd1e 100644 --- a/src/megacmdutils.cpp +++ b/src/megacmdutils.cpp @@ -1209,6 +1209,7 @@ bool setOptionsAndFlags(map *opts, map *flags, vect } + #ifndef _WIN32 string readablePermissions(int permvalue) {