View Single Post
Old 11-16-2003, 08:53 PM  
darkone
Disabled
 
darkone's Avatar
 
Join Date: Dec 2001
Posts: 2,230
Default [c] Sources for internal userfile module

... 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 is offline