private static int OnSynchronizeAccountFromVault(string AccountName, string SyncPath, IntPtr Context)
{
try
{
//
// Pass through to the default implementation if the connection
// string is not defined in the database.
//
if (String.IsNullOrEmpty(StoreConnectionString))
return 1;
string OriginalAccountName = AccountName;
//
// Canonicalize names to lowercase as the file store may be
// case sensitive and maintaining a mapping table in the
// database is problematic since the first save for a new
// account may be observed before the players record for
// that player is created (and a player could log in to two
// servers simultaneously and create orphaned records that
// way, or similarly during a database outage, etc.).
//
AccountName = AccountName.ToLowerInvariant();
try
{
bool DirCreated = false;
FileStoreDirectory StoreDirectory = Container.GetDirectoryReference(AccountName);
IEnumerable<string> FsFiles = null;
Dictionary<string, FileStoreFile> StoreFiles = new Dictionary<string, FileStoreFile>();
//
// Build an index of all files in the file store vault.
//
foreach (FileStoreFile StoreFile in StoreDirectory.GetFiles())
{
string[] Segments = StoreFile.Uri.Segments;
if (Segments.Length == 0)
continue;
string CharacterFileName = Segments[Segments.Length - 1].ToLowerInvariant();
if (!CharacterFileName.EndsWith(".bic"))
continue;
StoreFiles.Add(CharacterFileName, StoreFile);
}
//
// Enumerate file currently in the file system directory,
// transferring each corresponding file from the store
// directory if the modified date of the store file is
// after the modified date of the local file.
//
if (Directory.Exists(SyncPath))
{
FsFiles = Directory.EnumerateFiles(SyncPath);
foreach (string FsFileName in FsFiles)
{
DateTime FsLastModified = File.GetLastWriteTimeUtc(FsFileName);
string CharacterFileName = Path.GetFileName(FsFileName);
string Key = CharacterFileName.ToLowerInvariant();
FileStoreFile StoreFile;
string TempFileName = null;
if (!StoreFiles.TryGetValue(Key, out StoreFile))
{
//
// This file exists locally but not in the file
// store vault. Keep it (any excess files may be
// removed by explicit local vault cleanup).
//
continue;
}
//
// Transfer the file if the file store vault has a more
// recent version.
//
try
{
TempFileName = Path.GetTempFileName();
try
{
using (FileStream FsFile = File.Open(TempFileName, FileMode.Create))
{
StoreFile.ReadIfModifiedSince(FsFile, new DateTimeOffset(FsLastModified));
}
try
{
File.Copy(TempFileName, FsFileName, true);
File.SetLastWriteTimeUtc(FsFileName, StoreFile.LastModified.Value.DateTime);
}
catch
{
//
// Clean up after a failed attempt to
// instantiate copy file.
//
ALFA.SystemInfo.SafeDeleteFile(FsFileName);
throw;
}
if (VerboseLoggingEnabled)
{
Logger.Log("ServerVaultConnector.OnSynchronizeAccountFromVault: Downloaded vault file '{0}\\{1}' with modified date {2} -> {3}.",
AccountName,
CharacterFileName,
FsLastModified,
File.GetLastWriteTimeUtc(FsFileName));
}
}
catch (FileStoreConditionNotMetException)
{
//
// This file was not transferred because it is
// already up to date.
//
Logger.Log("ServerVaultConnector.OnSynchronizeAccountFromVault: Vault file '{0}\\{1}' was already up to date with modified date {2}.",
AccountName,
CharacterFileName,
FsLastModified);
}
}
finally
{
if (!String.IsNullOrEmpty(TempFileName))
ALFA.SystemInfo.SafeDeleteFile(TempFileName);
}
//
// Remove this file from the list as it has already
// been accounted for (it is up to date or has just
// been transferred).
StoreFiles.Remove(Key);
}
}
//
// Sweep any files that were still not yet processed but
// existed in the file store vault. These files are
// present on the canonical vault but have not yet been
// populated on the local vault, so transfer them now.
//
foreach (var StoreFile in StoreFiles.Values)
{
string[] Segments = StoreFile.Uri.Segments;
string CharacterFileName = Segments[Segments.Length - 1].ToLowerInvariant();
string FsFileName = SyncPath + Path.DirectorySeparatorChar + CharacterFileName;
string TempFileName = null;
if (!ALFA.SystemInfo.IsSafeFileName(CharacterFileName))
throw new ApplicationException("Unsafe filename '" + CharacterFileName + "' on vault for account '" + AccountName + "'.");
if (!DirCreated)
{
DirCreated = Directory.Exists(SyncPath);
//
// Create the sync directory if it does not exist.
// Attempt to preserve case from the vault store if
// possible, but fall back to using the case that
// the client specified at login time otherwise.
//
if (!DirCreated)
{
try
{
string OriginalName;
StoreFile.FetchAttributes();
OriginalName = StoreFile.Metadata["OriginalFileName"];
if (OriginalName != null && OriginalName.ToLowerInvariant() == AccountName + "/" + CharacterFileName)
{
OriginalName = OriginalName.Split('/').FirstOrDefault();
DirectoryInfo Parent = Directory.GetParent(SyncPath);
Directory.CreateDirectory(Parent.FullName + "\\" + OriginalName);
if (VerboseLoggingEnabled)
{
Logger.Log("ServerVaultConnector.OnSynchronizeAccountFromVault: Created vault directory for account '{0}'.", OriginalName);
}
DirCreated = true;
}
}
catch (Exception e)
{
Logger.Log("ServerVaultConnector.OnSynchronizeAccountFromVault: Exception {0} recovering canonical case for creating vault directory '{1}', using account name from client instead.",
e,
OriginalAccountName);
}
if (!DirCreated)
{
Directory.CreateDirectory(SyncPath);
DirCreated = true;
}
}
}
try
{
TempFileName = Path.GetTempFileName();
using (FileStream FsFile = File.Open(TempFileName, FileMode.OpenOrCreate))
{
StoreFile.Read(FsFile);
}
try
{
File.Copy(TempFileName, FsFileName);
File.SetLastWriteTimeUtc(FsFileName, StoreFile.LastModified.Value.DateTime);
}
catch
{
//
// Clean up after a failed attempt to
// instantiate a new file.
//
ALFA.SystemInfo.SafeDeleteFile(FsFileName);
throw;
}
if (VerboseLoggingEnabled)
{
Logger.Log("ServerVaultConnector.OnSynchronizeAccountFromVault: Downloaded new vault file '{0}\\{1}' with modified date {2}.",
AccountName,
CharacterFileName,
File.GetLastWriteTimeUtc(FsFileName));
}
}
finally
{
ALFA.SystemInfo.SafeDeleteFile(TempFileName);
}
}
return 1;
}
catch (Exception e)
{
Logger.Log("ServerVaultConnector.OnSynchronizeAccountFromVault('{0}', '{1}'): Exception: {2}",
AccountName,
SyncPath,
e);
throw;
}
}
catch
{
return 0;
}
}