#include #include "fzputtygen_interface.h" #include "Options.h" #include "filezillaapp.h" #include "inputdialog.h" CFZPuttyGenInterface::CFZPuttyGenInterface(wxWindow* parent) : m_parent(parent) { } CFZPuttyGenInterface::~CFZPuttyGenInterface() { if (m_pProcess) EndProcess(); } void CFZPuttyGenInterface::EndProcess() { m_pProcess->CloseOutput(); m_pProcess->Detach(); m_pProcess = 0; } void CFZPuttyGenInterface::DeleteProcess() { delete m_pProcess; m_pProcess = 0; } bool CFZPuttyGenInterface::IsProcessCreated() { return m_pProcess != 0; } bool CFZPuttyGenInterface::IsProcessStarted() { return m_initialized; } int CFZPuttyGenInterface::NeedsConversion(wxString keyFile, bool silent) { if (!Send(_T("file " + keyFile))) return -1; wxString reply; ReplyCode code = GetReply(reply); if (code == failure) return -1; if (code == error || (reply != _T("ok") && reply != _T("convertible"))) { if (!silent) { wxString msg; if (reply == _T("incompatible")) { msg.Printf(_("The file '%s' contains an SSH1 key. The SSH1 protocol has been deprecated, FileZilla only supports SSH2 keys."), keyFile); } else { msg.Printf(_("The file '%s' could not be loaded or does not contain a private key."), keyFile); } wxMessageBoxEx(msg, _("Could not load key file"), wxICON_EXCLAMATION); } return -1; } return reply == _T("convertible") ? 1 : 0; } int CFZPuttyGenInterface::IsKeyFileEncrypted() { if (!Send(_T("encrypted"))) return -1; wxString reply; ReplyCode code = GetReply(reply); if (code != success) { wxASSERT(code != error); return -1; } return reply == _T("1") ? 1 : 0; } bool CFZPuttyGenInterface::LoadKeyFile(wxString& keyFile, bool silent, wxString& comment, wxString& data) { if (!LoadProcess(silent)) { return false; } int needs_conversion = NeedsConversion(keyFile, silent); if (needs_conversion < 0) { return false; } ReplyCode code; if (needs_conversion) { if (silent) { return false; } int encrypted = IsKeyFileEncrypted(); if (encrypted < 0) { return false; } wxString msg = wxString::Format(_("The file '%s' is not in a format supported by FileZilla.\nWould you like to convert it into a supported format?"), keyFile); int res = wxMessageBoxEx(msg, _("Convert key file"), wxICON_QUESTION | wxYES_NO); if (res != wxYES) return false; msg = wxString::Format(_("Enter the password for the file '%s'.\nThe converted file will be protected with the same password."), keyFile); CInputDialog dlg; if (!dlg.Create(m_parent, _("Password required"), msg)) return false; dlg.SetPasswordMode(true); if (dlg.ShowModal() != wxID_OK) return false; if (!Send(_T("password " + dlg.GetValue()))) return false; wxString reply; code = GetReply(reply); if (code != success) { msg = wxString::Format(_("Failed to load private key: %s"), reply); wxMessageBoxEx(msg, _("Could not load private key"), wxICON_EXCLAMATION); return false; } wxFileDialog fileDlg(m_parent, _("Select filename for converted key file"), wxString(), wxString(), _T("PuTTY private key files (*.ppk)|*.ppk"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (fileDlg.ShowModal() != wxID_OK) return false; wxString newName = fileDlg.GetPath(); if (newName.empty()) return false; if (newName == keyFile) { // Not actually a requirement by fzputtygen, but be on the safe side. We don't want the user to lose his keys. wxMessageBoxEx(_("Source and target file may not be the same"), _("Could not convert private key"), wxICON_EXCLAMATION); return false; } Send(_T("write ") + newName); code = GetReply(reply); if (code == failure) return false; if (code != success) { wxMessageBoxEx(wxString::Format(_("Could not write key file: %s"), reply), _("Could not convert private key"), wxICON_EXCLAMATION); return false; } keyFile = newName; } if (!Send(_T("fingerprint"))) return false; code = GetReply(data); if (code != success) return false; Send(_T("comment")); code = GetReply(comment); if (code != success) return false; return true; } bool CFZPuttyGenInterface::LoadProcess(bool silent) { if (m_initialized) return m_pProcess != 0; wxString executable = COptions::Get()->GetOption(OPTION_FZSFTP_EXECUTABLE); int pos = executable.Find(wxFileName::GetPathSeparator(), true); if (pos == -1) { if (!silent) { wxMessageBoxEx(_("fzputtygen could not be started.\nPlease make sure this executable exists in the same directory as the main FileZilla executable."), _("Error starting program"), wxICON_EXCLAMATION); } return false; } else { executable = executable.Left(pos + 1) + _T("fzputtygen"); #ifdef __WXMSW__ executable += _T(".exe"); #endif if (!executable.empty() && executable[0] == '"') executable += '"'; if (executable.Find(' ') != -1 && executable[0] != '"') { executable = '"' + executable + '"'; } } m_pProcess = new wxProcess(m_parent); m_pProcess->Redirect(); wxLogNull log; if (!wxExecute(executable, wxEXEC_ASYNC, m_pProcess)) { delete m_pProcess; m_pProcess = 0; if (!silent) { wxMessageBoxEx(_("fzputtygen could not be started.\nPlease make sure this executable exists in the same directory as the main FileZilla executable."), _("Error starting program"), wxICON_EXCLAMATION); } return false; } m_initialized = true; return true; } bool CFZPuttyGenInterface::Send(const wxString& cmd) { if (!m_pProcess) return false; const wxWX2MBbuf buf = (cmd + _T("\n")).mb_str(wxConvUTF8); const size_t len = strlen (buf); wxOutputStream* stream = m_pProcess->GetOutputStream(); stream->Write((const char *) buf, len); if (stream->GetLastError() != wxSTREAM_NO_ERROR || stream->LastWrite() != len) { wxMessageBoxEx(_("Could not send command to fzputtygen."), _("Command failed"), wxICON_EXCLAMATION); return false; } return true; } CFZPuttyGenInterface::ReplyCode CFZPuttyGenInterface::GetReply(wxString& reply) { if (!m_pProcess) return failure; wxInputStream *pStream = m_pProcess->GetInputStream(); if (!pStream) { wxMessageBoxEx(_("Could not get reply from fzputtygen."), _("Command failed"), wxICON_EXCLAMATION); return failure; } char buffer[100]; wxString input; for (;;) { int pos = input.Find('\n'); if (pos == wxNOT_FOUND) { pStream->Read(buffer, 99); int read; if (pStream->Eof() || !(read = pStream->LastRead())) { wxMessageBoxEx(_("Could not get reply from fzputtygen."), _("Command failed"), wxICON_EXCLAMATION); return failure; } buffer[read] = 0; // Should only ever return ASCII strings so this is ok input += wxString(buffer, wxConvUTF8); pos = input.Find('\n'); if (pos == wxNOT_FOUND) continue; } int pos2; if (pos && input[pos - 1] == '\r') pos2 = pos - 1; else pos2 = pos; if (!pos2) { input = input.Mid(pos + 1); continue; } if (input.empty()) { continue; } wxChar c = input[0]; reply = input.Mid(1, pos2 - 1); input = input.Mid(pos + 1); if (c == '0' || c == '1') return success; else if (c == '2') return error; // Ignore others } }