示例#1
0
        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);
        }
示例#2
0
        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 });
        }
示例#3
0
        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 });
        }
示例#4
0
        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 });
        }
示例#5
0
        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 });
        }
示例#6
0
        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 });
        }
示例#7
0
        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
                };
            }
        }
示例#8
0
        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;
        }
示例#9
0
        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());
            }
        }