CmisSync.Lib.Sync.CmisRepo.SynchronizedFolder.DownloadFile C# (CSharp) Method

DownloadFile() private method

Download a single file from the CMIS server. Algorithm: Skip if invalid filename If directory exists with same name, delete it If temporary file already exists but database has a different modification date than server, delete it Download data and metadata, return if that fails If a file with this name already exists locally If conflict Rename the existing file and put the server file instead Notify the user If file update Replace the file Else (new file) Save Set creation date and last modification date if available Make read-only if remote can not be modified Create CmisSync database entry for this file
private DownloadFile ( IDocument remoteDocument, string remotePath, string localFolder ) : bool
remoteDocument IDocument
remotePath string
localFolder string
return bool
            private bool DownloadFile(IDocument remoteDocument, string remotePath, string localFolder)
            {
                SleepWhileSuspended();

                var syncItem = database.GetSyncItemFromRemotePath(remotePath);
                if (null == syncItem)
                {
                    syncItem = SyncItemFactory.CreateFromRemoteDocument(remotePath, remoteDocument, repoInfo, database);
                }

                Logger.Info("Downloading: " + syncItem.LocalLeafname);

                // Skip if invalid file name. See https://github.com/aegif/CmisSync/issues/196
                if (Utils.IsInvalidFileName(syncItem.LocalLeafname)) 
                {
                    Logger.Info("Skipping download of file with illegal filename: " + syncItem.LocalLeafname);
                    return true;
                }

                try
                {
                    DotCMIS.Data.IContentStream contentStream = null;
                    string filePath = syncItem.LocalPath;
                    string tmpFilePath = filePath + ".sync";
                    if (database.GetOperationRetryCounter(filePath, Database.Database.OperationType.DOWNLOAD) > repoInfo.MaxDownloadRetries)
                    {
                        Logger.Info(String.Format("Skipping download of file {0} because of too many failed ({1}) downloads", database.GetOperationRetryCounter(filePath, Database.Database.OperationType.DOWNLOAD)));
                        return true;
                    }

                    // If there was previously a directory with this name, delete it.
                    // TODO warn if local changes inside the folder.
                    if (Directory.Exists(filePath))
                    {
                        Utils.DeleteEvenIfReadOnly(filePath);
                    }

                    if (File.Exists(tmpFilePath))
                    {
                        DateTime? remoteDate = remoteDocument.LastModificationDate;
                        if (null == remoteDate)
                        {
                            Utils.DeleteEvenIfReadOnly(tmpFilePath);
                        }
                        else
                        {
                            remoteDate = ((DateTime)remoteDate).ToUniversalTime();
                            DateTime? serverDate = database.GetDownloadServerSideModificationDate(syncItem);
                            if (remoteDate != serverDate)
                            {
                                Utils.DeleteEvenIfReadOnly(tmpFilePath);
                            }
                        }
                    }

                    // Download file.
                    Boolean success = false;
                    byte[] filehash = { };
                    try
                    {
                        contentStream = remoteDocument.GetContentStream();

                        // If this file does not have a content stream, ignore it.
                        // Even 0 bytes files have a contentStream.
                        // null contentStream sometimes happen on IBM P8 CMIS server, not sure why.
                        if (contentStream == null)
                        {
                            Logger.Warn("Skipping download of file with null content stream: " + syncItem.RemoteLeafname);
                            return true;
                        }
                        // Skip downloading the content, just go on with an empty file
                        if (remoteDocument.ContentStreamLength == 0)
                        {
                            Logger.Info("Skipping download of file with content length zero: " + syncItem.RemoteLeafname);
                            using (FileStream s = File.Create(tmpFilePath))
                            {
                                s.Close();
                            }
                        }
                        else
                        {
                            filehash = DownloadStream(contentStream, tmpFilePath);
                            contentStream.Stream.Close();
                        }
                        success = true;
                    }
                    catch (CmisBaseException e)
                    {
                        ProcessRecoverableException("Download failed: " + syncItem.RemoteLeafname, e);
                        if (contentStream != null) contentStream.Stream.Close();
                        success = false;
                        File.Delete(tmpFilePath);
                    }

                    if ( ! success)
                    {
                        return false;
                    }

                    Logger.Info(String.Format("Downloaded remote object({0}): {1}", remoteDocument.Id, syncItem.RemoteLeafname));

                    // TODO Control file integrity by using hash compare?

                    // Get metadata.
                    Dictionary<string, string[]> metadata = null;
                    try
                    {
                        metadata = FetchMetadata(remoteDocument);
                    }
                    catch (CmisBaseException e)
                    {
                        ProcessRecoverableException("Could not fetch metadata: " + syncItem.RemoteLeafname, e);
                        // Remove temporary local document to avoid it being considered a new document.
                        File.Delete(tmpFilePath);
                        return false;
                    }

                    // Either it is an update; or a file with the same name has been created at the same time locally, resulting in a conflict.
                    if (File.Exists(filePath))
                    {
                        if (database.LocalFileHasChanged(filePath)) // Conflict. Server-side file and local file both modified.
                        {
                            Logger.Info(String.Format("Conflict with file: {0}", syncItem.RemoteLeafname));

                            // Rename local file with a conflict suffix.
                            string conflictFilename = Utils.CreateConflictFilename(filePath, repoInfo.User);
                            Logger.Debug(String.Format("Renaming conflicted local file {0} to {1}", filePath, conflictFilename));
                            File.Move(filePath, conflictFilename);

                            // Remove the ".sync" suffix.
                            Logger.Debug(String.Format("Renaming temporary local download file {0} to {1}", tmpFilePath, filePath));
                            File.Move(tmpFilePath, filePath);
                            SetLastModifiedDate(remoteDocument, filePath, metadata);

                            // Warn user about conflict.
                            string lastModifiedBy = CmisUtils.GetProperty(remoteDocument, "cmis:lastModifiedBy");
                            string message =
                                String.Format("User {0} added a file named {1} at the same time as you.", lastModifiedBy, filePath)
                                + "\n\n"
                                + "Your version has been renamed '" + conflictFilename + "', please merge your important changes from it and then delete it.";
                            Logger.Info(message);
                            Utils.NotifyUser(message);
                        }
                        else // Server side file was modified, but local file was not modified. Just need to update the file.
                        {
                            Logger.Debug(String.Format("Deleting old local file {0}", filePath));
                            Utils.DeleteEvenIfReadOnly(filePath);

                            Logger.Debug(String.Format("Renaming temporary local download file {0} to {1}", tmpFilePath, filePath));
                            // Remove the ".sync" suffix.
                            File.Move(tmpFilePath, filePath);
                            SetLastModifiedDate(remoteDocument, filePath, metadata);
                        }
                    }
                    else // New file
                    {
                        Logger.Debug(String.Format("Renaming temporary local download file {0} to {1}", tmpFilePath, filePath));
                        // Remove the ".sync" suffix.
                        File.Move(tmpFilePath, filePath);
                        SetLastModifiedDate(remoteDocument, filePath, metadata);
                    }

                    if (null != remoteDocument.CreationDate)
                    {
                        File.SetCreationTime(filePath, (DateTime)remoteDocument.CreationDate);
                    }
                    if (null != remoteDocument.LastModificationDate)
                    {
                        File.SetLastWriteTime(filePath, (DateTime)remoteDocument.LastModificationDate);
                    }

                    // Should the local file be made read-only?
                    // Check ther permissions of the current user to the remote document.
                    bool readOnly = ! remoteDocument.AllowableActions.Actions.Contains(Actions.CanSetContentStream);
                    if (readOnly)
                    {
                        File.SetAttributes(filePath, FileAttributes.ReadOnly);
                    }

                    // Create database entry for this file.
                    database.AddFile(syncItem, remoteDocument.Id, remoteDocument.LastModificationDate, metadata, filehash);
                    Logger.Info("Added file to database: " + filePath);

                    return success;
                }
                catch (Exception e)
                {
                    ProcessRecoverableException("Could not download file: " + syncItem.LocalPath, e);
                    return false;
                }
            }