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 = StandardNoteConfig.REX_LINEBREAK.Replace(content.text, Environment.NewLine), InternalTitle = content.title, AuthHash = encNote.auth_hash, ContentVersion = StandardNoteCrypt.GetSchemaVersion(encNote.content), IsPinned = GetAppDataBool(content.appData, APPDATA_PINNED, false), IsLocked = GetAppDataBool(content.appData, APPDATA_LOCKED, false), }; var refTags = new List <StandardFileTagRef>(); 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 StandardFileTagRef(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}"); } } foreach (var tref in dat.Tags.Where(tag => tag.References.Any(tref => tref == encNote.uuid))) { refTags.Add(new StandardFileTagRef(tref.UUID, tref.Title)); } refTags = refTags.DistinctBy(t => t.UUID).ToList(); n.SetTags(refTags); n.SetReferences(content.references); n.CreationDate = encNote.created_at; n.ModificationDate = encNote.updated_at; return(n); }
public static SyncResult Sync(ISimpleJsonRest web, StandardNoteConnection conn, APIResultAuthorize authToken, StandardNoteConfig cfg, StandardNoteData dat, List <StandardFileNote> allNotes, List <StandardFileNote> notesUpload, List <StandardFileNote> notesDelete, List <StandardFileTag> tagsDelete) { APIBodySync d = new APIBodySync(); d.cursor_token = null; d.sync_token = string.IsNullOrWhiteSpace(dat.SyncToken) ? null : dat.SyncToken; d.limit = 150; d.items = new List <APIBodyItem>(); var allTags = dat.Tags.ToList(); // Upload new notes foreach (var mvNote in notesUpload) { PrepareForUpload(web, d, mvNote, allTags, authToken, cfg, false); } // Delete deleted notes foreach (var rmNote in notesDelete) { PrepareForUpload(web, d, rmNote, allTags, authToken, cfg, true); } // Update references on tags (from changed notes) foreach (var upTag in notesUpload.SelectMany(n => n.InternalTags).Concat(notesDelete.SelectMany(n => n.InternalTags)).Except(tagsDelete)) { PrepareForUpload(web, d, upTag, allNotes, authToken, cfg, false); } // Remove unused tags if (cfg.RemEmptyTags) { foreach (var rmTag in tagsDelete) { PrepareForUpload(web, d, rmTag, allNotes, authToken, cfg, true); } } var result = GetCursorResult(web, dat, d); var syncresult = new SyncResult(); syncresult.retrieved_tags = result .retrieved_items .Where(p => p.content_type.ToLower() == "tag") .Where(p => !p.deleted) .Select(n => CreateTag(web, n, authToken)) .ToList(); syncresult.deleted_tags = result .retrieved_items .Where(p => p.content_type.ToLower() == "tag") .Where(p => p.deleted) .Select(n => CreateTag(web, n, authToken)) .ToList(); syncresult.saved_tags = result .saved_items .Where(p => p.content_type.ToLower() == "tag") .Select(n => CreateTag(web, n, authToken)) .ToList(); syncresult.unsaved_tags = result .unsaved .Where(p => p.item.content_type.ToLower() == "tag") .Select(n => CreateTag(web, n.item, authToken)) .ToList(); dat.UpdateTags(syncresult.retrieved_tags, syncresult.saved_tags, syncresult.unsaved_tags, syncresult.deleted_tags); syncresult.retrieved_notes = result .retrieved_items .Where(p => p.content_type.ToLower() == "note") .Where(p => !p.deleted) .Select(n => CreateNote(web, conn, n, authToken, cfg, dat)) .ToList(); syncresult.deleted_notes = result .retrieved_items .Where(p => p.content_type.ToLower() == "note") .Where(p => p.deleted) .Select(n => CreateNote(web, conn, n, authToken, cfg, dat)) .ToList(); syncresult.saved_notes = result .saved_items .Where(p => p.content_type.ToLower() == "note") .Select(n => CreateNote(web, conn, n, authToken, cfg, dat)) .ToList(); syncresult.conflict_notes = result .unsaved .Where(p => p.item.content_type.ToLower() == "note") .Where(p => p.error.tag == "sync_conflict") .Select(n => CreateNote(web, conn, n.item, authToken, cfg, dat)) .ToList(); syncresult.error_notes = result .unsaved .Where(p => p.item.content_type.ToLower() == "note") .Where(p => p.error.tag != "sync_conflict") .Select(n => CreateNote(web, conn, n.item, authToken, cfg, dat)) .ToList(); return(syncresult); }
public static SyncResult Sync(ISimpleJsonRest web, StandardNoteConnection conn, APIResultAuthorize authToken, StandardNoteConfig cfg, StandardNoteData dat, List <StandardFileNote> allNotes, List <StandardFileNote> notesUpload, List <StandardFileNote> notesDelete, List <StandardFileTag> tagsDelete) { APIBodySync d = new APIBodySync(); d.cursor_token = null; d.sync_token = string.IsNullOrWhiteSpace(dat.SyncToken) ? null : dat.SyncToken; d.limit = 150; d.items = new List <APIBodyItem>(); var items_raw = new List <APIRawBodyItem>(); var allTags = dat.Tags.ToList(); // Upload new notes foreach (var mvNote in notesUpload) { PrepareNoteForUpload(web, d, ref items_raw, mvNote, allTags, authToken, cfg, false); } // Delete deleted notes foreach (var rmNote in notesDelete) { PrepareNoteForUpload(web, d, ref items_raw, rmNote, allTags, authToken, cfg, true); } // Update references on tags (from changed notes) foreach (var upTag in GetTagsInNeedOfUpdate(dat.Tags, notesUpload, notesDelete)) { PrepareTagForUpload(web, d, ref items_raw, upTag, authToken, false); } // Remove unused tags if (cfg.RemEmptyTags) { foreach (var rmTag in tagsDelete) { PrepareTagForUpload(web, d, ref items_raw, rmTag, authToken, true); } } Logger.Debug( StandardNotePlugin.Name, $"Perform sync request ({items_raw.Count} items send)", "Sent Items (unencrypted):\n\n" + string.Join("\n", items_raw.Select(i => $"{{\n content_type = {i.content_type}\n uuid = {i.uuid}\n created_at = {i.created_at}\n deleted = {i.deleted}\n content =\n{CompactJsonFormatter.FormatJSON(i.content, 2, 1)}\n}}"))); var result = GetCursorResult(web, dat, d); var syncresult = new SyncResult(); syncresult.retrieved_tags = result .retrieved_items .Where(p => p.content_type.ToLower() == "tag") .Where(p => !p.deleted) .Select(n => CreateTag(web, n, authToken)) .ToList(); syncresult.deleted_tags = result .retrieved_items .Where(p => p.content_type.ToLower() == "tag") .Where(p => p.deleted) .Select(n => CreateTag(web, n, authToken)) .ToList(); syncresult.saved_tags = result .saved_items .Where(p => p.content_type.ToLower() == "tag") .Select(n => CreateTag(web, n, authToken)) .ToList(); syncresult.unsaved_tags = result .unsaved .Where(p => p.item.content_type.ToLower() == "tag") .Select(n => CreateTag(web, n.item, authToken)) .ToList(); dat.UpdateTags(syncresult.retrieved_tags, syncresult.saved_tags, syncresult.unsaved_tags, syncresult.deleted_tags); syncresult.retrieved_notes = result .retrieved_items .Where(p => p.content_type.ToLower() == "note") .Where(p => !p.deleted) .Select(n => CreateNote(web, conn, n, authToken, cfg, dat)) .ToList(); syncresult.deleted_notes = result .retrieved_items .Where(p => p.content_type.ToLower() == "note") .Where(p => p.deleted) .Select(n => CreateNote(web, conn, n, authToken, cfg, dat)) .ToList(); syncresult.saved_notes = result .saved_items .Where(p => p.content_type.ToLower() == "note") .Select(n => CreateNote(web, conn, n, authToken, cfg, dat)) .ToList(); syncresult.conflict_notes = result .unsaved .Where(p => p.item.content_type.ToLower() == "note") .Where(p => p.error.tag == "sync_conflict") .Select(n => CreateNote(web, conn, n.item, authToken, cfg, dat)) .ToList(); syncresult.error_notes = result .unsaved .Where(p => p.item.content_type.ToLower() == "note") .Where(p => p.error.tag != "sync_conflict") .Select(n => CreateNote(web, conn, n.item, authToken, cfg, dat)) .ToList(); syncresult.retrieved_notes.AddRange(GetMissingNoteUpdates(syncresult.retrieved_tags.Concat(syncresult.saved_tags), dat.Tags, allNotes, syncresult.retrieved_notes)); return(syncresult); }
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, AuthHash = encNote.auth_hash, ContentVersion = StandardNoteCrypt.GetSchemaVersion(encNote.content), }; nd.RawModificationDate = encNote.updated_at; return(nd); } ContentNote content; string appDataContentString; 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); appDataContentString = web.ParseJsonAndGetSubJson(contentJson, "appData", string.Empty); } 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); using (n.SuppressDirtyChanges()) { n.Text = StandardNoteConfig.REX_LINEBREAK.Replace(content.text, Environment.NewLine); n.InternalTitle = content.title; n.AuthHash = encNote.auth_hash; n.ContentVersion = StandardNoteCrypt.GetSchemaVersion(encNote.content); n.IsPinned = GetAppDataBool(content.appData, APPDATA_PINNED, false); n.IsLocked = GetAppDataBool(content.appData, APPDATA_LOCKED, false); n.IsArchived = GetAppDataBool(content.appData, APPDATA_ARCHIVED, false); n.IsProtected = content.@protected; n.IsHidePreview = content.hidePreview; var refTags = new List <StandardFileTagRef>(); 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 StandardFileTagRef(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}"); } } foreach (var tref in dat.Tags.Where(tag => tag.References.Any(tref => tref == encNote.uuid))) { refTags.Add(new StandardFileTagRef(tref.UUID, tref.Title)); } refTags = refTags.DistinctBy(t => t.UUID).ToList(); n.SetTags(refTags); n.SetReferences(content.references); n.CreationDate = encNote.created_at; n.RawModificationDate = encNote.updated_at; n.ClientUpdatedAt = GetAppDataDTO(content.appData, APPDATA_CLIENTUPDATEDAT, null); n.NoteCreationDate = GetAppDataDTO(content.appData, APPDATA_NOTECDATE, null); n.NoteModificationDate = GetAppDataDTO(content.appData, APPDATA_NOTEMDATE, null); n.TextModificationDate = GetAppDataDTO(content.appData, APPDATA_TEXTMDATE, null); n.TitleModificationDate = GetAppDataDTO(content.appData, APPDATA_TITLEMDATE, null); n.TagsModificationDate = GetAppDataDTO(content.appData, APPDATA_TAGSMDATE, null); n.PathModificationDate = GetAppDataDTO(content.appData, APPDATA_PATHMDATE, null); n.RawAppData = appDataContentString; } return(n); }