public override RemoteUploadResult UploadNoteToRemote(ref INote inote, out INote conflict, ConflictResolutionStrategy strategy) { var note = (StandardFileNote)inote; if (_syncResult.saved_notes.Any(n => n.ID == note.ID)) { note.ApplyUpdatedData(_syncResult.saved_notes.First(n => n.ID == note.ID)); conflict = null; return(RemoteUploadResult.Uploaded); } if (_syncResult.retrieved_notes.Any(n => n.ID == note.ID)) { _logger.Warn(StandardNotePlugin.Name, "Uploaded note found in retrieved notes ... upload failed ?"); note.ApplyUpdatedData(_syncResult.retrieved_notes.First(n => n.ID == note.ID)); conflict = null; return(RemoteUploadResult.Merged); } if (_syncResult.error_notes.Any(n => n.ID == note.ID)) { throw new Exception("Could not upload note - server returned note in {unsaved_notes}"); } if (_syncResult.conflict_notes.Any(n => n.ID == note.ID)) { conflict = _syncResult.conflict_notes.First(n => n.ID == note.ID); return(RemoteUploadResult.Conflict); } conflict = null; return(RemoteUploadResult.UpToDate); }
private Tuple <IRemoteStorageSyncPersistance, List <INote> > DoSync(RemoteStorageAccount acc, IAlephLogger log) { var data = SelectedProvider.CreateEmptyRemoteSyncData(); var conn = acc.Plugin.CreateRemoteStorageConnection(PluginManagerSingleton.Inst.GetProxyFactory().Build(), acc.Config, new HierachyEmulationConfig(false, "\\", '\\')); var resultNotes = new List <INote>(); Application.Current.Dispatcher.Invoke(() => { SyncInfoText = "Connect to remote"; }); conn.StartSync(data, new List <INote>(), new List <INote>()); { Application.Current.Dispatcher.Invoke(() => { SyncInfoText = "List notes from remote"; }); var missing = conn.ListMissingNotes(new List <INote>()); int idx = 0; foreach (var xnoteid in missing) { var noteid = xnoteid; idx++; try { string msg = $"Download Note {idx}/{missing.Count}"; Application.Current.Dispatcher.Invoke(() => { SyncInfoText = msg; }); var note = conn.DownloadNote(noteid, out var isnewnote); if (isnewnote) { note.SetLocalDirty(); note.ResetRemoteDirty(); resultNotes.Add(note); } else { log.Warn("Sync_FirstStart", string.Format("Download new note {{id:'{0}'}} returned false", noteid)); } } catch (ThreadAbortException) { throw; } catch (Exception e) { throw new Exception(string.Format("Could not download new note '{0}' on remote cause of {1}", noteid, e.Message)); } } } Application.Current.Dispatcher.Invoke(() => { SyncInfoText = "Finish synchronization"; }); conn.FinishSync(); return(Tuple.Create(data, resultNotes)); }
private static DateTimeOffset ConvertFromEpochDate(double seconds) { const double DTO_MAX = 10.0 * 1000 * 1000 * 1000; if (seconds <= 0) { Logger.Warn(SimpleNotePlugin.Name, "ConvertFromEpochDate with invalid value (<=0)", $"seconds: {seconds}"); return(TIMESTAMP_ORIGIN); } if (double.IsNaN(seconds)) { Logger.Warn(SimpleNotePlugin.Name, "ConvertFromEpochDate with invalid value (IsNaN)", $"seconds: {seconds}"); return(TIMESTAMP_ORIGIN); } if (double.IsInfinity(seconds)) { Logger.Warn(SimpleNotePlugin.Name, "ConvertFromEpochDate with invalid value (IsInfinity)", $"seconds: {seconds}"); return(TIMESTAMP_ORIGIN); } if (seconds > DTO_MAX) { Logger.Warn(SimpleNotePlugin.Name, "ConvertFromEpochDate with invalid value (>Max)", $"seconds: {seconds}"); return(TIMESTAMP_ORIGIN); } return(TIMESTAMP_ORIGIN.AddSeconds(seconds)); }
private void UploadNotes(List <Tuple <INote, INote> > notesToUpload, List <Tuple <INote, INote> > notesToResetRemoteDirty, ref List <Tuple <string, Exception> > errors) { foreach (var notetuple in notesToUpload) { var realnote = notetuple.Item1; var clonenote = notetuple.Item2; _log.Info("Sync", string.Format("Upload note {0}", clonenote.UniqueName)); try { if (!clonenote.IsLocalSaved) { dispatcher.Invoke(() => { if (!realnote.IsLocalSaved) { repo.SaveNote(realnote); } }); } var result = repo.Connection.UploadNoteToRemote(ref clonenote, out var conflictnote, conflictStrategy); switch (result) { case RemoteUploadResult.UpToDate: case RemoteUploadResult.Uploaded: dispatcher.Invoke(() => { if (realnote.IsLocalSaved) { realnote.OnAfterUpload(clonenote); realnote.ResetRemoteDirty(); repo.SaveNote(realnote); } }); break; case RemoteUploadResult.Merged: dispatcher.Invoke(() => { realnote.ApplyUpdatedData(clonenote); realnote.TriggerOnChanged(true); realnote.SetLocalDirty(); realnote.ResetRemoteDirty(); }); break; case RemoteUploadResult.Conflict: _log.Warn("Sync", "Uploading note " + clonenote.UniqueName + " resulted in conflict"); ResolveUploadConflict(realnote, clonenote, conflictnote); break; default: throw new ArgumentOutOfRangeException(); } } catch (Exception e) { var message = string.Format("Could not upload note '{2}' ({0}) cause of {1}", clonenote.UniqueName, e.Message, clonenote.Title); _log.Error("Sync", message, e); errors.Add(Tuple.Create(message, e)); } } foreach (var notetuple in notesToResetRemoteDirty) { var realnote = notetuple.Item1; var clonenote = notetuple.Item2; _log.Info("Sync", string.Format("Reset remote dirty of note {0} (no upload needed)", clonenote.UniqueName)); try { if (!clonenote.IsLocalSaved) { dispatcher.Invoke(() => { if (!realnote.IsLocalSaved) { repo.SaveNote(realnote); } }); } dispatcher.Invoke(() => { if (realnote.IsLocalSaved) { realnote.ResetRemoteDirty(); repo.SaveNote(realnote); } }); } catch (Exception e) { var message = string.Format("Could not reset remote dirty note '{2}' ({0}) cause of {1}", clonenote.UniqueName, e.Message, clonenote.Title); _log.Error("Sync", message, e); errors.Add(Tuple.Create(message, e)); } } }
private static StandardFileNote CreateNote(ISimpleJsonRest web, StandardNoteConnection conn, APIResultItem encNote, APIResultAuthorize authToken, StandardNoteConfig cfg, StandardNoteData dat) { if (encNote.deleted) { var nd = new StandardFileNote(encNote.uuid, cfg, conn.HConfig) { CreationDate = encNote.created_at, Text = "", InternalTitle = "", AuthHash = encNote.auth_hash, ContentVersion = StandardNoteCrypt.GetSchemaVersion(encNote.content), }; nd.ModificationDate = encNote.updated_at; return(nd); } ContentNote content; try { var contentJson = StandardNoteCrypt.DecryptContent(encNote.content, encNote.enc_item_key, encNote.auth_hash, authToken.masterkey, authToken.masterauthkey); Logger.Debug( StandardNotePlugin.Name, $"DecryptContent of note {encNote.uuid:B}", $"[content]:\r\n{encNote.content}\r\n" + $"[enc_item_key]:\r\n{encNote.enc_item_key}\r\n" + $"[auth_hash]:\r\n{encNote.auth_hash}\r\n" + $"\r\n\r\n" + $"[contentJson]:\r\n{contentJson}\r\n"); content = web.ParseJsonWithoutConverter <ContentNote>(contentJson); } catch (RestException) { throw; } catch (Exception e) { throw new StandardNoteAPIException("Cannot decrypt note with local masterkey", e); } var n = new StandardFileNote(encNote.uuid, cfg, conn.HConfig) { Text = content.text, InternalTitle = content.title, AuthHash = encNote.auth_hash, ContentVersion = StandardNoteCrypt.GetSchemaVersion(encNote.content), IsPinned = GetAppDataBool(content.appData, "org.standardnotes.sn", "pinned", false), }; var refTags = new List <StandardFileTag>(); foreach (var cref in content.references) { if (cref.content_type == "Note") { // ignore } else if (dat.Tags.Any(t => t.UUID == cref.uuid)) { refTags.Add(new StandardFileTag(cref.uuid, dat.Tags.First(t => t.UUID == cref.uuid).Title)); } else if (cref.content_type == "Tag") { Logger.Warn(StandardNotePlugin.Name, $"Reference to missing tag {cref.uuid} in note {encNote.uuid}"); } else { Logger.Error(StandardNotePlugin.Name, $"Downloaded note contains an unknown reference :{cref.uuid} ({cref.content_type}) in note {encNote.uuid}"); } } n.SetTags(refTags); n.SetReferences(content.references); n.CreationDate = encNote.created_at; n.ModificationDate = encNote.updated_at; return(n); }