/// <inheritdoc/> public void LogMessage(string message, string sourcePath = null, string targetPath = null) { string att = FsPath.Exists(sourcePath) ? FsPath.GetAttString(sourcePath) : null; string size = FsPath.Size(sourcePath); Log.Debug($"\n{DateTimeOffset.Now} [{Thread.CurrentThread.ManagedThreadId,2}] {componentName,-26}{message,-45} {sourcePath,-80} {size,7} {att} {targetPath}"); }
/// <summary> /// Returns MS Office lock file path if such file exists. /// </summary> /// <param name="msOfficeFilePath">MS Office file path.</param> /// <returns>Lock file path.</returns> /// <remarks> /// mydoc.docx -> ~$mydoc.docx /// mydocfi.docx -> ~$ydocfi.docx /// mydocfile.docx -> ~$docfile.docx /// mydocfile.pptx -> ~$mydocfile.pptx /// mydocfile.ppt -> ~$mydocfile.ppt /// mydocfile.xlsx -> ~$mydocfile.xlsx /// mydocfile.xls -> null /// </remarks> private static string GetLockPathFromMsOfficePath(string msOfficeFilePath) { string msOfficeLockFilePath = null; int separatorIndex = msOfficeFilePath.LastIndexOf(Path.DirectorySeparatorChar); if ((separatorIndex != -1) && !string.IsNullOrEmpty(Path.GetExtension(msOfficeFilePath))) { msOfficeLockFilePath = msOfficeFilePath.Insert(separatorIndex + 1, "~$"); if (FsPath.Exists(msOfficeLockFilePath)) { return(msOfficeLockFilePath); } int fileNameLength = Path.GetFileNameWithoutExtension(msOfficeFilePath).Length; if (fileNameLength > 6) { int removeChars = fileNameLength == 7 ? 1 : 2; msOfficeLockFilePath = msOfficeLockFilePath.Remove(separatorIndex + 1 + "~$".Length, removeChars); if (FsPath.Exists(msOfficeLockFilePath)) { return(msOfficeLockFilePath); } } } return(null); }
/// <inheritdoc/> public async Task OpenAsync(IOperationContext operationContext, IResultContext context) { Logger.LogMessage("IFile.OpenAsync()", UserFileSystemPath); // Auto-lock the file. string userFileSystemFilePath = UserFileSystemPath; if (Engine.ChangesProcessingEnabled && FsPath.Exists(userFileSystemFilePath)) { if (Config.Settings.AutoLock && !FsPath.AvoidAutoLock(userFileSystemFilePath) && !await Lock.IsLockedAsync(userFileSystemFilePath) && FsPath.IsWriteLocked(userFileSystemFilePath) && !new PlaceholderFile(userFileSystemFilePath).IsNew()) { try { await new RemoteStorageRawItem(userFileSystemFilePath, VirtualDrive, Logger).LockAsync(LockMode.Auto); } catch (ClientLockFailedException ex) { // Lock file is blocked by the concurrent thread. This is a normal behaviour. Logger.LogMessage(ex.Message, userFileSystemFilePath); } } } }
/// <inheritdoc/> public async Task GetChildrenAsync(string pattern, IOperationContext operationContext, IFolderListingResultContext resultContext) { // This method has a 60 sec timeout. // To process longer requests and reset the timout timer call one of the following: // - resultContext.ReturnChildren() method. // - resultContext.ReportProgress() method. Logger.LogMessage($"IFolder.GetChildrenAsync({pattern})", UserFileSystemPath); IUserFolder userFolder = await VirtualDrive.GetItemAsync <IUserFolder>(UserFileSystemPath); IEnumerable <FileSystemItemBasicInfo> children = await userFolder.EnumerateChildrenAsync(pattern); // Filtering existing files/folders. This is only required to avoid extra errors in the log. List <IFileSystemItemBasicInfo> newChildren = new List <IFileSystemItemBasicInfo>(); foreach (FileSystemItemBasicInfo child in children) { string userFileSystemItemPath = Path.Combine(UserFileSystemPath, child.Name); if (!FsPath.Exists(userFileSystemItemPath)) { Logger.LogMessage("Creating", child.Name); // If the file is moved/renamed and the app is not running this will help us // to sync the file/folder to remote storage after app starts. child.CustomData = new CustomData { OriginalPath = userFileSystemItemPath }.Serialize(); newChildren.Add(child); } } // To signal that the children enumeration is completed // always call ReturnChildren(), even if the folder is empty. resultContext.ReturnChildren(newChildren.ToArray(), newChildren.Count()); // Save ETags and set "locked by another user" icon. foreach (FileSystemItemBasicInfo child in children) { string userFileSystemItemPath = Path.Combine(UserFileSystemPath, child.Name); // Create ETags. // ETags must correspond with a server file/folder, NOT with a client placeholder. // It should NOT be moved/deleted/updated when a placeholder in the user file system is moved/deleted/updated. // It should be moved/deleted when a file/folder in the remote storage is moved/deleted. await ETag.SetETagAsync(userFileSystemItemPath, child.ETag); // Set the "locked by another user" icon and all custom columns data. await new UserFileSystemRawItem(userFileSystemItemPath).SetLockedByAnotherUserAsync(child.LockedByAnotherUser); await new UserFileSystemRawItem(userFileSystemItemPath).SetCustomColumnsDataAsync(child.CustomProperties); } }
/// <summary> /// Returns true if the file or folder is marked with Hidden or Temporaty attributes. /// </summary> /// <param name="path">Path to a file or folder.</param> /// <returns> /// True if the file or folder is marked with Hidden or Temporaty attributes. /// Returns false if no Hidden or Temporaty attributes found or file/folder does not exists. /// </returns> private static bool IsHiddenOrTemp(string path) { if (!FsPath.Exists(path)) { return(false); } FileAttributes att = File.GetAttributes(path); return(((att & System.IO.FileAttributes.Hidden) != 0) || ((att & System.IO.FileAttributes.Temporary) != 0)); }
//$<IFolder.CloseAsync /// <inheritdoc/> public async Task CloseAsync(IOperationContext operationContext, IResultContext context) { // Here, if the file in the user file system is modified (not in-sync), you will send the file content, // creation time, modification time and attributes to the remote storage. // We also send ETag, to make sure the changes on the server, if any, are not overwritten. Logger.LogMessage("IFile.CloseAsync()", UserFileSystemPath); string userFileSystemFilePath = UserFileSystemPath; // In case the file is moved it does not exist in user file system when CloseAsync() is called. if (Engine.ChangesProcessingEnabled && FsPath.Exists(userFileSystemFilePath) && !FsPath.AvoidSync(userFileSystemFilePath)) { // In case the file is overwritten it is converted to a regular file prior to CloseAsync(). // we need to convert it back into file/folder placeholder. if (!PlaceholderItem.IsPlaceholder(userFileSystemFilePath)) { PlaceholderItem.ConvertToPlaceholder(userFileSystemFilePath, false); Logger.LogMessage("Converted to placeholder", userFileSystemFilePath); } try { if (PlaceholderItem.GetItem(userFileSystemFilePath).IsNew()) { // Create new file in the remote storage. await RemoteStorageRawItem.CreateAsync(userFileSystemFilePath, VirtualDrive, Logger); } else { // Send content to remote storage. Unlock if auto-locked. await new RemoteStorageRawItem(userFileSystemFilePath, VirtualDrive, Logger).UpdateAsync(); } } catch (IOException ex) { // Either the file is already being synced in another thread or client or server file is blocked by concurrent process. // This is a normal behaviour. // The file must be synched by your synchronyzation service at a later time, when the file becomes available. Logger.LogMessage("Failed to upload file. Possibly in use by an application or blocked for synchronization in another thread:", ex.Message); } } }
//$<IFileSystemItem.MoveToAsync ///<inheritdoc> public async Task MoveToAsync(string userFileSystemNewPath, IOperationContext operationContext, IConfirmationResultContext resultContext) { string userFileSystemOldPath = this.UserFileSystemPath; Logger.LogMessage("IFileSystemItem.MoveToAsync()", userFileSystemOldPath, userFileSystemNewPath); // Process move. if (Engine.ChangesProcessingEnabled) { if (FsPath.Exists(userFileSystemOldPath)) { await new RemoteStorageRawItem(userFileSystemOldPath, VirtualDrive, Logger).MoveToAsync(userFileSystemNewPath, resultContext); } } else { resultContext.ReturnConfirmationResult(); } // Restore Original Path and locked icon, lost during MS Office transactional save. if (FsPath.Exists(userFileSystemNewPath) && PlaceholderItem.IsPlaceholder(userFileSystemNewPath)) { PlaceholderItem userFileSystemNewItem = PlaceholderItem.GetItem(userFileSystemNewPath); if (!userFileSystemNewItem.IsNew() && string.IsNullOrEmpty(userFileSystemNewItem.GetOriginalPath())) { // Restore Original Path. Logger.LogMessage("Saving Original Path", userFileSystemNewPath); userFileSystemNewItem.SetOriginalPath(userFileSystemNewPath); // Restore the 'locked' icon. bool isLocked = await Lock.IsLockedAsync(userFileSystemNewPath); ServerLockInfo existingLock = isLocked ? await(await Lock.LockAsync(userFileSystemNewPath, FileMode.Open, LockMode.None, Logger)).GetLockInfoAsync() : null; await new UserFileSystemRawItem(userFileSystemNewPath).SetLockInfoAsync(existingLock); } } }
/// <inheritdoc/> public void LogError(string message, string sourcePath = null, string targetPath = null, Exception ex = null) { string att = FsPath.Exists(sourcePath) ? FsPath.GetAttString(sourcePath) : null; Log.Error($"\n{DateTimeOffset.Now} [{Thread.CurrentThread.ManagedThreadId,2}] {componentName,-26}{message,-45} {sourcePath,-80} {att} ", ex); }