PDA

View Full Version : [c] Sources for internal userfile module


darkone
11-16-2003, 08:53 PM
... as you can see, I'm slowly swapping CHARs to TCHARs; so when writing modules, prefer TCHARs.

Notes:
1) If write fails, ioFTPD automatically sets timer for retry attempt. Pending locks are cancelled with error, and user/groupfile becomes read-only to daemon until write succeeds.
2) ioFTPD calls open at the daemon initialization & close on deinitialization
3) Lock request is always exclusive.
4) Data within USERFILE structure is expected to be synchronized on lock.
5) Module may call Update routine to update cached values.
6) Module may not call any of the UserFile/GroupFile routines. Otherwise dead-lock may occur.
7) UID and GID are unique to site. On shared userbase, handle resources by name! (do not use ids in container names & store groups in user containers in names, rather than using ids)
8) All requests may block for undeterminated amount of time. However, consider anything that takes longer than few hundred milliseconds to execute, as invalid implementation.
9) Module must check validity of group at give site.

... If you create shared database modules, it's recommended to synchronize to data between database and ioFTPD's cache in batches: You can setup timer using StartIoTimer() to run once a minute, and read list of changes from database.

Initialization routines are: UserModuleInit GroupModuleInit

#include <ioFTPD.h>

// Local declarations
INT User_StandardOpen(LPTSTR tszUserName, LPUSERFILE lpUserFile);
BOOL User_StandardLock(LPUSERFILE lpUserFile);
BOOL User_StandardUnlock(LPUSERFILE lpUserFile);
BOOL User_StandardWrite(LPUSERFILE lpUserFile);
BOOL User_StandardClose(LPUSERFILE lpUserFile);
BOOL User_StandardCreate(LPTSTR tszUserName);
BOOL User_StandardDelete(LPTSTR tszUserName, INT32 Uid);
BOOL User_StandardRename(LPTSTR tszUserName, INT32 Uid, LPTSTR tszNewUserName);
INT User_StandardRead(LTPSTR tszFileName, LPUSERFILE lpUserFile);


DATAROW UserDataRow[] =
{
"admingroups", offsetof(USERFILE, AdminGroups), DT_GROUPID, MAX_GROUPS, 0,
"alldn", offsetof(USERFILE, AllDn), DT_INT64, MAX_SECTIONS * 3, 0,
"allup", offsetof(USERFILE, AllUp), DT_INT64, MAX_SECTIONS * 3, 0,
"credits", offsetof(USERFILE, Credits), DT_INT64, MAX_SECTIONS, 0,
"daydn", offsetof(USERFILE, DayDn), DT_INT64, MAX_SECTIONS * 3, 0,
"dayup", offsetof(USERFILE, DayUp), DT_INT64, MAX_SECTIONS * 3, 0,
"flags", offsetof(USERFILE, Flags), DT_STRING, 1, 32,
"groups", offsetof(USERFILE, Groups), DT_GROUPID, MAX_GROUPS, 0,
"home", offsetof(USERFILE, Home), DT_STRING, 1, _MAX_PATH,
"ips", offsetof(USERFILE, Ip), DT_STRING, MAX_IPS, _IP_LINE_LENGTH,
"limits", offsetof(USERFILE, Limits), DT_INT32, 5, 0,
"monthdn", offsetof(USERFILE, MonthDn), DT_INT64, MAX_SECTIONS * 3, 0,
"monthup", offsetof(USERFILE, MonthUp), DT_INT64, MAX_SECTIONS * 3, 0,
"password", offsetof(USERFILE, Password), DT_PASSWORD, 0, 20,
"ratio", offsetof(USERFILE, Ratio), DT_INT32, MAX_SECTIONS, 0,
"tagline", offsetof(USERFILE, Tagline), DT_STRING, 1, 128,
"vfsfile", offsetof(USERFILE, MountFile), DT_STRING, 1, _MAX_PATH,
"wkdn", offsetof(USERFILE, WkDn), DT_INT64, MAX_SECTIONS * 3, 0,
"wkup", offsetof(USERFILE, WkUp), DT_INT64, MAX_SECTIONS * 3, 0
};


LPUSER_MODULE lpUserModule;



