Ejemplo n.º 1
0
        /// <summary>
        /// Synchronize a single character file from the spool directory to the
        /// remote storage provider.  The contents of the file to transfer are
        /// already in memory.
        ///
        /// This function is called from the transfer synchronization thread.
        /// </summary>
        /// <param name="AccountName">Supplies the account name.</param>
        /// <param name="CharacterFileName">Supplies the character file name,
        /// without path (e.g. character.bic).</param>
        /// <param name="Data">Supplies a pointer to the BIC memory
        /// image.</param>
        /// <param name="Length">Supplies the length, in bytes, of the BIC to
        /// transfer.</param>
        /// <param name="Context">Supplies a context handle.</param>
        /// <returns>TRUE on success.</returns>
        private static int OnSynchronizeAccountFileToVault(string AccountName, string CharacterFile, IntPtr Data, IntPtr Length, 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);
                }

                try
                {
                    if (Length == IntPtr.Zero)
                    {
                        return(0);
                    }

                    //
                    // 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.).
                    //
                    // The original filename is stored as metadata to keep local
                    // filesystems case preserving on servers.
                    //

                    string OriginalFileName = AccountName + "/" + CharacterFile;

                    AccountName   = AccountName.ToLowerInvariant();
                    CharacterFile = CharacterFile.ToLowerInvariant();

                    //
                    // Get the file store file for the character file and
                    // replace it with the new character file.
                    //

                    FileStoreDirectory StoreDirectory = Container.GetDirectoryReference(AccountName);
                    FileStoreFile      StoreFile      = StoreDirectory.GetFileReference(CharacterFile);

                    byte[] CharacterData = new byte[(int)Length];
                    Marshal.Copy(Data, CharacterData, 0, CharacterData.Length);

                    StoreFile.Metadata["OriginalFileName"] = OriginalFileName;
                    StoreFile.Write(new MemoryStream(CharacterData));

                    if (VerboseLoggingEnabled)
                    {
                        Logger.Log("ServerVaultConnector.OnSynchronizeAccountFileToVault: Uploaded vault file '{0}\\{1}'.",
                                   AccountName,
                                   CharacterFile);
                    }

                    return(1);
                }
                catch (Exception e)
                {
                    Logger.Log("ServerVaultConnector.OnSynchronizeAccountFileToVault('{0}', '{1}'): Exception: {2}",
                               AccountName,
                               CharacterFile,
                               e);

                    throw;
                }
            }
            catch
            {
                return(0);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Synchronize new files from the vault storage provider.  The
        /// SyncPath specifies the path to download changed files from the
        /// vault storage provider to.  It may be populated with existing
        /// files, and only newer files should be transferred from the vault
        /// storage provider.
        ///
        /// This function is called from the transfer synchronization thread.
        /// </summary>
        /// <param name="AccountName">Supplies the account name.</param>
        /// <param name="SyncPath">Supplies the path to synchronize files to
        /// from the vault.  Existing files should be datestamp compared with
        /// the vault before being replaced.  File are downloaded from the
        /// vault, not uploaded, with this routine.</param>
        /// <param name="Context">Supplies a context handle.</param>
        /// <returns>TRUE on success.</returns>
        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);
            }
        }