public virtual void UploadNotes(IList <Note> notes) { if (Directory.Exists(newRevisionPath) == false) { DirectoryInfo info = Directory.CreateDirectory(newRevisionPath); AdjustPermissions(info.Parent.FullName); AdjustPermissions(newRevisionPath); } Logger.Debug("UploadNotes: notes.Count = {0}", notes.Count); foreach (Note note in notes) { try { string serverNotePath = Path.Combine(newRevisionPath, Path.GetFileName(note.FilePath)); SecurityWrapper.CopyAndEncrypt(note.FilePath, serverNotePath, myKey); //File.Copy(note.FilePath, serverNotePath, true); // upload to webdav takes place in commit-function AdjustPermissions(serverNotePath); updatedNotes.Add(Path.GetFileNameWithoutExtension(note.FilePath)); } catch (Exception e) { Logger.Error("Sync: Error uploading note \"{0}\": {1}", note.Title, e.Message); } } }
public IList <string> GetAllNoteUUIDs() { List <string> noteUUIDs = new List <string>(); if (IsValidXmlFile(manifestPath)) { // TODO: Permission errors using (FileStream fs = new FileStream(manifestPath, FileMode.Open)) { bool ok; Stream plainStream = SecurityWrapper.DecryptFromStream(manifestPath, fs, myKey, out ok); if (!ok) { throw new EncryptionException("ENCRYPTION ERROR"); } XmlDocument doc = new XmlDocument(); doc.Load(plainStream); XmlNodeList noteIds = doc.SelectNodes("//note/@id"); Logger.Debug("GetAllNoteUUIDs has {0} notes", noteIds.Count); foreach (XmlNode idNode in noteIds) { noteUUIDs.Add(idNode.InnerText); } } } return(noteUUIDs); }
/// <summary> /// Check that xmlFilePath points to an existing valid XML file. /// This is done by ensuring that an XmlDocument can be created from /// its contents. ///</summary> private bool IsValidXmlFile(string xmlFilePath) { // Check that file exists if (!File.Exists(xmlFilePath)) { return(false); } // TODO: Permissions errors // Attempt to load the file and parse it as XML try { using (FileStream fs = new FileStream(xmlFilePath, FileMode.Open)) { bool ok; Stream plainStream = SecurityWrapper.DecryptFromStream(xmlFilePath, fs, myKey, out ok); if (!ok) { throw new EncryptionException("ENCRYPTION ERROR!"); } XmlDocument doc = new XmlDocument(); // TODO: Make this be a validating XML reader. Not sure if it's validating yet. doc.Load(plainStream); } } catch (PasswordException) { throw; } catch (EncryptionException ee) { throw new Exception("there was a problem with the file encryption, can't sync!", ee); } catch (Exception e) { Logger.Debug("Exception while validating lock file: " + e.ToString()); return(false); } return(true); }
public virtual bool CommitSyncTransaction() { bool commitSucceeded = false; if (updatedNotes.Count > 0 || deletedNotes.Count > 0) { // TODO: error-checking, etc string manifestFilePath = Path.Combine(newRevisionPath, "manifest.xml"); if (!Directory.Exists(newRevisionPath)) { DirectoryInfo info = Directory.CreateDirectory(newRevisionPath); AdjustPermissions(info.Parent.FullName); AdjustPermissions(newRevisionPath); } XmlNodeList noteNodes = null; if (IsValidXmlFile(manifestPath) == true) { using (FileStream fs = new FileStream(manifestPath, FileMode.Open)) { bool ok; Stream plainStream = SecurityWrapper.DecryptFromStream(manifestPath, fs, myKey, out ok); if (!ok) { throw new Exception("ENCRYPTION ERROR!"); } XmlDocument doc = new XmlDocument(); doc.Load(plainStream); noteNodes = doc.SelectNodes("//note"); } } else { using (StringReader sr = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<sync>\n</sync>")) { XmlDocument doc = new XmlDocument(); doc.Load(sr); noteNodes = doc.SelectNodes("//note"); } } #region createManifestFile // Write out the new manifest file MemoryStream plainBuf = new MemoryStream(); XmlWriter xml = XmlWriter.Create(plainBuf, XmlEncoder.DocumentSettings); try { xml.WriteStartDocument(); xml.WriteStartElement(null, "sync", null); xml.WriteAttributeString("revision", newRevision.ToString()); xml.WriteAttributeString("server-id", serverId); foreach (XmlNode node in noteNodes) { string id = node.SelectSingleNode("@id").InnerText; string rev = node.SelectSingleNode("@rev").InnerText; // Don't write out deleted notes if (deletedNotes.Contains(id)) { continue; } // Skip updated notes, we'll update them in a sec if (updatedNotes.Contains(id)) { continue; } xml.WriteStartElement(null, "note", null); xml.WriteAttributeString("id", id); xml.WriteAttributeString("rev", rev); xml.WriteEndElement(); } // Write out all the updated notes foreach (string uuid in updatedNotes) { xml.WriteStartElement(null, "note", null); xml.WriteAttributeString("id", uuid); xml.WriteAttributeString("rev", newRevision.ToString()); xml.WriteEndElement(); } xml.WriteEndElement(); xml.WriteEndDocument(); } finally { xml.Close(); // now store in encrypted version: SecurityWrapper.SaveAsEncryptedFile(manifestFilePath, plainBuf.ToArray(), myKey); // dispose of plain data plainBuf.Dispose(); } AdjustPermissions(manifestFilePath); #endregion // only use this if we use the revision-folder-mode if (!manifestFilePath.Equals(manifestPath)) { #region DIR_VERSION // Rename original /manifest.xml to /manifest.xml.old string oldManifestPath = manifestPath + ".old"; if (File.Exists(manifestPath) == true) { if (File.Exists(oldManifestPath)) { File.Delete(oldManifestPath); } File.Move(manifestPath, oldManifestPath); } // * * * Begin Cleanup Code * * * // TODO: Consider completely discarding cleanup code, in favor // of periodic thorough server consistency checks (say every 30 revs). // Even if we do continue providing some cleanup, consistency // checks should be implemented. // Copy the /${parent}/${rev}/manifest.xml -> /manifest.xml // don't encrypt here because file is already encrypted! //SecurityWrapper.CopyAndEncrypt(manifestFilePath, manifestPath, myKey); File.Copy(manifestFilePath, manifestPath, true); AdjustPermissions(manifestPath); try { // Delete /manifest.xml.old if (File.Exists(oldManifestPath)) { File.Delete(oldManifestPath); } string oldManifestFilePath = Path.Combine(GetRevisionDirPath(newRevision - 1), "manifest.xml"); if (File.Exists(oldManifestFilePath)) { // TODO: Do step #8 as described in http://bugzilla.gnome.org/show_bug.cgi?id=321037#c17 // Like this? FileInfo oldManifestFilePathInfo = new FileInfo(oldManifestFilePath); foreach (FileInfo file in oldManifestFilePathInfo.Directory.GetFiles()) { string fileGuid = Path.GetFileNameWithoutExtension(file.Name); if (deletedNotes.Contains(fileGuid) || updatedNotes.Contains(fileGuid)) { File.Delete(file.FullName); } // TODO: Need to check *all* revision dirs, not just previous (duh) // Should be a way to cache this from checking earlier. } // TODO: Leaving old empty dir for now. Some stuff is probably easier // when you can guarantee the existence of each intermediate directory? } } catch (Exception e) { Logger.Error("Exception during server cleanup while committing. " + "Server integrity is OK, but there may be some excess " + "files floating around. Here's the error:\n"+ e.Message); } #endregion } else { Logger.Info("probably doing sync without revision-hierarchy"); OnManifestFileCreated(manifestFilePath); // this is just a simple cleanup because we only use one directory // delete the notes that were deleted in this one directory: try { FileInfo manifestFilePathInfo = new FileInfo(manifestFilePath); foreach (FileInfo file in manifestFilePathInfo.Directory.GetFiles()) { string fileGuid = Path.GetFileNameWithoutExtension(file.Name); if (deletedNotes.Contains(fileGuid)) { File.Delete(file.FullName); OnDeleteFile(file.FullName); } if (updatedNotes.Contains(fileGuid)) { OnUploadFile(file.FullName); } } } catch (Exception e) { Logger.Error("Exception during server cleanup while committing. " + "Server integrity is OK, but there may be some excess " + "files floating around. Here's the error:\n"+ e.Message); } } } else { // no changes (no updates/deletes) } lockTimeout.Cancel(); RemoveLockFile(lockPath); commitSucceeded = true; // TODO: When return false? return(commitSucceeded); }
public virtual IDictionary <string, NoteUpdate> GetNoteUpdatesSince(int revision) { Dictionary <string, NoteUpdate> noteUpdates = new Dictionary <string, NoteUpdate>(); if (IsValidXmlFile(manifestPath)) { // TODO: Permissions errors using (FileStream fs = new FileStream(manifestPath, FileMode.Open)) { Stream plainStream; { bool ok; plainStream = SecurityWrapper.DecryptFromStream(manifestPath, fs, myKey, out ok); if (!ok) { throw new Exception("ENCRYPTION ERROR!"); } } XmlDocument doc = new XmlDocument(); doc.Load(plainStream); string xpath = string.Format("//note[@rev > {0}]", revision.ToString()); XmlNodeList noteNodes = doc.SelectNodes(xpath); Logger.Debug("GetNoteUpdatesSince xpath returned {0} nodes", noteNodes.Count); foreach (XmlNode node in noteNodes) { string id = node.SelectSingleNode("@id").InnerText; int rev = Int32.Parse(node.SelectSingleNode("@rev").InnerText); if (noteUpdates.ContainsKey(id) == false) { // Copy the file from the server to the temp directory string revDir = GetRevisionDirPath(rev); string serverNotePath = Path.Combine(revDir, id + ".note"); //string noteTempPath = Path.Combine(tempPath, id + ".note"); // DON'T ENCRYPT HERE because we are getting the already encrypted file from the server //SecurityWrapper.CopyAndEncrypt(serverNotePath, noteTempPath, myKey); //File.Copy(serverNotePath, noteTempPath, true); // Get the title, contents, etc. string noteTitle = string.Empty; string noteXml = null; { // decrypt the note: bool ok; CryptoFormat ccf = CryptoFormatProviderFactory.INSTANCE.GetCryptoFormat(); byte[] contents = ccf.DecryptFile(serverNotePath, myKey, out ok); noteXml = Util.FromBytes(contents); // solve nasty BOM problem -__- int index = noteXml.IndexOf('<'); if (index > 0) { noteXml = noteXml.Substring(index, noteXml.Length - index); } } NoteUpdate update = new NoteUpdate(noteXml, noteTitle, id, rev); noteUpdates[id] = update; } } } } Logger.Debug("GetNoteUpdatesSince ({0}) returning: {1}", revision, noteUpdates.Count); return(noteUpdates); }