BOOL User_StandardInit(LPUSER_MODULE lpModule)
{
lpModule->tszModuleName = _TEXT("STANDARD");
lpModule->Open = User_StandardOpen;
lpModule->Lock = User_StandardLock;
lpModule->Unlock = User_StandardUnlock;
lpModule->Write = User_StandardWrite;
lpModule->Create = User_StandardCreate;
lpModule->Rename = User_StandardRename;
lpModule->Delete = User_StandardDelete;
lpModule->Close = User_StandardClose;
lpUserModule = lpModule;

return FALSE;
}


BOOL User_StandardDeInit(LPUSER_MODULE lpModule)
{
lpModule->tszModuleName = NULL;
lpModule->Open = NULL;
lpModule->Lock = NULL;
lpModule->Unlock = NULL;
lpModule->Write = NULL;
lpModule->Create = NULL;
lpModule->Rename = NULL;
lpModule->Delete = NULL;
lpModule->Close = NULL;

return FALSE;
}



INT32 User_StandardCreate(LPTSTR tszUserName)
{
USERFILE UserFile;
LPTSTR tszSourceFile, tszTargetFile;
DWORD dwError;
TCHAR *tpOffset;
TCHAR pBuffer[128];
INT32 iReturn;

// Setup local variables
ZeroMemory(&UserFile, sizeof(USERFILE));
wsprintf(pBuffer, "%s.temporary", tszUserName);

UserFile.Groups[0] = NOGROUP_ID;
UserFile.Groups[1] = -1;
UserFile.AdminGroups[0] = -1;
// Get locations
tszSourceFile = Config_Get_Path("Locations", "User_Files", "Default.User", NULL);
tszTargetFile = Config_Get_Path("Locations", "User_Files", pBuffer, NULL);

if (! tszSourceFile ||
! tszTargetFile)
{
// Free memory
Free(tszTargetFile);
ERROR_RETURN(ERROR_NOT_ENOUGH_MEMORY, -1);
}
// Find last '\' (used later)
tpOffset = _tcsrchr(tszSourceFile, '\\') + 1;
if (! tpOffset)tpOffset = _tcsrchr(tszSourceFile, '/') + 1;

// Copy file
if (! CopyFile(tszSourceFile, tszTargetFile, FALSE))
{
dwError = GetLastError();
// Free memory
Free(tszSourceFile);
Free(tszTargetFile);
ERROR_RETURN(dwError, -1);
}

// Try to read file
if (User_StandardRead(tszTargetFile, &UserFile) != UM_SUCCESS)
{
dwError = GetLastError();
// Delete file
DeleteFile(tszTargetFile);
// Free memory
Free(tszSourceFile);
Free(tszTargetFile);
SetLastError(dwError);
return -1;
}
// Register user
iReturn = lpUserModule->Register((LPVOID)lpUserModule,
tszUserName, &UserFile);
// Verify user registration
if (iReturn == -1)
{
dwError = GetLastError();
// Close userfile
User_StandardClose(&UserFile);
// Delete file
DeleteFile(tszTargetFile);
}
else
{
// Update filename (int32 max/min, isn't longer as string than "default.user")
wsprintf(tpOffset, "%i", iReturn);
// Move file
if (! MoveFileEx(tszTargetFile, tszSourceFile, MOVEFILE_REPLACE_EXISTING))
{
dwError = GetLastError();
// Unregister user
if (! lpUserModule->Unregister((LPVOID)lpUserModule, tszUserName))
{
// Close userfile
User_StandardClose(&UserFile);
}
// Delete file
DeleteFile(tszTargetFile);
iReturn = -1;
}
else dwError = NO_ERROR;
}
// Free memory
Free(tszSourceFile);
Free(tszTargetFile);
// Set error
ERROR_RETURN(dwError, iReturn);
}


BOOL User_StandardRename(LPTSTR tszUserName, INT32 Uid, LPSTR szNewUserName)
{
// Call register as
return lpUserModule->RegisterAs((LPVOID)lpUserModule, tszUserName, szNewUserName);
}


