public ActionResult AddSharedFolder(long invitationId) { Invitation invitation = (from inv in _userManager.Context.Invitations where inv.Id == invitationId select inv).SingleOrDefault(); Folder folder = invitation.Target; long userId = User.Identity.GetUserId(); var internalClient = _fileManager.GetInternalClient(userId); Folder root = _fileManager.GetUserRootFolder(userId); string destinationDisplayName = folder.DisplayName; if (!EnsureAvailableName(ref destinationDisplayName, root, false)) { // ??? unsuccessful return null; } ClientSyncData data = new ClientSyncData(); data.ClientId = internalClient.Id; data.BaseChangelistId = _fileManager.GetLastChangelistId(); data.Changes.Add(new ClientChange { FullName = "/" + userId + "/" + destinationDisplayName, Type = ChangeType.Add, IsFolder = true, DisplayName = destinationDisplayName, InvitationId = invitation.Id }); _fileManager.SyncClientChanges(data); AddSharedFolderOutput output = new AddSharedFolderOutput(); output.FolderDisplayName= destinationDisplayName; return Json(output, JsonRequestBehavior.AllowGet); }
public ActionResult Delete(string fromPath, string fileDisplayName) { long userId = User.Identity.GetUserId(); string dummy; string syncFullName; string fromPathString = fromPath == null ? "" : fromPath + "/"; File file = _fileManager.FindFile(fromPathString + fileDisplayName, _fileManager.GetUserRootFolder(userId), out dummy, out syncFullName); if (file == null || file.State != ObjectState.Normal) return RedirectToAction("Browse"); Invitation invitation = null; if (file is Folder && ((Folder)file).InvitationId != null) invitation = ((Folder)file).Invitation; var internalClient = _fileManager.GetInternalClient(userId); ClientSyncData data = new ClientSyncData(); data.ClientId = internalClient.Id; data.BaseChangelistId = _fileManager.GetLastChangelistId(); data.Changes.Add(new ClientChange { FullName = syncFullName, Type = ChangeType.Delete }); if (_fileManager.SyncClientChanges(data).State == ClientSyncResultState.Success) { // Delete any linked invitation. if (invitation != null) { _fileManager.Context.Invitations.Remove(invitation); _fileManager.Context.SaveChanges(); } } return RedirectToAction("Browse", new { path = fromPath }); }
public ActionResult RevertVersion(string fullName, long versionId) { long userId = User.Identity.GetUserId(); DocumentVersion version = _fileManager.FindDocumentVersion(versionId); if (version == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } string dummy; string syncFullName; File file = _fileManager.FindFile(fullName, _fileManager.GetUserRootFolder(userId), out dummy, out syncFullName); if (file == null || !(file is Document)) return RedirectToAction("Browse"); var internalClient = _fileManager.GetInternalClient(userId); ClientSyncData data = new ClientSyncData(); data.ClientId = internalClient.Id; data.BaseChangelistId = _fileManager.GetLastChangelistId(); data.Changes.Add(new ClientChange { FullName = syncFullName, Type = ChangeType.Add, Hash = version.Blob.Hash, Size = version.Blob.Size }); _fileManager.SyncClientChanges(data); return RedirectToAction("Versions", new { fullName = fullName }); }
public ActionResult Upload(string fromPath, HttpPostedFileBase uploadFile) { long userId = User.Identity.GetUserId(); var internalClient = _fileManager.GetInternalClient(userId); string dummy; string syncFullName; File file = _fileManager.FindFile(fromPath ?? "", _fileManager.GetUserRootFolder(userId), out dummy, out syncFullName, followEndInvitation: true); if (file == null || !(file is Folder) || uploadFile == null || !Utilities.ValidateFileName(uploadFile.FileName)) return RedirectToAction("Browse"); Folder folder = (Folder)file; string destinationDisplayName = uploadFile.FileName; if (!EnsureAvailableName(ref destinationDisplayName, folder, true, true)) return RedirectToAction("Browse"); try { string hash; long fileSize; ClientController.UploadBlob(_fileManager, internalClient, uploadFile.InputStream, out hash, out fileSize); ClientSyncData data = new ClientSyncData(); data.ClientId = internalClient.Id; data.BaseChangelistId = _fileManager.GetLastChangelistId(); data.Changes.Add(new ClientChange { FullName = syncFullName + "/" + destinationDisplayName, Type = ChangeType.Add, IsFolder = false, Size = fileSize, Hash = hash, DisplayName = destinationDisplayName }); _fileManager.SyncClientChanges(data); } finally { _fileManager.CleanClientUploadDirectory(internalClient.Id); } return RedirectToAction("Browse", new { path = fromPath }); }
public ActionResult Rename(string fromPath, string oldFileDisplayName, string newFileDisplayName) { long userId = User.Identity.GetUserId(); var internalClient = _fileManager.GetInternalClient(userId); string dummy; string parentFolderSyncFullName; File parentFolderFile = _fileManager.FindFile(fromPath ?? "", _fileManager.GetUserRootFolder(userId), out dummy, out parentFolderSyncFullName, followEndInvitation: true); if (!Utilities.ValidateFileName(newFileDisplayName)) return RedirectToAction("Browse", new { path = fromPath }); if (parentFolderFile == null || !(parentFolderFile is Folder) || oldFileDisplayName == newFileDisplayName) return RedirectToAction("Browse", new { path = fromPath }); Folder parentFolder = (Folder)parentFolderFile; // Check if the old file exists. string oldFileName = oldFileDisplayName.ToUpperInvariant(); File existingFile = parentFolder.Files.AsQueryable().Where(f => f.Name == oldFileName).SingleOrDefault(); if (existingFile == null || existingFile.State != ObjectState.Normal) return RedirectToAction("Browse", new { path = fromPath }); ClientSyncData data = new ClientSyncData(); data.ClientId = internalClient.Id; data.BaseChangelistId = _fileManager.GetLastChangelistId(); // Check if the user is just trying to change the case. if (string.Equals(oldFileDisplayName, newFileDisplayName, StringComparison.InvariantCultureIgnoreCase)) { data.Changes.Add(new ClientChange { FullName = parentFolderSyncFullName + "/" + oldFileDisplayName, Type = ChangeType.SetDisplayName, DisplayName = newFileDisplayName }); } else { string destinationDisplayName = newFileDisplayName; if (!EnsureAvailableName(ref destinationDisplayName, parentFolder, !(existingFile is Folder))) return RedirectToAction("Browse", new { path = fromPath }); data.Changes.Add(new ClientChange { FullName = parentFolderSyncFullName + "/" + oldFileDisplayName, Type = ChangeType.Delete }); AddFileRecursive(data.Changes, existingFile, parentFolderSyncFullName + "/" + destinationDisplayName, destinationDisplayName); } _fileManager.SyncClientChanges(data); return RedirectToAction("Browse", new { path = fromPath }); }
public ActionResult NewFolder(string fromPath, string newFolderName) { long userId = User.Identity.GetUserId(); var internalClient = _fileManager.GetInternalClient(userId); string dummy; string syncFullName; File file = _fileManager.FindFile(fromPath ?? "", _fileManager.GetUserRootFolder(userId), out dummy, out syncFullName, followEndInvitation: true); if (file == null || !(file is Folder) || !Utilities.ValidateFileName(newFolderName)) return RedirectToAction("Browse"); Folder folder = (Folder)file; string destinationDisplayName = newFolderName; if (!EnsureAvailableName(ref destinationDisplayName, folder, false)) return RedirectToAction("Browse"); ClientSyncData data = new ClientSyncData(); data.ClientId = internalClient.Id; data.BaseChangelistId = _fileManager.GetLastChangelistId(); data.Changes.Add(new ClientChange { FullName = syncFullName + "/" + destinationDisplayName, Type = ChangeType.Add, IsFolder = true, DisplayName = destinationDisplayName }); _fileManager.SyncClientChanges(data); return RedirectToAction("Browse", new { path = fromPath }); }
public ClientSyncResult SyncClientChanges(ClientSyncData clientData) { try { var client = FindClient(clientData.ClientId); if (clientData.BaseChangelistId == 0) { long changelistId = GetLastChangelistId(); return new ClientSyncResult { State = ClientSyncResultState.Success, LastChangelistId = changelistId, Changes = GetChangesForFolder(client.User.RootFolder), NewChangelistId = changelistId }; } var translatedChanges = TranslateClientChangesIn(clientData.Changes, client); var clientNode = ChangeNode.FromItems(translatedChanges); var clientChangesByFullName = translatedChanges.ToDictionary(change => Utilities.NormalizeFullName(change.FullName).ToUpperInvariant()); // Verify that the names are valid and the display names match the names. // Also create missing entries in clientChangesByFullName. foreach (var node in clientNode.RecursiveEnumerate()) { if (!string.IsNullOrEmpty(node.Name) && !Utilities.ValidateFileName(node.Name)) throw new Exception("Name '" + node.Name + "' is invalid"); if (node.Type == ChangeType.Add || node.Type == ChangeType.SetDisplayName || node.Type == ChangeType.Undelete) { if (clientChangesByFullName.ContainsKey(node.FullName)) { string displayName = clientChangesByFullName[node.FullName].DisplayName; if (!string.IsNullOrEmpty(displayName) && displayName.ToUpperInvariant() != node.Name) throw new Exception("Invalid display name '" + displayName + "' for '" + node.FullName + "'"); } else { // This node must be a folder with no display name specified. // Create a dummy entry. clientChangesByFullName[node.FullName] = new ClientChange { DisplayName = null }; } } } // Construct the list of changes that have occurred from the client's base changelist ID up to now. var intermediateChanges = from changelist in _context.Changelists where changelist.Id > clientData.BaseChangelistId join change in _context.Changes on changelist.Id equals change.ChangelistId into changes orderby changelist.Id select new { ChangelistId = changelist.Id, Changes = changes }; var intermediateNodes = from x in intermediateChanges.AsEnumerable() select new { ChangelistId = x.ChangelistId, Nodes = ChangeNode.FromItems(from change in x.Changes select new ChangeItem { FullName = change.FullName, Type = change.Type, IsFolder = change.IsFolder }) }; var mergedNode = ChangeNode.CreateRoot(); bool nextChangelistFound = false; long lastChangelistId = clientData.BaseChangelistId; foreach (var changelist in intermediateNodes) { mergedNode.SequentialMerge(changelist.Nodes); lastChangelistId = changelist.ChangelistId; if (changelist.ChangelistId == clientData.BaseChangelistId + 1) nextChangelistFound = true; } if (lastChangelistId != clientData.BaseChangelistId && !nextChangelistFound) return new ClientSyncResult { State = ClientSyncResultState.TooOld }; if (translatedChanges.Count == 0) { return new ClientSyncResult { State = ClientSyncResultState.Success, LastChangelistId = lastChangelistId, Changes = GetChangesForNode(mergedNode, client), NewChangelistId = lastChangelistId }; } // Check if the client's changes conflict with ours. if (mergedNode.PreservingConflicts(clientNode)) { return new ClientSyncResult { State = ClientSyncResultState.Conflict, LastChangelistId = lastChangelistId, Changes = GetChangesForNode(mergedNode, client), UploadRequiredFor = new HashSet<string>() }; } // The client's changes don't conflict, so turn it into a sequential changelist. mergedNode.MakeSequentialByPreserving(clientNode); // Check for all required data. var uploadDirectory = AccessClientUploadDirectory(clientData.ClientId); var presentHashes = new Dictionary<string, Blob>(); var missingHashes = new HashSet<string>(); var missingBlobHashes = new Dictionary<string, ClientChange>(); foreach (var addNode in clientNode.RecursiveEnumerate()) { if (addNode.Type != ChangeType.Add || addNode.IsFolder) continue; var clientChange = clientChangesByFullName[addNode.FullName]; var hash = clientChange.Hash.ToUpperInvariant(); if (presentHashes.ContainsKey(hash) || missingHashes.Contains(hash)) continue; var blobForHash = (from blob in _context.Blobs where blob.Hash == hash select blob).FirstOrDefault(); if (blobForHash != null || System.IO.File.Exists(uploadDirectory.FullName + "\\" + hash)) { presentHashes.Add(hash, blobForHash); if (blobForHash == null) missingBlobHashes.Add(hash, clientChange); } else { missingHashes.Add(hash); } } if (missingHashes.Count != 0) { return new ClientSyncResult { State = ClientSyncResultState.UploadRequired, LastChangelistId = lastChangelistId, Changes = GetChangesForNode(mergedNode, client), UploadRequiredFor = missingHashes }; } // Create blobs for hashes with no associated blobs. foreach (var hash in missingBlobHashes.Keys) { var clientChange = missingBlobHashes[hash]; presentHashes[hash] = CreateBlob(clientChange.Size, hash, uploadDirectory.FullName + "\\" + hash); } if (missingBlobHashes.Count != 0) _context.SaveChanges(); // Apply the changes to the database. using (var transaction = _context.Database.BeginTransaction(System.Data.IsolationLevel.Serializable)) { // Lock the tables. _context.Database.LockTableExclusive("dbo.Changelists"); _context.Database.LockTableExclusive("dbo.Files"); // Make sure no one changed anything since we started computing changes. var moreChangelists = (from changelist in _context.Changelists where changelist.Id > lastChangelistId select changelist.Id).Any(); if (moreChangelists) return new ClientSyncResult { State = ClientSyncResultState.Retry }; var newChangelist = ApplyClientChanges(client, clientNode, clientChangesByFullName, presentHashes); _context.SaveChanges(); transaction.Commit(); return new ClientSyncResult { State = ClientSyncResultState.Success, LastChangelistId = lastChangelistId, Changes = GetChangesForNode(mergedNode, client), NewChangelistId = newChangelist.Id }; } } catch (Exception ex) { return new ClientSyncResult { State = ClientSyncResultState.Error, ErrorMessage = ex.Message }; } }
private bool DeleteInvitation(Invitation invitation) { // Delete any associated accepted folder. var acceptedFolder = invitation.AcceptedFolders.SingleOrDefault(); if (acceptedFolder != null) { var internalClient = _fileManager.GetInternalClient(acceptedFolder.OwnerId); ClientSyncData data = new ClientSyncData(); data.ClientId = internalClient.Id; data.BaseChangelistId = _fileManager.GetLastChangelistId(); data.Changes.Add(new ClientChange { FullName = _fileManager.GetFullName(acceptedFolder), Type = ChangeType.Delete }); if (_fileManager.SyncClientChanges(data).State != ClientSyncResultState.Success) return false; } // Delete the invitation. _fileManager.Context.Invitations.Remove(invitation); _fileManager.Context.SaveChanges(); return true; }
public static ClientSyncResult Sync(ClientSyncData data) { HttpWebRequest req = WebRequest.Create(MakeUrl("Sync")) as HttpWebRequest; var serializer = new JavaScriptSerializer(); var serial = serializer.Serialize(new ClientSyncPostData { Id = Properties.Settings.Default.ID, Secret = Properties.Settings.Default.Secret, Data = data }); byte[] dataBytes = UTF8Encoding.UTF8.GetBytes(serial); req.KeepAlive = true; req.Method = "POST"; req.ContentLength = dataBytes.Length; req.ContentType = "application/x-www-form-urlencoded"; using (Stream postStream = req.GetRequestStream()) postStream.Write(dataBytes, 0, dataBytes.Length); using (var response = req.GetResponse()) using (var reader = new System.IO.StreamReader(response.GetResponseStream(), Encoding.UTF8)) { return serializer.Deserialize<ClientSyncResult>(reader.ReadToEnd()); } }