#include "FileZilla.h" #include "serverpath.h" #define FTP_MVS_DOUBLE_QUOTE (wxChar)0xDC CServerPath::CServerPath() { m_type = DEFAULT; m_bEmpty = true; } CServerPath::CServerPath(const CServerPath &path, wxString subdir /*=_T("")*/) { m_type = path.m_type; m_bEmpty = path.m_bEmpty; m_prefix = path.m_prefix; m_Segments = path.m_Segments; subdir.Trim(true); subdir.Trim(false); if (subdir == _T("")) return; if (!ChangePath(subdir)) Clear(); } CServerPath::CServerPath(wxString path, ServerType type /*=DEFAULT*/) { m_type = type; SetPath(path); } CServerPath::~CServerPath() { } void CServerPath::Clear() { m_bEmpty = true; m_type = DEFAULT; m_prefix = _T(""); m_Segments.clear(); } bool CServerPath::SetPath(wxString newPath) { return SetPath(newPath, false); } bool CServerPath::SetPath(wxString &newPath, bool isFile) { m_Segments.clear(); m_prefix = _T(""); wxString path = newPath; wxString file; path.Trim(true); path.Trim(false); if (path == _T("")) { m_bEmpty = true; return false; } else m_bEmpty = false; if (m_type == DEFAULT) { int pos1 = path.Find(_T(":[")); if (pos1 != -1) { int pos2 = path.Find(']', true); if (pos2 != -1 && static_cast(pos2) == (path.Length() - 1) && !isFile) m_type = VMS; else if (isFile && pos2 > pos1) m_type = VMS; } else if (path.Length() >= 3 && ((path.c_str()[0] >= 'A' && path.c_str()[0] <= 'Z') || (path.c_str()[0] >= 'a' && path.c_str()[0] <= 'z')) && path.c_str()[1] == ':' && (path.c_str()[2] == '\\' || path.c_str()[2] == '/')) m_type = DOS; else if (path.c_str()[0] == FTP_MVS_DOUBLE_QUOTE && path.Last() == FTP_MVS_DOUBLE_QUOTE) m_type = MVS; else if (path[0] == ':' && (pos1 = path.Mid(1).Find(':')) > 0) { int slash = path.Find('/'); if (slash == -1 || slash > pos1) m_type = VXWORKS; } if (m_type == DEFAULT) m_type = UNIX; } int pos; switch (m_type) { case VMS: if (isFile) { pos = path.Find(']', true); if (pos == -1) { m_bEmpty = true; return false; } file = path.Mid(pos + 1); path = path.Left(pos + 1); } pos = path.Find(_T("[")); if (pos == -1 || path.Right(1) != _T("]")) { m_bEmpty = true; return false; } path.RemoveLast(); if (pos) m_prefix = path.Left(pos); path = path.Mid(pos + 1); pos = path.Find(_T(".")); while (pos != -1) { m_Segments.push_back(path.Left(pos)); path = path.Mid(pos + 1); pos = path.Find(_T(".")); } if (path != _T("")) m_Segments.push_back(path); break; case MVS: { int i = 0; wxChar c = path.c_str()[i]; while (c == FTP_MVS_DOUBLE_QUOTE || c == '\'' || c == '.') c = path.c_str()[++i]; path.Remove(0, i); while (path != _T("")) { c = path.Last(); if (c != FTP_MVS_DOUBLE_QUOTE && c != '\'') break; else path.RemoveLast(); } while (path.Replace(_T(".."), _T("."))); int pos = path.Find(_T(".")); while (pos != -1) { m_Segments.push_back(path.Left(pos)); path = path.Mid(pos + 1); pos = path.Find( _T(".") ); } if (path != _T("")) m_Segments.push_back(path); else m_prefix = _T("."); if (isFile) { if (m_prefix == _T(".")) return false; if (m_Segments.empty()) return false; file = m_Segments.back(); m_Segments.pop_back(); int pos = file.Find('('); int pos2 = file.Find(')'); if (pos != -1) { if (!pos || pos2 <= pos || pos2 != (int)file.Length() - 2) return false; m_Segments.push_back(file.Left(pos)); file = file.Mid(pos + 1, pos2 - pos - 1); m_prefix = _T(""); } else if (pos2 != -1) return false; else m_prefix = _T("."); } } break; case VXWORKS: { int colon2; if (path[0] != ':' || (colon2 = path.Mid(1).Find(':')) < 1) { m_bEmpty = true; return false; } int slash = path.Find('/'); if (slash != -1 && slash <= colon2) { m_bEmpty = true; return false; } m_prefix = path.Left(colon2 + 2); path = path.Mid(colon2 + 1); goto set_path_default; } break; case DOS: // Check for starting drive letter path.Replace(_T("\\"), _T("/")); pos = path.Find('/'); if (pos != 2 || path.c_str()[1] != ':' || !((path.c_str()[0] >= 'A' && path.c_str()[0] <= 'Z') || (path.c_str()[0] >= 'a' && path.c_str()[0] <= 'z'))) { m_bEmpty = true; return false; } // No break on purpose! default: set_path_default: while (path.Replace(_T("//"), _T("/"))); if (path.c_str()[0] == '/') path.Remove(0, 1); if (isFile) { pos = path.Find('/', true); if (pos == -1 || static_cast(pos) == (path.Length() - 1)) { m_bEmpty = true; return false; } file = path.Mid(pos + 1); path = path.Left(pos + 1); if (file == _T(".") || file == _T("..")) { m_bEmpty = true; return false; } } else if (path != _T("") && path.Right(1) != _T("/")) path = path + _T("/"); pos = path.Find(_T("/")); while (pos != -1) { wxString segment = path.Left(pos); if (segment == _T("..")) { if (m_Segments.empty()) { m_bEmpty = true; return false; } m_Segments.pop_back(); } else if (segment != _T(".")) m_Segments.push_back(segment); path = path.Mid(pos + 1); pos = path.Find(_T("/")); } break; } if (isFile) newPath = file; return true; } wxString CServerPath::GetPath() const { if (m_bEmpty) return _T(""); wxString path; switch (m_type) { case VMS: { path = m_prefix + _T("["); for (tConstSegmentIter iter = m_Segments.begin(); iter != m_Segments.end(); iter++) path += *iter + _T("."); path.RemoveLast(); path += _T("]"); } break; case DOS: { for (tConstSegmentIter iter = m_Segments.begin(); iter != m_Segments.end(); iter++) path += *iter + _T("\\"); } break; case MVS: path = _T("'"); for (tConstSegmentIter iter = m_Segments.begin(); iter != m_Segments.end(); iter++) { if (iter != m_Segments.begin()) path += _T("."); path += *iter; } path += m_prefix + _T("'"); break; case VXWORKS: path = m_prefix; for (tConstSegmentIter iter = m_Segments.begin(); iter != m_Segments.end(); iter++) { if (iter != m_Segments.begin()) path += _T("/"); path += *iter; } break; default: path = _T("/"); for (tConstSegmentIter iter = m_Segments.begin(); iter != m_Segments.end(); iter++) path += *iter + _T("/"); break; } return path; } bool CServerPath::HasParent() const { if (m_bEmpty) return false; if (m_type == DOS || m_type == VMS || m_type == MVS) return m_Segments.size() > 1; return !m_Segments.empty(); } CServerPath CServerPath::GetParent() const { if (!HasParent()) return CServerPath(); CServerPath parent = *this; parent.m_Segments.pop_back(); if (m_type == MVS) parent.m_prefix = _T("."); return parent; } wxString CServerPath::GetLastSegment() const { if (!HasParent()) return _T(""); if (!m_Segments.empty()) return m_Segments.back(); else return _T(""); } wxString CServerPath::GetSafePath() const { if (m_bEmpty) return _T(""); wxString safepath; safepath.Printf(_T("%d %d "), m_type, m_prefix.Length()); if (m_prefix != _T("")) safepath += m_prefix + _T(" "); for (tConstSegmentIter iter = m_Segments.begin(); iter != m_Segments.end(); iter++) safepath += wxString::Format(_T("%d %s "), iter->Length(), iter->c_str()); if (!m_Segments.empty()) safepath.RemoveLast(); return safepath; } bool CServerPath::SetSafePath(wxString path) { m_bEmpty = true; m_prefix = _T(""); m_Segments.clear(); int pos = path.Find(' '); if (pos < 1) return false; long type; if (!path.Left(pos).ToLong(&type)) return false; m_type = (ServerType)type; path = path.Mid(pos + 1); pos = path.Find(' '); if (pos == -1) { if (path != _T("0")) return false; else { // Is root folder, like / on unix like systems. m_bEmpty = false; return true; } } if (pos < 1) return false; unsigned long len; if (!path.Left(pos).ToULong(&len)) return false; path = path.Mid(pos + 1); if (path.Length() < len) return false; if (len) { m_prefix = path.Left(len); path = path.Mid(len); } while (path != _T("")) { pos = path.Find(' '); if (pos < 1) return false; if (!path.Left(pos).ToULong(&len)) return false; path = path.Mid(pos + 1); if (path.Length() < len) return false; if (!len) return false; if (path.Length() < len) return false; m_Segments.push_back(path.Left(len)); path = path.Mid(len + 1); } m_bEmpty = false; return true; } bool CServerPath::SetType(enum ServerType type) { if (!m_bEmpty && m_type != DEFAULT) return false; m_type = type; return true; } enum ServerType CServerPath::GetType() const { return m_type; } bool CServerPath::IsSubdirOf(const CServerPath &path, bool cmpNoCase) const { if (m_bEmpty || path.m_bEmpty) return false; if (cmpNoCase && m_prefix.CmpNoCase(path.m_prefix)) return false; if (!cmpNoCase && m_prefix != path.m_prefix) return false; if (m_type != path.m_type) return false; if (!HasParent()) return false; tConstSegmentIter iter1 = m_Segments.begin(); tConstSegmentIter iter2 = path.m_Segments.begin(); while (iter1 != m_Segments.end()) { if (iter2 == path.m_Segments.end()) return true; if (cmpNoCase) { if (iter1->CmpNoCase(*iter2)) return false; } else if (*iter1 != *iter2) return false; iter1++; iter2++; } return false; } bool CServerPath::IsParentOf(const CServerPath &path, bool cmpNoCase) const { if (!this) return false; return path.IsSubdirOf(*this, cmpNoCase); } bool CServerPath::ChangePath(wxString subdir) { wxString subdir2 = subdir; return ChangePath(subdir2, false); } bool CServerPath::ChangePath(wxString &subdir, bool isFile) { wxString dir = subdir; wxString file; dir.Trim(true); dir.Trim(false); if (dir == _T("")) { if (IsEmpty() || isFile) return false; else return true; } switch (m_type) { case VMS: { int pos1 = subdir.Find(_T("[")); if (pos1 == -1) { int pos2 = dir.Find(']', true); if (pos2 != -1) return false; if (isFile) { if (IsEmpty()) return false; subdir = dir; return true; } while (dir.Replace(_T(".."), _T("."))); } else { int pos2 = dir.Find(']', true); if (pos2 == -1) return false; if (isFile && static_cast(pos2) == (dir.Length() - 1)) return false; if (isFile && static_cast(pos2) != (dir.Length() - 1)) return false; if (pos2 <= pos1) return false; if (isFile) file = dir.Mid(pos2 + 1); dir = dir.Left(pos2); if (pos1) m_prefix = dir.Left(pos1); else m_prefix = _T(""); dir = dir.Mid(pos1 + 1); m_Segments.clear(); } int pos = dir.Find(_T(".")); while (pos != -1) { m_Segments.push_back(dir.Left(pos)); dir = dir.Mid(pos + 1); pos = dir.Find(_T(".")); } if (dir != _T("")) m_Segments.push_back(dir); } break; case DOS: { dir.Replace(_T("\\"), _T("/")); while (dir.Replace(_T("//"), _T("/"))); if (dir.Length() >= 2 && dir.c_str()[1] == ':') m_Segments.clear(); else if (dir.Left(1) == _T("/")) { if (m_Segments.empty()) { Clear(); return false; } wxString first = m_Segments.front(); m_Segments.clear(); m_Segments.push_back(first); dir = dir.Mid(1); } if (isFile) { int pos = dir.Find('/', true); if (pos == (int)dir.Length() - 1) { Clear(); return false; } if (pos == -1) { subdir = dir; return true; } else { file = dir.Mid(pos + 1); dir = dir.Left(pos + 1); } } else if (dir != _T("") && dir.Right(1) != _T("/")) dir += _T("/"); int pos = dir.Find(_T("/")); while (pos != -1) { wxString segment = dir.Left(pos); if (segment == _T("..")) { if (m_Segments.size() <= 1) { Clear(); return false; } m_Segments.pop_back(); } else if (segment != _T(".")) m_Segments.push_back(segment); dir = dir.Mid(pos + 1); pos = dir.Find(_T("/")); } } break; case MVS: { int i = 0; wxChar c = subdir.c_str()[i]; while (c == FTP_MVS_DOUBLE_QUOTE) c = subdir.c_str()[++i]; subdir.Remove(0, i); while (subdir != _T("")) { c = subdir.Last(); if (c != FTP_MVS_DOUBLE_QUOTE) break; else subdir.RemoveLast(); } } if (subdir == _T("")) return false; while (subdir.Replace(_T(".."), _T("."))); if (subdir.c_str()[0] == '\'') { if (subdir.Last() != '\'') return false; if (SetPath(subdir, isFile)) file = subdir; else return false; } else if (subdir.Last() == '\'') return false; else if (!IsEmpty()) { if (m_prefix != _T(".")) return false; if (subdir.c_str()[0] == '.') subdir.Remove(0, 1); int pos = subdir.Find('.'); while (pos != -1) { m_Segments.push_back(subdir.Left(pos)); subdir = subdir.Mid(pos + 1); } if (subdir != _T("")) { m_Segments.push_back(subdir); m_prefix = _T(""); } else m_prefix = _T("."); if (isFile) { if (m_prefix == _T(".")) return false; if (m_Segments.empty()) return false; file = m_Segments.back(); m_Segments.pop_back(); int pos = file.Find('('); int pos2 = file.Find(')'); if (pos != -1) { if (!pos || pos2 <= pos || pos2 != (int)file.Length() - 2) return false; m_Segments.push_back(file.Left(pos)); file = file.Mid(pos + 1, pos2 - pos - 1); } else if (pos2 != -1) return false; else m_prefix = _T("."); } } else if (SetPath(subdir, isFile)) file = subdir; else return false; break; case VXWORKS: { if (dir[0] != ':') { if (IsEmpty()) return false; } else { int colon2; if ((colon2 = dir.Mid(1).Find(':')) < 1) return false; int slash = dir.Find('/'); if (slash != -1 && slash <= colon2) return false; m_prefix = dir.Left(colon2 + 2); dir = dir.Mid(colon2 + 1); if (dir[0] == '/') return false; m_Segments.clear(); } if (isFile && dir.Find('/') == -1) return false; } // No break on purpose default: { while (dir.Replace(_T("//"), _T("/"))); if (dir.c_str()[0] == '/') { m_prefix = _T(""); m_Segments.clear(); dir = dir.Mid(1); } if (isFile) { int pos = dir.Find('/', true); if (pos == (int)dir.Length() - 1) { Clear(); return false; } if (pos == -1) { subdir = dir; return true; } else { file = dir.Mid(pos + 1); dir = dir.Left(pos + 1); } } else if (dir != _T("") && dir.Right(1) != _T("/")) dir += _T("/"); int pos = dir.Find(_T("/")); while (pos != -1) { wxString segment = dir.Left(pos); if (segment == _T("..")) { if (m_Segments.empty()) { Clear(); return false; } m_Segments.pop_back(); } else if (segment != _T(".")) m_Segments.push_back(segment); dir = dir.Mid(pos + 1); pos = dir.Find(_T("/")); } } break; } if (isFile) subdir = file; m_bEmpty = false; return true; } bool CServerPath::operator==(const CServerPath &op) const { if (m_bEmpty != op.m_bEmpty) return false; else if (m_prefix != op.m_prefix) return false; else if (m_type != op.m_type) return false; else if (m_Segments != op.m_Segments) return false; return true; } bool CServerPath::operator!=(const CServerPath &op) const { if (!this) return false; return !(*this == op); } wxString CServerPath::FormatFilename(const wxString &filename, bool omitPath /*=false*/) const { if (m_bEmpty) return filename; if (filename == _T("")) return _T(""); wxString fullpath; tConstSegmentIter iter; switch (m_type) { case MVS: if (m_prefix == _T(".") && omitPath) return filename; fullpath = _T("'"); for (iter = m_Segments.begin(); iter != m_Segments.end(); iter++) fullpath += *iter + _T("."); if (m_prefix != _T(".")) { if (fullpath.Last() == '.') fullpath.RemoveLast(); fullpath += _T("(") + filename + _T(")"); } else fullpath += filename; fullpath += _T("'"); break; case VXWORKS: if (omitPath) fullpath = filename; else fullpath = GetPath() + _T("/") + filename; break; default: if (omitPath) fullpath = filename; else fullpath = GetPath() + filename; } return fullpath; } int CServerPath::CmpNoCase(const CServerPath &op) const { if (m_bEmpty != op.m_bEmpty) return 1; else if (m_prefix != op.m_prefix) return 1; else if (m_type != op.m_type) return 1; if (m_Segments.size() > op.m_Segments.size()) return 1; else if (m_Segments.size() < op.m_Segments.size()) return -1; tConstSegmentIter iter = m_Segments.begin(); tConstSegmentIter iter2 = op.m_Segments.begin(); while (iter != m_Segments.end()) { int res = iter++->CmpNoCase(*iter2++); if (res) return res; } return 0; } bool CServerPath::AddSegment(const wxString& segment) { if (IsEmpty()) return false; // TODO: Check for invalid characters m_Segments.push_back(segment); return true; } CServerPath CServerPath::GetCommonParent(const CServerPath& path) const { if (m_bEmpty || path.m_bEmpty) return CServerPath(); if (m_type != path.m_type || m_prefix != path.m_prefix) return CServerPath(); if (!HasParent() || !path.HasParent()) return CServerPath(); CServerPath parent; parent.m_bEmpty = false; parent.m_type = m_type; parent.m_prefix = m_prefix; std::list::const_iterator iter = m_Segments.begin(); std::list::const_iterator iter2 = path.m_Segments.begin(); while (iter != m_Segments.end() && iter2 != path.m_Segments.end()) { if (*iter != *iter2) return parent; parent.m_Segments.push_back(*iter); iter++; iter2++; } return parent; }