home *** CD-ROM | disk | FTP | other *** search
Wrap
// FileZilla Server - a Windows ftp server // Copyright (C) 2002-2004 - Tim Kosse <tim.kosse@gmx.de> // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU 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 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. // Permissions.cpp: Implementierung der Klasse CPermissions. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "misc\md5.h" #include "Permissions.h" #include "misc\MarkupSTL.h" #include "options.h" #include "iputils.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CPermissionsHelperWindow class CPermissionsHelperWindow { public: CPermissionsHelperWindow(CPermissions *pPermissions) { ASSERT(pPermissions); m_pPermissions = pPermissions; //Create window WNDCLASSEX wndclass; wndclass.cbSize = sizeof wndclass; wndclass.style = 0; wndclass.lpfnWndProc = WindowProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = GetModuleHandle(0); wndclass.hIcon = 0; wndclass.hCursor = 0; wndclass.hbrBackground = 0; wndclass.lpszMenuName = 0; wndclass.lpszClassName = _T("CPermissions Helper Window"); wndclass.hIconSm = 0; RegisterClassEx(&wndclass); m_hWnd=CreateWindow(_T("CPermissions Helper Window"), _T("CPermissions Helper Window"), 0, 0, 0, 0, 0, 0, 0, 0, GetModuleHandle(0)); ASSERT(m_hWnd); SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG)this); }; virtual ~CPermissionsHelperWindow() { //Destroy window if (m_hWnd) { DestroyWindow(m_hWnd); m_hWnd = 0; } } HWND GetHwnd() { return m_hWnd; } protected: static LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message==WM_USER) { /* If receiving WM_USER, update the permission data of the instance with the permission * data from the global data */ // Get own instance CPermissionsHelperWindow *pWnd=(CPermissionsHelperWindow *)GetWindowLongPtr(hWnd, GWLP_USERDATA); if (!pWnd) return 0; if (!pWnd->m_pPermissions) return 0; EnterCritSection(pWnd->m_pPermissions->m_sync); // Clear old group data and copy over the new data pWnd->m_pPermissions->m_GroupsList.clear(); for (CPermissions::t_GroupsList::iterator groupiter=pWnd->m_pPermissions->m_sGroupsList.begin(); groupiter!=pWnd->m_pPermissions->m_sGroupsList.end(); groupiter++) pWnd->m_pPermissions->m_GroupsList.push_back(*groupiter); // Clear old user data and copy over the new data pWnd->m_pPermissions->m_UsersList.clear(); for (CPermissions::t_UsersList::iterator iter=pWnd->m_pPermissions->m_sUsersList.begin(); iter!=pWnd->m_pPermissions->m_sUsersList.end(); iter++) { CUser user = *iter; user.pOwner = NULL; if (user.group != _T("")) { // Set owner for (CPermissions::t_GroupsList::iterator groupiter=pWnd->m_pPermissions->m_GroupsList.begin(); groupiter!=pWnd->m_pPermissions->m_GroupsList.end(); groupiter++) if (groupiter->group == user.group) { user.pOwner = &(*groupiter); break; } } pWnd->m_pPermissions->m_UsersList.push_back(user); } LeaveCritSection(pWnd->m_pPermissions->m_sync); } return ::DefWindowProc(hWnd, message, wParam, lParam); } protected: CPermissions *m_pPermissions; private: HWND m_hWnd; }; ///////////////////////////////////////////////////////////////////////////// // CPermissions CCriticalSectionWrapper CPermissions::m_sync; CPermissions::t_UsersList CPermissions::m_sUsersList; CPermissions::t_GroupsList CPermissions::m_sGroupsList; std::list<CPermissions *> CPermissions::m_sInstanceList; ////////////////////////////////////////////////////////////////////// // Konstruktion/Destruktion ////////////////////////////////////////////////////////////////////// CPermissions::CPermissions() { Init(); m_pPermissionsHelperWindow = new CPermissionsHelperWindow(this); } CPermissions::~CPermissions() { EnterCritSection(m_sync); std::list<CPermissions *>::iterator instanceIter; for (instanceIter=m_sInstanceList.begin(); instanceIter!=m_sInstanceList.end(); instanceIter++) if (*instanceIter==this) break; ASSERT(instanceIter!=m_sInstanceList.end()); m_sInstanceList.erase(instanceIter); LeaveCritSection(m_sync); if (m_pPermissionsHelperWindow) delete m_pPermissionsHelperWindow; } void CPermissions::AddListingEntry(t_dirlisting *&pDir, CStdString name, bool dir, _int64 size, FILETIME *pTime, const t_directory &perms) { // This wastes some memory but keeps the whole thing fast if ((8192 - pDir->len) < (60 + 2 * MAX_PATH)) { pDir->pNext = new t_dirlisting; pDir = pDir->pNext; pDir->len = 0; pDir->pNext = NULL; } if (dir) { memcpy(pDir->buffer + pDir->len, "drwxr-xr-x", 10); pDir->len += 10; } else { pDir->buffer[pDir->len++] = '-'; pDir->buffer[pDir->len++] = perms.bFileRead ? 'r' : '-'; pDir->buffer[pDir->len++] = perms.bFileWrite ? 'w' : '-'; BOOL isexe = FALSE; CStdString ext = name.Right(4); ext.MakeLower(); if (ext.ReverseFind('.')!=-1) { if (ext == ".exe") isexe = TRUE; else if (ext == ".bat") isexe = TRUE; else if (ext == ".com") isexe = TRUE; } pDir->buffer[pDir->len++] = isexe ? 'x' : '-'; pDir->buffer[pDir->len++] = perms.bFileRead ? 'r' : '-'; pDir->buffer[pDir->len++] = '-'; pDir->buffer[pDir->len++] = isexe ? 'x' : '-'; pDir->buffer[pDir->len++] = perms.bFileRead ? 'r' : '-'; pDir->buffer[pDir->len++] = '-'; pDir->buffer[pDir->len++] = isexe ? 'x' : '-'; } memcpy(pDir->buffer + pDir->len, " 1 ftp ftp ", 11); pDir->len += 11; pDir->len += sprintf(pDir->buffer + pDir->len, "% 14I64d", size); // Adjust time zone info and output file date/time SYSTEMTIME sLocalTime; GetLocalTime(&sLocalTime); FILETIME fTime; VERIFY(SystemTimeToFileTime(&sLocalTime, &fTime)); FILETIME mtime; if (pTime) mtime = *pTime; else mtime = fTime; TIME_ZONE_INFORMATION tzInfo; int tzRes = GetTimeZoneInformation(&tzInfo); _int64 offset = tzInfo.Bias+((tzRes==TIME_ZONE_ID_DAYLIGHT)?tzInfo.DaylightBias:tzInfo.StandardBias); offset *= 60 * 10000000; _int64 t1 = ((_int64)mtime.dwHighDateTime<<32) + mtime.dwLowDateTime; t1 -= offset; mtime.dwHighDateTime = (DWORD)(t1>>32); mtime.dwLowDateTime = (DWORD)(t1%0xFFFFFFFF); SYSTEMTIME sFileTime; FileTimeToSystemTime(&mtime, &sFileTime); _int64 t2 = ((_int64)fTime.dwHighDateTime<<32) + fTime.dwLowDateTime; const char months[][4]={"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; pDir->len += sprintf(pDir->buffer + pDir->len, " %s %02d ", months[sFileTime.wMonth-1], sFileTime.wDay); if (t1 > t2 || (t2-t1) > ((_int64)1000000*60*60*24*350)) pDir->len += sprintf(pDir->buffer + pDir->len, " %d ", sFileTime.wYear); else pDir->len += sprintf(pDir->buffer + pDir->len, "%02d:%02d ", sFileTime.wHour, sFileTime.wMinute); int len = name.GetLength(); memcpy(pDir->buffer + pDir->len, name.c_str(), len); pDir->len += len; pDir->buffer[pDir->len++] = '\r'; pDir->buffer[pDir->len++] = '\n'; } int CPermissions::GetDirectoryListing(LPCTSTR username, CStdString currentDir, CStdString dirToDisplay, t_dirlisting *&pResult, CStdString& physicalDir, CStdString& logicalDir) { // Get user CUser user; if (!GetUser(username, user)) return PERMISSION_DENIED; CStdString dir = CanonifyServerDir(currentDir, dirToDisplay); if (dir == "") return PERMISSION_INVALIDNAME; logicalDir = dir; // Get directory from directory name t_directory directory; BOOL bTruematch; int res = GetRealDirectory(dir, user, directory, bTruematch); CStdString sFileSpec = "*"; // Which files to list in the directory if (res == PERMISSION_FILENOTDIR || res == PERMISSION_NOTFOUND) // Try listing using a direct wildcard filespec instead? { // Check dirToDisplay if we are allowed to go back a directory dirToDisplay.Replace('\\', '/'); while (dirToDisplay.Replace("//", "/")); if (dirToDisplay.Right(1) == "/") return res; int pos = dirToDisplay.ReverseFind('/'); if (res != PERMISSION_FILENOTDIR && dirToDisplay.Mid(pos + 1).Find('*') == -1) return res; dirToDisplay = dirToDisplay.Left(pos + 1); if (dir == "/") return res; pos = dir.ReverseFind('/'); sFileSpec = dir.Mid(pos + 1); if (pos) dir = dir.Left(pos); else dir = "/"; if (sFileSpec.Find("*") == -1 && res != PERMISSION_FILENOTDIR) return res; res = GetRealDirectory(dir, user, directory, bTruematch); } if (res) return res; // Check permissions if (!directory.bDirList) return PERMISSION_DENIED; WIN32_FIND_DATA FindFileData; WIN32_FIND_DATA NextFindFileData; HANDLE hFind; TIME_ZONE_INFORMATION tzInfo; int tzRes = GetTimeZoneInformation(&tzInfo); _int64 offset = tzInfo.Bias+((tzRes==TIME_ZONE_ID_DAYLIGHT)?tzInfo.DaylightBias:tzInfo.StandardBias); offset *= 60 * 10000000; t_dirlisting *pDir = new t_dirlisting; pDir->len = 0; pDir->pNext = NULL; pResult = pDir; CStdString curDir = directory.dir; curDir.MakeLower(); // List aliases in current directory for (std::multimap<CStdString, CUser::t_alias>::const_iterator iter = user.aliasMap.find(curDir); iter != user.aliasMap.end() && iter->first == curDir; iter++) { t_directory directory; BOOL truematch = false; if (GetRealDirectory(dir + "/" + iter->second.name, user, directory, truematch)) continue; if (!directory.bDirList) continue; if (!truematch && !directory.bDirSubdirs) continue; if (sFileSpec != "*.*" && sFileSpec != "*") { // Do a really primitive wildcard check, does even ignore ? CStdString name = iter->second.name; CStdString spec = sFileSpec; name.MakeLower(); spec.MakeLower(); bool match = true; while (spec != "") { int pos = spec.Find('*'); if (pos == -1) { if (spec != name) { match = false; break; } break; } else if (!pos) { spec = spec.Mid(1); } else { int npos = name.Find(spec.Left(pos)); if (npos == -1) { match = false; break; } spec = spec.Mid(pos + 1); name = name.Mid(npos + 1); } } if (!match) continue; } CFileStatus64 status; if (GetStatus64(directory.dir, status)) AddListingEntry(pDir, iter->second.name, true, status.m_size, &status.m_mtime, directory); else AddListingEntry(pDir, iter->second.name, true, 0, 0, directory); } physicalDir = directory.dir; if (sFileSpec != "*" && sFileSpec != "*.*") physicalDir += sFileSpec; hFind = FindFirstFile(directory.dir + "\\" + sFileSpec, &NextFindFileData); while (hFind != INVALID_HANDLE_VALUE) { FindFileData = NextFindFileData; if (!FindNextFile(hFind, &NextFindFileData)) { FindClose(hFind); hFind = INVALID_HANDLE_VALUE; } if (!_tcscmp(FindFileData.cFileName, _T(".")) || !_tcscmp(FindFileData.cFileName, _T(".."))) continue; CStdString fn = FindFileData.cFileName; if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // Check permissions of subdir. If we don't have LIST permission, // don't display the subdir. BOOL truematch; t_directory subDir; if (GetRealDirectory(dir + "/" + fn, user, subDir, truematch)) continue; if (subDir.bDirList) AddListingEntry(pDir, fn, true, FindFileData.nFileSizeLow + ((_int64)FindFileData.nFileSizeHigh<<32), &FindFileData.ftLastWriteTime, directory); } else AddListingEntry(pDir, fn, false, FindFileData.nFileSizeLow + ((_int64)FindFileData.nFileSizeHigh<<32), &FindFileData.ftLastWriteTime, directory); } return 0; } int CPermissions::CheckDirectoryPermissions(LPCTSTR username, CStdString dirname, CStdString currentdir, int op, CStdString& physicalDir, CStdString& logicalDir) { //Get user from username CUser user; if (!GetUser(username, user)) return PERMISSION_DENIED; // No user found CStdString dir = CanonifyServerDir(currentdir, dirname); if (dir == "") return PERMISSION_INVALIDNAME; if (dir == "/") return PERMISSION_NOTFOUND; int pos = dir.ReverseFind('/'); if (pos == -1 || !dir[pos + 1]) return PERMISSION_NOTFOUND; logicalDir = dir; dirname = dir.Mid(pos + 1); if (!pos) dir = "/"; else dir = dir.Left(pos); // dir now is the absolute path (logical server path of course) // awhile dirname is the pure dirname without the full path CStdString realDir; CStdString realDirname; //Get the physical path, only of dir to get the right permissions t_directory directory; BOOL truematch; int res; CStdString dir2 = dir; CStdString dirname2 = dirname; do { res = GetRealDirectory(dir2, user, directory, truematch); if (res & PERMISSION_NOTFOUND && op == DOP_CREATE) { //that path could not be found. Maybe more than one directory level has to be created, check that if (dir2 == "/") return res; int pos = dir2.ReverseFind('/'); if (pos == -1) return res; dirname2 = dir2.Mid(pos+1) + "/" + dirname2; if (pos) dir2 = dir2.Left(pos); else dir2 = "/"; continue; } else if (res) return res; realDir = directory.dir; realDirname = dirname2; if (!directory.bDirDelete && op & DOP_DELETE) res |= PERMISSION_DENIED; if (!directory.bDirCreate && op & DOP_CREATE) res |= PERMISSION_DENIED; break; } while (TRUE); realDirname.Replace("/", "\\"); physicalDir = realDir + "\\" + realDirname; //Check if dir + dirname is a valid path int res2 = GetRealDirectory(dir + "/" + dirname, user, directory, truematch); if (!res2 && op&DOP_CREATE) res |= PERMISSION_DOESALREADYEXIST; else if (!(res2 & PERMISSION_NOTFOUND)) return res | res2; // check dir attributes DWORD nAttributes = GetFileAttributes(physicalDir); if (nAttributes==0xFFFFFFFF && !(op&DOP_CREATE)) res |= PERMISSION_NOTFOUND; else if (!(nAttributes&FILE_ATTRIBUTE_DIRECTORY)) res |= PERMISSION_FILENOTDIR; //Finally, a valid path+dirname! return res; } int CPermissions::CheckFilePermissions(LPCTSTR username, CStdString filename, CStdString currentdir, int op, CStdString& physicalFile, CStdString& logicalFile) { //Get user from username CUser user; if (!GetUser(username, user)) return PERMISSION_DENIED; // No user found CStdString dir = CanonifyServerDir(currentdir, filename); if (dir == "") return PERMISSION_INVALIDNAME; if (dir == "/") return PERMISSION_NOTFOUND; int pos = dir.ReverseFind('/'); if (pos == -1) return PERMISSION_NOTFOUND; logicalFile = dir; filename = dir.Mid(pos + 1); if (pos) dir = dir.Left(pos); else dir = "/"; // dir now is the absolute path (logical server path of course) // while filename is the filename //Get the physical path t_directory directory; BOOL truematch; int res = GetRealDirectory(dir, user, directory, truematch); if (res) return res; if (!directory.bFileRead && op&FOP_READ) res |= PERMISSION_DENIED; if (!directory.bFileDelete && op&FOP_DELETE) res |= PERMISSION_DENIED; if (!directory.bFileWrite && op&(FOP_CREATENEW|FOP_WRITE|FOP_APPEND)) res |= PERMISSION_DENIED; physicalFile = directory.dir + "\\" + filename; DWORD nAttributes = GetFileAttributes(physicalFile); if (nAttributes == 0xFFFFFFFF) { if (!(op&(FOP_WRITE|FOP_APPEND|FOP_CREATENEW))) res |= PERMISSION_NOTFOUND; } else { if (nAttributes&FILE_ATTRIBUTE_DIRECTORY) res |= PERMISSION_DIRNOTFILE; if (!directory.bFileAppend && op&FOP_APPEND) res |= PERMISSION_DENIED; if (!directory.bFileDelete && op&FOP_WRITE) res |= PERMISSION_DENIED; if (op & FOP_CREATENEW) res |= PERMISSION_DOESALREADYEXIST; } //If res is 0 we finally have a valid path+filename! return res; } CStdString CPermissions::GetHomeDir(const CUser &user, bool physicalPath /*=false*/) const { if (user.homedir == "") return ""; if (!physicalPath) return "/"; CStdString path; path = user.homedir; user.DoReplacements(path); return path; } CStdString CPermissions::GetHomeDir(LPCTSTR username, bool physicalPath /*=false*/) const { CUser user; if (!GetUser(username, user)) return ""; return GetHomeDir(user, physicalPath); } int CPermissions::GetRealDirectory(CStdString directory, const CUser &user, t_directory &ret, BOOL &truematch) { /* * This function translates pathnames from absolute server paths * into absolute local paths. * The given server directory is already an absolute canonified path, so * parsing it is very quick. * To find the absolute local path, we go though each segment of the server * path. For the local path, we start form the homedir and append segments * sequentially or resolve aliases if required. */ directory.TrimLeft("/"); // Split server path // -------------------- //Split dir into pieces std::list<CStdString> PathPieces; int pos; while((pos = directory.Find('/')) != -1) { PathPieces.push_back(directory.Left(pos)); directory = directory.Mid(pos + 1); } if (directory != "") PathPieces.push_back(directory); // Get absolute local path // ----------------------- //First get the home dir CStdString homepath = GetHomeDir(user, true); if (homepath == "") //No homedir found return PERMISSION_DENIED; // Reassamble path to get local path CStdString path = homepath; // Start with homedir as root // Go through all pieces for (std::list<CStdString>::iterator iter = PathPieces.begin(); iter != PathPieces.end(); iter++) { //Check if piece exists CStdString piece = *iter; DWORD nAttributes = GetFileAttributes(path + "\\" + piece); if (nAttributes != 0xFFFFFFFF) { if (!(nAttributes&FILE_ATTRIBUTE_DIRECTORY)) return PERMISSION_FILENOTDIR; path += "\\" + piece; continue; } else { CStdString target = user.GetAliasTarget(path, piece); if (target != "") { if (target.Right(1) != ":") { nAttributes = GetFileAttributes(target); if (nAttributes == 0xFFFFFFFF) return PERMISSION_NOTFOUND; else if (!(nAttributes & FILE_ATTRIBUTE_DIRECTORY)) return PERMISSION_FILENOTDIR; } path = target; continue; } } return PERMISSION_NOTFOUND; } CStdString realpath = path; // Check permissions // ----------------- /* We got a valid local path, now find the closest matching path within the * permissions. * We do this by sequentially comparing the path with all permissions and * sequentially removing the last path segment until we found a match or * all path segments have been removed * Distinguish the case */ truematch = TRUE; path.MakeLower(); while (path != "") { BOOL bFoundMatch = FALSE; unsigned int i; // Check user permissions for (i = 0; i < user.permissions.size(); i++) { CStdString permissionPath = user.permissions[i].dir; user.DoReplacements(permissionPath); permissionPath.MakeLower(); if (permissionPath == path) { bFoundMatch = TRUE; ret = user.permissions[i]; break; } } // Check owner (group) permissions if (!bFoundMatch && user.pOwner) for (i = 0; i < user.pOwner->permissions.size(); i++) { CStdString permissionPath = user.pOwner->permissions[i].dir; user.DoReplacements(permissionPath); permissionPath.MakeLower(); if (permissionPath == path) { bFoundMatch = TRUE; ret = user.pOwner->permissions[i]; break; } } if (!bFoundMatch) { // No match found, remove last segment and try again int pos = path.ReverseFind('\\'); if (pos != -1) path = path.Left(pos); else return PERMISSION_DENIED; truematch = FALSE; continue; } ret.dir = realpath; // We can check the bDirSubdirs permission right here if (!truematch && !ret.bDirSubdirs) return PERMISSION_DENIED; return 0; } return PERMISSION_NOTFOUND; } int CPermissions::ChangeCurrentDir(LPCTSTR username, CStdString ¤tdir, CStdString &dir) { //Get user from username CUser user; if (!GetUser(username, user)) return PERMISSION_DENIED; // No user found CStdString canonifiedDir = CanonifyServerDir(currentdir, dir); if (canonifiedDir == "") return PERMISSION_INVALIDNAME; dir = canonifiedDir; //Get the physical path t_directory directory; BOOL truematch; int res = GetRealDirectory(dir, user, directory, truematch); if (res) return res; if (!directory.bDirList) { if (!directory.bFileRead && !directory.bFileWrite) return PERMISSION_DENIED; } //Finally, a valid path! currentdir = dir; //Server paths are relative, so we can use the absolute server path return 0; } BOOL CPermissions::GetUser(CStdString username, CUser &userdata) const { // Get user from username for (unsigned int i=0; i<m_UsersList.size(); i++) { if (!username.CompareNoCase(m_UsersList[i].user)) { userdata = m_UsersList[i]; return TRUE; } } return FALSE; } int CPermissions::GetShortDirectoryListing(LPCTSTR username, CStdString currentDir, CStdString dirToDisplay, t_dirlisting *&pResult, CStdString& physicalDir, CStdString& logicalDir) { // Get user from username CUser user; if (!GetUser(username, user)) return PERMISSION_DENIED; // No user found CStdString dir = CanonifyServerDir(currentDir, dirToDisplay); if (dir == "") return PERMISSION_INVALIDNAME; logicalDir = dir; t_directory directory; BOOL bTruematch; int res = GetRealDirectory(dir, user, directory, bTruematch); CStdString sFileSpec = "*"; if (res == PERMISSION_FILENOTDIR || res == PERMISSION_NOTFOUND) // Try listing using a direct wildcard filespec instead? { // Check dirToDisplay if we are allowed to go back a directory dirToDisplay.Replace('\\', '/'); while (dirToDisplay.Replace("//", "/")); if (dirToDisplay.Right(1) == "/") return res; int pos = dirToDisplay.ReverseFind('/'); if (res != PERMISSION_FILENOTDIR && dirToDisplay.Mid(pos + 1).Find('*') == -1) return res; dirToDisplay = dirToDisplay.Left(pos + 1); if (dir == "/") return res; pos = dir.ReverseFind('/'); sFileSpec = dir.Mid(pos + 1); if (pos) dir = dir.Left(pos); else dir = "/"; if (sFileSpec.Find("*") == -1 && res != PERMISSION_FILENOTDIR) return res; res = GetRealDirectory(dir, user, directory, bTruematch); } if (res) return res; if (!directory.bDirList) return PERMISSION_DENIED; if (dirToDisplay != "" && dirToDisplay.Right(1) != "/") dirToDisplay += "/"; t_dirlisting *pDir = new t_dirlisting; pDir->len = 0; pDir->pNext = NULL; pResult = pDir; CStdString curDir = directory.dir; curDir.MakeLower(); physicalDir = directory.dir; for (std::multimap<CStdString, CUser::t_alias>::const_iterator iter = user.aliasMap.find(curDir); iter != user.aliasMap.end() && iter->first == curDir; iter++) { t_directory directory; BOOL truematch = false; if (GetRealDirectory(dir + "/" + iter->second.name, user, directory, truematch)) continue; if (!directory.bDirList) continue; if (!truematch && !directory.bDirSubdirs) continue; if (sFileSpec != "*.*" && sFileSpec != "*") { // Do a really primitive wildcard check, does even ignore ? CStdString name = iter->second.name; CStdString spec = sFileSpec; name.MakeLower(); spec.MakeLower(); bool match = true; while (spec != "") { int pos = spec.Find('*'); if (pos == -1) { if (spec != name) { match = false; break; } break; } else if (!pos) { spec = spec.Mid(1); } else { int npos = name.Find(spec.Left(pos)); if (npos == -1) { match = false; break; } spec = spec.Mid(pos + 1); name = name.Mid(npos + 1); } } if (!match) continue; } // This wastes some memory but keeps the whole thing fast if ((8192 - pDir->len) < (10 + 2 * MAX_PATH)) { pDir->pNext = new t_dirlisting; pDir = pDir->pNext; pDir->len = 0; pDir->pNext = NULL; } int len = dirToDisplay.GetLength(); memcpy(pDir->buffer + pDir->len, dirToDisplay.c_str(), len); pDir->len += len; len = iter->second.name.GetLength(); memcpy(pDir->buffer + pDir->len, iter->second.name, len); pDir->len += len; pDir->buffer[pDir->len++] = '\r'; pDir->buffer[pDir->len++] = '\n'; } if (sFileSpec != "*" && sFileSpec != "*.*") physicalDir += sFileSpec; WIN32_FIND_DATA FindFileData; WIN32_FIND_DATA NextFindFileData; HANDLE hFind; hFind = FindFirstFile(directory.dir + "\\" + sFileSpec, &NextFindFileData); while (hFind != INVALID_HANDLE_VALUE) { FindFileData = NextFindFileData; if (!FindNextFile(hFind, &NextFindFileData)) { FindClose(hFind); hFind = INVALID_HANDLE_VALUE; } if (!_tcscmp(FindFileData.cFileName, _T(".")) || !_tcscmp(FindFileData.cFileName, _T(".."))) continue; CStdString fn = FindFileData.cFileName; if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // Check permissions of subdir. If we don't have LIST permission, // don't display the subdir. BOOL truematch; t_directory subDir; if (GetRealDirectory(dir + "/" + fn, user, subDir, truematch)) continue; if (!subDir.bDirList) continue; } // This wastes some memory but keeps the whole thing fast if ((8192 - pDir->len) < (10 + 2 * MAX_PATH)) { pDir->pNext = new t_dirlisting; pDir = pDir->pNext; pDir->len = 0; pDir->pNext = NULL; } int len = dirToDisplay.GetLength(); memcpy(pDir->buffer + pDir->len, dirToDisplay.c_str(), len); pDir->len += len; len = fn.GetLength(); memcpy(pDir->buffer + pDir->len, fn.c_str(), len); pDir->len += len; pDir->buffer[pDir->len++] = '\r'; pDir->buffer[pDir->len++] = '\n'; } return 0; } BOOL CPermissions::CheckUserLogin(LPCTSTR username, LPCTSTR pass, CUser &userdata, BOOL noPasswordCheck /*=FALSE*/) { const char *tmp = pass; MD5 md5; md5.update((unsigned char *)tmp, _tcslen(pass)); md5.finalize(); char *res = md5.hex_digest(); CStdString hash=res; delete [] res; CUser user; if (!GetUser(username, user)) return FALSE; if (noPasswordCheck || user.password == hash || user.password == "") { userdata = user; return TRUE; } return FALSE; } void CPermissions::UpdateInstances() { EnterCritSection(m_sync); for (std::list<CPermissions *>::iterator iter=m_sInstanceList.begin(); iter!=m_sInstanceList.end(); iter++) { if (*iter!=this) { ASSERT((*iter)->m_pPermissionsHelperWindow); ::PostMessage((*iter)->m_pPermissionsHelperWindow->GetHwnd(), WM_USER, 0, 0); } } LeaveCritSection(m_sync); } void CPermissions::SetKey(CMarkupSTL *pXML, LPCTSTR name, LPCTSTR value) { ASSERT(pXML); pXML->AddChildElem(_T("Option"), value); pXML->AddChildAttrib(_T("Name"), name); } void CPermissions::SetKey(CMarkupSTL *pXML, LPCTSTR name, int value) { ASSERT(pXML); CStdString str; str.Format("%d", value); SetKey(pXML, name, str); } void CPermissions::SavePermissions(CMarkupSTL *pXML, const t_group &user) { pXML->AddChildElem(_T("Permissions")); pXML->IntoElem(); for (unsigned int i=0; i<user.permissions.size(); i++) { pXML->AddChildElem(_T("Permission")); pXML->AddChildAttrib(_T("Dir"), user.permissions[i].dir); pXML->IntoElem(); if (!user.permissions[i].aliases.empty()) { pXML->AddChildElem(_T("Aliases")); pXML->IntoElem(); for (std::list<CStdString>::const_iterator iter = user.permissions[i].aliases.begin(); iter != user.permissions[i].aliases.end(); iter++) { pXML->AddChildElem(_T("Alias")); pXML->SetChildData(*iter); } pXML->OutOfElem(); } SetKey(pXML, "FileRead", user.permissions[i].bFileRead ? "1":"0"); SetKey(pXML, "FileWrite", user.permissions[i].bFileWrite ? "1":"0"); SetKey(pXML, "FileDelete", user.permissions[i].bFileDelete ?"1":"0"); SetKey(pXML, "FileAppend", user.permissions[i].bFileAppend ? "1":"0"); SetKey(pXML, "DirCreate", user.permissions[i].bDirCreate ? "1":"0"); SetKey(pXML, "DirDelete", user.permissions[i].bDirDelete ? "1":"0"); SetKey(pXML, "DirList", user.permissions[i].bDirList ? "1":"0"); SetKey(pXML, "DirSubdirs", user.permissions[i].bDirSubdirs ? "1":"0"); SetKey(pXML, "IsHome", user.permissions[i].bIsHome ? "1":"0"); SetKey(pXML, "AutoCreate", user.permissions[i].bAutoCreate ? "1":"0"); pXML->OutOfElem(); } pXML->OutOfElem(); } BOOL CPermissions::GetAsCommand(char **pBuffer, DWORD *nBufferLength) { // This function returns all account data as a command string which will be // sent to the user interface. if (!pBuffer || !nBufferLength) return FALSE; EnterCritSection(m_sync); // First calculate the required buffer length DWORD len = 4; t_GroupsList::iterator groupiter; for (groupiter = m_sGroupsList.begin(); groupiter != m_sGroupsList.end(); groupiter++) len += groupiter->GetRequiredBufferLen(); t_UsersList::iterator iter; for (iter = m_sUsersList.begin(); iter != m_sUsersList.end(); iter++) len += iter->GetRequiredBufferLen(); // Allocate memory *pBuffer = new char[len]; char* p = *pBuffer; // Write groups to buffer *p++ = m_sGroupsList.size()/256; *p++ = m_sGroupsList.size()%256; for (groupiter = m_sGroupsList.begin(); groupiter != m_sGroupsList.end(); groupiter++) { p = groupiter->FillBuffer(p); if (!p) { delete [] *pBuffer; *pBuffer = NULL; LeaveCritSection(m_sync); return FALSE; } } // Write users to buffer *p++ = m_sUsersList.size()/256; *p++ = m_sUsersList.size()%256; for (iter = m_sUsersList.begin(); iter != m_sUsersList.end(); iter++) { p = iter->FillBuffer(p); if (!p) { delete [] *pBuffer; *pBuffer = NULL; LeaveCritSection(m_sync); return FALSE; } } LeaveCritSection(m_sync); *nBufferLength = len; return TRUE; } BOOL CPermissions::ParseUsersCommand(unsigned char *pData, DWORD dwDataLength) { m_GroupsList.clear(); m_UsersList.clear(); unsigned char *p = pData; unsigned char* endMarker = pData + dwDataLength; if (dwDataLength < 2) return FALSE; int num = *p * 256 + p[1]; p+=2; int i; for (i = 0; i < num; i++) { t_group group; p = group.ParseBuffer(p, endMarker - p); if (!p) return FALSE; if (group.group != _T("")) { //Set a home dir if no home dir could be read BOOL bGotHome = FALSE; for (unsigned int dir = 0; dir < group.permissions.size(); dir++) if (group.permissions[dir].bIsHome) { bGotHome = TRUE; break; } if (!bGotHome && !group.permissions.empty()) group.permissions.begin()->bIsHome = TRUE; m_GroupsList.push_back(group); } } if ((endMarker - p) < 2) return FALSE; num = *p * 256 + p[1]; p+=2; for (i = 0; i < num; i++) { CUser user; p = user.ParseBuffer(p, endMarker - p); if (!p) return FALSE; if (user.user != _T("")) { user.pOwner = NULL; if (user.group != _T("")) { for (t_GroupsList::iterator groupiter = m_GroupsList.begin(); groupiter != m_GroupsList.end(); groupiter++) if (groupiter->group == user.group) { user.pOwner = &(*groupiter); break; } if (!user.pOwner) user.group = ""; } if (!user.pOwner) { //Set a home dir if no home dir could be read BOOL bGotHome = FALSE; for (unsigned int dir = 0; dir < user.permissions.size(); dir++) if (user.permissions[dir].bIsHome) { bGotHome = TRUE; break; } if (!bGotHome && !user.permissions.empty()) user.permissions.begin()->bIsHome = TRUE; } std::vector<t_directory>::iterator iter; for (iter = user.permissions.begin(); iter != user.permissions.end(); iter++) { if (iter->bIsHome) { user.homedir = iter->dir; break; } } if (user.homedir=="" && user.pOwner) { for (iter = user.pOwner->permissions.begin(); iter != user.pOwner->permissions.end(); iter++) { if (iter->bIsHome) { user.homedir = iter->dir; break; } } } user.PrepareAliasMap(); m_UsersList.push_back(user); } } // Update the account list EnterCritSection(m_sync); m_sGroupsList.clear(); for (t_GroupsList::const_iterator groupiter=m_GroupsList.begin(); groupiter!=m_GroupsList.end(); groupiter++) m_sGroupsList.push_back(*groupiter); m_sUsersList.clear(); for (t_UsersList::const_iterator iter=m_UsersList.begin(); iter!=m_UsersList.end(); iter++) m_sUsersList.push_back(*iter); UpdateInstances(); LeaveCritSection(m_sync); // Write the new account data into xml file CMarkupSTL *pXML=COptions::GetXML(); if (pXML) { while(pXML->FindChildElem(_T("Groups"))) pXML->RemoveChildElem(); pXML->AddChildElem(_T("Groups")); pXML->IntoElem(); //Save the changed user details for (t_GroupsList::const_iterator groupiter=m_GroupsList.begin(); groupiter!=m_GroupsList.end(); groupiter++) { pXML->AddChildElem(_T("Group")); pXML->AddChildAttrib(_T("Name"), groupiter->group); pXML->IntoElem(); SetKey(pXML, "Bypass server userlimit", groupiter->nBypassUserLimit); SetKey(pXML, "User Limit", groupiter->nUserLimit); SetKey(pXML, "IP Limit", groupiter->nIpLimit); SetKey(pXML, "Enabled", groupiter->nEnabled); SetKey(pXML, "Comments", groupiter->comment); SetKey(pXML, "ForceSsl", groupiter->forceSsl); SaveIpFilter(pXML, *groupiter); SavePermissions(pXML, *groupiter); SaveSpeedLimits(pXML, *groupiter); pXML->OutOfElem(); } pXML->OutOfElem(); pXML->ResetChildPos(); while(pXML->FindChildElem(_T("Users"))) pXML->RemoveChildElem(); pXML->AddChildElem(_T("Users")); pXML->IntoElem(); //Save the changed user details for (t_UsersList::const_iterator iter=m_UsersList.begin(); iter!=m_UsersList.end(); iter++) { pXML->AddChildElem(_T("User")); pXML->AddChildAttrib(_T("Name"), iter->user); pXML->IntoElem(); SetKey(pXML, "Pass", iter->password); SetKey(pXML, "Group", iter->group); SetKey(pXML, "Bypass server userlimit", iter->nBypassUserLimit); SetKey(pXML, "User Limit", iter->nUserLimit); SetKey(pXML, "IP Limit", iter->nIpLimit); SetKey(pXML, "Enabled", iter->nEnabled); SetKey(pXML, "Comments", iter->comment); SetKey(pXML, "ForceSsl", iter->forceSsl); SaveIpFilter(pXML, *iter); SavePermissions(pXML, *iter); SaveSpeedLimits(pXML, *iter); pXML->OutOfElem(); } if (!COptions::FreeXML(pXML)) return FALSE; } else return FALSE; return TRUE; } bool CPermissions::Init() { EnterCritSection(m_sync); if (m_sInstanceList.empty() && m_sUsersList.empty() && m_sGroupsList.empty()) { // It's the first time Init gets called after application start, read // permissions from xml file. ReadSettings(); } else { m_GroupsList.clear(); for (t_GroupsList::iterator groupiter=m_sGroupsList.begin(); groupiter!=m_sGroupsList.end(); groupiter++) m_GroupsList.push_back(*groupiter); m_UsersList.clear(); for (t_UsersList::iterator iter = m_sUsersList.begin(); iter != m_sUsersList.end(); iter++) { CUser user = *iter; user.pOwner = NULL; if (user.group != _T("")) { for (t_GroupsList::iterator groupiter=m_GroupsList.begin(); groupiter!=m_GroupsList.end(); groupiter++) if (groupiter->group == user.group) { user.pOwner = &(*groupiter); break; } } m_UsersList.push_back(user); } } std::list<CPermissions *>::iterator instanceIter; for (instanceIter = m_sInstanceList.begin(); instanceIter != m_sInstanceList.end(); instanceIter++) if (*instanceIter==this) break; if (instanceIter == m_sInstanceList.end()) m_sInstanceList.push_back(this); LeaveCritSection(m_sync); return TRUE; } void CPermissions::ReadPermissions(CMarkupSTL *pXML, t_group &user, BOOL &bGotHome) { bGotHome = FALSE; pXML->ResetChildPos(); while (pXML->FindChildElem(_T("Permissions"))) { pXML->IntoElem(); while (pXML->FindChildElem(_T("Permission"))) { t_directory dir; dir.dir = pXML->GetChildAttrib(_T("Dir")); if (dir.dir != _T("")) { dir.dir.Replace('/', '\\'); dir.dir.TrimRight('\\'); pXML->IntoElem(); while (pXML->FindChildElem(_T("Aliases"))) { pXML->IntoElem(); while (pXML->FindChildElem(_T("Alias"))) { CStdString alias = pXML->GetChildData(); alias.Replace("/", "\\"); while (alias.Replace("\\\\", "\\")); alias.TrimRight('\\'); if (alias[0] != '\\' && alias != "") dir.aliases.push_back(alias); } pXML->OutOfElem(); } pXML->ResetChildPos(); while (pXML->FindChildElem(_T("Option"))) { CStdString name = pXML->GetChildAttrib(_T("Name")); CStdString value = pXML->GetChildData(); if (name == _T("FileRead")) dir.bFileRead = value == _T("1"); else if (name == _T("FileWrite")) dir.bFileWrite = value == _T("1"); else if (name == _T("FileDelete")) dir.bFileDelete = value == _T("1"); else if (name == _T("FileAppend")) dir.bFileAppend = value == _T("1"); else if (name == _T("DirCreate")) dir.bDirCreate = value == _T("1"); else if (name == _T("DirDelete")) dir.bDirDelete = value == _T("1"); else if (name == _T("DirList")) dir.bDirList = value == _T("1"); else if (name == _T("DirSubdirs")) dir.bDirSubdirs = value == _T("1"); else if (name == _T("IsHome")) dir.bIsHome = value == _T("1"); else if (name == _T("AutoCreate")) dir.bAutoCreate = value == _T("1"); } //Avoid multiple home dirs if (dir.bIsHome) if (!bGotHome) bGotHome = TRUE; else dir.bIsHome = FALSE; if (user.permissions.size() < 20000) user.permissions.push_back(dir); pXML->OutOfElem(); } } pXML->OutOfElem(); } } void CPermissions::AutoCreateDirs(const char *username) { // Create missing directores after a user has logged on CUser user; if (!GetUser(username, user)) return; for (std::vector<t_directory>::iterator permissioniter = user.permissions.begin(); permissioniter != user.permissions.end(); permissioniter++) if (permissioniter->bAutoCreate) { CStdString dir = permissioniter->dir; user.DoReplacements(dir); dir += "\\"; CStdString str; while (dir != "") { int pos = dir.Find("\\"); CStdString piece = dir.Left(pos + 1); dir = dir.Mid(pos + 1); str += piece; CreateDirectory(str, 0); } } if (user.pOwner) for (std::vector<t_directory>::iterator permissioniter = user.pOwner->permissions.begin(); permissioniter != user.pOwner->permissions.end(); permissioniter++) if (permissioniter->bAutoCreate) { CStdString dir = permissioniter->dir; user.DoReplacements(dir); dir += "\\"; CStdString str; while (dir != "") { int pos = dir.Find("\\"); CStdString piece = dir.Left(pos + 1); dir = dir.Mid(pos + 1); str += piece; CreateDirectory(str, 0); } } } void CPermissions::ReadSpeedLimits(CMarkupSTL *pXML, t_group &group) { pXML->ResetChildPos(); const CStdString prefixes[] = { "Dl", "Ul" }; const CStdString names[] = { "Download", "Upload" }; while (pXML->FindChildElem(_T("SpeedLimits"))) { CStdString str; int n; for (int i = 0; i < 2; i++) { str = pXML->GetChildAttrib(prefixes[i] + "Type"); n = _ttoi(str); if (n >= 0 && n < 4) group.nSpeedLimitType[i] = n; str = pXML->GetChildAttrib(prefixes[i] + "Limit"); n = _ttoi(str); if (n > 0 && n < 65536) group.nSpeedLimit[i] = n; str = pXML->GetChildAttrib("Server" + prefixes[i] + "LimitBypass"); n = _ttoi(str); if (n >= 0 && n < 4) group.nBypassServerSpeedLimit[i] = n; pXML->IntoElem(); while (pXML->FindChildElem(names[i])) { pXML->IntoElem(); while (pXML->FindChildElem(_T("Rule"))) { CSpeedLimit limit; str = pXML->GetChildAttrib("Speed"); n = _ttoi(str); if (n < 0 || n > 65535) n = 10; limit.m_Speed = n; pXML->IntoElem(); if (pXML->FindChildElem("Days")) { str = pXML->GetChildData(); if (str != "") n = _ttoi(str); else n = 0x7F; limit.m_Day = n & 0x7F; } pXML->ResetChildPos(); limit.m_DateCheck = FALSE; if (pXML->FindChildElem("Date")) { limit.m_DateCheck = TRUE; str = pXML->GetChildAttrib("Year"); n = _ttoi(str); if (n < 1900 || n > 3000) n = 2003; limit.m_Date.y = n; str = pXML->GetChildAttrib("Month"); n = _ttoi(str); if (n < 1 || n > 12) n = 1; limit.m_Date.m = n; str = pXML->GetChildAttrib("Day"); n = _ttoi(str); if (n < 1 || n > 31) n = 1; limit.m_Date.d = n; } pXML->ResetChildPos(); limit.m_FromCheck = FALSE; if (pXML->FindChildElem("From")) { limit.m_FromCheck = TRUE; str = pXML->GetChildAttrib("Hour"); n = _ttoi(str); if (n < 0 || n > 23) n = 0; limit.m_FromTime.h = n; str = pXML->GetChildAttrib("Minute"); n = _ttoi(str); if (n < 0 || n > 59) n = 0; limit.m_FromTime.m = n; str = pXML->GetChildAttrib("Second"); n = _ttoi(str); if (n < 0 || n > 59) n = 0; limit.m_FromTime.s = n; } pXML->ResetChildPos(); limit.m_ToCheck = FALSE; if (pXML->FindChildElem("To")) { limit.m_ToCheck = TRUE; str = pXML->GetChildAttrib("Hour"); n = _ttoi(str); if (n < 0 || n > 23) n = 0; limit.m_ToTime.h = n; str = pXML->GetChildAttrib("Minute"); n = _ttoi(str); if (n < 0 || n > 59) n = 0; limit.m_ToTime.m = n; str = pXML->GetChildAttrib("Second"); n = _ttoi(str); if (n < 0 || n > 59) n = 0; limit.m_ToTime.s = n; } pXML->ResetChildPos(); pXML->OutOfElem(); if (group.SpeedLimits[i].size() < 20000) group.SpeedLimits[i].push_back(limit); } pXML->OutOfElem(); } pXML->ResetChildPos(); pXML->OutOfElem(); } } } void CPermissions::SaveSpeedLimits(CMarkupSTL *pXML, const t_group &group) { pXML->AddChildElem(_T("SpeedLimits")); CStdString str; const CStdString prefixes[] = { "Dl", "Ul" }; const CStdString names[] = { "Download", "Upload" }; for (int i = 0; i < 2; i++) { pXML->SetChildAttrib(prefixes[i] + "Type", group.nSpeedLimitType[i]); pXML->SetChildAttrib(prefixes[i] + "Limit", group.nSpeedLimit[i]); pXML->SetChildAttrib("Server" + prefixes[i] + "LimitBypass", group.nBypassServerSpeedLimit[i]); pXML->IntoElem(); pXML->AddChildElem(names[i]); pXML->IntoElem(); for (unsigned int j = 0; j < group.SpeedLimits[i].size(); j++) { CSpeedLimit limit = group.SpeedLimits[i][j]; pXML->AddChildElem(_T("Rule")); pXML->SetChildAttrib(_T("Speed"), limit.m_Speed); pXML->IntoElem(); str.Format("%d", limit.m_Day); pXML->AddChildElem(_T("Days"), str); if (limit.m_DateCheck) { pXML->AddChildElem(_T("Date")); pXML->SetChildAttrib(_T("Year"), limit.m_Date.y); pXML->SetChildAttrib(_T("Month"), limit.m_Date.m); pXML->SetChildAttrib(_T("Day"), limit.m_Date.d); } if (limit.m_FromCheck) { pXML->AddChildElem(_T("From")); pXML->SetChildAttrib(_T("Hour"), limit.m_FromTime.h); pXML->SetChildAttrib(_T("Minute"), limit.m_FromTime.m); pXML->SetChildAttrib(_T("Second"), limit.m_FromTime.s); } if (limit.m_ToCheck) { pXML->AddChildElem(_T("To")); pXML->SetChildAttrib(_T("Hour"), limit.m_ToTime.h); pXML->SetChildAttrib(_T("Minute"), limit.m_ToTime.m); pXML->SetChildAttrib(_T("Second"), limit.m_ToTime.s); } pXML->OutOfElem(); } pXML->OutOfElem(); pXML->OutOfElem(); } } void CPermissions::ReloadConfig() { m_UsersList.clear(); m_GroupsList.clear(); EnterCritSection(m_sync); ReadSettings(); UpdateInstances(); LeaveCritSection(m_sync); return; } void CPermissions::ReadIpFilter(CMarkupSTL *pXML, t_group &group) { pXML->ResetChildPos(); while (pXML->FindChildElem(_T("IpFilter"))) { pXML->IntoElem(); while (pXML->FindChildElem(_T("Disallowed"))) { pXML->IntoElem(); while (pXML->FindChildElem(_T("IP"))) { CStdString ip = pXML->GetChildData(); if (group.disallowedIPs.size() >= 20000) break; if (ip == "*") group.disallowedIPs.push_back(ip); else { if (IsValidAddressFilter(ip)) group.disallowedIPs.push_back(ip); } } pXML->OutOfElem(); } pXML->ResetChildPos(); while (pXML->FindChildElem(_T("Allowed"))) { pXML->IntoElem(); while (pXML->FindChildElem(_T("IP"))) { CStdString ip = pXML->GetChildData(); if (group.allowedIPs.size() >= 20000) break; if (ip == "*") group.allowedIPs.push_back(ip); else { if (IsValidAddressFilter(ip)) group.allowedIPs.push_back(ip); } } pXML->OutOfElem(); } pXML->OutOfElem(); } } void CPermissions::SaveIpFilter(CMarkupSTL *pXML, const t_group &group) { pXML->AddChildElem(_T("IpFilter")); pXML->IntoElem(); pXML->AddChildElem(_T("Disallowed")); pXML->IntoElem(); std::list<CStdString>::const_iterator iter; for (iter = group.disallowedIPs.begin(); iter != group.disallowedIPs.end(); iter++) { pXML->AddChildElem(_T("IP")); pXML->SetChildData(*iter); } pXML->OutOfElem(); pXML->AddChildElem(_T("Allowed")); pXML->IntoElem(); for (iter = group.allowedIPs.begin(); iter != group.allowedIPs.end(); iter++) { pXML->AddChildElem(_T("IP")); pXML->SetChildData(*iter); } pXML->OutOfElem(); pXML->OutOfElem(); } CStdString CPermissions::CanonifyServerDir(CStdString currentDir, CStdString newDir) const { /* * CanonifyPath takes the current and the new server dir as parameter, * concats the paths if neccessary and canonifies the dir: * - remove dot-segments * - convert backslashes into slashes * - remove double slashes */ if (newDir == "") return currentDir; // Make segment separators pretty newDir.Replace("\\", "/"); while (newDir.Replace("//", "/")); if (newDir == "/") return newDir; // This list will hold the individual path segments std::list<CStdString> piecelist; /* * Check the type of the path: Absolute or relative? * On relative paths, use currentDir as base, else use * only dir. */ if (newDir.Left(1) != "/") { // New relative path, split currentDir and add it to the piece list. currentDir.TrimLeft("/"); int pos; while((pos = currentDir.Find("/")) != -1) { piecelist.push_back(currentDir.Left(pos)); currentDir = currentDir.Mid(pos + 1); } if (currentDir != "") piecelist.push_back(currentDir); } /* * Now split up the new dir into individual segments. Here we * check for dot segments and remove the proper number of segments * from the piece list on dots. */ int pos; newDir.TrimLeft("/"); if (newDir.Right(1) != "/") newDir += "/"; while ((pos = newDir.Find("/")) != -1) { CStdString piece = newDir.Left(pos); newDir = newDir.Mid(pos + 1); if (piece == _T("")) continue; bool allDots = true; int dotCount = 0; for (int i = 0; i < piece.GetLength(); i++) if (piece[i] != '.') { allDots = false; break; } else dotCount++; if (allDots) { while (--dotCount) { if (!piecelist.empty()) piecelist.pop_back(); } } else piecelist.push_back(piece); } // Reassemble the directory CStdString result; if (piecelist.empty()) return "/"; const char *(x[23]) = { "OK", "k", "k" }; // List of reserved filenames which may not be used on a Windows system const char *reservedNames[] = { "CON", "PRN", "AUX", "CLOCK$", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", "" }; for (std::list<CStdString>::iterator iter = piecelist.begin(); iter != piecelist.end(); iter++) { // Check for reserved filenames CStdString piece = *iter; piece.MakeUpper(); for (const char **reserved = reservedNames; **reserved; reserved++) { if (piece == *reserved) return ""; } int pos = piece.Find("."); if (pos >= 3) { piece = piece.Left(pos); for (const char **reserved = reservedNames; **reserved; reserved++) { if (piece == *reserved) return ""; } } result += "/" + *iter; } // Now dir is the canonified absolute server path. return result; } int CPermissions::GetFact(LPCTSTR username, CStdString currentDir, CStdString file, CStdString& fact, CStdString& logicalName) { // Get user from username CUser user; if (!GetUser(username, user)) return PERMISSION_DENIED; // No user found CStdString dir = CanonifyServerDir(currentDir, file); if (dir == "") return PERMISSION_INVALIDNAME; logicalName = dir; t_directory directory; BOOL bTruematch; int res = GetRealDirectory(dir, user, directory, bTruematch); if (res == PERMISSION_FILENOTDIR) { if (dir == "/") return res; int pos = dir.ReverseFind('/'); if (pos == -1) return res; CStdString dir2; if (pos) dir2 = dir.Left(pos); else dir2 = "/"; CStdString fn = dir.Mid(pos + 1); int res = GetRealDirectory(dir2, user, directory, bTruematch); if (res) return res | PERMISSION_FILENOTDIR; if (!directory.bFileRead) return PERMISSION_DENIED; file = directory.dir + "\\" + fn; fact = "type=file;"; } else if (res) return res; else { if (!directory.bDirList) return PERMISSION_DENIED; if (!bTruematch && !directory.bDirSubdirs) return PERMISSION_DENIED; file = directory.dir; fact = "type=dir;"; } CFileStatus64 status; if (GetStatus64(file, status)) { if (!(status.m_attribute & FILE_ATTRIBUTE_DIRECTORY)) { CStdString str; str.Format("size=%I64d;", status.m_size); fact += str; } // Get last modification time FILETIME ftime = status.m_mtime; if (!ftime.dwHighDateTime && !ftime.dwLowDateTime) ftime = status.m_ctime; if (ftime.dwHighDateTime || ftime.dwLowDateTime) { SYSTEMTIME time; FileTimeToSystemTime(&ftime, &time); CStdString str; str.Format("modify=%04d%02d%02d%02d%02d%02d;", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); fact += str; } } fact += " " + logicalName; return 0; } int CPermissions::GetFacts(LPCTSTR username, CStdString currentDir, CStdString file, t_dirlisting *&pResult, CStdString &physicalDir, CStdString &logicalDir) { // Get user from username CUser user; if (!GetUser(username, user)) return PERMISSION_DENIED; // No user found CStdString dir = CanonifyServerDir(currentDir, file); if (dir == "") return PERMISSION_INVALIDNAME; t_directory directory; BOOL bTruematch; int res = GetRealDirectory(dir, user, directory, bTruematch); if (res) return res; if (!directory.bDirList) return PERMISSION_DENIED; logicalDir = dir; physicalDir = directory.dir; t_dirlisting *pDir = new t_dirlisting; pDir->len = 0; pDir->pNext = NULL; pResult = pDir; CStdString curDir = directory.dir; curDir.MakeLower(); // List aliases in current directory for (std::multimap<CStdString, CUser::t_alias>::const_iterator iter = user.aliasMap.find(curDir); iter != user.aliasMap.end() && iter->first == curDir; iter++) { t_directory directory; BOOL truematch = false; if (GetRealDirectory(dir + "/" + iter->second.name, user, directory, truematch)) continue; if (!directory.bDirList) continue; if (!truematch && !directory.bDirSubdirs) continue; // This wastes some memory but keeps the whole thing fast if ((8192 - pDir->len) < (60 + MAX_PATH)) { pDir->pNext = new t_dirlisting; pDir = pDir->pNext; pDir->len = 0; pDir->pNext = NULL; } memcpy(pDir->buffer + pDir->len, "type=dir;", 9); pDir->len += 9; CFileStatus64 status; if (GetStatus64(directory.dir, status)) { // Get last modification time FILETIME ftime = status.m_mtime; if (!ftime.dwHighDateTime && !ftime.dwLowDateTime) ftime = status.m_ctime; if (ftime.dwHighDateTime || ftime.dwLowDateTime) { SYSTEMTIME time; FileTimeToSystemTime(&ftime, &time); CStdString str; str.Format("modify=%04d%02d%02d%02d%02d%02d;", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); memcpy(pDir->buffer + pDir->len, str.c_str(), str.GetLength()); pDir->len += str.GetLength(); } } pDir->buffer[pDir->len++] = ' '; memcpy(pDir->buffer + pDir->len, iter->second.name.c_str(), iter->second.name.GetLength()); pDir->len += iter->second.name.GetLength(); pDir->buffer[pDir->len++] = '\r'; pDir->buffer[pDir->len++] = '\n'; } WIN32_FIND_DATA FindFileData; WIN32_FIND_DATA NextFindFileData; HANDLE hFind; hFind = FindFirstFile(directory.dir + "\\*", &NextFindFileData); while (hFind != INVALID_HANDLE_VALUE) { FindFileData = NextFindFileData; if (!FindNextFile(hFind, &NextFindFileData)) { FindClose(hFind); hFind = INVALID_HANDLE_VALUE; } if (!_tcscmp(FindFileData.cFileName, _T(".")) || !_tcscmp(FindFileData.cFileName, _T(".."))) continue; // This wastes some memory but keeps the whole thing fast if ((8192 - pDir->len) < (60 + MAX_PATH)) { pDir->pNext = new t_dirlisting; pDir = pDir->pNext; pDir->len = 0; pDir->pNext = NULL; } bool file; if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // Check permissions of subdir. If we don't have LIST permission, // don't display the subdir. BOOL truematch; t_directory subDir; if (GetRealDirectory(dir + "/" + FindFileData.cFileName, user, subDir, truematch)) continue; if (!subDir.bDirList) continue; file = false; memcpy(pDir->buffer + pDir->len, "type=dir;", 9); pDir->len += 9; } else { file = true; memcpy(pDir->buffer + pDir->len, "type=file;", 10); pDir->len += 10; } // Get last modification time FILETIME ftime = FindFileData.ftLastWriteTime; if (!ftime.dwHighDateTime && !ftime.dwLowDateTime) ftime = FindFileData.ftCreationTime; if (ftime.dwHighDateTime || ftime.dwLowDateTime) { SYSTEMTIME time; FileTimeToSystemTime(&ftime, &time); CStdString str; str.Format("modify=%04d%02d%02d%02d%02d%02d;", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); memcpy(pDir->buffer + pDir->len, str.c_str(), str.GetLength()); pDir->len += str.GetLength(); } if (file) { CStdString size; size.Format("size=%I64d;", FindFileData.nFileSizeLow + ((_int64)FindFileData.nFileSizeHigh<<32)); memcpy(pDir->buffer + pDir->len, size.c_str(), size.GetLength()); pDir->len += size.GetLength(); } pDir->buffer[pDir->len++] = ' '; memcpy(pDir->buffer + pDir->len, FindFileData.cFileName, strlen(FindFileData.cFileName)); pDir->len += strlen(FindFileData.cFileName); pDir->buffer[pDir->len++] = '\r'; pDir->buffer[pDir->len++] = '\n'; } return 0; } void CUser::PrepareAliasMap() { /* * Prepare the alias map. * For fast access, aliases are stored as key/value pairs. * The key is the folder part of the alias. * The value is a structure containing the name of the alias * and the target folder. * Example: * Shared folder c:\myfolder, alias d:\myotherfolder\myalias * Key: d:\myotherfolder, Value = myalias, c:\myfolder */ aliasMap.clear(); std::vector<t_directory>::const_iterator permIter; std::list<CStdString>::const_iterator aliasIter; for (permIter = permissions.begin(); permIter != permissions.end(); permIter++) { for (aliasIter = permIter->aliases.begin(); aliasIter != permIter->aliases.end(); aliasIter++) { CStdString alias = *aliasIter; DoReplacements(alias); int pos = alias.ReverseFind('\\'); if (pos == -1) continue; t_alias aliasStruct; aliasStruct.name = alias.Mid(pos + 1); if (aliasStruct.name == "") continue; alias = alias.Left(pos); alias.MakeLower(); aliasStruct.targetFolder = permIter->dir; DoReplacements(aliasStruct.targetFolder); aliasMap.insert(std::pair<CStdString, t_alias>(alias, aliasStruct)); } } if (!pOwner) return; for (permIter = pOwner->permissions.begin(); permIter != pOwner->permissions.end(); permIter++) { for (aliasIter = permIter->aliases.begin(); aliasIter != permIter->aliases.end(); aliasIter++) { CStdString alias = *aliasIter; DoReplacements(alias); int pos = alias.ReverseFind('\\'); if (pos == -1) continue; t_alias aliasStruct; aliasStruct.name = alias.Mid(pos + 1); if (aliasStruct.name == "") continue; alias = alias.Left(pos); alias.MakeLower(); aliasStruct.targetFolder = permIter->dir; DoReplacements(aliasStruct.targetFolder); aliasMap.insert(std::pair<CStdString, t_alias>(alias, aliasStruct)); } } } CStdString CUser::GetAliasTarget(CStdString path, CStdString name) const { // Find the target for the alias with the specified path and name path.MakeLower(); std::multimap<CStdString, CUser::t_alias>::const_iterator iter = aliasMap.find(path); bool foundAlias = false; while (iter != aliasMap.end() && iter->first == path) { if (!iter->second.name.CompareNoCase(name)) return iter->second.targetFolder; iter++; } return ""; } void CPermissions::ReadSettings() { CMarkupSTL *pXML = COptions::GetXML(); if (!pXML) return; if (!pXML->FindChildElem(_T("Groups"))) pXML->AddChildElem(_T("Groups")); pXML->IntoElem(); while (pXML->FindChildElem(_T("Group"))) { t_group group; group.nIpLimit = group.nIpLimit = group.nUserLimit = 0; group.nBypassUserLimit = 2; group.group = pXML->GetChildAttrib(_T("Name")); if (group.group!="") { pXML->IntoElem(); while (pXML->FindChildElem(_T("Option"))) { CStdString name = pXML->GetChildAttrib(_T("Name")); CStdString value = pXML->GetChildData(); if (name == _T("Bypass server userlimit")) group.nBypassUserLimit = _ttoi(value); else if (name == _T("User Limit")) group.nUserLimit = _ttoi(value); else if (name == _T("IP Limit")) group.nIpLimit = _ttoi(value); else if (name == _T("Enabled")) group.nEnabled = _ttoi(value); else if (name == _T("Comments")) group.comment = value; else if (name == _T("ForceSsl")) group.forceSsl = _ttoi(value); if (group.nUserLimit<0 || group.nUserLimit>999999999) group.nUserLimit=0; if (group.nIpLimit<0 || group.nIpLimit>999999999) group.nIpLimit=0; } ReadIpFilter(pXML, group); BOOL bGotHome = FALSE; ReadPermissions(pXML, group, bGotHome); //Set a home dir if no home dir could be read if (!bGotHome && !group.permissions.empty()) group.permissions.begin()->bIsHome = TRUE; ReadSpeedLimits(pXML, group); if (m_GroupsList.size() < 20000) m_GroupsList.push_back(group); pXML->OutOfElem(); } } pXML->OutOfElem(); pXML->ResetChildPos(); if (!pXML->FindChildElem(_T("Users"))) pXML->AddChildElem(_T("Users")); pXML->IntoElem(); while (pXML->FindChildElem(_T("User"))) { CUser user; user.nIpLimit = user.nIpLimit = user.nUserLimit = 0; user.nBypassUserLimit = 2; user.user=pXML->GetChildAttrib(_T("Name")); if (user.user == "") continue; pXML->IntoElem(); while (pXML->FindChildElem(_T("Option"))) { CStdString name = pXML->GetChildAttrib(_T("Name")); CStdString value = pXML->GetChildData(); if (name == _T("Pass")) { // If provided password is not a MD5 has, convert it into a MD5 hash if (value != "" && value.GetLength() != 32) { const char *tmp = value; MD5 md5; md5.update((unsigned char *)tmp, _tcslen(tmp)); md5.finalize(); char *res = md5.hex_digest(); pXML->SetChildData(res); user.password = res; delete [] res; } else user.password = value; } else if (name == _T("Bypass server userlimit")) user.nBypassUserLimit = _ttoi(value); else if (name == _T("User Limit")) user.nUserLimit = _ttoi(value); else if (name == _T("IP Limit")) user.nIpLimit = _ttoi(value); else if (name == _T("Group")) user.group = value; else if (name == _T("Enabled")) user.nEnabled = _ttoi(value); else if (name == _T("Comments")) user.comment = value; else if (name == _T("ForceSsl")) user.forceSsl = _ttoi(value); if (user.nUserLimit < 0 || user.nUserLimit > 999999999) user.nUserLimit = 0; if (user.nIpLimit < 0 || user.nIpLimit > 999999999) user.nIpLimit = 0; } if (user.group != _T("")) { for (t_GroupsList::iterator groupiter = m_GroupsList.begin(); groupiter != m_GroupsList.end(); groupiter++) if (groupiter->group == user.group) { user.pOwner = &(*groupiter); break; } if (!user.pOwner) user.group = ""; } ReadIpFilter(pXML, user); BOOL bGotHome = FALSE; ReadPermissions(pXML, user, bGotHome); user.PrepareAliasMap(); //Set a home dir if no home dir could be read if (!bGotHome && !user.pOwner) { if (!user.permissions.empty()) user.permissions.begin()->bIsHome = TRUE; } std::vector<t_directory>::iterator iter; for (iter = user.permissions.begin(); iter != user.permissions.end(); iter++) { if (iter->bIsHome) { user.homedir = iter->dir; break; } } if (user.homedir == "" && user.pOwner) { for (iter = user.pOwner->permissions.begin(); iter != user.pOwner->permissions.end(); iter++) { if (iter->bIsHome) { user.homedir = iter->dir; break; } } } ReadSpeedLimits(pXML, user); if (m_UsersList.size() < 20000) m_UsersList.push_back(user); pXML->OutOfElem(); } COptions::FreeXML(pXML); EnterCritSection(m_sync); m_sGroupsList.clear(); for (t_GroupsList::iterator groupiter = m_GroupsList.begin(); groupiter != m_GroupsList.end(); groupiter++) m_sGroupsList.push_back(*groupiter); m_sUsersList.clear(); for (t_UsersList::iterator iter = m_UsersList.begin(); iter != m_UsersList.end(); iter++) { CUser user = *iter; user.pOwner = NULL; if (user.group != _T("")) { for (t_GroupsList::iterator groupiter = m_GroupsList.begin(); groupiter != m_GroupsList.end(); groupiter++) if (groupiter->group == user.group) { user.pOwner = &(*groupiter); break; } } m_sUsersList.push_back(user); } LeaveCritSection(m_sync); } // Replace :u and :g (if a group it exists) void CUser::DoReplacements(CStdString& path) const { path.Replace(":u", user); path.Replace(":U", user); if (group != _T("")) { path.Replace(":g", group); path.Replace(":G", group); } }