BOOL User_StandardDelete(LPSTR tszUserName, INT32 Uid)
{
LPTSTR tszFileName;
DWORD dwError;
TCHAR pBuffer[32];

wsprintf(pBuffer, "%i", Uid);
// Get target file
tszFileName = Config_Get_Path("Locations", "User_Files", pBuffer, NULL);
if (! tszFileName) ERROR_RETURN(ERROR_NOT_ENOUGH_MEMORY, TRUE);
// Delete file
if (! DeleteFile(tszFileName) &&
(dwError = GetLastError()) != ERROR_FILE_NOT_FOUND)
{
Free(tszFileName);
ERROR_RETURN(dwError, TRUE);
}
Free(tszFileName);
// Unregister user (should not ever fail)
return lpUserModule->Unregister((LPVOID)lpUserModule, tszUserName);
}



BOOL User_StandardLock(LPUSERFILE lpUserFile)
{
// Lock should lock resource (exclusive lock), and synchronize contents of lpUserFile with database
return FALSE;
}



BOOL User_StandardUnlock(LPUSERFILE lpUserFile)
{
// Unlocks resource
return FALSE;
}



BOOL Ascii2UserFile(PCHAR pBuffer, DWORD dwBuffer, LPUSERFILE lpUserFile)
{
DataRow_ParseBuffer(pBuffer, dwBuffer,
(LPVOID)lpUserFile, UserDataRow, sizeof(UserDataRow) / sizeof(DATAROW));
return FALSE;
}

BOOL UserFile2Ascii(LPBUFFER lpBuffer, LPUSERFILE lpUserFile)
{
DataRow_Dump(lpBuffer, (LPVOID)lpUserFile,
UserDataRow, sizeof(UserDataRow) / sizeof(DATAROW));

return FALSE;
}


INT User_StandardRead(LPTSTR tszFileName, LPUSERFILE lpUserFile)
{
LPUSERFILE_CONTEXT lpContext;
DWORD dwFileSize, dwBytesRead, dwError;
CHAR *pBuffer;
INT iReturn;

pBuffer = NULL;
iReturn = UM_FATAL;
// Allocate context
if (! (lpContext = (LPUSERFILE_CONTEXT)Allocate(NULL, sizeof(USERFILE_CONTEXT)))) ERROR_RETURN(ERROR_NOT_ENOUGH_MEMORY, UM_FATAL);

// Open userfile
lpContext->hFileHandle = CreateFile(tszFileName, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE , NULL, OPEN_EXISTING, 0, NULL);

if (lpContext->hFileHandle == INVALID_HANDLE_VALUE)
{
iReturn = (GetLastError() == ERROR_FILE_NOT_FOUND ? UM_DELETED : UM_FATAL);
// Free resources
goto DONE;
}

// Get filesize
if ((dwFileSize = GetFileSize(lpContext->hFileHandle, NULL)) == INVALID_FILE_SIZE ||
dwFileSize < 5) goto DONE;
// Allocate read buffer
if (! (pBuffer = (PCHAR)Allocate(NULL, dwFileSize + 1))) goto DONE;
// Read userfile to buffer
if (! ReadFile(lpContext->hFileHandle, pBuffer, dwFileSize, &dwBytesRead, NULL) ||
dwBytesRead < 5) goto DONE;
// Pad buffer with newline
pBuffer[dwBytesRead++] = '\n';
// Parse buffer
Ascii2UserFile(pBuffer, dwBytesRead, lpUserFile);

lpUserFile->Gid = lpUserFile->Groups[0];
lpUserFile->lpInternal = (LPVOID)lpContext;
iReturn = UM_SUCCESS;
DONE:
if (iReturn != UM_SUCCESS)
{
dwError = GetLastError();
if (lpContext->hFileHandle != INVALID_HANDLE_VALUE) CloseHandle(lpContext->hFileHandle);
Free(lpContext);
Free(pBuffer);
SetLastError(dwError);
}
else Free(pBuffer);

return iReturn;
}




