From 8f2ff3eaf5ad5cfcba8fac0d3bda3d8b72330bed Mon Sep 17 00:00:00 2001 From: drbob Date: Thu, 9 Aug 2012 13:45:05 +0000 Subject: [PATCH] Changes to bring the SSH Menu system online! * Commandline Options added (./retroshare-nogui -h for help). * Added Password Hash system. * Shifted Menu output to std::string buffers. * Built interface to SSH server. * changed menus to lowercase. * Fixed SSH server restart issue. * Updates Output regularly now. git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-gxs-b1@5398 b45a01b8-16f6-495d-af2f-9b41ad6348cc --- retroshare-nogui/src/menu/menu.cc | 261 ++++++++++++--------- retroshare-nogui/src/menu/menu.h | 67 ++++-- retroshare-nogui/src/menu/menus.cc | 47 ++-- retroshare-nogui/src/menu/menus.h | 3 + retroshare-nogui/src/menu/menutest.h | 64 +++++- retroshare-nogui/src/retroshare-nogui.pro | 10 +- retroshare-nogui/src/retroshare.cc | 262 ++++++++++++++++++++-- retroshare-nogui/src/rstermserver.h | 13 ++ retroshare-nogui/src/ssh/rssshd.cc | 248 ++++++++++++++++++-- retroshare-nogui/src/ssh/rssshd.h | 25 ++- 10 files changed, 814 insertions(+), 186 deletions(-) create mode 100644 retroshare-nogui/src/rstermserver.h diff --git a/retroshare-nogui/src/menu/menu.cc b/retroshare-nogui/src/menu/menu.cc index 567dc4b29..504558837 100644 --- a/retroshare-nogui/src/menu/menu.cc +++ b/retroshare-nogui/src/menu/menu.cc @@ -6,48 +6,59 @@ #include +#include "util/rsstring.h" + /********************************************************** * Menu Base Interface. */ -int tailrec_printparents(Menu *m, std::ostream &out) + // RsTermServer Interface. +void MenuInterface::reset() +{ + mBase->reset(); + mCurrentMenu = mBase; + mInputRequired = false; +} + +int MenuInterface::tick(bool haveInput, char keypress, std::string &output) +{ + if (!haveInput) + { + /* make a harmless key */ + keypress = ' '; + } + + if ((mInputRequired) && (!haveInput)) + { + return 1; + } + + uint32_t rt = process(keypress, mDrawFlags, output); + mInputRequired = (rt == MENU_PROCESS_NEEDDATA); + + if (rt == MENU_PROCESS_QUIT) + { + return -1; + } + return 1; +} + + +int tailrec_printparents(Menu *m, std::string &buffer) { Menu *p = m->parent(); if (p) { - tailrec_printparents(p, out); + tailrec_printparents(p, buffer); } - out << m->ShortFnDesc() << " => "; - -#if 0 - MenuList *ml = dynamic_cast(m); - MenuOpBasicKey *mbk = dynamic_cast(m); - MenuOpTwoKeys *mtk = dynamic_cast(m); - - if (ml) - { - out << "MenuList@" << (void *) m << " "; - } - else if (mbk) - { - out << "MenuBasicKey@" << (void *) m << " "; - } - else if (mtk) - { - out << "MenuOpTwoKeys@" << (void *) m << " "; - } - else - { - out << "Menu@" << (void *) m << " "; - } -#endif - + buffer += m->ShortFnDesc(); + buffer += " => "; return 1; } -uint32_t MenuInterface::process(char key) +uint32_t MenuInterface::process(char key, uint32_t drawFlags, std::string &buffer) { #ifdef MENU_DEBUG @@ -66,18 +77,23 @@ uint32_t MenuInterface::process(char key) std::cout << std::endl; #endif // MENU_DEBUG - switch(rt) + + uint32_t base_rt = (rt & MENU_PROCESS_MASK); + bool needData = (rt & MENU_PROCESS_NEEDDATA); + switch(base_rt) { case MENU_PROCESS_NONE: + if (needData) + { + /* no redraw, this could be called many times */ + doRedraw = false; + } + break; + case MENU_PROCESS_DONE: /* no changes - operation performed, or noop */ break; - case MENU_PROCESS_NEEDDATA: - /* no redraw, this could be called many times */ - doRedraw = false; - break; - case MENU_PROCESS_ERROR: /* Show Error at top of Page */ showError = true; @@ -114,6 +130,11 @@ uint32_t MenuInterface::process(char key) break; } + if (drawFlags & MENU_DRAW_FLAGS_ECHO) + { + buffer += key; + } + /* now we redraw, and wait for next data */ if (!doRedraw) { @@ -123,7 +144,7 @@ uint32_t MenuInterface::process(char key) /* HEADER */ for(int i = 0; i < 20; i++) { - std::cout << std::endl; + buffer += "\r\n"; } /* ERROR */ @@ -138,17 +159,19 @@ uint32_t MenuInterface::process(char key) } /* MENU PAGE */ - drawHeader(); - mCurrentMenu->drawPage(); - return MENU_PROCESS_NEEDDATA; + drawHeader(drawFlags, buffer); + mCurrentMenu->drawPage(drawFlags, buffer); + + if (needData) + return MENU_PROCESS_NEEDDATA; + + return MENU_PROCESS_NONE; } -uint32_t MenuInterface::drawHeader() +uint32_t MenuInterface::drawHeader(uint32_t drawFlags, std::string &buffer) { - std::cout << "======================================================="; - std::cout << std::endl; - std::cout << "Retroshare Terminal Menu V2.xxxx ======================"; - std::cout << std::endl; + buffer += "=======================================================\r\n"; + buffer += "Retroshare Terminal Menu V2.xxxx ======================\r\n"; unsigned int nTotal = 0; unsigned int nConnected = 0; @@ -200,18 +223,19 @@ uint32_t MenuInterface::drawHeader() float upKb = 0; rsicontrol -> ConfigGetDataRates(downKb, upKb); - std::cout << "Friends " << nConnected << "/" << nTotal; - std::cout << " Network: " << natState; - std::cout << std::endl; - std::cout << "Down: " << downKb << " (kB/s) "; - std::cout << " Up: " << upKb << " (kB/s) "; - std::cout << std::endl; - std::cout << "Menu State: "; - tailrec_printparents(mCurrentMenu, std::cout); - std::cout << std::endl; + rs_sprintf_append(buffer, "Friends %d / %d Network: %s\r\n", + nConnected, nTotal, natState.c_str()); - std::cout << "======================================================="; - std::cout << std::endl; + rs_sprintf_append(buffer, "Down: %2.2f Up %2.2f\r\n", downKb, upKb); + + std::string menuState; + tailrec_printparents(mCurrentMenu, menuState); + + buffer += "Menu State: "; + buffer += menuState; + buffer += "\r\n"; + + buffer += "=======================================================\r\n"; return 1; } @@ -251,6 +275,16 @@ int Menu::addMenuItem(char key, Menu *child) return 1; } +void Menu::reset() +{ + mSelectedMenu = NULL; + std::map::iterator it; + for(it = mChildren.begin(); it != mChildren.end(); it++) + { + it->second->reset(); + } +} + uint32_t Menu::process(char key) { @@ -309,11 +343,11 @@ uint32_t Menu::process_children(char key) /* now return, depending on type */ switch(it->second->op()) { - /* Think I can handle these the same! Both will call DrawPage, - * then Process on New Menu - */ - case MENU_OP_NEEDDATA: + setSelectedMenu(it->second); + return MENU_PROCESS_MENU | MENU_PROCESS_NEEDDATA; + break; + case MENU_OP_SUBMENU: setSelectedMenu(it->second); return MENU_PROCESS_MENU; @@ -335,31 +369,40 @@ uint32_t Menu::process_children(char key) } -uint32_t Menu::drawPage() +uint32_t Menu::drawPage(uint32_t drawFlags, std::string &buffer) { - std::cout << "Universal Commands ( "; - std::cout << (char) MENU_KEY_QUIT << ":Quit "; - std::cout << (char) MENU_KEY_HELP << ":Help "; - std::cout << (char) MENU_KEY_TOP << ":Top "; - std::cout << (char) MENU_KEY_UP << ":Up "; - std::cout << ")"; - std::cout << std::endl; + buffer += "Universal Commands ( "; + if (!(drawFlags & MENU_DRAW_FLAGS_NOQUIT)) + { + buffer += (char) MENU_KEY_QUIT; + buffer += ":Quit "; + } + buffer += (char) MENU_KEY_HELP; + buffer += ":Help "; + buffer += (char) MENU_KEY_TOP; + buffer += ":Top "; + buffer += (char) MENU_KEY_UP; + buffer += ":Up "; + buffer += ")"; + buffer += "\r\n"; - std::cout << "Specific Commands ("; + buffer += "Specific Commands ( "; std::map::iterator it; for(it = mChildren.begin(); it != mChildren.end(); it++) { - std::cout << (char) it->first << ":"; - std::cout << it->second->ShortFnDesc() << " "; + buffer += (char) it->first; + buffer += ":"; + buffer += it->second->ShortFnDesc(); + buffer += " "; } - std::cout << ")"; - std::cout << std::endl; + buffer += ")"; + buffer += "\r\n"; return 1; } -uint32_t Menu::drawHelpPage() +uint32_t Menu::drawHelpPage(uint32_t drawFlags, std::string &buffer) { std::cout << "Menu Help: Universal Commands are:"; std::cout << std::endl; @@ -391,22 +434,20 @@ uint32_t Menu::drawHelpPage() */ -uint32_t MenuList::drawPage() +uint32_t MenuList::drawPage(uint32_t drawFlags, std::string &buffer) { - Menu::drawPage(); + Menu::drawPage(drawFlags, buffer); - std::cout << "Navigation Commands ("; - //std::cout << (char) MENULIST_KEY_LIST << ":List "; - std::cout << (char) MENULIST_KEY_NEXT << ":Next "; - std::cout << (char) MENULIST_KEY_PREV << ":Prev "; - std::cout << ")"; - std::cout << std::endl; + buffer += "Navigation Commands ("; + buffer += (char) MENULIST_KEY_NEXT; + buffer += ":Next "; + buffer += (char) MENULIST_KEY_PREV; + buffer += ":Prev "; + buffer += ")"; + buffer += "\r\n"; - std::cout << "MenuList::Internals "; - std::cout << "ListSize: " << getListCount(); - std::cout << " SelectIdx: " << mSelectIdx; - std::cout << " Cursor: " << mCursor; - std::cout << std::endl; + rs_sprintf_append(buffer, "MenuList::Internals ListSize: %d, SelectIdx: %d Cursor: %d\r\n", + getListCount(), mSelectIdx, mCursor); int i = 0; @@ -420,28 +461,28 @@ uint32_t MenuList::drawPage() if (mSelectIdx >= 0) { - std::cout << "Current Selection Idx: " << mSelectIdx << " : "; + + rs_sprintf_append(buffer, "Current Selection Idx: %d : ", mSelectIdx); std::string desc; if (getEntryDesc(mSelectIdx, desc) & (desc != "")) { - std::cout << desc; + buffer += desc; } else { - std::cout << "Missing Description"; + buffer += "Missing Description"; } - std::cout << std::endl; + buffer += "\r\n"; } else { - std::cout << "No Current Selection: Use 0 - 9 to choose an Entry"; - std::cout << std::endl; + buffer += "No Current Selection: Use 0 - 9 to choose an Entry"; + buffer += "\r\n"; } - std::cout << "Showing " << startCount; - std::cout << " to " << endCount << " of " << listCount << " Entries"; - std::cout << std::endl; + rs_sprintf_append(buffer, "Showing %d to %d of %d Entries\r\n", + startCount, endCount, listCount); std::list::iterator it; for (it = mList.begin(); it != mList.end(); it++, i++) @@ -451,32 +492,35 @@ uint32_t MenuList::drawPage() { if (i == mSelectIdx) { - std::cout << "SELECTED => (" << curIdx << ") "; + rs_sprintf_append(buffer, "SELECTED (%d) ", curIdx); } else { - std::cout << "\t(" << curIdx << ") "; + rs_sprintf_append(buffer, "\t(%d) ", curIdx); } std::string desc; if (getEntryDesc(i, desc) & (desc != "")) { - std::cout << desc; + buffer += desc; } else { - std::cout << *it << " => "; - std::cout << "Missing Description"; + buffer += *it; + buffer += " => "; + buffer += "Missing Description"; } - std::cout << std::endl; + buffer += "\r\n"; } } + buffer += "\r\n"; + buffer += "Make Your Choice > "; return 1; } -uint32_t MenuList::drawHelpPage() +uint32_t MenuList::drawHelpPage(uint32_t drawFlags, std::string &buffer) { - Menu::drawPage(); + Menu::drawHelpPage(drawFlags, buffer); std::cout << "MenuList Help: Navigation Commands are:"; std::cout << std::endl; @@ -499,11 +543,18 @@ uint32_t MenuList::drawHelpPage() } +void MenuList::reset() +{ + Menu::reset(); // clears children too. + + mList.clear(); + mSelectIdx = -1; + mCursor = 0; +} + uint32_t MenuList::op() { - /* load friend list*/ mList.clear(); - //rsPeers->getGpgAcceptedList(mList); mSelectIdx = -1; mCursor = 0; @@ -734,7 +785,7 @@ uint32_t MenuOpLineInput::process(char key) { /* read data in and add to buffer */ mInput += key; - if (key != '\n') + if ((key != '\n') && (key != '\r')) { return MENU_PROCESS_NEEDDATA; } diff --git a/retroshare-nogui/src/menu/menu.h b/retroshare-nogui/src/menu/menu.h index 23e3c015d..4d10c762a 100644 --- a/retroshare-nogui/src/menu/menu.h +++ b/retroshare-nogui/src/menu/menu.h @@ -7,25 +7,30 @@ #include #include -#define MENU_PROCESS_NONE 0 -#define MENU_PROCESS_TOP 1 -#define MENU_PROCESS_MENU 2 -#define MENU_PROCESS_NEEDDATA 3 -#define MENU_PROCESS_DONE 4 -#define MENU_PROCESS_QUIT 5 -#define MENU_PROCESS_SHUTDOWN 6 -#define MENU_PROCESS_HELP 7 -#define MENU_PROCESS_ERROR 8 +#include "rstermserver.h" // generic processing command. + +#define MENU_PROCESS_MASK 0x0fff + +#define MENU_PROCESS_NONE 0x0000 +#define MENU_PROCESS_TOP 0x0001 +#define MENU_PROCESS_MENU 0x0002 +#define MENU_PROCESS_DONE 0x0004 +#define MENU_PROCESS_QUIT 0x0008 +#define MENU_PROCESS_SHUTDOWN 0x0010 +#define MENU_PROCESS_HELP 0x0020 +#define MENU_PROCESS_ERROR 0x0040 + +#define MENU_PROCESS_NEEDDATA 0x1000 // Able to be OR'd with ANOTHER CASE. #define MENU_KEY_QUIT 'Q' -#define MENU_KEY_HELP 'H' -#define MENU_KEY_TOP 'T' -#define MENU_KEY_REPEAT 'R' -#define MENU_KEY_UP 'U' +#define MENU_KEY_HELP 'h' +#define MENU_KEY_TOP 't' +#define MENU_KEY_REPEAT 'r' +#define MENU_KEY_UP 'u' -#define MENULIST_KEY_LIST 'L' // Don't need this. -#define MENULIST_KEY_NEXT 'N' -#define MENULIST_KEY_PREV 'P' +#define MENULIST_KEY_LIST 'l' // Don't need this. +#define MENULIST_KEY_NEXT 'n' +#define MENULIST_KEY_PREV 'p' #define MENU_OP_ERROR 0 #define MENU_OP_INSTANT 1 @@ -35,6 +40,11 @@ #define MENU_ENTRY_NONE 0 #define MENU_ENTRY_OKAY 1 +#define MENU_DRAW_FLAGS_STD 0 +#define MENU_DRAW_FLAGS_HTML 1 +#define MENU_DRAW_FLAGS_ECHO 2 +#define MENU_DRAW_FLAGS_NOQUIT 4 + class Menu; class Screen; @@ -49,19 +59,20 @@ virtual ~Menu(); Menu *selectedMenu() { return mSelectedMenu; } int addMenuItem(char key, Menu *child); +virtual void reset(); virtual uint32_t op() { return MENU_OP_SUBMENU; } /* what type is it? returns SUBMENU, INSTANT, NEEDINPUT */ virtual uint32_t process(char key); // THE BIT STILL TO BE DEFINED! std::string ShortFnDesc() { return mShortDesc; }// Menu Text (for Help). +virtual uint32_t drawPage(uint32_t drawFlags, std::string &buffer); +virtual uint32_t drawHelpPage(uint32_t drawFlags, std::string &buffer); //virtual std::string menuText() = 0; //virtual std::string menuHelp() = 0; virtual void setOpMessage(std::string msg) { return; } virtual void setErrorMessage(std::string msg) { return; } -virtual uint32_t drawPage(); // { return 1; } //= 0; -virtual uint32_t drawHelpPage(); // { return 1; } //= 0; virtual uint32_t showError() { return 1; } //= 0; virtual uint32_t showHelp() { return 1; } //= 0; @@ -92,6 +103,7 @@ class MenuList: public Menu public: MenuList(std::string shortDesc): Menu(shortDesc) { return; } +virtual void reset(); virtual uint32_t op(); virtual uint32_t process(char key); @@ -103,8 +115,8 @@ virtual uint32_t process(char key); virtual int getEntryDesc(int idx, std::string &desc); // Output. -virtual uint32_t drawPage(); -virtual uint32_t drawHelpPage(); +virtual uint32_t drawPage(uint32_t drawFlags, std::string &buffer); +virtual uint32_t drawHelpPage(uint32_t drawFlags, std::string &buffer); protected: virtual uint32_t list_process(char key); @@ -161,17 +173,24 @@ protected: -class MenuInterface +class MenuInterface: public RsTermServer { public: - MenuInterface(Menu *b) :mCurrentMenu(b), mBase(b) { return; } - uint32_t process(char key); - uint32_t drawHeader(); + MenuInterface(Menu *b, uint32_t drawFlags) :mCurrentMenu(b), mBase(b), mDrawFlags(drawFlags), mInputRequired(false) { return; } + uint32_t process(char key, uint32_t drawFlags, std::string &buffer); + uint32_t drawHeader(uint32_t drawFlags, std::string &buffer); + + // RsTermServer Interface. + virtual void reset(); + virtual int tick(bool haveInput, char keypress, std::string &output); + private: Menu *mCurrentMenu; Menu *mBase; + uint32_t mDrawFlags; + bool mInputRequired; }; diff --git a/retroshare-nogui/src/menu/menus.cc b/retroshare-nogui/src/menu/menus.cc index d81f24e95..f783abc66 100644 --- a/retroshare-nogui/src/menu/menus.cc +++ b/retroshare-nogui/src/menu/menus.cc @@ -10,30 +10,30 @@ // or L, N, P (list ops). // or 0-9,a-f (list selection). -#define MENU_FRIENDS_KEY_ADD 'A' -#define MENU_FRIENDS_KEY_VIEW 'V' -#define MENU_FRIENDS_KEY_REMOVE 'D' -#define MENU_FRIENDS_KEY_CHAT 'C' +#define MENU_FRIENDS_KEY_ADD 'a' +#define MENU_FRIENDS_KEY_VIEW 'v' +#define MENU_FRIENDS_KEY_REMOVE 'd' +#define MENU_FRIENDS_KEY_CHAT 'c' -#define MENU_TRANSFER_KEY_STOP 'S' -#define MENU_TRANSFER_KEY_CANCEL 'C' +#define MENU_TRANSFER_KEY_STOP 's' +#define MENU_TRANSFER_KEY_CANCEL 'c' -#define MENU_SEARCH_KEY_ADD 'A' -#define MENU_SEARCH_KEY_REMOVE 'D' -#define MENU_SEARCH_KEY_VIEW 'V' -#define MENU_SEARCH_KEY_DOWNLOAD 'G' +#define MENU_SEARCH_KEY_ADD 'a' +#define MENU_SEARCH_KEY_REMOVE 'd' +#define MENU_SEARCH_KEY_VIEW 'v' +#define MENU_SEARCH_KEY_DOWNLOAD 'g' -#define MENU_FRIENDS_KEY_ADD 'A' -#define MENU_FRIENDS_KEY_VIEW 'V' -#define MENU_FRIENDS_KEY_REMOVE 'D' -#define MENU_FRIENDS_KEY_CHAT 'C' +#define MENU_FRIENDS_KEY_ADD 'a' +#define MENU_FRIENDS_KEY_VIEW 'v' +#define MENU_FRIENDS_KEY_REMOVE 'd' +#define MENU_FRIENDS_KEY_CHAT 'c' -#define MENU_TOPLEVEL_KEY_FRIENDS 'F' -#define MENU_TOPLEVEL_KEY_NETWORK 'W' -#define MENU_TOPLEVEL_KEY_TRANSFER 'D' -#define MENU_TOPLEVEL_KEY_SEARCH 'S' -#define MENU_TOPLEVEL_KEY_FORUMS 'O' +#define MENU_TOPLEVEL_KEY_FRIENDS 'f' +#define MENU_TOPLEVEL_KEY_NETWORK 'w' +#define MENU_TOPLEVEL_KEY_TRANSFER 'd' +#define MENU_TOPLEVEL_KEY_SEARCH 's' +#define MENU_TOPLEVEL_KEY_FORUMS 'o' Menu *CreateMenuStructure(NotifyTxt *notify) @@ -199,7 +199,7 @@ int MenuListTransfer::getEntryDesc(int idx, std::string &desc) return 0; } - float frac = (float) info.transfered / info.size; + float frac = 100.0 * (float) info.transfered / info.size; if (frac != 1.0) { @@ -335,6 +335,13 @@ int MenuListSearch::removeSearch(std::string strSearchId) return 1; } +uint32_t MenuOpSearchNew::drawPage(uint32_t drawFlags, std::string &buffer) +{ + buffer += "Enter New Search Term > "; + return 1; +} + + uint32_t MenuOpSearchNew::process_lines(std::string input) { diff --git a/retroshare-nogui/src/menu/menus.h b/retroshare-nogui/src/menu/menus.h index 61a26639e..44806a013 100644 --- a/retroshare-nogui/src/menu/menus.h +++ b/retroshare-nogui/src/menu/menus.h @@ -139,6 +139,9 @@ class MenuOpSearchNew: public MenuOpLineInput MenuOpSearchNew() :MenuOpLineInput("New") { return; } virtual uint32_t process_lines(std::string input); + virtual uint32_t drawPage(uint32_t drawFlags, std::string &buffer); + + }; diff --git a/retroshare-nogui/src/menu/menutest.h b/retroshare-nogui/src/menu/menutest.h index 8ff9ef732..94c58d4ee 100644 --- a/retroshare-nogui/src/menu/menutest.h +++ b/retroshare-nogui/src/menu/menutest.h @@ -15,7 +15,9 @@ int tick() { int c = mIn.get(); uint8_t key = (uint8_t) c; - mMenus->process(key); + uint32_t drawFlags = 0; + std::string buffer; + mMenus->process(key, drawFlags, buffer); return 1; } @@ -28,5 +30,65 @@ private: std::ostream &mOut; }; +#include +#include +#include "rstermserver.h" + +class RsConsole +{ + public: + + RsConsole(RsTermServer *s, int infd, int outfd) + :mServer(s), mIn(infd), mOut(outfd) + { + const int fcflags = fcntl(mIn,F_GETFL); + if (fcflags < 0) + { + std::cerr << "RsConsole() ERROR getting fcntl FLAGS"; + std::cerr << std::endl; + exit(1); + } + if (fcntl(mIn,F_SETFL,fcflags | O_NONBLOCK) <0) + { + std::cerr << "RsConsole() ERROR setting fcntl FLAGS"; + std::cerr << std::endl; + exit(1); + } + } + + int tick() + { + char buf; + std::string output; + int size = read(mIn, &buf, 1); + + bool haveInput = (size > 0); + + int rt = mServer->tick(haveInput, buf, output); + + if (output.size() > 0) + { + write(mOut, output.c_str(), output.size()); + } + + if (rt < 0) + { + std::cerr << "Exit Request"; + exit(1); + } + + if (!haveInput) + { + return 0; + } + + return 1; + } + +private: + + RsTermServer *mServer; + int mIn, mOut; +}; diff --git a/retroshare-nogui/src/retroshare-nogui.pro b/retroshare-nogui/src/retroshare-nogui.pro index 5c72ab734..c4822441a 100644 --- a/retroshare-nogui/src/retroshare-nogui.pro +++ b/retroshare-nogui/src/retroshare-nogui.pro @@ -2,7 +2,7 @@ TEMPLATE = app TARGET = retroshare-nogui CONFIG += bitdht #CONFIG += introserver -#CONFIG += sshserver +CONFIG += sshserver CONFIG += debug debug { @@ -23,6 +23,7 @@ linux-* { LIBS += ../../libretroshare/src/lib/libretroshare.a LIBS += ../../openpgpsdk/src/lib/libops.a -lbz2 LIBS += -lssl -lupnp -lixml -lgnome-keyring + LIBS += -lsqlite3 } linux-g++ { @@ -134,10 +135,12 @@ sshserver { # # You can connect from a standard ssh, eg: ssh -p 7022 127.0.0.1 # - # The Menu system is available from the command-line now, - # but not over SSH yet... + # The Menu system is available from the command-line (-T) and SSH (-S) # if it get covered by debug gunk, just press to refresh. # + # ./retroshare-nogui -h provides some more instructions. + # + INCLUDEPATH += ../../../lib/libssh-0.5.2/include/ LIBS += ../../../lib/libssh-0.5.2/build/src/libssh.a LIBS += ../../../lib/libssh-0.5.2/build/src/threads/libssh_threads.a @@ -146,6 +149,7 @@ sshserver { HEADERS += menu/menu.h \ menu/menus.h \ + rstermserver.h \ SOURCES += menu/menu.cc \ menu/menus.cc \ diff --git a/retroshare-nogui/src/retroshare.cc b/retroshare-nogui/src/retroshare.cc index 0f67cd33d..917511cce 100644 --- a/retroshare-nogui/src/retroshare.cc +++ b/retroshare-nogui/src/retroshare.cc @@ -82,8 +82,198 @@ int main(int argc, char **argv) * LoadPassword(...) set password for existing certificate. **/ + bool strictCheck = true; + +#ifdef RS_SSH_SERVER + /* parse commandline for additional nogui options */ + + int c; + // libretroshare's getopt str - so don't use any of these: "hesamui:p:c:w:l:d:U:r:R:" + // need options for + // enable SSH. (-S) + // set user/password for SSH. -L "user:pwdhash" + // accept RSA Key Auth. -K "RsaPubKeyFile" + // Terminal mode. -T + bool enableSsh = false; + bool enableSshHtml = false; + bool enableSshPwd = false; + bool enableTerminal = false; + bool enableSshRsa = false; + bool genPwdHash = false; + std::string sshUser = "user"; + std::string sshPwdHash = ""; + std::string sshRsaFile = ""; + std::string sshPortStr = "7022"; + + while((c = getopt(argc, argv,"hTL:P:K:GS::")) != -1) + { + switch(c) + { + case 'S': + enableSsh = true; + if (optarg) + { + sshPortStr = optarg; // optional. + } + strictCheck = false; + break; + case 'H': + enableSshHtml = true; + strictCheck = false; + break; + case 'T': + enableTerminal = true; + strictCheck = false; + break; + case 'L': + sshUser = optarg; + strictCheck = false; + break; + case 'P': + enableSshPwd = true; + sshPwdHash = optarg; + strictCheck = false; + break; +#if 0 // NOT FINISHED YET. + case 'K': + enableSshRsa = true; + sshRsaFile = optarg; + strictCheck = false; + break; +#endif + case 'G': + genPwdHash = true; + break; + case 'h': + /* nogui help */ + std::cerr << argv[0] << std::endl; + std::cerr << "Specific Help Options: " << std::endl; + std::cerr << "\t-G Generate a Password Hash for SSH Server" << std::endl; + std::cerr << "\t-T Enable Terminal Interface" << std::endl; + std::cerr << "\t-S [port] Enable SSH Server, optionally specify port" << std::endl; + std::cerr << "\t-L Specify SSH login user (default:user)" << std::endl; + std::cerr << "\t-P Enable SSH login via Password" << std::endl; + //std::cerr << "\t-K [rsapubkeyfile] Enable SSH login via RSA key" << std::endl; + //std::cerr << "\t NB: Two Factor Auth, specify both -P & -K" << std::endl; + std::cerr << std::endl; + std::cerr << "\t To setup rs-nogui as a SSH Server is a three step process: " << std::endl; + std::cerr << "\t 1) \"ssh-keygen -t rsa -f rs_ssh_host_rsa_key\" " << std::endl; + std::cerr << "\t 2) \"./retroshare-nogui -G\" " << std::endl; + std::cerr << "\t 3) \"./retroshare-nogui -S [port] -L -P \" " << std::endl; + std::cerr << std::endl; + std::cerr << "Further Options "; + /* libretroshare will call exit(1) after printing its options */ + break; + + default: + /* let others through - for libretroshare */ + break; + } + } + // reset optind for Retroshare commandline arguments. + optind = 1; + + if (genPwdHash) + { + std::string saltBin; + std::string pwdHashRadix64; + std::string sshPwdForHash = ""; + + std::cout << "Type in your Password:" << std::flush; + char pwd[1024]; + if (!fgets(pwd, 1024, stdin)) + { + std::cerr << "Error Reading Password"; + std::cerr << std::endl; + exit(1); + } + + // strip newline. + for(int i = 0; (i < 1024) && (pwd[i] != '\n') && (pwd[i] != '\0'); i++) + { + sshPwdForHash += pwd[i]; + } + + std::cerr << "Chosen Password : " << sshPwdForHash; + std::cerr << std::endl; + + + GenerateSalt(saltBin); + if (!GeneratePasswordHash(saltBin, sshPwdForHash, pwdHashRadix64)) + { + std::cerr << "Error Generating Password Hash, password probably too short"; + std::cerr << pwdHashRadix64; + std::cerr << std::endl; + exit(1); + } + + std::cout << "Generated Password Hash for rs-nogui: "; + std::cout << pwdHashRadix64; + std::cout << std::endl; + std::cout << std::endl; + + /* checking match */ + if (CheckPasswordHash(pwdHashRadix64, sshPwdForHash)) + { + std::cerr << "Passed Check Okay!"; + std::cerr << std::endl; + } + else + { + std::cerr << "ERROR: Failed CheckPassword!"; + std::cerr << std::endl; + exit(1); + } + + + std::cerr << "Usage: ./retroshare-nogui -S [port] -L -P " << pwdHashRadix64; + std::cerr << std::endl; + exit(1); + } + + + /* enforce conditions */ + if (((enableSshRsa) || (enableSshPwd)) && (!enableSsh)) + { + std::cerr << "ERROR: SSH Server (-S) must be enabled to specify SSH Pwd (-P) or SSH RSA (-K)"; + std::cerr << std::endl; + exit(1); + } + + if (enableSsh && (!enableSshRsa) && (!enableSshPwd)) + { + std::cerr << "ERROR: One of (or both) SSH Pwd (-P) and SSH RSA (-K) must be specified with SSH Server (-S)"; + std::cerr << std::endl; + exit(1); + } + + + /* parse -S, -L & -K parameters */ + if (enableSshRsa) + { + /* check the file exists */ + /* TODO */ + + } + + if (enableSsh) + { + /* try parse it */ + /* TODO */ + + } + + if (enableSshPwd) + { + /* try parse it */ + /* TODO */ + + } +#endif + + RsInit::InitRsConfig(); - int initResult = RsInit::InitRetroShare(argc, argv); + int initResult = RsInit::InitRetroShare(argc, argv, strictCheck); if (initResult < 0) { /* Error occured */ @@ -143,8 +333,25 @@ int main(int argc, char **argv) #ifdef RS_SSH_SERVER // Says it must be called before all the threads are launched! */ // NB: this port number is not currently used. - RsSshd *ssh = RsSshd::InitRsSshd(22, "rs_ssh_host_rsa_key"); - ssh->adduser("anrsuser", "test"); + RsSshd *ssh = NULL; + + if (enableSsh) + { + ssh = RsSshd::InitRsSshd(sshPortStr, "rs_ssh_host_rsa_key"); + // TODO Parse Option + if (enableSshRsa) + { + //ssh->adduser("anrsuser", "test"); + } + + if (enableSshPwd) + { + ssh->adduserpwdhash(sshUser, sshPwdHash); + } + + } + + #endif /* Start-up libretroshare server threads */ @@ -155,11 +362,30 @@ int main(int argc, char **argv) #endif #ifdef RS_SSH_SERVER - ssh->start(); + uint32_t baseDrawFlags = 0; + if (enableSshHtml) + baseDrawFlags = MENU_DRAW_FLAGS_HTML; + + if (enableSsh) + { + /* create menu system for SSH */ + Menu *baseMenu = CreateMenuStructure(notify); + MenuInterface *menuInterface = new MenuInterface(baseMenu, baseDrawFlags | MENU_DRAW_FLAGS_ECHO); + ssh->setTermServer(menuInterface); + + ssh->start(); + } + + //MenuTest *menuTerminal = NULL; + RsConsole *menuTerminal = NULL; + if (enableTerminal) + { + /* Terminal Version */ + Menu *baseMenu = CreateMenuStructure(notify); + MenuInterface *menuInterface = new MenuInterface(baseMenu, baseDrawFlags | MENU_DRAW_FLAGS_NOQUIT); + menuTerminal = new RsConsole(menuInterface, fileno(stdin), fileno(stdout)); + } - Menu *baseMenu = CreateMenuStructure(notify); - MenuInterface *menuInterface = new MenuInterface(baseMenu); - MenuTest menuTest(menuInterface, std::cin, std::cout); #endif @@ -167,20 +393,30 @@ int main(int argc, char **argv) while(1) { //std::cerr << "GUI Tick()" << std::endl; -#ifndef WINDOWS_SYS - sleep(1); -#else - Sleep(1000); -#endif #ifdef RS_INTRO_SERVER rsIS.tick(); #endif + int rt = 0; #ifdef RS_SSH_SERVER - menuTest.tick(); + if (menuTerminal) + { + rt = menuTerminal->tick(); + } #endif + // If we have a MenuTerminal ... + // only want to sleep if there is no input. (rt == 0). + if (rt == 0) + { +#ifndef WINDOWS_SYS + sleep(1); +#else + Sleep(1000); +#endif + } + } return 1; } diff --git a/retroshare-nogui/src/rstermserver.h b/retroshare-nogui/src/rstermserver.h new file mode 100644 index 000000000..2658e8c93 --- /dev/null +++ b/retroshare-nogui/src/rstermserver.h @@ -0,0 +1,13 @@ +#ifndef RS_TERM_SERVER_H +#define RS_TERM_SERVER_H + +class RsTermServer +{ +public: + /* this must be regularly ticked to update the display */ + virtual void reset() = 0; + virtual int tick(bool haveInput, char keypress, std::string &output) = 0; +}; + + +#endif // RS_TERM_SERVER_H diff --git a/retroshare-nogui/src/ssh/rssshd.cc b/retroshare-nogui/src/ssh/rssshd.cc index 493151a0d..0d486d43f 100644 --- a/retroshare-nogui/src/ssh/rssshd.cc +++ b/retroshare-nogui/src/ssh/rssshd.cc @@ -30,12 +30,12 @@ clients must be made or how a client should react. RsSshd *rsSshd = NULL; // External Reference Variable. // NB: This must be called EARLY before all the threads are launched. -RsSshd *RsSshd::InitRsSshd(uint16_t port, std::string rsakeyfile) +RsSshd *RsSshd::InitRsSshd(std::string portStr, std::string rsakeyfile) { ssh_threads_set_callbacks(ssh_threads_get_pthread()); ssh_init(); - rsSshd = new RsSshd(port); + rsSshd = new RsSshd(portStr); if (rsSshd->init(rsakeyfile)) { return rsSshd; @@ -46,12 +46,13 @@ RsSshd *RsSshd::InitRsSshd(uint16_t port, std::string rsakeyfile) } -RsSshd::RsSshd(uint16_t port) -:mSshMtx("sshMtx"), mPort(port), mChannel(0) +RsSshd::RsSshd(std::string portStr) +:mSshMtx("sshMtx"), mPortStr(portStr), mChannel(0) { mState = RSSSHD_STATE_NULL; mBindState = 0; + mTermServer = NULL; return; } @@ -62,13 +63,12 @@ int RsSshd::init(std::string pathrsakey) { mBind=ssh_bind_new(); - mSession=ssh_new(); //ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_DSAKEY, KEYS_FOLDER "ssh_host_dsa_key"); //ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_RSAKEY, KEYS_FOLDER "ssh_host_rsa_key"); //ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); - ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_BINDPORT_STR, "7022"); + ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_BINDPORT_STR, mPortStr.c_str()); //ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_DSAKEY, arg); //ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_HOSTKEY, arg); ssh_bind_options_set(mBind, SSH_BIND_OPTIONS_RSAKEY, pathrsakey.c_str()); @@ -136,6 +136,7 @@ int RsSshd::listenConnect() std::cerr << std::endl; } + mSession=ssh_new(); int r=ssh_bind_accept(mBind,mSession); if(r==SSH_ERROR) { @@ -188,7 +189,8 @@ int RsSshd::interactive() std::cerr << "RsSshd::interactive()"; std::cerr << std::endl; - doEcho(); + doTermServer(); + //doEcho(); return 1; } @@ -337,12 +339,74 @@ int RsSshd::doEcho() } +int RsSshd::setTermServer(RsTermServer *s) +{ + mTermServer = s; + return 1; +} + + + +int RsSshd::doTermServer() +{ + std::cerr << "RsSshd::doTermServer()"; + std::cerr << std::endl; + + if (!mTermServer) + { + std::cerr << "RsSshd::doTermServer() ERROR Not Set"; + std::cerr << std::endl; + return 0; + } + + mTermServer->reset(); // clear everything for new user. + + bool okay = true; + while(okay) + { + char buf; + int size = ssh_channel_read_nonblocking(mChannel, &buf, 1, 0); + bool haveInput = (size > 0); + std::string output; + + int rt = mTermServer->tick(haveInput, buf, output); + + if (output.size() > 0) + { + ssh_channel_write(mChannel, output.c_str(), output.size()); + } + + if (!haveInput) + { + /* have a little sleep */ + sleep(1); //0000000); // 1/10th sec. + //usleep(10000000); // 1/10th sec. + } + else + { + usleep(10000); // 1/100th sec. + } + + if (rt < 0) + { + okay = false; // exit. + } + } + + std::cerr << "RsSshd::doTermServer() Finished"; + std::cerr << std::endl; + + return 1; +} + + int RsSshd::cleanupSession() { std::cerr << "RsSshd::cleanupSession()"; std::cerr << std::endl; ssh_disconnect(mSession); + ssh_free(mSession); return 1; } @@ -433,15 +497,17 @@ int RsSshd::auth_password_basic(char *name, char *pwd) } #endif // ALLOW_CLEARPWDS -#define RSSSHD_HASH_PWD_LENGTH 40 +//#define RSSSHD_HASH_PWD_LENGTH 40 int RsSshd::adduserpwdhash(std::string username, std::string hash) { +#if 0 if (hash.length() != RSSSHD_HASH_PWD_LENGTH) { std::cerr << "RsSshd::adduserpwdhash() Hash Wrong Length"; return 0; } +#endif if (username.length() < RSSSHD_MIN_USERNAME) { @@ -464,21 +530,18 @@ int RsSshd::adduserpwdhash(std::string username, std::string hash) int RsSshd::auth_password_hashed(char *name, char *pwd) { - std::cerr << "RsSshd::auth_password_hashed() Not Finished Yet!"; - return 0; - std::string username(name); std::string password(pwd); std::map::iterator it; - it = mPasswords.find(username); - if (it == mPasswords.end()) + it = mPwdHashs.find(username); + if (it == mPwdHashs.end()) { std::cerr << "RsSshd::auth_password_hashed() Unknown username"; return 0; } - if (it->second == password) + if (CheckPasswordHash(it->second, password)) { std::cerr << "RsSshd::auth_password_hashed() logged in " << username; return 1; @@ -488,4 +551,161 @@ int RsSshd::auth_password_hashed(char *name, char *pwd) return 0; } +#include "util/radix64.h" +#include "util/rsrandom.h" +#include + +#define RSSSHD_PWD_SALT_LEN 16 +#define RSSSHD_PWD_MIN_LEN 8 + + +#if 0 +int printHex(const char *data, int len) +{ + for(int i = 0; i < len; i++) + { + fprintf(stderr, "%02x", (uint8_t) data[i]); + } + return 1; +} +#endif + + + +int GenerateSalt(std::string &saltBin) +{ + /* get from random */ + for(int i = 0; i < RSSSHD_PWD_SALT_LEN / 4; i++) + { + uint32_t rnd = RSRandom::random_u32(); + saltBin += ((char *) &rnd)[0]; + saltBin += ((char *) &rnd)[1]; + saltBin += ((char *) &rnd)[2]; + saltBin += ((char *) &rnd)[3]; + } + +#if 0 + std::cerr << "HexSalt: "; + printHex(saltBin.c_str(), saltBin.size()); + std::cerr << std::endl; +#endif + + return 1; +} + +int GeneratePasswordHash(std::string saltBin, std::string password, std::string &pwdHashRadix64) +{ +#if 0 + std::cerr << "GeneratePasswordHash()"; + std::cerr << std::endl; + + std::cerr << "HexSalt: "; + printHex(saltBin.c_str(), saltBin.size()); + std::cerr << std::endl; +#endif + + if (saltBin.size() != RSSSHD_PWD_SALT_LEN) + { + return 0; + } + + if (password.size() < RSSSHD_PWD_MIN_LEN) + { + return 0; + } + + EVP_MD_CTX *ctx = EVP_MD_CTX_create(); + EVP_DigestInit(ctx, EVP_sha256()); + + EVP_DigestUpdate(ctx, saltBin.c_str(), saltBin.size()); + EVP_DigestUpdate(ctx, password.c_str(), password.size()); + + + unsigned char hash[1024]; + unsigned int s = 1024 - RSSSHD_PWD_SALT_LEN; + + for(int i = 0; i < RSSSHD_PWD_SALT_LEN; i++) + { + hash[i] = saltBin[i]; + } + + EVP_DigestFinal(ctx, &(hash[RSSSHD_PWD_SALT_LEN]), &s); + + Radix64::encode((char *)hash, s + RSSSHD_PWD_SALT_LEN, pwdHashRadix64); + +#if 0 + std::cerr << "Salt Length: " << RSSSHD_PWD_SALT_LEN; + std::cerr << std::endl; + std::cerr << "Hash Length: " << s; + std::cerr << std::endl; + std::cerr << "Total Length: " << s + RSSSHD_PWD_SALT_LEN; + std::cerr << std::endl; + + + std::cerr << "Encoded Length: " << pwdHashRadix64.size(); + std::cerr << std::endl; + + std::cerr << "GeneratePasswordHash() Output: " << pwdHashRadix64; + std::cerr << std::endl; +#endif + + return 1; +} + + +int CheckPasswordHash(std::string pwdHashRadix64, std::string password) +{ + char output[1024]; + char *buf = NULL; + size_t len = 1024; + Radix64::decode(pwdHashRadix64, buf, len); + for(int i = 0; (i < len) && (i < 1024); i++) + { + output[i] = buf[i]; + } + delete []buf; + +#if 0 + std::cerr << "CheckPasswordHash() Input: " << pwdHashRadix64; + std::cerr << std::endl; + std::cerr << "Decode Length: " << len; + std::cerr << std::endl; + std::cerr << "HexDecoded: "; + printHex(output, len); + std::cerr << std::endl; +#endif + + /* first N bytes are SALT */ + EVP_MD_CTX *ctx = EVP_MD_CTX_create(); + EVP_DigestInit(ctx, EVP_sha256()); + + EVP_DigestUpdate(ctx, output, RSSSHD_PWD_SALT_LEN); + EVP_DigestUpdate(ctx, password.c_str(), password.size()); + +#if 0 + std::cerr << "HexSalt: "; + printHex(output, RSSSHD_PWD_SALT_LEN); + std::cerr << std::endl; +#endif + + unsigned char hash[128]; + unsigned int s = 128; + EVP_DigestFinal(ctx, hash, &s); + + /* Final Comparison */ + if (s != len - RSSSHD_PWD_SALT_LEN) + { + std::cerr << "Length Mismatch"; + return 0; + } + + if (0 == strncmp(&(output[RSSSHD_PWD_SALT_LEN]), (char *) hash, s)) + { + return 1; + } + + return 0; +} + + diff --git a/retroshare-nogui/src/ssh/rssshd.h b/retroshare-nogui/src/ssh/rssshd.h index 48623b397..ef2be2709 100644 --- a/retroshare-nogui/src/ssh/rssshd.h +++ b/retroshare-nogui/src/ssh/rssshd.h @@ -34,6 +34,7 @@ clients must be made or how a client should react. #include #include +#include "rstermserver.h" #ifndef KEYS_FOLDER #ifdef _WIN32 @@ -53,17 +54,22 @@ clients must be made or how a client should react. -#define ALLOW_CLEARPWDS 1 +//#define ALLOW_CLEARPWDS 1 class RsSshd; extern RsSshd *rsSshd; + + // TODO: NB: THIS FN DOES NOT USE A "SLOW" HASH FUNCTION. + // THE FIRST HALF OF THE HASH STRING IS THE SALT +int CheckPasswordHash(std::string pwdHashRadix64, std::string password); +int GeneratePasswordHash(std::string saltBin, std::string password, std::string &pwdHashRadix64); +int GenerateSalt(std::string &saltBin); + class RsSshd: public RsThread { public: - // TODO: NB: THIS FN DOES NOT USE A "SLOW" HASH FUNCTION. - // THE FIRST HALF OF THE HASH STRING IS THE SALT int adduserpwdhash(std::string username, std::string hash); #ifdef ALLOW_CLEARPWDS int adduser(std::string username, std::string password); @@ -74,10 +80,13 @@ int adduser(std::string username, std::string password); virtual void run(); /* overloaded from RsThread => called once the thread is started */ // NB: This must be called EARLY before all the threads are launched. -static RsSshd *InitRsSshd(uint16_t port, std::string rsakeyfile); +static RsSshd *InitRsSshd(std::string portstr, std::string rsakeyfile); + + // Terminal Handling! +int setTermServer(RsTermServer *s); private: - RsSshd(uint16_t port); /* private constructor => so can only create with */ + RsSshd(std::string portStr); /* private constructor => so can only create with */ int init(std::string pathrsakey); @@ -92,6 +101,9 @@ int setupChannel(); int setupShell(); int doEcho(); + // Terminal Handling! +int doTermServer(); + int cleanupSession(); int cleanupAll(); @@ -109,11 +121,12 @@ int auth_password_basic(char *name, char *pwd); uint32_t mState; uint32_t mBindState; - uint16_t mPort; + std::string mPortStr; ssh_session mSession; ssh_bind mBind; ssh_channel mChannel; + RsTermServer *mTermServer; #ifdef ALLOW_CLEARPWDS std::map mPasswords; #endif // ALLOW_CLEARPWDS