// $Id: MySession.cpp 705 2012-03-16 13:01:13Z felfert $ // // Copyright (C) 2006 The OpenNX Team // Author: Fritz Elfert // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as // published by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this program; if not, write to the // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifdef HAVE_CONFIG_H # include "config.h" #endif #if defined(__GNUG__) && !defined(__APPLE__) #pragma implementation "SessionList.h" #endif // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #include "wx/wx.h" #endif #include "CardWaiterDialog.h" #include "ConnectDialog.h" #include "MySession.h" #include "MyXmlConfig.h" #include "MyIPC.h" #include "ResumeDialog.h" #include "WinShare.h" #include "opennxApp.h" #include "osdep.h" #include "pwcrypt.h" #include "ProxyPasswordDialog.h" #include "SimpleXauth.h" #include "Icon.h" #include "SupressibleMessageDialog.h" #include "PulseAudio.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __VISUALC__ # include #else # include #endif #ifndef __WXMSW__ # include #endif #ifdef __UNIX__ # include #endif #include "trace.h" ENABLE_TRACE; static const int CUPS_PORT_OFFSET = 2000; static const int SMB_PORT_OFFSET = 3000; static const int PROXY_PORT_OFFSET = 4000; static const int X_PORT_OFFSET = 6000; static const int SOUND_PORT_OFFSET = 7000; static const int KBD_PORT_OFFSET = 8000; static const int HTTP_PORT_OFFSET = 9000; static const int USBIP_PORT_OFFSET = 40000; #define X11ARCH_NONE 0 #define X11ARCH_CYGWIN 1 #define X11ARCH_MINGW 2 #ifdef __WXMSW__ wxString cygPath(const wxString &dir, const wxString &file = wxEmptyString) { wxFileName fn(dir); wxString ret = wxT("/cygdrive/"); ret += fn.GetVolume().Lower(); ret += fn.GetShortPath().AfterFirst(wxT(':')); if (!file.IsEmpty()) ret << wxT("\\") << file; ret.Replace(wxT("\\"), wxT("/")); return ret; } class FontpathTraverser : public wxDirTraverser { public: wxString GetFontPath(MySession::tXarch x11arch) { switch (x11arch) { case MySession::XARCH_CYGWIN: return m_sFontPathCygwin; break; case MySession::XARCH_XMING: return m_sFontPath; break; default: wxLogError(_("Invalid X11 server platform")); break; } return wxEmptyString; } virtual wxDirTraverseResult OnFile(const wxString& WXUNUSED(filename)) { return wxDIR_CONTINUE; } virtual wxDirTraverseResult OnDir(const wxString& dirname) { if (!m_sFontPath.IsEmpty()) m_sFontPath += wxT(","); m_sFontPath += dirname; if (!m_sFontPathCygwin.IsEmpty()) m_sFontPathCygwin += wxT(","); m_sFontPathCygwin += cygPath(dirname); return wxDIR_IGNORE; } private: wxString m_sFontPath; wxString m_sFontPathCygwin; }; #else wxString cygPath(const wxString &path) { return wxFileName(path).GetFullPath(); } wxString cygPath(const wxString &dir, const wxString &file) { return wxFileName(dir, file).GetFullPath(); } #endif class RunLog : public wxLogChain { public: RunLog(wxLog *logger) :wxLogChain(logger) { SetVerbose(true); } void DoLogRecord(wxLogLevel level, const wxString & szString, const wxLogRecordInfo & info) { PassMessages(level <= minlevel); wxLogChain::DoLogRecord((level > minlevel) ? minlevel : level, szString, info); } private: static const wxLogLevel minlevel = wxLOG_Message; }; class SessionCleaner : public wxDirTraverser { public: SessionCleaner(const wxString &toplevel) { m_sTopLevel = toplevel; wxString s = wxFileName::GetPathSeparator(); if (!toplevel.EndsWith(s)) m_sTopLevel += s; wxString r = m_sTopLevel + wxT("temp") + s + wxT("([0-9]+)"); r.Replace(wxT("\\"), wxT("\\\\")); m_cRegex.Compile(r, wxRE_ADVANCED); wxASSERT(m_cRegex.IsValid()); } ~SessionCleaner() { wxLogNull nolog; int n = m_aFiles.GetCount() - 1; while (n >= 0) ::wxRemoveFile(m_aFiles[n--]); n = m_aDirs.GetCount() - 1; while (n >= 0) ::wxRmdir(m_aDirs[n--]); } virtual wxDirTraverseResult OnFile(const wxString &name) { for (size_t i = 0; i < m_aDirs.GetCount(); i++) { if (name.StartsWith(m_aDirs[i])) { myLogTrace(MYTRACETAG, wxT("adding file '%s'"), VMB(name)); m_aFiles.Add(name); return wxDIR_CONTINUE; } } return wxDIR_CONTINUE; } virtual wxDirTraverseResult OnDir(const wxString &name) { if (name.StartsWith(m_sTopLevel + wxT("S-"))) { myLogTrace(MYTRACETAG, wxT("Session dir: '%s'"), VMB(name)); wxTextFile tf(name + wxFileName::GetPathSeparator() + wxT("session")); if (tf.Exists()) { wxString line; if (tf.Open()) { for (line = tf.GetFirstLine(); !tf.Eof(); line = tf.GetNextLine()) { if (line.Contains(wxT("mode with pid"))) { long pid; line.AfterFirst(wxT('\'')).BeforeLast(wxT('\'')).ToLong(&pid); myLogTrace(MYTRACETAG, wxT("Proxy-PID: %d"), (int)pid); if (!wxProcess::Exists(pid)) { myLogTrace(MYTRACETAG, wxT("PID does not exist, adding '%s'"), VMB(name)); m_aDirs.Add(name); return wxDIR_CONTINUE; } } } } } myLogTrace(MYTRACETAG, wxT("Keeping '%s'"), VMB(name)); } if (name.StartsWith(m_sTopLevel + wxT("D-"))) { myLogTrace(MYTRACETAG, wxT("Service dir: '%s'"), VMB(name)); wxTextFile tf(name + wxFileName::GetPathSeparator() + wxT("pid")); if (tf.Exists()) { long pid; if (tf.Open()) { tf.GetFirstLine().ToLong(&pid); myLogTrace(MYTRACETAG, wxT("Service-PID: %d"), (int)pid); if (!wxProcess::Exists(pid)) { myLogTrace(MYTRACETAG, wxT("PID does not exist, adding '%s'"), VMB(name)); m_aDirs.Add(name); return wxDIR_CONTINUE; } } } myLogTrace(MYTRACETAG, wxT("Keeping '%s'"), VMB(name)); } if (name.StartsWith(m_sTopLevel + wxT("F-"))) { myLogTrace(MYTRACETAG, wxT("Failed session dir: '%s'"), VMB(name)); m_aDirs.Add(name); return wxDIR_CONTINUE; } if (m_cRegex.Matches(name)) { myLogTrace(MYTRACETAG, wxT("Temp dir: '%s'"), VMB(name)); long mpid = 0; long spid = 0; m_cRegex.GetMatch(name, 1).ToLong(&mpid); myLogTrace(MYTRACETAG, wxT("Main PID: %d"), (int)mpid); wxTextFile tf(name + wxFileName::GetPathSeparator() + wxT("sshlog")); if (tf.Exists()) { if (tf.Open()) { wxString line = tf.GetFirstLine(); line.AfterLast(wxT(':')).Strip(wxString::both).ToLong(&spid); myLogTrace(MYTRACETAG, wxT("Ssh-PID: %d"), (int)spid); } } if (mpid && (!wxProcess::Exists(mpid)) && ((!spid) || (!wxProcess::Exists(spid)))) { myLogTrace(MYTRACETAG, wxT("PIDs do not exist, adding '%s'"), VMB(name)); m_aDirs.Add(name); return wxDIR_CONTINUE; } myLogTrace(MYTRACETAG, wxT("Keeping '%s'"), VMB(name)); return wxDIR_IGNORE; } if (name.StartsWith(m_sTopLevel + wxT("temp"))) return wxDIR_CONTINUE; return wxDIR_IGNORE; } private: wxArrayString m_aDirs; wxArrayString m_aFiles; wxString m_sTopLevel; wxRegEx m_cRegex; }; DECLARE_EVENT_TYPE(wxEVT_SESSION, -6); DEFINE_EVENT_TYPE(wxEVT_SESSION); /** * This class is a helper for watching a file until a specific * string has appeared in it. When found, it triggers an event. */ class SessionWatch : public wxThreadHelper { public: SessionWatch(wxEvtHandler *handler, const wxString &logfile, const wxString &search) : wxThreadHelper() { m_pHandler = handler; m_sLog = logfile; m_sSearch = search; if (CreateThread() == wxTHREAD_NO_ERROR) GetThread()->Run(); } private: virtual wxThread::ExitCode Entry() { wxStopWatch sw; wxTextFile tf(m_sLog); long timeout = 5000; bool bFound = false; while ((!bFound) && (sw.Time() < timeout)) { if (tf.Exists()) { if (timeout == 5000) timeout = sw.Time() + 10000; if (!tf.Open()) break; wxString line; for (line = tf.GetFirstLine(); !tf.Eof(); line = tf.GetNextLine()) { if (line.Contains(m_sSearch)) { bFound = true; break; } } tf.Close(); } wxThread::Sleep(500); } if (m_pHandler) { wxCommandEvent upevent(wxEVT_SESSION, wxID_ANY); upevent.SetInt(bFound ? 1 : 0); m_pHandler->AddPendingEvent(upevent); } return 0; } wxEvtHandler *m_pHandler; wxString m_sLog; wxString m_sSearch; }; /** * Our specialization of wxHTTP. Needed, because wxHTTP stops * parsing headers when recognizing an error status header (4xx, 5xx ...) * but we want it to continue parsing in order to get the Server: header. */ class MyHTTP : public wxHTTP { public: MyHTTP() : wxHTTP() {} wxInputStream *GetInputStream(const wxString& path) { wxSocketInputStream *inp_stream; wxString new_path; m_lastError = wxPROTO_CONNERR; if (!m_addr) return NULL; // We set m_connected back to false so wxSocketBase will know what to do. #ifdef __WXMAC__ wxSocketClient::Connect(*m_addr , false ); wxSocketClient::WaitOnConnect(10); if (!wxSocketClient::IsConnected()) return NULL; #else if (!wxProtocol::Connect(*m_addr)) return NULL; #endif if (!BuildRequest(path, m_postBuffer.IsEmpty() ? wxT("GET") : wxT("POST"))) return NULL; inp_stream = new wxSocketInputStream(*this); Notify(false); SetFlags(wxSOCKET_BLOCK | wxSOCKET_WAITALL); return inp_stream; } protected: bool BuildRequest(const wxString& path, const wxString& method) { bool ret = wxHTTP::BuildRequest(path, method); myLogTrace(MYTRACETAG, wxT("calling ParseHeaders()")); ParseHeaders(); return ret; } }; IMPLEMENT_DYNAMIC_CLASS(MySession, wxEvtHandler); BEGIN_EVENT_TABLE(MySession, wxEvtHandler) EVT_COMMAND(wxID_ANY, wxEVT_NXSSH, MySession::OnSshEvent) EVT_COMMAND(wxID_ANY, wxEVT_SESSION, MySession::OnSessionEvent) END_EVENT_TABLE(); MySession::MySession(wxString dir, wxString status, wxString stype, wxString host, int port, wxString md5) : wxEvtHandler() , m_pSshLog(NULL) , m_pNxSsh(NULL) , m_pCfg(NULL) , m_pDlg(NULL) , m_pParent(NULL) , m_pSessionWatch(NULL) , m_bTouched(true) , m_iPort(port) , m_lPid(0) , m_eSessionType(None) , m_eSessionStatus(Unknown) , m_sHost(host) , m_sMd5(md5) , m_sDir(dir) { m_bValid = false; if (stype == wxT("C")) m_eSessionType = Server; if (stype == wxT("S")) m_eSessionType = Client; if (status == wxT("T")) m_eSessionStatus = Terminated; if (status == wxT("F")) m_eSessionStatus = Failed; m_rePID.Compile(wxT("([[:digit:]]+)")); initversion(); CheckState(); } MySession::MySession(const MySession &src) : wxEvtHandler() , m_pSshLog(NULL) , m_pNxSsh(NULL) , m_pCfg(NULL) , m_pDlg(NULL) , m_pParent(NULL) , m_pSessionWatch(NULL) { m_sHost = src.m_sHost; m_iPort = src.m_iPort; m_lPid = src.m_lPid; m_sMd5 = src.m_sMd5; m_eSessionType = src.m_eSessionType; m_eSessionStatus = src.m_eSessionStatus; m_sDir = src.m_sDir; m_bTouched = src.m_bTouched; m_rePID.Compile(wxT("([[:digit:]]+)")); m_iReader = src.m_iReader; m_lProtocolVersion = src.m_lProtocolVersion; m_lSrvProtocolVersion = src.m_lSrvProtocolVersion; m_sProtocolVersion = src.m_sProtocolVersion; } MySession::MySession() : wxEvtHandler() , m_pSshLog(NULL) , m_pNxSsh(NULL) , m_pCfg(NULL) , m_pDlg(NULL) , m_pParent(NULL) , m_pSessionWatch(NULL) { m_iReader = -1; initversion(); } MySession & MySession::operator =(const MySession &src) { m_pSshLog = NULL; m_pCfg = NULL; m_pDlg = NULL; m_pNxSsh = NULL; m_pParent = NULL; m_pSessionWatch = NULL; m_sHost = src.m_sHost; m_iPort = src.m_iPort; m_lPid = src.m_lPid; m_iReader = src.m_iReader; m_sMd5 = src.m_sMd5; m_eSessionType = src.m_eSessionType; m_eSessionStatus = src.m_eSessionStatus; m_sDir = src.m_sDir; m_bTouched = src.m_bTouched; m_rePID.Compile(wxT("([[:digit:]]+)")); m_lProtocolVersion = src.m_lProtocolVersion; m_lSrvProtocolVersion = src.m_lSrvProtocolVersion; m_sProtocolVersion = src.m_sProtocolVersion; return *this; } MySession::~MySession() { myLogTrace(MYTRACETAG, wxT("~MySession")); if (m_pSshLog) { m_pSshLog->Flush(); delete m_pSshLog; } m_pSshLog = NULL; if (m_pSessionWatch) { delete m_pSessionWatch; m_pSessionWatch = NULL; } wxLog *l = wxLog::SetActiveTarget(NULL); if (l) delete l; } wxString MySession::sGetCreationTime() { wxString ret(_("unknown")); if (m_bValid) { wxLogNull l; wxDateTime ctime; wxFileName fn(m_sDir, wxT("session")); if (fn.GetTimes(NULL, NULL, &ctime)) { ret = ctime.Format(); } } return ret; } wxString MySession::sGetSessionStatus() { switch (m_eSessionStatus) { case Terminated: return _("terminated"); break; case Failed: return _("failed"); break; case Running: return _("running"); break; case Unknown: return _("unknown"); break; } return _("unknown"); } wxString MySession::sGetSessionType() { switch (m_eSessionType) { case Server: return _("Server"); break; case Client: return _("Client"); break; case None: return _("Unknown"); break; } return _("Unknown"); } bool MySession::bGetPidFromFile() { m_lPid = 0; if (!m_bValid) return false; wxFileInputStream input(m_sDir + wxFileName::GetPathSeparator() + wxT("session")); wxTextInputStream text(input); int cnt = 0; while ((!input.Eof()) && (cnt < 100)) { wxString line = text.ReadLine(); int idx = line.Find(wxT("pid")); if (idx != wxNOT_FOUND && (!(line.Contains(wxT("NXAGENT")) || line.Contains(wxT("Agent"))))) { line = line.Mid(idx + 4); if (m_rePID.Matches(line)) m_rePID.GetMatch(line, 1).ToLong(&m_lPid); } cnt++; } return (m_lPid > 0); } void MySession::CheckState() { wxString logfilename = m_sDir + wxFileName::GetPathSeparator() + wxT("session"); m_eSessionStatus = Unknown; if (!wxFile::Exists(logfilename)) return; if ((m_eSessionStatus == Terminated) || (m_eSessionStatus == Failed)) return; wxFile fi(logfilename); if (!fi.IsOpened()) return; m_bValid = true; wxFileInputStream input(fi); wxTextInputStream text(input); while (!input.Eof()) { wxString line = text.ReadLine(); if (line.StartsWith(wxT("Session: Session terminated"))) { m_eSessionStatus = Terminated; break; } } if (bGetPidFromFile()) { if (wxProcess::Exists(m_lPid)) m_eSessionStatus = Running; } } unsigned short MySession::getFirstFreePort(unsigned short startPort) { #ifdef __WXMAC__ // wxSocketServer appears to be broken on wxMac, // so we use plain unix code, implemented in osdep.c ... return macFirstFreePort(startPort); #else unsigned short port = startPort; wxIPV4address a; a.LocalHost(); while (port < 65535) { a.Service(port); wxSocketServer ss(a); if (ss.Ok()) return port; port++; } #endif return 0; } wxString MySession::getXauthCookie(int display /* = 0 */, wxString proto) { #ifdef __UNIX__ wxString dpy; wxString ret; if (wxGetEnv(wxT("DISPLAY"), &dpy) && (!dpy.IsEmpty())) { wxString dpyNr = dpy.AfterFirst(wxT(':')); const char *fn = XauFileName(); FILE *f = fopen(fn, "r"); if (f) { Xauth *auth = NULL; // Fetch the first entry of FamilyLocal and a matching display number while (ret.IsEmpty() && (NULL != (auth = XauReadAuth(f)))) { if (FamilyLocal == auth->family) { wxString edpy((const char *)auth->number, *wxConvCurrent, auth->number_length); if (edpy.IsSameAs(dpyNr)) { int i; for (i = 0; i < auth->data_length; ++i) { ret.Append(wxString::Format(wxT("%02x"), auth->data[i] & 0xff)); } } } XauDisposeAuth(auth); } } fclose(f); } return ret; #endif #ifdef __WXMSW__ // On windows we *create* the cookie instead SimpleXauth xa(getXauthPath()); xa.AddDisplay(display); return xa.GetCookie(); #else wxUnusedVar(display); wxUnusedVar(proto); #endif return wxEmptyString; } wxString MySession::getXauthPath(tXarch xarch) { #ifdef __UNIX__ wxUnusedVar(xarch); const char *xafn = XauFileName(); // static in libXau, DO NOT free() !! return wxString(xafn, *wxConvCurrent); #endif #ifdef __WXMSW__ wxFileName fn; switch (xarch) { case XARCH_CYGWIN: return cygPath(m_sUserDir, wxT(".Xauthority")); case XARCH_XMING: fn.Assign(m_sUserDir, wxT(".Xauthority")); return fn.GetFullPath(); default: wxLogError(_("Invalid X11 server platform")); return wxEmptyString; } #endif return wxEmptyString; } wxString MySession::formatOptFilename() { #ifdef __WXMSW__ return cygPath(m_sOptFilename); #else return m_sOptFilename; #endif } void MySession::OnSessionEvent(wxCommandEvent &event) { if (event.GetInt()) m_bSessionEstablished = true; else m_bGotError = true; } void MySession::SshLog(const wxChar *fmt, ...) { if (m_pSshLog) { va_list args; va_start(args, fmt); wxLog *oldLog = wxLog::SetActiveTarget(m_pSshLog); ::wxVLogMessage(fmt, args); wxLog::SetActiveTarget(oldLog); va_end(args); } } void MySession::OnSshEvent(wxCommandEvent &event) { MyIPC::tSessionEvents e = wx_static_cast(MyIPC::tSessionEvents, event.GetInt()); wxString msg(event.GetString()); wxString scmd; switch (e) { case MyIPC::ActionNone: case MyIPC::ActionStdout: case MyIPC::ActionStderr: break; case MyIPC::ActionStatus: m_pDlg->SetStatusText(msg); break; case MyIPC::ActionHello: initversion(msg); m_eConnectState = STATE_HELLO; break; case MyIPC::ActionLog: m_pDlg->SetProgress(m_iProgress++); if (m_bCollectSessions || m_bCollectResources) m_aParseBuffer.Add(msg); if (m_bCollectConfig) { m_sConfigBuffer << msg << wxT("\n"); if (m_sConfigBuffer.Length() >= m_nSessionPushLength) { wxLogInfo(wxT("session override finished")); m_bCollectConfig = false; m_pCfg->LoadFromString(m_sConfigBuffer, true); if (m_pCfg->IsWritable()) m_pCfg->SaveToFile(); } } SshLog(msg); break; case MyIPC::ActionWarning: { wxString cfgid(wxT("sshwarn.")); SupressibleMessageDialog d(m_pParent, msg, _("Warning - OpenNX"), wxOK|wxICON_EXCLAMATION); d.ShowConditional(cfgid.Append(msg.Left(15)), wxID_OK); } break; case MyIPC::ActionError: wxLogError(msg); m_bGotError = true; break; case MyIPC::ActionPromptYesNo: { wxString cfgid(wxT("sshyesno.")); SupressibleMessageDialog d(m_pParent, msg, _("Warning - OpenNX"), wxYES_NO|wxICON_EXCLAMATION); if (d.ShowConditional(cfgid.Append(msg.Left(15)), wxID_YES) == wxID_YES) printSsh(wxT("yes")); else { printSsh(wxT("no")); m_bGotError = true; } } break; case MyIPC::ActionKeyChangedYesNo: { wxString cfgid(wxT("sshkeychanged.")); msg << _("\nDo you want to delete the key and retry ?"); SupressibleMessageDialog d(m_pParent, msg, _("Warning - OpenNX"), wxYES_NO|wxICON_EXCLAMATION); if (d.ShowConditional(cfgid.Append(msg.Left(15)), wxID_YES) == wxID_YES) m_bRemoveKey = true; m_bGotError = true; } break; case MyIPC::ActionOffendingKey: m_sOffendingKey = msg; break; case MyIPC::ActionSendUsername: { // At this point key-based auth is finished and // thus the key-file isn't needed anymore wxLogNull logdummy; wxFileName fn; fn.Assign(m_sTempDir, wxT("keylog")); if (fn.IsFileWritable()) ::wxRemoveFile(fn.GetFullPath()); } m_pDlg->SetStatusText(_("Sending username")); //printSsh(m_pCfg->sGetSessionUser()); break; case MyIPC::ActionSendPassword: m_pDlg->SetStatusText(_("Authenticating")); if (m_pCfg->bGetRememberPassword()) printSsh(m_pCfg->sGetSessionPassword(), false); else printSsh(m_sClearPassword, false); break; case MyIPC::ActionWelcome: m_pDlg->SetStatusText(_("Authentication successful")); m_eConnectState = (m_lProtocolVersion >= 0x00040000) ? STATE_LIST_RESOURCES : STATE_LIST_SESSIONS; break; case MyIPC::ActionSessionPushLength: // Session file length: 213 msg.Mid(21).ToULong(&m_nSessionPushLength); break; case MyIPC::ActionSessionPushStart: m_sConfigBuffer.Empty(); m_bCollectConfig = true; wxLogInfo(wxT("receiving %d bytes of session override"), (int)m_nSessionPushLength); break; case MyIPC::ActionNextCommand: m_iProgress += 4; m_pDlg->SetProgress(m_iProgress); switch (m_eConnectState) { case STATE_WAIT: m_pDlg->SetStatusText(_("Waiting user prompt")); break; case STATE_INIT: case STATE_ABORT: break; case STATE_HELLO: m_pDlg->SetStatusText(_("Authenticating")); printSsh(wxT("hello NXCLIENT - Version ") + m_sProtocolVersion); m_eConnectState = STATE_SHELLMODE; break; case STATE_SHELLMODE: scmd = wxT("SET SHELL_MODE SHELL\n"); scmd << wxT("SET AUTH_MODE PASSWORD\n"); scmd << wxT("login\n"); scmd << m_pCfg->sGetSessionUser(); printSsh(scmd); m_eConnectState = STATE_LOGIN; break; // Do not delete it! // Sending multiple messages at the same time does not // correspond to the exchange protocol, but these messages // are always sent in that order. // FreeNX understands and correctly processes this. // This does not give much startup acceleration, but still // gives. /* printSsh(wxT("SET SHELL_MODE SHELL")); m_eConnectState = STATE_AUTHMODE; break; case STATE_AUTHMODE: printSsh(wxT("SET AUTH_MODE PASSWORD")); m_eConnectState = STATE_LOGIN; break; case STATE_LOGIN: printSsh(wxT("login")); break; */ case STATE_LIST_RESOURCES: m_pDlg->SetStatusText(_("Query server-side features")); printSsh(wxT("resourcelist")); m_eConnectState = STATE_PARSE_RESOURCES; m_bNextCmd = false; break; case STATE_PARSE_RESOURCES: // Server has sent list of attachable sessions m_bCollectResources = false; wxLogInfo(wxT("received end of feature list")); parseResources(); // intentionally fall thru case STATE_LIST_SESSIONS: m_pDlg->SetStatusText(_("Query server-side sessions")); scmd = wxT("listsession") + m_pCfg->sGetListParams(m_lProtocolVersion); m_bInParseSessions = false; printSsh(scmd); m_eConnectState = STATE_PARSE_SESSIONS; m_bNextCmd = false; break; case STATE_PARSE_SESSIONS: m_bNextCmd = true; if (m_bIsShadow) { // Server has sent list of attachable sessions m_bCollectSessions = false; wxLogInfo(wxT("received end of attachable session list")); parseSessions(false); } break; case STATE_START_SESSION: m_pDlg->SetStatusText(_("Starting session")); scmd = wxT("startsession"); scmd << m_pCfg->sGetSessionParams(m_lProtocolVersion, true, m_sClearPassword); printSsh(scmd); m_eConnectState = STATE_FINISH; break; case STATE_ATTACH_VIEW_SESSION: case STATE_ATTACH_SESSION: m_pDlg->SetStatusText(_("Attaching to session")); scmd = wxT("attachsession"); scmd << m_pCfg->sGetSessionParams(m_lProtocolVersion, false, m_sClearPassword) << wxT(" --display=\"") << m_sResumePort << wxT("\" --id=\"") << m_sResumeId << wxT("\"") // TODO: Check, since which version this is supported << wxT(" --resize=\"1\""); if (m_eConnectState == STATE_ATTACH_VIEW_SESSION) scmd << wxT(" --shadowviewonly=\"1\""); printSsh(scmd); m_eConnectState = STATE_FINISH; break; case STATE_RESUME_SESSION: m_pDlg->SetStatusText(_("Resuming session")); scmd = wxT("restoresession"); scmd << m_pCfg->sGetSessionParams(m_lProtocolVersion, true, m_sClearPassword) << wxT(" --session=\"") << m_sResumeName << wxT("\" --type=\"") << m_sResumeType << wxT("\" --id=\"") << m_sResumeId << wxT("\""); printSsh(scmd); m_eConnectState = STATE_FINISH; break; case STATE_KILL_SESSION: m_pDlg->SetStatusText(_("Terminate session")); scmd = wxT("terminate --sessionid=\""); scmd << m_sKillId << wxT("\""); printSsh(scmd); m_eConnectState = STATE_LIST_SESSIONS; break; case STATE_FINISH: if (m_bGotError) { m_eConnectState = STATE_ABORT; printSsh(wxT("quit"), true, wxT("Mainloop: Got error, ")); } break; } break; case MyIPC::ActionPinDialog: printSsh(wxGetPasswordFromUser( _("Enter PIN for Smart Card access."), _("Smart Card PIN"), wxEmptyString, m_pParent), false); break; case MyIPC::ActionPassphraseDialog: scmd = ::wxGetPasswordFromUser(::wxGetTranslation(msg), _("Enter passphrase"), wxEmptyString, m_pParent); if (scmd.IsEmpty()) { msg = _("Empty passphrase"); m_bGotError = true; } printSsh(scmd, false); break; case MyIPC::ActionSetShadowGeometry: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_sShadowGeometry = msg; break; case MyIPC::ActionSetSessionID: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_sSessionID = msg; break; case MyIPC::ActionSetProxyCookie: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_sProxyCookie = msg; break; case MyIPC::ActionSetProxyIP: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_sProxyIP = msg; break; case MyIPC::ActionSetSessionType: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_sSessionType = msg; break; case MyIPC::ActionSetSessionCache: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_sSessionCache = msg; break; case MyIPC::ActionSetSessionDisplay: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_sSessionDisplay = msg; break; case MyIPC::ActionSetAgentCookie: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_sAgentCookie = msg; break; case MyIPC::ActionSetSslTunneling: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_bSslTunneling = (msg == wxT("1")); break; case MyIPC::ActionSetSubscription: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_sSubscription = msg; break; case MyIPC::ActionSetSmbPort: m_pDlg->SetStatusText(_("Negotiating session parameter")); m_sSmbPort = msg; break; case MyIPC::ActionExit: if (m_eConnectState == STATE_ABORT) { m_bAbort = true; } else { if (m_eConnectState == STATE_FINISH) { m_pDlg->SetStatusText(_("Starting session")); msg = wxT("NX> 299 Switch connection to: "); if (m_lProtocolVersion > 0x00020000) { msg << wxT("NX mode: ") << (m_bSslTunneling ? wxT("encrypted") : wxT("unencrypted")) << wxT(" options: nx,options=") << formatOptFilename() << wxT(":") << m_sSessionDisplay; } else { msg << m_sProxyIP << wxT(":") << m_sProxyPort << wxT(" cookie: ") << m_sProxyCookie; } m_bSessionRunning = true; wxString slog = m_sSessionDir; slog << wxFileName::GetPathSeparator() << wxT("session"); m_pSessionWatch = new SessionWatch(this, slog, wxT("Session: Session started at")); printSsh(msg); } else { m_bSessionRunning = true; } } break; case MyIPC::ActionTerminated: if ((m_eConnectState <= STATE_PARSE_SESSIONS) && (!m_bGotError) && (!m_bRemoveKey) && (m_sOffendingKey.IsEmpty())) { msg = _("Unexpected termination of nxssh"); wxLogError(msg); m_bGotError = true; } break; case MyIPC::ActionStartProxy: if (m_eConnectState == STATE_FINISH) { m_pDlg->SetStatusText(_("Starting session")); startSharing(); startProxy(); } break; case MyIPC::ActionSessionRunning: m_bSessionRunning = true; { wxString slog = m_sSessionDir; slog << wxFileName::GetPathSeparator() << wxT("session"); m_pSessionWatch = new SessionWatch(this, slog, wxT("Session: Session started at")); } break; case MyIPC::ActionSessionListStart: // Server starts sending session list wxLogInfo(wxT("receiving session list ..")); m_aParseBuffer.Empty(); m_bCollectSessions = true; break; case MyIPC::ActionSessionListEnd: // Server has sent list of running & suspended sessions m_bCollectSessions = false; wxLogInfo(wxT("received end of session list")); parseSessions((event.GetExtraLong() == 148) && (!m_bIsShadow)); break; case MyIPC::ActionResList: // NX4: Server starts sending resource info wxLogInfo(wxT("receiving resource info ..")); m_aParseBuffer.Empty(); m_bCollectResources = true; break; } } void MySession::initversion(const wxString &s /* = wxEmptyString */) { wxString s02; wxString s1 = s.BeforeFirst(wxT('-'), &s02); m_lProtocolVersion = 0; if (!::wxGetEnv(wxT("NX_PROTOCOL_VERSION"), &m_sProtocolVersion)) m_sProtocolVersion = wxT(NX_PROTOCOL_VERSION); if (!s1.IsEmpty()) m_sProtocolVersion = s1; wxStringTokenizer t(m_sProtocolVersion, wxT(".")); int digits = 0; while (t.HasMoreTokens()) { long n; t.GetNextToken().ToLong(&n); m_lProtocolVersion = (m_lProtocolVersion << 8) + n; if (++digits > 3) break; } while (digits++ < 3) m_lProtocolVersion = (m_lProtocolVersion << 8); myLogTrace(MYTRACETAG, wxT("protocol version: %08x"), (int)m_lProtocolVersion); wxString s3; wxString s2 = s02.BeforeFirst(wxT('-'), &s3); m_lSrvProtocolVersion = 0; if (s3 != wxT("CE")) return; digits = 0; t.SetString(s2, wxT(".")); while (t.HasMoreTokens()) { long n; t.GetNextToken().ToLong(&n); m_lSrvProtocolVersion = (m_lSrvProtocolVersion << 8) + n; if (++digits > 3) break; } myLogTrace(MYTRACETAG, wxT("server protocol version: %08x"), (int)m_lSrvProtocolVersion); } void MySession::printSsh(const wxString &s, bool doLog /* = true */, const wxString &reason /* = wxT("") */) { if (m_pNxSsh) { myLogTrace(MYTRACETAG, wxT("%ssending '%s'"), VMB(reason), #ifdef __WXMSW__ (doLog ? VMB(s) : wxT("********"))); #else (doLog ? VMB(s) : "********")); #endif m_pNxSsh->Print(s, doLog); } } void MySession::parseResources() { size_t n = m_aParseBuffer.GetCount(); myLogTrace(MYTRACETAG, wxT("parseResources: Got %d lines to parse"), (int)n); wxRegEx re(wxT("^((?:session)|(?:service)|(?:feature))\\s+([^\\s]+)(?:\\s+([^\\s]+))?$"), wxRE_ADVANCED); wxASSERT(re.IsValid()); for (size_t i = 0; i < n; i++) { wxString line(m_aParseBuffer[i].Strip(wxString::both)); if (re.Matches(line) && (4 == re.GetMatchCount())) { if (re.GetMatchCount() == 4) { wxString sClass(re.GetMatch(line, 1)); wxString sType(re.GetMatch(line, 2)); wxString sValue(re.GetMatch(line, 3)); myLogTrace(MYTRACETAG, wxT("parseResources: match c='%s' t='%s', v='%s'"), VMB(sClass), VMB(sType), VMB(sValue)); if (sClass.IsSameAs(wxT("session"))) { // Not yet clear what to do with that data. } if (sClass.IsSameAs(wxT("service"))) { // Not yet clear what to do with that data. } if (sClass.IsSameAs(wxT("feature"))) { // Not yet clear what to do with that data. } } } else myLogTrace(MYTRACETAG, wxT("parseResources: NO match line='%s'"), VMB(line)); } } void MySession::parseSessions(bool moreAllowed) { if (m_bInParseSessions) return; m_bInParseSessions = true; size_t n = m_aParseBuffer.GetCount(); myLogTrace(MYTRACETAG, wxT("parseSessions: Got %d lines to parse"), (int)n); wxRegEx re( wxT("^(\\d+)\\s+([\\w-]+)\\s+([0-9A-F]{32})\\s+([A-Z-]{8})\\s+(\\d+)\\s+(\\d+x\\d+)\\s+(\\w+)\\s+([^\\s].*)$"), wxRE_ADVANCED); wxASSERT(re.IsValid()); wxRegEx re2( wxT("^(\\d+)\\s+([\\w-]+)\\s+([0-9A-F]{32})\\s+([A-Z-]{8})\\s+(N/A)\\s+(N/A)\\s+(\\w+)\\s+([^\\s].*)$"), wxRE_ADVANCED); wxASSERT(re2.IsValid()); wxRegEx re3( wxT("^(\\d+)\\s+(shadow)\\s+([0-9A-F]{32})\\s+([A-Z-]{8})\\s+(\\w+)\\s+([^\\s].*)$"), wxRE_ADVANCED); wxASSERT(re3.IsValid()); ResumeDialog d(NULL); bool bFound = false; int iSessionCount = 0; wxString sName; wxString sUser(m_pCfg->sGetSessionUser()); if (m_bIsShadow) { d.SetAttachMode(true); } else { d.SetPreferredSession(m_pCfg->sGetName()); } for (size_t i = 0; i < n; i++) { wxString line = m_aParseBuffer[i].Trim(); myLogTrace(MYTRACETAG, wxT("parseSessions: line='%s'"), VMB(line)); if (re.Matches(line)) { myLogTrace(MYTRACETAG, wxT("parseSessions: re match")); wxString sPort(re.GetMatch(line, 1)); wxString sType(re.GetMatch(line, 2)); wxString sId(re.GetMatch(line, 3)); wxString sOpts(re.GetMatch(line, 4)); wxString sColors(re.GetMatch(line, 5)); wxString sSize(re.GetMatch(line, 6)); wxString sState(re.GetMatch(line, 7)); sName = re.GetMatch(line, 8); if (m_bIsShadow) { // In shadow session mode, a username follows the session name. // Our RE matches both in sName, so we have to split off the usernam. sUser = sName.AfterLast(wxT(' ')); sName = sName.BeforeLast(wxT(' ')).Trim(); } d.AddSession(sName, sState, sType, sSize, sColors, sPort, sOpts, sId, sUser); bFound = true; iSessionCount++; continue; } if (re2.Matches(line)) { myLogTrace(MYTRACETAG, wxT("parseSessions: re2 match")); wxString sPort(re2.GetMatch(line, 1)); wxString sType(re2.GetMatch(line, 2)); wxString sId(re2.GetMatch(line, 3)); wxString sOpts(re2.GetMatch(line, 4)); wxString sColors(re2.GetMatch(line, 5)); wxString sSize(re2.GetMatch(line, 6)); wxString sState(re2.GetMatch(line, 7)); sName = re2.GetMatch(line, 8); if (m_bIsShadow) { // In shadow session mode, a username follows the session name. // Our RE matches both in sName, so we have to split off the usernam. sUser = sName.AfterLast(wxT(' ')); sName = sName.BeforeLast(wxT(' ')).Trim(); } d.AddSession(sName, sState, sType, sSize, sColors, sPort, sOpts, sId, sUser); bFound = true; iSessionCount++; continue; } if (m_bIsShadow && re3.Matches(line)) { myLogTrace(MYTRACETAG, wxT("parseSessions: re3 match")); wxString sPort(re3.GetMatch(line, 1)); wxString sType(re3.GetMatch(line, 2)); wxString sId(re3.GetMatch(line, 3)); wxString sOpts(re3.GetMatch(line, 4)); wxString sColors; wxString sSize; wxString sState(re3.GetMatch(line, 5)); sName = re3.GetMatch(line, 6); sUser = wxT(""); d.AddSession(sName, sState, sType, sSize, sColors, sPort, sOpts, sId, sUser); bFound = true; iSessionCount++; continue; } myLogTrace(MYTRACETAG, wxT("parseSessions: NO match")); } if (bFound) { d.EnableNew(moreAllowed); if ((!m_bIsShadow) && wxGetApp().AutoResume() && (iSessionCount == 1) && (sName.IsSameAs(m_pCfg->sGetName()))) { wxLogInfo(wxT("RESUME")); m_sResumeName = sName; m_sResumeType = d.GetSelectedType(); m_sResumeId = d.GetSelectedId(); m_eConnectState = STATE_RESUME_SESSION; } else { m_eConnectState = STATE_WAIT; switch (d.ShowModal()) { case wxID_OK: myLogTrace(MYTRACETAG, wxT("ResumeDialog returned OK")); switch (d.GetMode()) { case ResumeDialog::Refresh: wxLogInfo(wxT("REFRESH")); m_eConnectState = STATE_LIST_SESSIONS; printSsh(wxEmptyString); break; case ResumeDialog::Terminate: wxLogInfo(wxT("TERMINATE")); m_sKillId = d.GetSelectedId(); m_eConnectState = STATE_KILL_SESSION; printSsh(wxEmptyString); break; case ResumeDialog::Resume: wxLogInfo(wxT("RESUME")); m_sResumeName = d.GetSelectedName(); m_sResumeType = d.GetSelectedType(); m_sResumeId = d.GetSelectedId(); m_sResumePort = d.GetSelectedPort(); m_eConnectState = m_bIsShadow ? STATE_ATTACH_SESSION : STATE_RESUME_SESSION; printSsh(wxEmptyString); break; case ResumeDialog::Takeover: wxLogInfo(wxT("TAKEOVER")); m_sResumeName = d.GetSelectedName(); m_sResumeType = d.GetSelectedType(); m_sResumeId = d.GetSelectedId(); if (m_bIsShadow) m_sResumePort = d.GetSelectedPort(); m_eConnectState = m_bIsShadow ? STATE_ATTACH_VIEW_SESSION : STATE_RESUME_SESSION; printSsh(wxEmptyString); break; case ResumeDialog::New: m_eConnectState = STATE_START_SESSION; printSsh(wxEmptyString); break; } if (m_bNextCmd) { wxCommandEvent upevent(wxEVT_NXSSH, wxID_ANY); upevent.SetInt(MyIPC::ActionNextCommand); AddPendingEvent(upevent); } break; case wxID_CANCEL: printSsh(wxT("quit"), true, wxT("ResumeDialog returned CANCEL, ")); m_eConnectState = STATE_ABORT; break; } } } else { if (m_bIsShadow) { m_eConnectState = STATE_ABORT; m_bGotError = true; m_bAbort = true; printSsh(wxT("quit"), true, wxT("No sessions to attach, ")); wxMessageDialog d(m_pParent, _("There are no sessions which can be attached to."), _("Error - OpenNX"), wxOK); d.SetIcon(CreateIconFromFile(wxT("res/nx.png"))); d.ShowModal(); } else { if (moreAllowed) m_eConnectState = STATE_START_SESSION; else { printSsh(wxT("quit"), true, wxT("No more sessions allowed, ")); wxMessageDialog d(m_pParent, _("You have reached your session limit. No more sessions allowed"), _("Error - OpenNX"), wxOK); d.SetIcon(CreateIconFromFile(wxT("res/nx.png"))); d.ShowModal(); m_bGotError = true; } } } } void MySession::startSharing() { if (!m_pCfg->bGetUseCups() && !m_pCfg->bGetEnableSmbSharing()) return; ArrayOfShareGroups sg = m_pCfg->aGetShareGroups(); wxArrayString used = m_pCfg->aGetUsedShareGroups(); CupsClient cc; SmbClient sc; ArrayOfShares sa; if (m_pCfg->bGetUseCups() && cc.IsAvailable()) sa = cc.GetShares(); if (m_pCfg->bGetEnableSmbSharing() && sc.IsAvailable()) WX_APPEND_ARRAY(sa, sc.GetShares()); long cupsport; long smbport; m_sSessionDisplay.ToLong(&cupsport); m_sSessionDisplay.ToLong(&smbport); cupsport += CUPS_PORT_OFFSET; smbport += SMB_PORT_OFFSET; for (size_t i = 0; i < sg.GetCount(); i++) { if (used.Index(sg[i].m_sGroupName) == wxNOT_FOUND) continue; bool bAvailable = false; wxString sn = sg[i].m_sShareName; myLogTrace(MYTRACETAG, wxT("startSharing: considering share '%s'"), VMB(sn)); for (size_t j = 0; j < sa.GetCount(); j++) { if ((sa[j].sharetype == sg[i].m_eType) && (sa[j].name == sn)) { bAvailable = true; break; } } myLogTrace(MYTRACETAG, wxT("'%s' is %savailable"), VMB(sn), (bAvailable ? "" : "not ")); if (!bAvailable) continue; wxString shcmd; switch (sg[i].m_eType) { case SharedResource::SHARE_UNKNOWN: break; case SharedResource::SHARE_SMB_DISK: shcmd = wxT("addmount"); shcmd << wxT(" --port=\"") << smbport << wxT("\"") << wxT(" --username=\"") << MyXmlConfig::UrlEsc(sg[i].m_sUsername) << wxT("\"") << wxT(" --password=\"") << MyXmlConfig::UrlEsc(sg[i].m_sPassword) << wxT("\"") << wxT(" --share=\"") << MyXmlConfig::UrlEsc(sn) << wxT("\"") << wxT(" --computername=\"") << MyXmlConfig::UrlEsc(::wxGetFullHostName()) << wxT("\"") << wxT(" --session_id=\"") << m_sSessionID.Right(32) << wxT("\"") << wxT(" --dir=\"") << MyXmlConfig::UrlEsc(sg[i].m_sAlias) << wxT("\""); printSsh(shcmd); break; case SharedResource::SHARE_SMB_PRINTER: shcmd = wxT("addprinter"); shcmd << wxT(" --type=\"smb\"") << wxT(" --username=\"") << MyXmlConfig::UrlEsc(sg[i].m_sUsername) << wxT("\"") << wxT(" --password=\"") << MyXmlConfig::UrlEsc(sg[i].m_sPassword) << wxT("\"") << wxT(" --port=\"") << (int)smbport << wxT("\"") << wxT(" --share=\"") << MyXmlConfig::UrlEsc(sn) << wxT("\"") << wxT(" --computername=\"") << MyXmlConfig::UrlEsc(::wxGetHostName()) << wxT("\"") << wxT(" --session_id=\"") << m_sSessionID.Right(32) << wxT("\"") << wxT(" --model=\"") << sg[i].m_sDriver << wxT("\""); if (sg[i].m_bDefault) shcmd << wxT(" --defaultprinter=\"1\""); if (sg[i].m_bPublic) shcmd << wxT(" --public=\"1\""); printSsh(shcmd); break; case SharedResource::SHARE_CUPS_PRINTER: shcmd = wxT("addprinter"); shcmd << wxT(" --type=\"ipp\"") << wxT(" --username=\"") << MyXmlConfig::UrlEsc(sg[i].m_sUsername) << wxT("\"") << wxT(" --port=\"") << cupsport << wxT("\"") << wxT(" --session_id=\"") << m_sSessionID.Right(32) << wxT("\"") << wxT(" --printer=\"") << MyXmlConfig::UrlEsc(sn) << wxT("\"") << wxT(" --password=\"") << MyXmlConfig::UrlEsc(sg[i].m_sPassword) << wxT("\"") << wxT(" --model=\"cups%20printer\""); if (sg[i].m_bDefault) shcmd << wxT(" --defaultprinter=\"1\""); if (sg[i].m_bPublic) shcmd << wxT(" --public=\"1\""); printSsh(shcmd); break; } } } void MySession::checkXarch() { m_eXarch = XARCH_NONE; #ifdef __WXMSW__ wxFileName fn(m_sSysDir, wxT("nxwin.exe")); fn.AppendDir(wxT("bin")); if (fn.IsFileExecutable()) { m_eXarch = XARCH_CYGWIN; return; } fn.SetFullName(wxT("XMing.exe")); if (fn.IsFileExecutable()) m_eXarch = XARCH_XMING; #endif } #ifdef __WXMSW__ wxString MySession::getXfontPath(tXarch Xarch) { wxLogNull l; wxFileName fn(m_sSysDir, wxEmptyString); fn.AppendDir(wxT("share")); fn.AppendDir(wxT("fonts")); wxDir d(fn.GetShortPath()); FontpathTraverser t; d.Traverse(t); wxString ret = t.GetFontPath(Xarch); if (!ret.IsEmpty()) ret.Prepend(wxT(" -fp ")); return ret; } void MySession::unhideNXWin() { if (XARCH_CYGWIN == m_eXarch) { // Required only for NXWin - Xming shows up by itself DWORD stored_nxserver_version = ::RegisterWindowMessage(wxT("STORED_NXSERVER_VERSION")); HWND h = ::GetTopWindow(0); while (h) { wxString wclass; int r = GetClassName(h, wxStringBuffer(wclass,40), 38); if ((r > 0) && wclass.Contains(wxT("cygwin/xfree86"))) { DWORD pid; GetWindowThreadProcessId(h, &pid); if ((int)pid == m_iXserverPID) { // Trigger unhiding of fullscreen NXWin SendMessage(h, WM_USER + 1, 0, 0); // Trigger for worked close button NXWin ::SendMessage(h, stored_nxserver_version, 1, 1); SetForegroundWindow(h); break; } } h = ::GetNextWindow(h , GW_HWNDNEXT); } } } void MySession::terminateXserver() { // nxwin and nonencrypted: keep running (here we need it?) if ((XARCH_CYGWIN == m_eXarch) && (!m_bSslTunneling)) return; // Xming non-fullscreen: keep running if ((XARCH_CYGWIN == m_eXarch) || (MyXmlConfig::DPTYPE_FULLSCREEN == m_pCfg->eGetDisplayType())) { if ((0 != m_iXserverPID) && wxProcess::Exists(m_iXserverPID)) wxProcess::Kill(m_iXserverPID, wxSIGKILL, wxKILL_NOCHILDREN); } } bool MySession::startXserver() { int display = getFirstFreePort(X_PORT_OFFSET); myLogTrace(MYTRACETAG, wxT("startXServer first free port is %d"), display); if (0 == display) return false; display -= X_PORT_OFFSET; wxString dpyStr = wxT(":"); dpyStr << display; wxString wxWinCmd; wxFileName fn(m_sSysDir, wxT("nxwin.exe")); fn.AppendDir(wxT("bin")); switch (m_eXarch) { case XARCH_CYGWIN: m_sXauthCookie = getXauthCookie(display, wxT("/unix")); if (m_sXauthCookie.IsEmpty()) { wxLogError(_("Could not create X11 authentication cookie")); return false; } wxWinCmd = fn.GetShortPath(); wxWinCmd << wxT(" -nowinkill"); wxWinCmd << wxT(" -clipboard"); wxWinCmd << wxT(" -noloadxkb"); wxWinCmd << wxT(" -agent"); wxWinCmd << wxT(" -hide"); wxWinCmd << wxT(" -noreset"); wxWinCmd << wxT(" -auth ") << getXauthPath(XARCH_CYGWIN); wxWinCmd << wxT(" -nolisten tcp"); wxWinCmd << getXfontPath(m_eXarch); wxWinCmd << m_pCfg->sGetXserverParams(true); if ( ( (m_pCfg->eGetDesktopType() == MyXmlConfig::DTYPE_CUSTOM) && (!m_pCfg->bGetVirtualDesktop()) ) || ( (m_pCfg->eGetSessionType() == MyXmlConfig::STYPE_WINDOWS) && (m_pCfg->bGetRdpRootless()) ) || ( (m_pCfg->eGetSessionType() == MyXmlConfig::STYPE_VNC) && (m_pCfg->bGetVncRootless()) ) ) wxWinCmd << wxT(" -multiwindow "); else if (m_pCfg->eGetDisplayType()==MyXmlConfig::DPTYPE_NODECORATION) wxWinCmd << wxT(" -nodecoration"); { wxString title = m_pCfg->sGetUsername(); if (m_pCfg->bGetGuestMode()) { title = m_pCfg->sGetGuestUser(); if (title.IsEmpty()) title << wxT("guest"); } wxWinCmd << wxT(" -name ") << title << wxT("@") << m_pCfg->sGetName(); } /* Warning from Djelf: format of cmd string at its end always MUST BE "-name :" */ wxWinCmd << wxT(" ") << dpyStr; break; case XARCH_XMING: if (m_pCfg->eGetDisplayType() != MyXmlConfig::DPTYPE_FULLSCREEN) { // If not fullscreen, we use a single instance of Xming, running in // multiwindow mode. In this case, we check for a running Xming, by // using a win32 named mutex. int xdpy = getXmingPort(display + X_PORT_OFFSET); if (0 != xdpy) { dpyStr = wxT("127.0.0.1:"); dpyStr << xdpy - X_PORT_OFFSET; ::wxSetEnv(wxT("DISPLAY"), dpyStr); wxLogInfo(wxT("env: DISPLAY='%s'"), VMB(dpyStr)); // Xauth cookie and X.hosts are still existing // from initial startup return true; } } fn.SetFullName(wxT("Xming.exe")); m_sXauthCookie = getXauthCookie(display, wxEmptyString); if (m_sXauthCookie.IsEmpty()) { wxLogError(_("Could not create X11 authentication cookie")); return false; } wxWinCmd = fn.GetShortPath(); wxWinCmd << wxT(" ") << dpyStr; fn.Assign(m_sUserDir, wxString::Format(wxT("X%d.log"), (int)display)); wxWinCmd << wxT(" -logfile \"") << fn.GetFullPath() << wxT("\""); wxWinCmd << wxT(" -br"); wxWinCmd << wxT(" -nowinkill"); wxWinCmd << wxT(" -clipboard "); switch (m_pCfg->iGetClipFilter()) { case 0: wxWinCmd << wxT("primary"); break; case 1: wxWinCmd << wxT("clipboard"); break; case 2: wxWinCmd << wxT("both"); break; } wxWinCmd << wxT(" -notrayicon"); wxWinCmd << getXfontPath(m_eXarch); wxWinCmd << wxT(" -silent-dup-error"); if (::checkMultiMonitors() > 1) wxWinCmd << wxT(" -multimonitors"); wxWinCmd << m_pCfg->sGetXserverParams(false); fn.Assign(m_sSysDir, wxEmptyString); fn.AppendDir(wxT("share")); fn.AppendDir(wxT("Xming")); fn.MakeAbsolute(); ::wxSetEnv(wxT("XMING_BASEDIR"), fn.GetPath()); wxLogInfo(wxT("env: XMING_BASEDIR='%s'"), VMB(fn.GetPath())); dpyStr.Prepend(wxT("127.0.0.1")); break; default: wxLogError(_("No X server found.")); return false; break; } wxLogInfo(wxT("Executing %s"), VMB(wxWinCmd)); int r = CreateDetachedProcess(wxWinCmd.ToUTF8()); if (r != 0) { wxLogError(_("Could not execute %s: %s\n"), VMB(wxWinCmd), wxSysErrorMsg(r)); return false; } m_iXserverPID = GetDetachedPID(); AllowSetForegroundWindow(m_iXserverPID); ::wxSetEnv(wxT("DISPLAY"), dpyStr); wxLogInfo(wxT("env: DISPLAY='%s'"), VMB(dpyStr)); return true; } #endif void MySession::startProxy() { myLogTrace(MYTRACETAG, wxT("MySession::startProxy() called")); long cupsport = m_pCfg->iGetCupsPort(); wxString popts; if (m_lProtocolVersion >= 0x00030000) popts << wxT("nx/"); popts << wxT("nx,cookie=") << m_sProxyCookie; if (m_lProtocolVersion < 0x00030000) popts << wxT(",root=") << cygPath(m_sUserDir); popts << m_pCfg->sGetProxyParams(m_lProtocolVersion); if (!m_sSubscription.IsEmpty()) popts << wxT(",product=") << m_sSubscription; if (m_pCfg->bGetEnableSmbSharing()) { SmbClient sc; if (sc.IsAvailable()) { if (m_sSmbPort.IsEmpty()) { popts << wxT(",samba=") << m_pCfg->iGetSmbPort(); } else { popts << wxT(",samba=") << m_sSmbPort; } } } if ((getActiveCupsPrinters().GetCount() > 0) && (isCupsRunning())) popts << wxT(",cups=") << cupsport; #ifdef SUPPORT_USBIP if (m_pCfg->bGetEnableUSBIP()) popts << wxT(",http=") << wxConfigBase::Get()->Read(wxT("Config/UsbipPort"), 3420); #endif if ((m_bEsdRunning || m_bNativePARunning) && (0 < m_lEsdPort)) popts << wxT(",media=") << m_lEsdPort; popts << wxT(",encryption=") << (m_bSslTunneling ? 1 : 0) << wxT(",session=session"); popts << wxT(",id=") << m_sSessionID; if (m_bSslTunneling) { if (m_lProtocolVersion <= 0x00020000) { m_sProxyIP = wxT("127.0.0.1"); m_sProxyPort = wxString::Format(wxT("%d"), (int)getFirstFreePort(PROXY_PORT_OFFSET)); popts << wxT(",listen=") << m_sProxyPort; } } else popts << wxT(",connect=") << m_sProxyIP; // Undocumented feature of the original: // If a file ~/.nx/options exists, it's content is // appended to the regular options. wxFileName mergeOpts(m_sUserDir, wxT("options")); if (mergeOpts.FileExists()) { wxLogNull dummy; wxTextFile f(mergeOpts.GetFullPath()); if (f.Open()) { wxString mopts = f.GetFirstLine().Strip(wxString::both); if (!mopts.IsEmpty()) { if ((!popts.IsEmpty()) && (!mopts.StartsWith(wxT(",")))) popts << wxT(","); popts << mopts; } f.Close(); } } popts << wxT(":") << m_sSessionDisplay; /* but in nxclient now is: popts << wxT(",display=:0:") << m_sSessionDisplay; */ m_sSessionDir = m_sUserDir; m_sSessionDir << wxFileName::GetPathSeparator() << wxT("S-") << m_sSessionID; { if (!wxFileName::Mkdir(m_sSessionDir, 0700, wxPATH_MKDIR_FULL)) { wxLogSysError(_("Could not create session directory\n%s\n"), VMB(m_sSessionDir)); m_bGotError = true; } m_sOptFilename = m_sSessionDir; m_sOptFilename << wxFileName::GetPathSeparator() << wxT("options"); wxFile f; if (f.Open(m_sOptFilename, wxFile::write, wxS_IRUSR|wxS_IWUSR)) { f.Write(popts + wxT("\n")); f.Close(); wxLogInfo(wxT("Option file='%s'\n"), VMB(m_sOptFilename)); wxLogInfo(wxT("Session options='%s'\n"), VMB(popts)); wxString pcmd; wxConfigBase::Get()->Read(wxT("Config/SystemNxDir"), &pcmd); pcmd << wxFileName::GetPathSeparator() << wxT("bin") << wxFileName::GetPathSeparator() << wxT("nxproxy -S nx,options=") << cygPath(m_sOptFilename) << wxT(":") << m_sSessionDisplay; printSsh(wxT("bye"), true, wxT("Options file written, ")); if (m_lProtocolVersion <= 0x00020000) { wxLogInfo(wxT("Executing %s"), VMB(pcmd)); #ifdef __WXMSW__ if (m_eXarch == XARCH_XMING) CreateDetachedProcess(pcmd.ToUTF8()); if (m_iXserverPID) AllowSetForegroundWindow(m_iXserverPID); #else setTurboPath(true); ::wxExecute(pcmd, wxEXEC_ASYNC); setTurboPath(false); #endif } } else { wxLogSysError(_("Could not write session options\n%s\n"), VMB(m_sOptFilename)); m_bGotError = true; } } } ArrayOfShareGroups MySession::getActiveCupsPrinters() { ArrayOfShareGroups ret; if (!m_pCfg->bGetUseCups()) return ret; CupsClient cc; if (cc.IsAvailable()) { ArrayOfShares sa = cc.GetShares(); ret = m_pCfg->aGetShareGroups(); wxArrayString used = m_pCfg->aGetUsedShareGroups(); for (size_t i = ret.GetCount(); i > 0; i--) { size_t idx = i - 1; if (used.Index(ret[idx].m_sGroupName) == wxNOT_FOUND) { myLogTrace(MYTRACETAG, wxT("removing[%d] '%s'"), (int)idx, VMB(ret[idx].m_sShareName)); ret.RemoveAt(idx); continue; } if (ret[idx].m_eType != SharedResource::SHARE_CUPS_PRINTER) { myLogTrace(MYTRACETAG, wxT("removing[%d] '%s'"), (int)idx, VMB(ret[idx].m_sShareName)); ret.RemoveAt(idx); continue; } bool bAvailable = false; myLogTrace(MYTRACETAG, wxT("Considering CUPS printer '%s' %d"), VMB(ret[idx].m_sShareName), (int)idx); for (size_t j = 0; j < sa.GetCount(); j++) { if (sa[j].name == ret[idx].m_sShareName) { bAvailable = true; break; } } myLogTrace(MYTRACETAG, wxT("'%s' is %savailable"), VMB(ret[idx].m_sShareName), (bAvailable ? "" : "not ")); if (!bAvailable) { myLogTrace(MYTRACETAG, wxT("removing[%d] '%s'"), (int)idx, VMB(ret[idx].m_sShareName)); ret.RemoveAt(idx); } } } myLogTrace(MYTRACETAG, wxT("# of active printers: %d"), (int)ret.GetCount()); return ret; } bool MySession::isCupsRunning() { bool ret = m_bCupsRunning; if (!ret) { CupsClient cc; ret = cc.IsAvailable(); m_bCupsRunning = ret; } myLogTrace(MYTRACETAG, wxT("isCupsRunning returning %s"), (ret ? "true" : "false")); return ret; } void MySession::setTurboPath(bool enable) { #ifdef __WXMAC__ return; wxString ldpath; bool isset = ::wxGetEnv(wxT("DYLD_LIBRARY_PATH"), &ldpath); bool contains = isset && ldpath.Contains(wxT("libjpeg-turbo")); if (enable) { if (!contains) { # if defined(__x86_64) || defined(__IA64__) wxString archlib = wxT("lib64"); # else wxString archlib = wxT("lib"); # endif wxString turbopath; wxFileName tjpeg; tjpeg.AssignDir(wxT("/usr")); tjpeg.AppendDir(archlib); tjpeg.AppendDir(wxT("libjpeg-turbo")); if (tjpeg.DirExists()) { turbopath = tjpeg.GetPath(); ldpath.Prepend(tjpeg.GetPath().Append(wxT(":"))); } else { tjpeg.AssignDir(wxT("/opt/libjpeg-turbo")); tjpeg.AppendDir(archlib); if (tjpeg.DirExists()) { turbopath = tjpeg.GetPath(); ldpath.Prepend(tjpeg.GetPath().Append(wxT(":"))); } } if (!turbopath.IsEmpty()) { if (!ldpath.IsEmpty()) ldpath.Prepend(wxT(":")); ldpath.Prepend(turbopath); } ::myLogDebug(wxT("DYLD_LIBRARY_PATH='%s'"), VMB(ldpath)); if (!::wxSetEnv(wxT("DYLD_LIBRARY_PATH"), ldpath)) { wxLogSysError(wxT("Cannot set DYLD_LIBRARY_PATH")); } } } else { if (contains) { wxString newpath; wxStringTokenizer t(ldpath, wxT(":")); while (t.HasMoreTokens()) { wxString fragment = t.GetNextToken(); if ((!fragment.IsEmpty()) && (!fragment.Contains(wxT("libjpeg-turbo")))) { if (!newpath.IsEmpty()) newpath.Append(wxT(":")); newpath.Append(fragment); } } if (newpath.IsEmpty()) { ::wxUnsetEnv(wxT("DYLD_LIBRARY_PATH")); } else { ::myLogDebug(wxT("DYLD_LIBRARY_PATH='%s'"), VMB(newpath)); if (!::wxSetEnv(wxT("DYLD_LIBRARY_PATH"), newpath)) { wxLogSysError(wxT("Cannot set DYLD_LIBRARY_PATH")); } } } } #else wxUnusedVar(enable); #endif } void MySession::cleanupOldSessions() { wxDir ud; myLogTrace(MYTRACETAG, wxT("Cleaning up old session datav in %s"), VMB(m_sUserDir)); if (ud.Open(m_sUserDir)) { SessionCleaner sc(m_sUserDir); ud.Traverse(sc); } } void MySession::clearSshKeys(const wxString &keyloc) { myLogTrace(MYTRACETAG, wxT("Clearing keys for %s at %s"), VMB(m_pCfg->sGetServerHost()), VMB(keyloc)); wxString keyfile = keyloc.BeforeLast(wxT(':')); #ifdef __WXMSW__ if (keyfile.StartsWith(wxT("/cygdrive/"), &keyfile)) { keyfile = keyfile.BeforeFirst(wxT('/')).Upper().Append(wxT(":/")).Append(keyfile.AfterFirst(wxT('/'))); keyfile.Replace(wxT("/"), wxT("\\")); myLogTrace(MYTRACETAG, wxT("Keyfile: %s"), VMB(keyfile)); } #endif long n; if (keyloc.AfterLast(wxT(':')).ToLong(&n)) { n--; wxTextFile tf(keyfile); if (tf.Exists()) { if (tf.Open()) { myLogTrace(MYTRACETAG, wxT("Removing '%s'"), VMB(tf[n])); tf.RemoveLine(n); wxIPV4address ip; if (ip.Hostname(m_pCfg->sGetServerHost())) { wxString ipnum = ip.IPAddress(); wxString line; for (line = tf.GetFirstLine(); !tf.Eof(); line = tf.GetNextLine()) { if (line.Contains(ipnum)) { myLogTrace(MYTRACETAG, wxT("Removing '%s'"), VMB(line)); tf.RemoveLine(tf.GetCurrentLine()); } } } tf.Write(); } } } } bool MySession::Create(MyXmlConfig &cfgpar, const wxString password, wxWindow *parent) { m_sClearPassword = password; m_bSessionRunning = false; m_bCupsRunning = false; m_bEsdRunning = false; m_bNativePARunning = false; m_lEsdPort = 0; m_bAbort = false; m_bSessionEstablished = false; m_bCollectSessions = false; m_bCollectConfig = false; m_bCollectResources = false; m_bIsShadow = false; m_bNextCmd = false; m_sSessionID = wxEmptyString; m_pParent = parent; MyXmlConfig cfg(cfgpar.sGetFileName()); m_pCfg = &cfg; if (cfg.IsValid()) { // Copy misc values from login dialog m_pCfg->bSetUseSmartCard(cfgpar.bGetUseSmartCard()); m_pCfg->bSetEnableSSL(cfgpar.bGetEnableSSL()); m_pCfg->bSetGuestMode(cfgpar.bGetGuestMode()); if (!cfgpar.bGetGuestMode()) { m_pCfg->sSetUsername(cfgpar.sGetUsername()); m_pCfg->sSetPassword(password); } wxConfigBase::Get()->Read(wxT("Config/SystemNxDir"), &m_sSysDir); wxConfigBase::Get()->Read(wxT("Config/UserNxDir"), &m_sUserDir); checkXarch(); wxFileName fn(m_sSysDir, wxEmptyString); fn.AppendDir(wxT("bin")); #ifdef __WXMSW__ fn.SetName(wxT("nxssh.exe")); #else fn.SetName(wxT("nxssh")); #endif wxString nxsshcmd = fn.GetShortPath(); nxsshcmd << wxT(" -nx -x -2 -4") << wxT(" -p ") << m_pCfg->iGetServerPort() << wxT(" -o 'RhostsAuthentication no'") << wxT(" -o 'PasswordAuthentication no'") << wxT(" -o 'RSAAuthentication no'") << wxT(" -o 'RhostsRSAAuthentication no'") << wxT(" -o 'PubkeyAuthentication yes'"); m_sTempDir = m_sUserDir; m_sTempDir << wxFileName::GetPathSeparator() << wxT("temp") << wxFileName::GetPathSeparator() << ::wxGetProcessId(); wxFileName::Mkdir(m_sTempDir, 0700, wxPATH_MKDIR_FULL); wxString logfn = m_sTempDir + wxFileName::GetPathSeparator() + wxT("runlog"); #ifdef __VISUALC__ ofstream *log = new ofstream(); #else std::ofstream *log = new std::ofstream(); #endif log->open(logfn.ToUTF8()); new RunLog(new wxLogStream(log)); logfn = m_sTempDir + wxFileName::GetPathSeparator() + wxT("sshlog"); #ifdef __VISUALC__ log = new ofstream(); #else log = new std::ofstream(); #endif log->open(logfn.ToUTF8()); m_pSshLog = new wxLogStream(log); wxLog::SetLogLevel(wxLOG_Max); m_bIsShadow = (m_pCfg->eGetSessionType() == MyXmlConfig::STYPE_SHADOW); if (m_pCfg->bGetRemoveOldSessionFiles()) cleanupOldSessions(); if (m_pCfg->bGetUseSmartCard()) { myLogTrace(MYTRACETAG, wxT("Checking for SmartCard")); CardWaiterDialog wd; m_iReader = wd.WaitForCard(parent); if (m_iReader == -1) return false; nxsshcmd << wxT(" -I ") << m_iReader; } else { if (m_pCfg->sGetSshKey().IsEmpty()) { fn.Assign(m_sSysDir, wxT("server.id_dsa.key")); fn.AppendDir(wxT("share")); fn.AppendDir(wxT("keys")); } else { fn.Assign(m_sTempDir, wxT("keylog")); wxFile f; if (fn.FileExists()) ::wxRemoveFile(fn.GetFullPath()); if (f.Open(fn.GetFullPath(), wxFile::write_excl, wxS_IRUSR|wxS_IWUSR)) { f.Write(m_pCfg->sGetSshKey()); f.Close(); } else { wxLogSysError(_("Could not write %s"), VMB(fn.GetFullPath())); return false; } } nxsshcmd << wxT(" -i ") << fn.GetShortPath(); } if (m_pCfg->bGetUseProxy()) { if (m_pCfg->bGetExternalProxy()) { if (!m_pCfg->sGetProxyCommand().IsEmpty()) nxsshcmd << wxT(" -o 'ProxyCommand ") << m_pCfg->sGetProxyCommand() << wxT("'"); } else { if (!m_pCfg->sGetProxyHost().IsEmpty()) { // Internal (NoMachine) proxy if (m_pCfg->sGetProxyUser().IsEmpty()) nxsshcmd << wxT(" -P ") << m_pCfg->sGetProxyHost() << wxT(":") << m_pCfg->iGetProxyPort(); else { wxString proxyPass = m_pCfg->sGetProxyPass(); if (!m_pCfg->bGetProxyPassRemember()) { ProxyPasswordDialog dlg(m_pParent); if (dlg.ShowModal() == wxID_OK) proxyPass = dlg.GetPassword(); else return false; } nxsshcmd << wxT(" -P ") << m_pCfg->sGetProxyUser() << wxT(":") << proxyPass << wxT("@") << m_pCfg->sGetProxyHost() << wxT(":") << m_pCfg->iGetProxyPort(); } } } } nxsshcmd << wxT(" -4 -B -E") << wxT(" nx@") << m_pCfg->sGetServerHost(); m_sHost = m_pCfg->sGetServerHost(); wxString stmp; ::wxGetEnv(wxT("PATH"), &stmp); // Prepend our system directory, so that pconnect can be found by nxssh (if necessary) fn.Assign(m_sSysDir, wxT("bin")); if (!stmp.Contains(fn.GetShortPath())) { #ifdef __WXMSW__ stmp.Prepend(wxT(";")).Prepend(fn.GetShortPath()); #else stmp.Prepend(wxT(":")).Prepend(fn.GetShortPath()); #endif #ifdef __WXMAC__ stmp.Append(wxT(":/usr/X11R6/bin:/usr/X11/bin")); #endif ::wxSetEnv(wxT("PATH"), stmp); } wxLogInfo(wxT("env: PATH='%s'"), VMB(stmp)); fn.Assign(wxFileName::GetHomeDir()); ::wxSetEnv(wxT("NX_HOME"), fn.GetShortPath()); wxLogInfo(wxT("env: NX_HOME='%s'"), VMB(fn.GetShortPath())); ::wxSetEnv(wxT("HOME"), fn.GetShortPath()); wxLogInfo(wxT("env: HOME='%s'"), VMB(fn.GetShortPath())); fn.Assign(m_sUserDir); ::wxSetEnv(wxT("NX_ROOT"), fn.GetShortPath()); wxLogInfo(wxT("env: NX_ROOT='%s'"), VMB(fn.GetShortPath())); fn.Assign(m_sSysDir); ::wxSetEnv(wxT("NX_SYSTEM"), fn.GetShortPath()); wxLogInfo(wxT("env: NX_SYSTEM='%s'"), VMB(fn.GetShortPath())); fn.Assign(::wxGetApp().GetSelfPath()); ::wxSetEnv(wxT("NX_CLIENT"), fn.GetShortPath()); wxLogInfo(wxT("env: NX_CLIENT='%s'"), VMB(fn.GetShortPath())); ::wxSetEnv(wxT("NX_VERSION"), m_sProtocolVersion); wxLogInfo(wxT("env: NX_VERSION='%s'"), VMB(m_sProtocolVersion)); if (m_pCfg->eGetDisplayType() == MyXmlConfig::DPTYPE_FULLSCREEN) { bool bVal = false; wxConfigBase::Get()->Read(wxT("Config/DisableMagicPixel"), &bVal, false); if (bVal) { int dspw, dsph; ::wxDisplaySize(&dspw, &dsph); wxString w = wxString::Format(wxT("%d"), (int)dspw); ::wxSetEnv(wxT("NX_KIOSK_X"), w); wxLogInfo(wxT("env: NX_KIOSK_X='%s'"), VMB(w)); } } ::wxSetEnv(wxT("XAUTHORITY"), getXauthPath(m_eXarch)); wxLogInfo(wxT("env: XAUTHORITY='%s'"), VMB(getXauthPath(m_eXarch))); #ifdef __UNIX__ // NX needs TEMP or NX_TEMP to be set to the same dir // where .X11-unix resides (typically /tmp) stmp = wxConvLocal.cMB2WX(x11_socket_path); if (!stmp.IsEmpty()) { fn.Assign(stmp); fn.RemoveLastDir(); fn.SetName(wxEmptyString); ::wxSetEnv(wxT("NX_TEMP"), cygPath(fn.GetFullPath())); wxLogInfo(wxT("env: NX_TEMP='%s'"), VMB(cygPath(fn.GetShortPath()))); } else { ::wxSetEnv(wxT("NX_TEMP"), wxT("/tmp")); wxLogInfo(wxT("env: NX_TEMP='/tmp'")); } #else ::wxSetEnv(wxT("NX_TEMP"), cygPath(m_sTempDir)); wxLogInfo(wxT("env: NX_TEMP='%s'"), VMB(cygPath(m_sTempDir))); #endif #ifdef __WXMSW__ if (m_eXarch == XARCH_CYGWIN) { wxString srvstr = wxT("server"); ::wxSetEnv(wxT("CYGWIN"), srvstr); wxLogInfo(wxT("env: CYGWIN='%s'"), VMB(srvstr)); } if (!startXserver()) { wxLogError(_("Could not start local X server")); return false; } if (XARCH_XMING == m_eXarch) { // Now, that the X server has been started, // set XAUTHORITY // again, but this time in cygwin notation (for nxssh). ::wxSetEnv(wxT("XAUTHORITY"), getXauthPath(XARCH_CYGWIN)); wxLogInfo(wxT("env: XAUTHORITY='%s'"), VMB(getXauthPath(XARCH_CYGWIN))); // Configure XMing's special clipboard filter HWND clpWnd = FindWindow(NULL ,wxT("OpenNXWinClip")); if (NULL != clpWnd) { PostMessage(clpWnd, WM_USER + 1004, m_pCfg->iGetClipFilter() + 1, 0); } } #endif fn.Assign(m_sSysDir, wxT("bin")); m_iProgress = 0; ConnectDialog dlg(m_pParent); m_pDlg = &dlg; dlg.Show(true); dlg.SetStatusText(wxString::Format(_("Connecting to %s ..."), VMB(m_pCfg->sGetServerHost()))); if (m_pCfg->bGetEnableMultimedia()) { m_bEsdRunning = false; m_bNativePARunning = false; dlg.SetStatusText(_("Preparing multimedia service ...")); PulseAudio pa; if (pa.IsAvailable()) { wxLogInfo(wxT("using existing pulseaudio")); m_lEsdPort = wxConfigBase::Get()->Read(wxT("State/nxesdPort"), -1); if (m_lEsdPort < 0) m_lEsdPort = getFirstFreePort(6000); if (0 < m_lEsdPort) { bool pa_started = false; if (m_pCfg->bGetEnableNativePA()) { wxLogInfo(wxT("Activating Native Module in pulseaudio on port %ld"), m_lEsdPort); int pa_rate = 0; switch (m_pCfg->eGetRatePA()) { case MyXmlConfig::RATEPA_NORESAMPLE: pa_rate = 0; break; case MyXmlConfig::RATEPA_48000: pa_rate = 48000; break; case MyXmlConfig::RATEPA_44100: pa_rate = 44100; break; case MyXmlConfig::RATEPA_32000: pa_rate = 32000; break; case MyXmlConfig::RATEPA_16000: pa_rate = 16000; break; case MyXmlConfig::RATEPA_8000: pa_rate = 8000; break; } bool pa_mono = pa_rate > 0 ? m_pCfg->bGetEnableMonoPA() : false; pa_started = m_bNativePARunning = pa.ActivateNative(m_lEsdPort, pa_rate, pa_mono); } else { wxLogInfo(wxT("Activating ESD Module in pulseaudio on port %ld"), m_lEsdPort); pa_started = m_bEsdRunning = pa.ActivateEsound(m_lEsdPort); } if (pa_started) { wxConfigBase::Get()->Write(wxT("State/nxesdPort"), m_lEsdPort); wxConfigBase::Get()->Write(wxT("State/nxesdPID"), -1); } else { wxLogWarning(_("Could not start multimedia support")); } } else wxLogWarning(_("Could not assign a free port for multimedia support")); } #ifndef __WXMSW__ if (!m_bEsdRunning && !m_bNativePARunning) { // Fallback: original old nxesd long esdpid = wxConfigBase::Get()->Read(wxT("State/nxesdPID"), -1); m_lEsdPort = wxConfigBase::Get()->Read(wxT("State/nxesdPort"), -1); if ((-1 != esdpid) && (0 < m_lEsdPort)) m_bEsdRunning = wxProcess::Exists(esdpid); if ((!dlg.bGetAbort()) && (!m_bEsdRunning)) { wxFileName fn(m_sSysDir, wxEmptyString); fn.AppendDir(wxT("bin")); fn.SetName(wxT("nxesd")); if (fn.FileExists()) { wxString esdcmd = fn.GetFullPath(); m_lEsdPort = getFirstFreePort(6000); if (0 < m_lEsdPort) { esdcmd << wxT(" -tcp -nobeeps -bind 127.0.0.1 -spawnfd 1 -port ") << m_lEsdPort; wxLogInfo(wxT("starting in background: %s"), VMB(esdcmd)); wxProcess *nxesd = wxProcess::Open(esdcmd, wxEXEC_ASYNC|wxEXEC_MAKE_GROUP_LEADER); if (nxesd) { nxesd->CloseOutput(); wxStopWatch sw; while (!(dlg.bGetAbort() || nxesd->IsInputAvailable())) { ::wxGetApp().Yield(true); wxLog::FlushActive(); // Timeout after 10 sec if (sw.Time() > 10000) break; } char msg = '\0'; if (nxesd->IsInputAvailable()) nxesd->GetInputStream()->Read(&msg, 1); long esdpid = nxesd->GetPid(); nxesd->Detach(); if (msg) { m_bEsdRunning = true; wxConfigBase::Get()->Write(wxT("State/nxesdPID"), esdpid); wxConfigBase::Get()->Write(wxT("State/nxesdPort"), m_lEsdPort); } } if (!m_bEsdRunning) wxLogWarning(_("Could not start multimedia support")); } else wxLogWarning(_("Could not assign a free port for multimedia support")); } else wxLogWarning(_("Could not start multimedia support")); } } #endif dlg.SetStatusText(wxString::Format(_("Connecting to %s ..."), VMB(m_pCfg->sGetServerHost()))); } if (dlg.bGetAbort()) { #ifdef __WXMSW__ terminateXserver(); #endif return false; } if (getActiveCupsPrinters().GetCount() > 0) { dlg.SetStatusText(_("Preparing CUPS service ...")); dlg.SetStatusText(wxString::Format(_("Connecting to %s ..."), VMB(m_pCfg->sGetServerHost()))); } MyIPC nxssh; m_pNxSsh = &nxssh; wxLogInfo(wxT("Starting %s"), VMB(nxsshcmd)); do { m_bRemoveKey = false; m_sOffendingKey = wxEmptyString; setTurboPath(true); if (nxssh.SshProcess(nxsshcmd, fn.GetShortPath(), this)) { setTurboPath(false); m_bGotError = false; m_eConnectState = STATE_INIT; while (!(dlg.bGetAbort() || m_bGotError || m_bAbort || (m_bSessionRunning && m_bSessionEstablished))) { wxLog::FlushActive(); ::wxGetApp().Yield(true); #ifdef __WXMSW__ if (m_iXserverPID) AllowSetForegroundWindow(m_iXserverPID); #endif } if (dlg.bGetAbort() || m_bGotError || m_bAbort) { if (m_bRemoveKey) { while (m_sOffendingKey.IsEmpty()) { wxLog::FlushActive(); ::wxGetApp().Yield(true); } } else { #ifndef __WXMSW__ nxssh.Kill(); #endif #ifdef __WXMSW__ terminateXserver(); #endif return false; } } /* // but original nxclient stay leave nxssh in case of unencrypted conn else { if (m_bSessionEstablished && (!m_bSslTunneling)) { // Unecrypted session (handled by nxproxy), get rid of nxssh nxssh.Kill(); } } */ wxThread::Sleep(500); #ifdef __WXMSW__ if (m_iXserverPID) AllowSetForegroundWindow(m_iXserverPID); #endif } else { setTurboPath(false); wxLogError(_("Called command was: ") + nxsshcmd); wxLogError(_("Could not start nxssh.")); #ifdef __WXMSW__ terminateXserver(); #endif return false; } if (m_bRemoveKey) clearSshKeys(m_sOffendingKey); } while (m_bRemoveKey); nxssh.Detach(); #ifdef __WXMSW__ if (m_iXserverPID) AllowSetForegroundWindow(m_iXserverPID); #endif if (m_pCfg->bGetEnableUSBIP()) { myLogTrace(MYTRACETAG, wxT("Enabling UsbIp")); ::wxGetApp().SetNxSshPID(nxssh.GetPID()); ::wxGetApp().SetSessionCfg(*m_pCfg); ::wxGetApp().SetSessionID(m_sSessionID.Right(32)); ::wxGetApp().SetRequireStartUsbIp(true); } else { if (m_pCfg->bGetUseSmartCard()) { myLogTrace(MYTRACETAG, wxT("Enabling WatchReader %d"), (int)m_iReader); ::wxGetApp().SetNxSshPID(nxssh.GetPID()); ::wxGetApp().SetReader(m_iReader); ::wxGetApp().SetRequireWatchReader(true); } } #ifdef __WXMSW__ unhideNXWin(); #endif return true; } return false; }