INT User_StandardOpen(LPTSTR tszUserName, LPUSERFILE lpUserFile)
{
LPTSTR tszFileName;
TCHAR tpIdBuffer[16];
INT iReturn;

// Print uid to buffer
wsprintf(tpIdBuffer, "%i", lpUserFile->Uid);
// Get filename
if (! (tszFileName = Config_Get_Path("Locations", "User_Files", tpIdBuffer, NULL))) return UM_FATAL;
// Read userfile
iReturn = User_StandardRead(tszFileName, lpUserFile);
Free(tszFileName);

return iReturn;
}




BOOL User_StandardWrite(LPUSERFILE lpUserFile)
{
LPUSERFILE_CONTEXT lpContext;
BUFFER WriteBuffer;
DWORD dwBytesWritten, dwError;

lpContext = (LPUSERFILE_CONTEXT)lpUserFile->lpInternal;
// Allocate write buffer
WriteBuffer.size = 4096;
WriteBuffer.len = 0;
WriteBuffer.buf = (PCHAR)Allocate(NULL, WriteBuffer.size);
if (! WriteBuffer.buf) ERROR_RETURN(ERROR_NOT_ENOUGH_MEMORY, TRUE);

// Dump to buffer
UserFile2Ascii(&WriteBuffer, lpUserFile);

// Write buffer to file
SetFilePointer(lpContext->hFileHandle, 0, 0, FILE_BEGIN);
if (! WriteFile(lpContext->hFileHandle,
WriteBuffer.buf, WriteBuffer.len, &dwBytesWritten, NULL))
{
dwError = GetLastError();
Free(WriteBuffer.buf);
SetLastError(dwError);
return TRUE;
}
SetEndOfFile(lpContext->hFileHandle);

// Free write buffer
Free(WriteBuffer.buf);

return FALSE;
}




BOOL User_StandardClose(LPUSERFILE lpUserFile)
{
LPUSERFILE_CONTEXT lpContext;
BOOL bReturn;

// Get context
lpContext = (LPUSERFILE_CONTEXT)lpUserFile->lpInternal;
if (! lpContext) return FALSE;
// Close filehandle
bReturn = CloseHandle(lpContext->hFileHandle);
// Free memory
Free(lpContext);

return bReturn;
}

darkone
11-16-2003, 11:41 PM
GetProc currently returns offsets for following procedures:

Allocate
ReAllocate
Free
Config_Read
Config_Write
Config_Get
Config_GetInt
Config_GetBool
Config_GetPath
Config_GetSection
Config_GetLinear
Config_GetPermission
InitalizeLockObject
DeleteLockObject
AcquireSharedLock
ReleaseSharedLock
AcquireExclusiveLock
ReleaseExclusiveLock
CreateUser
RenameUser
DeleteUser
Uid2User
User2Uid
GetUsers
UserFile_Open
UserFile_OpenPrimitive
UserFile_Lock
UserFile_Unlock
UserFile_Close
FindFirstUser
FindNextUser
CreateGroup
RenameGroup
DeleteGroup
Gid2Group
Group2Gid
GetGroups
GroupFile_Open
GroupFile_OpenPrimitive
GroupFile_Lock
GroupFile_Unlock
GroupFile_Close
StartIoTimer
StopIoTimer
SplitString
ConcatString
GetStringIndex
GetStringIndexStatic
GetStringRange
FreeString
Access
GetFileInfo
UpdateFileInfo
CloseFileInfo
FindFileContext
InsertFileContext
DeleteFileContext
CreateFileContext
FreeFileContext
OpenDirectory
MarkDirectory
CloseDirectory
IoRemoveDirectory
ioOpenFile
ioCloseFile
ioReadFile
ioWriteFile
ioSeekFile
ioSend
ioRecv
ioCloseSocket
Secure_SendResult
Secure_ReceiveResult
MountFile_Open
MountFile_Close
PWD_CWD
PWD_GetTable
PWD_Copy
PWD_Free
PWD_Resolve
FormatBuffer
InsertBuffer
AppendBuffer
Message_Load
Message_PreCompile
MessageFile_Show
Message_Compile
Message_Object_GetInt
Message_Object_GetString
Service_Start
Service_Stop
Services_Start
Services_Stop
QueueJob
QueueClientJobEx
QueueIoEx
EndJob
StartJobTimer
StopJobTimer
SetJobFilter
Putlog
InitDataOffsets
GetUserData
BindCompletionPort