├── .gitignore ├── .terminal ├── CONTRIBUTING.md ├── LICENSE ├── Mimer.Framework ├── Dev │ └── Dev.cs ├── IO │ ├── ByteBuilder.cs │ ├── ByteReader.cs │ ├── ByteWriter.cs │ └── IByteConvertible.cs ├── Json │ ├── IJsonFilter.cs │ ├── JsonArray.cs │ ├── JsonBuilder.cs │ ├── JsonObject.cs │ ├── JsonTokenizer.cs │ └── JsonValue.cs └── Mimer.Framework.csproj ├── Mimer.Notes.Model ├── Cryptography │ ├── CryptSignature.cs │ ├── KeySet.cs │ ├── PasswordHasher.cs │ └── SymmetricCrypt.cs ├── DataTypes │ ├── MimerKey.cs │ ├── MimerNotificationToken.cs │ ├── MimerUser.cs │ ├── Note.cs │ ├── NoteItem.cs │ ├── NoteItemArray.cs │ ├── NoteShareInfo.cs │ ├── ShareOffer.cs │ ├── UserSize.cs │ ├── UserType.cs │ └── VersionConflict.cs ├── INonRepeatableRequest.cs ├── IRequestObject.cs ├── IResponseObject.cs ├── ISignable.cs ├── Mimer.Notes.Model.csproj ├── Requests │ ├── BasicRequest.cs │ ├── CheckUsernameRequest.cs │ ├── CreateKeyRequest.cs │ ├── CreateUserRequest.cs │ ├── DeleteAccountRequest.cs │ ├── DeleteKeyRequest.cs │ ├── DeleteNoteRequest.cs │ ├── DeleteShareRequest.cs │ ├── LoginRequest.cs │ ├── MultiNoteRequest.cs │ ├── PublicKeyRequest.cs │ ├── ReadKeyRequest.cs │ ├── ReadNoteRequest.cs │ ├── ServerNotificationRequest.cs │ ├── ShareNoteRequest.cs │ ├── UpdateUserDataRequest .cs │ ├── UpdateUserRequest.cs │ └── WriteNoteRequest.cs ├── Responses │ ├── AllKeysResponse.cs │ ├── BasicResponse.cs │ ├── CheckUsernameResponse.cs │ ├── KeyResponse.cs │ ├── LoginResponse.cs │ ├── NotificationUrlResponse.cs │ ├── PreLoginResponse.cs │ ├── PublicKeyResponse.cs │ ├── ReadNoteResponse.cs │ ├── ShareOffersResponse.cs │ ├── UpdateNoteResponse.cs │ └── UserDataResponse.cs └── SignatureFilter.cs ├── Mimer.Notes.Rest.sln ├── Mimer.Notes.Server ├── ChallengeManager.cs ├── DbNote .cs ├── DbNoteItem.cs ├── DbShareOffer.cs ├── GlobalStatsManager .cs ├── IMimerDataSource.cs ├── Mimer.Notes.Server.csproj ├── MimerServer.cs ├── PostgresDataSource.cs ├── RequestValidator.cs ├── SqlLiteDataSource.cs └── UserStatsManager.cs ├── Mimer.Notes.SignalR ├── .config │ └── dotnet-tools.json ├── Authentication │ ├── MimerAuthenticationHandler.cs │ └── MimerIdentity.cs ├── Base │ ├── JsonModelBinder.cs │ └── JsonModelBinderProvider.cs ├── Controllers │ ├── HealthController.cs │ └── NotificationController.cs ├── Hubs │ └── NotificationsHub.cs ├── Mimer.Notes.SignalR.csproj ├── NotificationServer.cs ├── Program.cs └── appsettings.json ├── Mimer.Notes.WebApi ├── .config │ └── dotnet-tools.json ├── Base │ ├── DevExceptionHandler.cs │ ├── JsonModelBinder.cs │ ├── JsonModelBinderProvider.cs │ └── MimerController.cs ├── Controllers │ ├── HealthController.cs │ ├── KeyController.cs │ ├── NoteController.cs │ ├── NotificationController.cs │ ├── TestController.cs │ └── UserController.cs ├── Mimer.Notes.WebApi.csproj ├── Program.cs └── appsettings.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | *.suo 3 | *.user 4 | .idea/ 5 | .env 6 | .env.* 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | ## 11 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 12 | 13 | # User-specific files 14 | *.suo 15 | *.user 16 | *.userosscache 17 | *.sln.docstates 18 | 19 | # User-specific files (MonoDevelop/Xamarin Studio) 20 | *.userprefs 21 | 22 | # Build results 23 | x64/ 24 | x86/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # .NET Core 49 | project.lock.json 50 | project.fragment.lock.json 51 | artifacts/ 52 | **/Properties/launchSettings.json 53 | 54 | *_i.c 55 | *_p.c 56 | *_i.h 57 | *.ilk 58 | *.meta 59 | *.obj 60 | *.pch 61 | *.pdb 62 | *.pgc 63 | *.pgd 64 | *.rsp 65 | *.sbr 66 | *.tlb 67 | *.tli 68 | *.tlh 69 | *.tmp 70 | *.tmp_proj 71 | *.log 72 | *.vspscc 73 | *.vssscc 74 | .builds 75 | *.pidb 76 | *.svclog 77 | *.scc 78 | 79 | # Chutzpah Test files 80 | _Chutzpah* 81 | 82 | # Visual C++ cache files 83 | ipch/ 84 | *.aps 85 | *.ncb 86 | *.opendb 87 | *.opensdf 88 | *.sdf 89 | *.cachefile 90 | *.VC.db 91 | *.VC.VC.opendb 92 | 93 | # Visual Studio profiler 94 | *.psess 95 | *.vsp 96 | *.vspx 97 | *.sap 98 | 99 | # TFS 2012 Local Workspace 100 | $tf/ 101 | 102 | # Guidance Automation Toolkit 103 | *.gpState 104 | 105 | # ReSharper is a .NET coding add-in 106 | _ReSharper*/ 107 | *.[Rr]e[Ss]harper 108 | *.DotSettings.user 109 | 110 | # JustCode is a .NET coding add-in 111 | .JustCode 112 | 113 | # TeamCity is a build add-in 114 | _TeamCity* 115 | 116 | # DotCover is a Code Coverage Tool 117 | *.dotCover 118 | 119 | # Visual Studio code coverage results 120 | *.coverage 121 | *.coveragexml 122 | 123 | # NCrunch 124 | _NCrunch_* 125 | .*crunch*.local.xml 126 | nCrunchTemp_* 127 | 128 | # MightyMoose 129 | *.mm.* 130 | AutoTest.Net/ 131 | 132 | # Web workbench (sass) 133 | .sass-cache/ 134 | 135 | # Installshield output folder 136 | [Ee]xpress/ 137 | 138 | # DocProject is a documentation generator add-in 139 | DocProject/buildhelp/ 140 | DocProject/Help/*.HxT 141 | DocProject/Help/*.HxC 142 | DocProject/Help/*.hhc 143 | DocProject/Help/*.hhk 144 | DocProject/Help/*.hhp 145 | DocProject/Help/Html2 146 | DocProject/Help/html 147 | 148 | # Click-Once directory 149 | publish/ 150 | 151 | # Publish Web Output 152 | *.[Pp]ublish.xml 153 | *.azurePubxml 154 | # TODO: Comment the next line if you want to checkin your web deploy settings 155 | # but database connection strings (with potential passwords) will be unencrypted 156 | *.pubxml 157 | *.publishproj 158 | 159 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 160 | # checkin your Azure Web App publish settings, but sensitive information contained 161 | # in these scripts will be unencrypted 162 | PublishScripts/ 163 | 164 | # NuGet Packages 165 | *.nupkg 166 | # The packages folder can be ignored because of Package Restore 167 | **/packages/* 168 | # except build/, which is used as an MSBuild target. 169 | !**/packages/build/ 170 | # Uncomment if necessary however generally it will be regenerated when needed 171 | #!**/packages/repositories.config 172 | # NuGet v3's project.json files produces more ignorable files 173 | *.nuget.props 174 | *.nuget.targets 175 | 176 | # Microsoft Azure Build Output 177 | csx/ 178 | *.build.csdef 179 | 180 | # Microsoft Azure Emulator 181 | ecf/ 182 | rcf/ 183 | 184 | # Windows Store app package directories and files 185 | AppPackages/ 186 | BundleArtifacts/ 187 | Package.StoreAssociation.xml 188 | _pkginfo.txt 189 | 190 | # Visual Studio cache files 191 | # files ending in .cache can be ignored 192 | *.[Cc]ache 193 | # but keep track of directories ending in .cache 194 | !*.[Cc]ache/ 195 | 196 | # Others 197 | ClientBin/ 198 | ~$* 199 | *~ 200 | *.dbmdl 201 | *.dbproj.schemaview 202 | *.jfm 203 | *.pfx 204 | *.publishsettings 205 | orleans.codegen.cs 206 | 207 | # Since there are multiple workflows, uncomment next line to ignore bower_components 208 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 209 | #bower_components/ 210 | 211 | # RIA/Silverlight projects 212 | Generated_Code/ 213 | 214 | # Backup & report files from converting an old project file 215 | # to a newer Visual Studio version. Backup files are not needed, 216 | # because we have git ;-) 217 | _UpgradeReport_Files/ 218 | Backup*/ 219 | UpgradeLog*.XML 220 | UpgradeLog*.htm 221 | 222 | # SQL Server files 223 | *.mdf 224 | *.ldf 225 | *.ndf 226 | 227 | # Business Intelligence projects 228 | *.rdl.data 229 | *.bim.layout 230 | *.bim_*.settings 231 | 232 | # Microsoft Fakes 233 | FakesAssemblies/ 234 | 235 | # GhostDoc plugin setting file 236 | *.GhostDoc.xml 237 | 238 | # Node.js Tools for Visual Studio 239 | .ntvs_analysis.dat 240 | node_modules/ 241 | 242 | # Typescript v1 declaration files 243 | typings/ 244 | 245 | # Visual Studio 6 build log 246 | *.plg 247 | 248 | # Visual Studio 6 workspace options file 249 | *.opt 250 | 251 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 252 | *.vbw 253 | 254 | # Visual Studio LightSwitch build output 255 | **/*.HTMLClient/GeneratedArtifacts 256 | **/*.DesktopClient/GeneratedArtifacts 257 | **/*.DesktopClient/ModelManifest.xml 258 | **/*.Server/GeneratedArtifacts 259 | **/*.Server/ModelManifest.xml 260 | _Pvt_Extensions 261 | 262 | # Paket dependency manager 263 | .paket/paket.exe 264 | paket-files/ 265 | 266 | # FAKE - F# Make 267 | .fake/ 268 | 269 | # JetBrains Rider 270 | .idea/ 271 | *.sln.iml 272 | 273 | # CodeRush 274 | .cr/ 275 | 276 | # Python Tools for Visual Studio (PTVS) 277 | __pycache__/ 278 | *.pyc 279 | 280 | # Cake - Uncomment if you are using it 281 | # tools/** 282 | # !tools/packages.config 283 | 284 | # Telerik's JustMock configuration file 285 | *.jmconfig 286 | 287 | # BizTalk build output 288 | *.btp.cs 289 | *.btm.cs 290 | *.odx.cs 291 | *.xsd.cs 292 | client.electron/mimernotes/out 293 | client.electron/mimernotes/app/ 294 | client.electron/mimernotes/bundles/ 295 | bundle.json 296 | secrets/ 297 | Mimer.Notes.WebApi/appsettings.Development.json 298 | Mimer.Notes.SignalR/appsettings.Development.json 299 | publish.api/ 300 | publish.signalr/ 301 | -------------------------------------------------------------------------------- /.terminal: -------------------------------------------------------------------------------- 1 | TITLE=Server 2 | SYSTEM=INFRA 3 | COLOR_INDEX=264 4 | COLOR_RGB=60;60;113 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Mimiri Notes 2 | 3 | For now, talk to us on [Discord](https://discord.gg/pg69qPAVZR) 4 | 5 | More info to follow 6 | -------------------------------------------------------------------------------- /Mimer.Framework/Dev/Dev.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Text; 3 | 4 | namespace Mimer.Framework { 5 | public class Dev { 6 | private static ILogListener? FListener; 7 | private static string FDebugPath = string.Empty; 8 | public static bool LogToConsole = false; 9 | 10 | public static void SetLogListener(ILogListener listener) { 11 | FListener = listener; 12 | } 13 | 14 | public static void SetDebugPath(string path) { 15 | FDebugPath = path; 16 | } 17 | 18 | private static string DebugPath { 19 | get { 20 | if (!string.IsNullOrEmpty(FDebugPath)) { 21 | return FDebugPath; 22 | } 23 | var parentDir = Directory.GetParent(Assembly.GetExecutingAssembly().Location); 24 | return Path.Combine(parentDir!.FullName, "DebugLog.txt"); 25 | } 26 | } 27 | 28 | public static void Log(params object?[] items) { 29 | try { 30 | StringBuilder OLogData = new StringBuilder(); 31 | OLogData.Append(DateTime.Now.ToString("yyyy.MM.dd HH:mm:ss.fff# ")); 32 | bool OFirst = true; 33 | foreach (object? OItem in items) { 34 | if (!OFirst) { 35 | OLogData.Append(", "); 36 | } 37 | OLogData.Append(OItem); 38 | OFirst = false; 39 | } 40 | OLogData.AppendLine(); 41 | if (LogToConsole) { 42 | Console.Write(OLogData); 43 | } 44 | if (FListener != null) { 45 | FListener.WriteLogData(OLogData.ToString()); 46 | } 47 | else { 48 | using (StreamWriter OStream = new StreamWriter(DebugPath, true, Encoding.UTF8)) { 49 | OStream.Write(OLogData.ToString()); 50 | } 51 | } 52 | } 53 | catch (Exception) { 54 | //Console.WriteLine(e); 55 | } 56 | } 57 | 58 | } 59 | 60 | public interface ILogListener { 61 | void WriteLogData(string data); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Mimer.Framework/IO/ByteBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Text; 3 | 4 | namespace Mimer.Framework.IO { 5 | public class ByteBuilder { 6 | private List FSegments = new List(); 7 | private int FLength = 0; 8 | private static Encoding FEncoding = Encoding.UTF8; 9 | 10 | public void Append(sbyte data) { 11 | Add(new byte[] { (byte)data }); 12 | } 13 | 14 | public void Append(byte data) { 15 | Add(new byte[] { data }); 16 | } 17 | 18 | public void Append(short data) { 19 | Add(BitConverter.GetBytes(data)); 20 | } 21 | 22 | public void Append(ushort data) { 23 | Add(BitConverter.GetBytes(data)); 24 | } 25 | 26 | public void Append(char data) { 27 | Add(BitConverter.GetBytes(data)); 28 | } 29 | 30 | public void Append(int data) { 31 | Add(BitConverter.GetBytes(data)); 32 | } 33 | 34 | public void Append(bool data) { 35 | Add(new byte[] { (byte)(data ? 1 : 0) }); 36 | } 37 | 38 | public void Append(uint data) { 39 | Add(BitConverter.GetBytes(data)); 40 | } 41 | 42 | public void Append(long data) { 43 | Add(BitConverter.GetBytes(data)); 44 | } 45 | 46 | public void Append(ulong data) { 47 | Add(BitConverter.GetBytes(data)); 48 | } 49 | 50 | public void Append(decimal data) { 51 | int[] OData = decimal.GetBits(data); 52 | byte[] OBytes = new byte[OData.Length * 4]; 53 | Buffer.BlockCopy(OData, 0, OBytes, 0, OBytes.Length); 54 | Add(OBytes); 55 | } 56 | 57 | public void Append(float data) { 58 | Add(BitConverter.GetBytes(data)); 59 | } 60 | 61 | public void Append(double data) { 62 | Add(BitConverter.GetBytes(data)); 63 | } 64 | 65 | public void Append(Guid data) { 66 | Add(data.ToByteArray()); 67 | } 68 | 69 | public void Append(DateTime data) { 70 | Append(data.ToBinary()); 71 | } 72 | 73 | public void Append(IPEndPoint endpoint) { 74 | byte[] OData = endpoint.Address.GetAddressBytes(); 75 | Append(OData.Length); 76 | Add(OData); 77 | Append(endpoint.Port); 78 | } 79 | 80 | public void Append(string data) { 81 | if (data == null) { 82 | Append(-1); 83 | return; 84 | } 85 | byte[] OData = FEncoding.GetBytes(data); 86 | Append(OData.Length); 87 | Add(OData); 88 | } 89 | 90 | public void Append(string data, Encoding encoding) { 91 | if (data == null) { 92 | Append(-1); 93 | return; 94 | } 95 | byte[] OData = encoding.GetBytes(data); 96 | Append(OData.Length); 97 | Add(OData); 98 | } 99 | 100 | public void Append(string[] data) { 101 | if (data == null) { 102 | Append(-1); 103 | return; 104 | } 105 | Append(data.Length); 106 | for (int i = 0; i < data.Length; i++) { 107 | Append(data[i]); 108 | } 109 | } 110 | 111 | public void Append(Guid[] data) { 112 | if (data == null) { 113 | Append(-1); 114 | return; 115 | } 116 | Append(data.Length); 117 | for (int i = 0; i < data.Length; i++) { 118 | Append(data[i]); 119 | } 120 | } 121 | 122 | public void Append(byte[] data) { 123 | if (data == null) { 124 | Append(-1); 125 | return; 126 | } 127 | Append(data.Length); 128 | Add(data); 129 | } 130 | 131 | public void Append(IByteConvertible data) { 132 | if (data == null) { 133 | Append(-1); 134 | return; 135 | } 136 | byte[] OData = new byte[data.ByteLength]; 137 | data.CopyToByteArray(OData, 0); 138 | Add(OData); 139 | } 140 | 141 | public void AppendRaw(byte[] data) { 142 | Add(data); 143 | } 144 | 145 | private void Add(byte[] data) { 146 | FSegments.Add(data); 147 | FLength += data.Length; 148 | } 149 | 150 | public byte[] ToArray() { 151 | return ToArray(false); 152 | } 153 | 154 | public byte[] ToArray(bool prependLength) { 155 | byte[] OResult; 156 | int OOffset = 0; 157 | if (prependLength) { 158 | OResult = new byte[FLength + 4]; 159 | Buffer.BlockCopy(BitConverter.GetBytes(FLength), 0, OResult, 0, 4); 160 | OOffset += 4; 161 | } 162 | else { 163 | OResult = new byte[FLength]; 164 | } 165 | for (int i = 0; i < FSegments.Count; i++) { 166 | byte[] OSegment = FSegments[i]; 167 | Buffer.BlockCopy(OSegment, 0, OResult, OOffset, OSegment.Length); 168 | OOffset += OSegment.Length; 169 | } 170 | return OResult; 171 | } 172 | 173 | 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Mimer.Framework/IO/ByteReader.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Text; 3 | 4 | namespace Mimer.Framework.IO { 5 | public class ByteReader { 6 | private byte[] FData; 7 | private int FOffset; 8 | private int FLength; 9 | private static Encoding FEncoding = Encoding.UTF8; 10 | 11 | public ByteReader(byte[] data) { 12 | FOffset = 0; 13 | FLength = data.Length; 14 | FData = data; 15 | } 16 | 17 | public ByteReader(byte[] data, int offset, int length) { 18 | FOffset = offset; 19 | FLength = FOffset + length; 20 | FData = data; 21 | } 22 | 23 | public ByteReader(byte[] data, int offset) { 24 | FOffset = offset; 25 | FLength = data.Length; 26 | FData = data; 27 | } 28 | 29 | public T Read() where T : IByteConvertible, new() { 30 | T OResult = new T(); 31 | int OLength = ReadInt32(); 32 | if (OLength == -1) { 33 | return default(T)!; 34 | } 35 | ((IByteConvertible)OResult).FromByteArray(FData, FOffset); 36 | FOffset += OLength; 37 | return OResult; 38 | } 39 | 40 | public sbyte ReadSByte() { 41 | return (sbyte)FData[FOffset++]; 42 | } 43 | 44 | public byte ReadByte() { 45 | return FData[FOffset++]; 46 | } 47 | 48 | public bool ReadBoolean() { 49 | return FData[FOffset++] == 1; 50 | } 51 | 52 | public short ReadInt16() { 53 | short rtn = BitConverter.ToInt16(FData, FOffset); 54 | FOffset += 2; 55 | return rtn; 56 | } 57 | 58 | public ushort ReadUInt16() { 59 | ushort rtn = BitConverter.ToUInt16(FData, FOffset); 60 | FOffset += 2; 61 | return rtn; 62 | } 63 | 64 | public int ReadInt32() { 65 | int rtn = BitConverter.ToInt32(FData, FOffset); 66 | FOffset += 4; 67 | return rtn; 68 | } 69 | 70 | public static int ReadInt32(byte[] data, int offset) { 71 | int rtn = BitConverter.ToInt32(data, offset); 72 | return rtn; 73 | } 74 | 75 | public uint ReadUInt32() { 76 | uint rtn = BitConverter.ToUInt32(FData, FOffset); 77 | FOffset += 4; 78 | return rtn; 79 | } 80 | 81 | public long ReadInt64() { 82 | long rtn = BitConverter.ToInt64(FData, FOffset); 83 | FOffset += 8; 84 | return rtn; 85 | } 86 | 87 | public ulong ReadUInt64() { 88 | ulong rtn = BitConverter.ToUInt64(FData, FOffset); 89 | FOffset += 8; 90 | return rtn; 91 | } 92 | 93 | public decimal ReadDecimal() { 94 | int[] OData = new int[4]; 95 | Buffer.BlockCopy(FData, FOffset, OData, 0, 16); 96 | FOffset += 16; 97 | return new decimal(OData); 98 | } 99 | 100 | public float ReadSingle() { 101 | float rtn = BitConverter.ToSingle(FData, FOffset); 102 | FOffset += 4; 103 | return rtn; 104 | } 105 | 106 | public double ReadDouble() { 107 | double rtn = BitConverter.ToDouble(FData, FOffset); 108 | FOffset += 4; 109 | return rtn; 110 | } 111 | 112 | public Guid ReadGuid() { 113 | int a = ReadInt32(); 114 | short b = ReadInt16(); 115 | short c = ReadInt16(); 116 | byte d = ReadByte(); 117 | byte e = ReadByte(); 118 | byte f = ReadByte(); 119 | byte g = ReadByte(); 120 | byte h = ReadByte(); 121 | byte i = ReadByte(); 122 | byte j = ReadByte(); 123 | byte k = ReadByte(); 124 | return new Guid(a, b, c, d, e, f, g, h, i, j, k); 125 | } 126 | 127 | public static Guid ReadGuid(byte[] data, int offset) { 128 | int a = BitConverter.ToInt32(data, offset); 129 | short b = BitConverter.ToInt16(data, offset + 4); 130 | short c = BitConverter.ToInt16(data, offset + 6); 131 | return new Guid(a, b, c, data[offset + 8], data[offset + 9], data[offset + 10], data[offset + 11], data[offset + 12], data[offset + 13], data[offset + 14], data[offset + 15]); 132 | } 133 | 134 | public DateTime ReadDateTime() { 135 | return DateTime.FromBinary(ReadInt64()); 136 | } 137 | 138 | public IPEndPoint ReadIPEndPoint() { 139 | byte[] OData = ReadBytes(ReadInt32()); 140 | return new IPEndPoint(new IPAddress(OData), ReadInt32()); 141 | } 142 | 143 | public string? ReadString() { 144 | int OLength = ReadInt32(); 145 | if (OLength == -1) { 146 | return null; 147 | } 148 | string rtn = FEncoding.GetString(FData, FOffset, OLength); 149 | FOffset += OLength; 150 | return rtn; 151 | } 152 | 153 | public static string? ReadString(byte[] data, int offset) { 154 | int OLength = ReadInt32(data, offset); 155 | if (OLength == -1) { 156 | return null; 157 | } 158 | string rtn = FEncoding.GetString(data, offset, OLength); 159 | return rtn; 160 | } 161 | 162 | public string?[]? ReadStrings() { 163 | int OCount = ReadInt32(); 164 | if (OCount == -1) { 165 | return null; 166 | } 167 | string?[] OResult = new string?[OCount]; 168 | for (int i = 0; i < OCount; i++) { 169 | OResult[i] = ReadString(); 170 | } 171 | return OResult; 172 | } 173 | 174 | 175 | public Guid[]? ReadGuids() { 176 | int OCount = ReadInt32(); 177 | if (OCount == -1) { 178 | return null; 179 | } 180 | Guid[] OResult = new Guid[OCount]; 181 | for (int i = 0; i < OCount; i++) { 182 | OResult[i] = ReadGuid(); 183 | } 184 | return OResult; 185 | } 186 | 187 | public byte[]? ReadBytes() { 188 | int OLength = ReadInt32(); 189 | if (OLength == -1) { 190 | return null; 191 | } 192 | return ReadBytes(OLength); 193 | } 194 | 195 | public byte[] ReadBytes(int count) { 196 | byte[] OData = new byte[count]; 197 | Buffer.BlockCopy(FData, FOffset, OData, 0, count); 198 | FOffset += count; 199 | return OData; 200 | } 201 | 202 | public byte[] ReadToEnd() { 203 | byte[] OData = new byte[FData.Length - FOffset]; 204 | Buffer.BlockCopy(FData, FOffset, OData, 0, FData.Length - FOffset); 205 | FOffset = FData.Length - 1; 206 | return OData; 207 | } 208 | 209 | public int Offset { 210 | get { 211 | return FOffset; 212 | } 213 | } 214 | 215 | public void SetReadOffset(int offset) { 216 | FOffset = offset; 217 | } 218 | 219 | public bool HasMoreData { 220 | get { 221 | return FOffset < FLength; 222 | } 223 | } 224 | 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /Mimer.Framework/IO/ByteWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Text; 3 | 4 | namespace Mimer.Framework.IO { 5 | public class ByteWriter { 6 | private static Encoding FEncoding = Encoding.UTF8; 7 | private byte[] FData; 8 | private int FOffset; 9 | 10 | public ByteWriter(byte[] array, int offset) { 11 | FData = array; 12 | FOffset = offset; 13 | } 14 | 15 | public void Append(sbyte data) { 16 | FData[FOffset++] = (byte)data; 17 | } 18 | 19 | public void Append(byte data) { 20 | FData[FOffset++] = data; 21 | } 22 | 23 | public void Append(short data) { 24 | FData[FOffset++] = (byte)(data & 0xFF); 25 | FData[FOffset++] = (byte)((data >> 8) & 0xFF); 26 | } 27 | 28 | public void Append(ushort data) { 29 | FData[FOffset++] = (byte)(data & 0xFF); 30 | FData[FOffset++] = (byte)((data >> 8) & 0xFF); 31 | } 32 | 33 | public void Append(char data) { 34 | FData[FOffset++] = (byte)(data & 0xFF); 35 | FData[FOffset++] = (byte)((data >> 8) & 0xFF); 36 | FData[FOffset++] = (byte)((data >> 16) & 0xFF); 37 | FData[FOffset++] = (byte)((data >> 24) & 0xFF); 38 | } 39 | 40 | public void Append(int data) { 41 | FData[FOffset++] = (byte)(data & 0xFF); 42 | FData[FOffset++] = (byte)((data >> 8) & 0xFF); 43 | FData[FOffset++] = (byte)((data >> 16) & 0xFF); 44 | FData[FOffset++] = (byte)((data >> 24) & 0xFF); 45 | } 46 | 47 | public void Append(bool data) { 48 | FData[FOffset++] = (byte)(data ? 1 : 0); 49 | } 50 | 51 | public void Append(uint data) { 52 | FData[FOffset++] = (byte)(data & 0xFF); 53 | FData[FOffset++] = (byte)((data >> 8) & 0xFF); 54 | FData[FOffset++] = (byte)((data >> 16) & 0xFF); 55 | FData[FOffset++] = (byte)((data >> 24) & 0xFF); 56 | } 57 | 58 | public void Append(long data) { 59 | FData[FOffset++] = (byte)(data & 0xFF); 60 | FData[FOffset++] = (byte)((data >> 8) & 0xFF); 61 | FData[FOffset++] = (byte)((data >> 16) & 0xFF); 62 | FData[FOffset++] = (byte)((data >> 24) & 0xFF); 63 | FData[FOffset++] = (byte)((data >> 32) & 0xFF); 64 | FData[FOffset++] = (byte)((data >> 40) & 0xFF); 65 | FData[FOffset++] = (byte)((data >> 48) & 0xFF); 66 | FData[FOffset++] = (byte)((data >> 56) & 0xFF); 67 | } 68 | 69 | public void Append(ulong data) { 70 | FData[FOffset++] = (byte)(data & 0xFF); 71 | FData[FOffset++] = (byte)((data >> 8) & 0xFF); 72 | FData[FOffset++] = (byte)((data >> 16) & 0xFF); 73 | FData[FOffset++] = (byte)((data >> 24) & 0xFF); 74 | FData[FOffset++] = (byte)((data >> 32) & 0xFF); 75 | FData[FOffset++] = (byte)((data >> 40) & 0xFF); 76 | FData[FOffset++] = (byte)((data >> 48) & 0xFF); 77 | FData[FOffset++] = (byte)((data >> 56) & 0xFF); 78 | } 79 | 80 | public void Append(decimal data) { 81 | int[] OData = decimal.GetBits(data); 82 | int OLength = OData.Length * 4; 83 | Buffer.BlockCopy(OData, 0, FData, FOffset, OLength); 84 | FOffset += OLength; 85 | } 86 | 87 | public void Append(float data) { 88 | Add(BitConverter.GetBytes(data)); 89 | } 90 | 91 | public void Append(double data) { 92 | Add(BitConverter.GetBytes(data)); 93 | } 94 | 95 | public void Append(Guid data) { 96 | Add(data.ToByteArray()); 97 | } 98 | 99 | public void Append(DateTime data) { 100 | Append(data.ToBinary()); 101 | } 102 | 103 | public void Append(IPEndPoint endpoint) { 104 | byte[] OData = endpoint.Address.GetAddressBytes(); 105 | Append(OData.Length); 106 | Add(OData); 107 | Append(endpoint.Port); 108 | } 109 | 110 | public void Append(string data) { 111 | if (data == null) { 112 | Append(-1); 113 | return; 114 | } 115 | int OOffset = FOffset; 116 | FOffset += 4; 117 | int OLength = FEncoding.GetBytes(data, 0, data.Length, FData, FOffset); 118 | FOffset += OLength; 119 | FData[OOffset++] = (byte)(OLength & 0xFF); 120 | FData[OOffset++] = (byte)((OLength >> 8) & 0xFF); 121 | FData[OOffset++] = (byte)((OLength >> 16) & 0xFF); 122 | FData[OOffset] = (byte)((OLength >> 24) & 0xFF); 123 | } 124 | 125 | public void Append(string[] data) { 126 | if (data == null) { 127 | Append(-1); 128 | return; 129 | } 130 | Append(data.Length); 131 | for (int i = 0; i < data.Length; i++) { 132 | Append(data[i]); 133 | } 134 | } 135 | 136 | public void Append(Guid[] data) { 137 | if (data == null) { 138 | Append(-1); 139 | return; 140 | } 141 | Append(data.Length); 142 | for (int i = 0; i < data.Length; i++) { 143 | Append(data[i]); 144 | } 145 | } 146 | 147 | public void Append(byte[] data) { 148 | if (data == null) { 149 | Append(-1); 150 | return; 151 | } 152 | Append(data.Length); 153 | Add(data); 154 | } 155 | 156 | public void Append(IByteConvertible data) { 157 | if (data == null) { 158 | Append(-1); 159 | return; 160 | } 161 | data.CopyToByteArray(FData, FOffset); 162 | FOffset += data.ByteLength; 163 | } 164 | 165 | public void AppendRaw(byte[] data) { 166 | Add(data); 167 | } 168 | 169 | private void Add(byte[] data) { 170 | Buffer.BlockCopy(data, 0, FData, FOffset, data.Length); 171 | FOffset += data.Length; 172 | } 173 | 174 | 175 | public static int GetLength(sbyte data) { 176 | return 1; 177 | } 178 | 179 | public static int GetLength(byte data) { 180 | return 1; 181 | } 182 | 183 | public static int GetLength(short data) { 184 | return sizeof(short); 185 | } 186 | 187 | public static int GetLength(ushort data) { 188 | return sizeof(ushort); 189 | } 190 | 191 | public static int GetLength(char data) { 192 | return sizeof(char); 193 | } 194 | 195 | public static int GetLength(int data) { 196 | return sizeof(int); 197 | } 198 | 199 | public static int GetLength(bool data) { 200 | return 1; 201 | } 202 | 203 | public static int GetLength(uint data) { 204 | return sizeof(uint); 205 | } 206 | 207 | public static int GetLength(long data) { 208 | return sizeof(long); 209 | } 210 | 211 | public static int GetLength(ulong data) { 212 | return sizeof(ulong); 213 | } 214 | 215 | public static int GetLength(decimal data) { 216 | return 16; 217 | } 218 | 219 | public static int GetLength(float data) { 220 | return sizeof(float); 221 | } 222 | 223 | public static int GetLength(double data) { 224 | return sizeof(double); 225 | } 226 | 227 | public static int GetLength(Guid data) { 228 | return 16; 229 | } 230 | 231 | public static int GetLength(DateTime data) { 232 | return sizeof(long); 233 | } 234 | 235 | public static int GetLength(IPEndPoint endpoint) { 236 | if (endpoint == null) { 237 | return GetLength(-1); 238 | } 239 | return 4 + endpoint.Address.GetAddressBytes().Length + 4; 240 | } 241 | 242 | public static int GetLength(string data) { 243 | if (data == null) { 244 | return GetLength(-1); 245 | } 246 | return 4 + FEncoding.GetByteCount(data); 247 | } 248 | 249 | public static int GetLength(byte[] data) { 250 | if (data == null) { 251 | return GetLength(-1); 252 | } 253 | return 4 + data.Length; 254 | } 255 | 256 | public static int GetLength(IByteConvertible data) { 257 | if (data == null) { 258 | return GetLength(-1); 259 | } 260 | return data.ByteLength; 261 | } 262 | 263 | public int Offset { 264 | get { 265 | return FOffset; 266 | } 267 | } 268 | 269 | 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /Mimer.Framework/IO/IByteConvertible.cs: -------------------------------------------------------------------------------- 1 | namespace Mimer.Framework.IO { 2 | public interface IByteConvertible { 3 | 4 | void CopyToByteArray(byte[] data, int offset); 5 | int FromByteArray(byte[] data, int offset); 6 | int ByteLength { get; } 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Mimer.Framework/Json/IJsonFilter.cs: -------------------------------------------------------------------------------- 1 | namespace Mimer.Framework.Json { 2 | 3 | public struct JsonItem { 4 | public readonly string Name; 5 | public readonly int Index; 6 | public readonly JsonValue Value; 7 | public JsonItem(string name, int index, JsonValue value) { 8 | Name = name; 9 | Index = index; 10 | Value = value; 11 | } 12 | } 13 | 14 | public interface IJsonFilter { 15 | 16 | JsonValue Filter(string name, JsonValue item, JsonItem[] path, ref bool remove, object? param); 17 | JsonValue Filter(int index, JsonValue item, JsonItem[] path, ref bool remove, object? param); 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mimer.Framework/Json/JsonBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Text; 3 | 4 | namespace Mimer.Framework.Json { 5 | public class JsonBuilder { 6 | private StringBuilder FBuilder = new StringBuilder(); 7 | 8 | public static string EncodeStringLiteral(string str) { 9 | StringBuilder? OResult = null; 10 | char OPrevChar = ' '; 11 | for (int i = 0; i < str.Length; i++) { 12 | char OChar = str[i]; 13 | switch (OChar) { 14 | case '\\': 15 | if (OResult == null) { 16 | OResult = new StringBuilder(); 17 | if (i > 0) { 18 | OResult.Append(str.Substring(0, i)); 19 | } 20 | } 21 | OResult.Append("\\\\"); 22 | break; 23 | case '"': 24 | if (OResult == null) { 25 | OResult = new StringBuilder(); 26 | if (i > 0) { 27 | OResult.Append(str.Substring(0, i)); 28 | } 29 | } 30 | OResult.Append("\\\""); 31 | break; 32 | case '/': 33 | if (OPrevChar == '<') { 34 | if (OResult == null) { 35 | OResult = new StringBuilder(); 36 | if (i > 0) { 37 | OResult.Append(str.Substring(0, i)); 38 | } 39 | } 40 | OResult.Append("\\"); 41 | } 42 | if (OResult != null) { 43 | OResult.Append("/"); 44 | } 45 | break; 46 | case '\b': 47 | if (OResult == null) { 48 | OResult = new StringBuilder(); 49 | if (i > 0) { 50 | OResult.Append(str.Substring(0, i)); 51 | } 52 | } 53 | OResult.Append("\\b"); 54 | break; 55 | case '\t': 56 | if (OResult == null) { 57 | OResult = new StringBuilder(); 58 | if (i > 0) { 59 | OResult.Append(str.Substring(0, i)); 60 | } 61 | } 62 | OResult.Append("\\t"); 63 | break; 64 | case '\n': 65 | if (OResult == null) { 66 | OResult = new StringBuilder(); 67 | if (i > 0) { 68 | OResult.Append(str.Substring(0, i)); 69 | } 70 | } 71 | OResult.Append("\\n"); 72 | break; 73 | case '\f': 74 | if (OResult == null) { 75 | OResult = new StringBuilder(); 76 | if (i > 0) { 77 | OResult.Append(str.Substring(0, i)); 78 | } 79 | } 80 | OResult.Append("\\f"); 81 | break; 82 | case '\r': 83 | if (OResult == null) { 84 | OResult = new StringBuilder(); 85 | if (i > 0) { 86 | OResult.Append(str.Substring(0, i)); 87 | } 88 | } 89 | OResult.Append("\\r"); 90 | break; 91 | default: 92 | if (OChar < ' ') { 93 | if (OResult == null) { 94 | OResult = new StringBuilder(); 95 | if (i > 0) { 96 | OResult.Append(str.Substring(0, i)); 97 | } 98 | } 99 | OResult.Append("\\u"); 100 | OResult.Append(((int)OChar).ToString("x").PadLeft(4, '0')); 101 | } 102 | else if (OResult != null) { 103 | OResult.Append(OChar); 104 | } 105 | break; 106 | } 107 | OPrevChar = OChar; 108 | } 109 | if (OResult != null) { 110 | return OResult.ToString(); 111 | } 112 | return str; 113 | } 114 | 115 | public static string DecodeStringLiteral(string str) { 116 | StringBuilder? OResult = null; 117 | for (int i = 0; i < str.Length; i++) { 118 | char OChar = str[i]; 119 | if (OChar == '\\') { 120 | if (OResult == null) { 121 | OResult = new StringBuilder(); 122 | if (i > 0) { 123 | OResult.Append(str.Substring(0, i)); 124 | } 125 | } 126 | i++; 127 | OChar = str[i]; 128 | switch (OChar) { 129 | case '\\': 130 | OResult.Append("\\"); 131 | break; 132 | case '"': 133 | OResult.Append("\""); 134 | break; 135 | case '/': 136 | OResult.Append("/"); 137 | break; 138 | case 'b': 139 | OResult.Append("\b"); 140 | break; 141 | case 't': 142 | OResult.Append("\t"); 143 | break; 144 | case 'n': 145 | OResult.Append("\n"); 146 | break; 147 | case 'f': 148 | OResult.Append("\f"); 149 | break; 150 | case 'r': 151 | OResult.Append("\r"); 152 | break; 153 | case 'u': 154 | OResult.Append((char)int.Parse(str.Substring(i + 1, 4), NumberStyles.HexNumber)); 155 | i += 4; 156 | break; 157 | default: 158 | OResult.Append(OChar); 159 | break; 160 | } 161 | } 162 | else if (OResult != null) { 163 | OResult.Append(OChar); 164 | } 165 | } 166 | if (OResult != null) { 167 | return OResult.ToString(); 168 | } 169 | return str; 170 | } 171 | 172 | public void BeginObject() { 173 | if (FBuilder.Length > 0) { 174 | char OEnd = FBuilder[FBuilder.Length - 1]; 175 | if (OEnd != '[' && OEnd != '{') { 176 | FBuilder.Append(","); 177 | } 178 | } 179 | FBuilder.Append("{"); 180 | } 181 | 182 | public void BeginObject(string name) { 183 | char OEnd = FBuilder[FBuilder.Length - 1]; 184 | if (OEnd != '[' && OEnd != '{') { 185 | FBuilder.Append(","); 186 | } 187 | FBuilder.Append("\"" + name + "\":{"); 188 | } 189 | 190 | public void EndObject() { 191 | FBuilder.Append("}"); 192 | } 193 | 194 | public void BeginArray() { 195 | if (FBuilder.Length > 0) { 196 | char OEnd = FBuilder[FBuilder.Length - 1]; 197 | if (OEnd != '[' && OEnd != '{') { 198 | FBuilder.Append(","); 199 | } 200 | } 201 | FBuilder.Append("["); 202 | } 203 | 204 | public void BeginArray(string name) { 205 | char OEnd = FBuilder[FBuilder.Length - 1]; 206 | if (OEnd != '[' && OEnd != '{') { 207 | FBuilder.Append(","); 208 | } 209 | FBuilder.Append("\"" + name + "\":["); 210 | } 211 | 212 | public void EndArray() { 213 | FBuilder.Append("]"); 214 | } 215 | 216 | public void Add(string name, string value) { 217 | char OEnd = FBuilder[FBuilder.Length - 1]; 218 | if (OEnd != '[' && OEnd != '{') { 219 | FBuilder.Append(","); 220 | } 221 | FBuilder.Append("\""); 222 | FBuilder.Append(name); 223 | FBuilder.Append("\":"); 224 | FBuilder.Append("\""); 225 | FBuilder.Append(value); 226 | FBuilder.Append("\""); 227 | } 228 | 229 | public void Add(string value) { 230 | char OEnd = FBuilder[FBuilder.Length - 1]; 231 | if (OEnd != '[' && OEnd != '{') { 232 | FBuilder.Append(","); 233 | } 234 | FBuilder.Append("\""); 235 | FBuilder.Append(value); 236 | FBuilder.Append("\""); 237 | } 238 | 239 | public void AddObject(string name, string value) { 240 | char OEnd = FBuilder[FBuilder.Length - 1]; 241 | if (OEnd != '[' && OEnd != '{') { 242 | FBuilder.Append(","); 243 | } 244 | FBuilder.Append("\""); 245 | FBuilder.Append(name); 246 | FBuilder.Append("\":"); 247 | FBuilder.Append(value); 248 | } 249 | 250 | public void AddObject(string value) { 251 | if (FBuilder.Length > 0) { 252 | char OEnd = FBuilder[FBuilder.Length - 1]; 253 | if (OEnd != '[' && OEnd != '{') { 254 | FBuilder.Append(","); 255 | } 256 | } 257 | FBuilder.Append(value); 258 | } 259 | 260 | public void NewLine() { 261 | FBuilder.AppendLine(); 262 | } 263 | 264 | public override string ToString() { 265 | return FBuilder.ToString(); 266 | } 267 | 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /Mimer.Framework/Json/JsonTokenizer.cs: -------------------------------------------------------------------------------- 1 | namespace Mimer.Framework.Json { 2 | public class JsonTokenizer { 3 | 4 | public static List Tokenize(string json) { 5 | List OTokens = new List(); 6 | for (int i = 0; i < json.Length; i++) { 7 | if (char.IsWhiteSpace(json[i])) { 8 | 9 | } 10 | else if (json[i] == '{') { 11 | OTokens.Add("{"); 12 | } 13 | else if (json[i] == '}') { 14 | OTokens.Add("}"); 15 | } 16 | else if (json[i] == '[') { 17 | OTokens.Add("["); 18 | } 19 | else if (json[i] == ']') { 20 | OTokens.Add("]"); 21 | } 22 | else if (json[i] == ',') { 23 | OTokens.Add(","); 24 | } 25 | else if (json[i] == ':') { 26 | OTokens.Add(":"); 27 | } 28 | else if (json[i] == '"') { 29 | bool OFound = false; 30 | for (int j = i + 1; j < json.Length; j++) { 31 | if (json[j] == '\\') { 32 | j++; 33 | } 34 | else if (json[j] == '"') { 35 | OFound = true; 36 | OTokens.Add("\"" + json.Substring(i + 1, j - i - 1)); 37 | i = j; 38 | break; 39 | } 40 | } 41 | if (!OFound) { 42 | throw new Exception("Unexpected end of data looking for '\"' (double quote)"); 43 | } 44 | } 45 | else if (json[i] == '\'') { 46 | bool OFound = false; 47 | for (int j = i + 1; j < json.Length; j++) { 48 | if (json[j] == '\\') { 49 | j++; 50 | } 51 | else if (json[j] == '\'') { 52 | OFound = true; 53 | OTokens.Add("\"" + json.Substring(i + 1, j - i - 1)); 54 | i = j; 55 | break; 56 | } 57 | } 58 | if (!OFound) { 59 | throw new Exception("Unexpected end of data looking for '\'' (single quote)"); 60 | } 61 | } 62 | else { 63 | bool OFound = false; 64 | for (int j = i + 1; j < json.Length; j++) { 65 | if (char.IsWhiteSpace(json[j]) || json[j] == '}' || json[j] == ']' || json[j] == ',' || json[j] == ':') { 66 | OFound = true; 67 | OTokens.Add(json.Substring(i, j - i)); 68 | i = j - 1; 69 | break; 70 | } 71 | } 72 | if (!OFound) { 73 | throw new Exception("Unexpected end of data looking for '\'' (single quote)"); 74 | } 75 | } 76 | } 77 | return OTokens; 78 | } 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Mimer.Framework/Json/JsonValue.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Text; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace Mimer.Framework.Json { 6 | public enum JsonValueType { Object, Array, String, Simple } 7 | public class JsonValue { 8 | public static JsonValue Empty = new JsonValue(null!, JsonValueType.Simple); 9 | public static JsonValue EmptyString = new JsonValue("", JsonValueType.String); 10 | public static JsonValue EmptyArray = new JsonValue(new JsonArray()); 11 | public static JsonValue EmptyObject = new JsonValue(new JsonObject()); 12 | public static Regex EnglishTimeFormat = new Regex(@"T(\d+)\.(\d+)\.(\d+)\.(\d+)Z", RegexOptions.IgnoreCase | RegexOptions.Compiled); 13 | 14 | public object Value; 15 | public JsonValueType Type; 16 | 17 | public JsonValue(string value, JsonValueType type) { 18 | Value = value; 19 | Type = type; 20 | } 21 | 22 | public JsonValue(JsonValue value) { 23 | Value = value.Value; 24 | Type = value.Type; 25 | } 26 | 27 | public JsonValue(JsonObject value) { 28 | Value = value; 29 | Type = JsonValueType.Object; 30 | } 31 | 32 | public JsonValue(JsonArray value) { 33 | Value = value; 34 | Type = JsonValueType.Array; 35 | } 36 | 37 | public JsonValue(string value) { 38 | Value = JsonBuilder.EncodeStringLiteral(value); 39 | Type = JsonValueType.String; 40 | } 41 | 42 | public JsonValue(bool value) { 43 | Value = value ? "true" : "false"; 44 | Type = JsonValueType.Simple; 45 | } 46 | 47 | public JsonValue(sbyte value) { 48 | Value = value.ToString(); 49 | Type = JsonValueType.Simple; 50 | } 51 | 52 | public JsonValue(byte value) { 53 | Value = value.ToString(); 54 | Type = JsonValueType.Simple; 55 | } 56 | 57 | public JsonValue(short value) { 58 | Value = value.ToString(); 59 | Type = JsonValueType.Simple; 60 | } 61 | 62 | public JsonValue(ushort value) { 63 | Value = value.ToString(); 64 | Type = JsonValueType.Simple; 65 | } 66 | 67 | public JsonValue(int value) { 68 | Value = value.ToString(); 69 | Type = JsonValueType.Simple; 70 | } 71 | 72 | public JsonValue(uint value) { 73 | Value = value.ToString(); 74 | Type = JsonValueType.Simple; 75 | } 76 | 77 | public JsonValue(long value) { 78 | Value = value.ToString(); 79 | Type = JsonValueType.String; 80 | } 81 | 82 | public JsonValue(ulong value) { 83 | Value = value.ToString(); 84 | Type = JsonValueType.String; 85 | } 86 | 87 | public JsonValue(float value) { 88 | Value = value.ToString(CultureInfo.InvariantCulture); 89 | Type = JsonValueType.Simple; 90 | } 91 | 92 | public JsonValue(double value) { 93 | Value = value.ToString(CultureInfo.InvariantCulture); 94 | Type = JsonValueType.Simple; 95 | } 96 | 97 | public JsonValue(decimal value) { 98 | Value = value.ToString(CultureInfo.InvariantCulture); 99 | Type = JsonValueType.Simple; 100 | } 101 | 102 | public JsonValue(DateTime value) { 103 | Value = value.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); 104 | Type = JsonValueType.String; 105 | } 106 | 107 | public string String() { 108 | if (Type == JsonValueType.String) { 109 | return JsonBuilder.DecodeStringLiteral((string)Value); 110 | } 111 | return Value.ToString()!; 112 | } 113 | 114 | public bool Boolean() { 115 | return (string)Value == "true"; 116 | } 117 | 118 | public sbyte SByte() { 119 | return sbyte.Parse((string)Value); 120 | } 121 | 122 | public byte Byte() { 123 | return byte.Parse((string)Value); 124 | } 125 | 126 | public short Int16() { 127 | return short.Parse((string)Value); 128 | } 129 | 130 | public ushort UInt16() { 131 | return ushort.Parse((string)Value); 132 | } 133 | 134 | public int Int32() { 135 | return int.Parse((string)Value); 136 | } 137 | 138 | public uint UInt32() { 139 | return uint.Parse((string)Value); 140 | } 141 | 142 | public long Int64() { 143 | return long.Parse((string)Value); 144 | } 145 | 146 | public ulong UInt64() { 147 | return ulong.Parse((string)Value); 148 | } 149 | 150 | public float Single() { 151 | return float.Parse((string)Value, CultureInfo.InvariantCulture); 152 | } 153 | 154 | public double Double() { 155 | return double.Parse((string)Value, CultureInfo.InvariantCulture); 156 | } 157 | 158 | public decimal Decimal() { 159 | return decimal.Parse((string)Value, CultureInfo.InvariantCulture); 160 | } 161 | 162 | public DateTime DateTime() { 163 | string stringValue = (string)Value; 164 | if (EnglishTimeFormat.IsMatch(stringValue)) { 165 | stringValue = EnglishTimeFormat.Replace(stringValue, "T$1:$2:$3.$4Z"); 166 | } 167 | return System.DateTime.Parse(stringValue, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal); 168 | } 169 | 170 | public JsonObject Object() { 171 | return (JsonObject)Value; 172 | } 173 | 174 | public JsonArray Array() { 175 | return (JsonArray)Value; 176 | } 177 | 178 | public string ToJson() { 179 | if (Type == JsonValueType.String) { 180 | return "\"" + Value + "\""; 181 | } 182 | return Value.ToString()!; 183 | } 184 | 185 | public void ToJson(StringBuilder builder) { 186 | if (Type == JsonValueType.String) { 187 | builder.Append("\""); 188 | builder.Append(Value.ToString()); 189 | builder.Append("\""); 190 | } 191 | else { 192 | builder.Append(Value.ToString()); 193 | } 194 | } 195 | 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /Mimer.Framework/Mimer.Framework.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Cryptography/CryptSignature.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework; 2 | using Mimer.Framework.Json; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | 6 | namespace Mimer.Notes.Model.Cryptography { 7 | public class CryptSignature : IDisposable { 8 | private bool disposedValue; 9 | private RSA? _rsa; 10 | private string _algorithm; 11 | private const string DEFAULT_SYMMETRIC_ALGORITHM = "AES;CBC;PKCS7;32"; 12 | 13 | 14 | public CryptSignature(string algorithm) { 15 | if (algorithm != "RSA;3072" && algorithm != "RSA;4096") { 16 | throw new ArgumentException($"Algorithm not supported {algorithm}"); 17 | } 18 | _algorithm = algorithm; 19 | _rsa = RSA.Create(); 20 | } 21 | 22 | public CryptSignature(string algorithm, string key) { 23 | if (algorithm != "RSA;3072" && algorithm != "RSA;4096") { 24 | throw new ArgumentException($"Algorithm not supported {algorithm}"); 25 | } 26 | _algorithm = algorithm; 27 | _rsa = RSA.Create(); 28 | _rsa.ImportFromPem(key); 29 | } 30 | 31 | protected virtual void Dispose(bool disposing) { 32 | if (!disposedValue) { 33 | if (_rsa != null) { 34 | _rsa.Dispose(); 35 | _rsa = null; 36 | } 37 | disposedValue = true; 38 | } 39 | } 40 | 41 | public void Dispose() { 42 | Dispose(disposing: true); 43 | GC.SuppressFinalize(this); 44 | } 45 | 46 | public void SignRequest(string name, ISignable signable) { 47 | var payload = Encoding.UTF8.GetBytes(signable.PayloadToSign); 48 | var signature = _rsa!.SignData(payload, 0, payload.Length, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); 49 | signable.AddSignature(name, Convert.ToBase64String(signature)); 50 | } 51 | 52 | public bool VerifySignature(string name, ISignable signable) { 53 | var payload = Encoding.UTF8.GetBytes(signable.PayloadToSign); 54 | var signature = signable.GetSignature(name); 55 | if (signature == null) { 56 | throw new KeyNotFoundException(name); 57 | } 58 | var result = _rsa!.VerifyData(payload, Convert.FromBase64String(signature), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); 59 | if (!result) { 60 | Dev.Log("Signature validation failed", name, signature); 61 | } 62 | return result; 63 | } 64 | 65 | public string Encrypt(string data) { 66 | return Encrypt(Encoding.UTF8.GetBytes(data)); 67 | } 68 | 69 | public string Encrypt(byte[] data) { 70 | var crypt = new SymmetricCrypt(DEFAULT_SYMMETRIC_ALGORITHM); 71 | var encryptedData = crypt.Encrypt(data); 72 | var encryptedKey = Convert.ToBase64String(_rsa!.Encrypt(crypt.Key, RSAEncryptionPadding.OaepSHA256)); 73 | JsonObject json = new JsonObject(); 74 | json.String("data", encryptedData); 75 | json.String("encryptedKey", encryptedKey); 76 | return Convert.ToBase64String(Encoding.UTF8.GetBytes(json.ToString())); 77 | } 78 | 79 | public string Decrypt(string data) { 80 | var json = new JsonObject(Encoding.UTF8.GetString(Convert.FromBase64String(data))); 81 | byte[] key = _rsa!.Decrypt(Convert.FromBase64String(json.String("encryptedKey")), RSAEncryptionPadding.OaepSHA256); 82 | var crypt = new SymmetricCrypt(DEFAULT_SYMMETRIC_ALGORITHM, key); 83 | return crypt.Decrypt(json.String("data")); 84 | } 85 | 86 | public string PrivateKey { 87 | get { 88 | return _rsa!.ExportPkcs8PrivateKeyPem(); 89 | } 90 | } 91 | 92 | public string PublicKey { 93 | get { 94 | return _rsa!.ExportSubjectPublicKeyInfoPem(); 95 | } 96 | } 97 | 98 | public string Algorithm { 99 | get { 100 | return _algorithm; 101 | } 102 | } 103 | 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Cryptography/KeySet.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Cryptography { 5 | public class KeySet { 6 | public Guid Id { get; } 7 | public Guid Name { get; } 8 | public SymmetricCrypt Symmetric { get; } 9 | public CryptSignature Signature { get; } 10 | public JsonObject Metadata { get; } 11 | public KeySet(Guid id, Guid name, SymmetricCrypt symmetric, CryptSignature signature, JsonObject metadata) { 12 | Id = id; 13 | Name = name; 14 | Symmetric = symmetric; 15 | Signature = signature; 16 | Metadata = metadata; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Cryptography/PasswordHasher.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | namespace Mimer.Notes.Model.Cryptography { 4 | public class PasswordHasher { 5 | public static readonly PasswordHasher Instance = new PasswordHasher(); 6 | 7 | public string HashPassword(string password, string salt, string algorithm, int iterations) { 8 | var algorithmParts = algorithm.Split(';'); 9 | if (algorithmParts[0] != "PBKDF2") { 10 | throw new ArgumentException($"Algorithm not supported {algorithm}"); 11 | } 12 | if (algorithmParts[1] != "SHA512") { 13 | throw new ArgumentException($"Algorithm not supported {algorithm}"); 14 | } 15 | var size = int.Parse(algorithmParts[2]); 16 | using Rfc2898DeriveBytes deriver = new Rfc2898DeriveBytes(password, Convert.FromHexString(salt), iterations, HashAlgorithmName.SHA512); 17 | return Convert.ToHexString(deriver.GetBytes(size)); 18 | } 19 | 20 | public string ComputeResponse(string passwordHash, string challenge) { 21 | using var hmac = new HMACSHA512(Convert.FromHexString(passwordHash)); 22 | return Convert.ToHexString(hmac.ComputeHash(Convert.FromHexString(challenge))); 23 | } 24 | 25 | public string CreateSalt(int size) { 26 | var salt = new byte[size]; 27 | using var random = RandomNumberGenerator.Create(); 28 | random.GetBytes(salt); 29 | return Convert.ToHexString(salt); 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Cryptography/SymmetricCrypt.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Security.Cryptography; 3 | using System.Text; 4 | 5 | namespace Mimer.Notes.Model.Cryptography { 6 | public class SymmetricCrypt : IDisposable { 7 | private string _algorithm; 8 | private const int IV_SIZE = 16; 9 | private byte[] _key; 10 | private bool disposedValue; 11 | 12 | public SymmetricCrypt(string algorithm, byte[] key) { 13 | if (algorithm != "AES;CBC;PKCS7;32") { 14 | throw new ArgumentException($"Algorithm not supported {algorithm}"); 15 | } 16 | _algorithm = algorithm; 17 | _key = key; 18 | } 19 | 20 | public SymmetricCrypt(string algorithm) { 21 | if (algorithm != "AES;CBC;PKCS7;32") { 22 | throw new ArgumentException($"Algorithm not supported {algorithm}"); 23 | } 24 | _algorithm = algorithm; 25 | var seed = new byte[32]; 26 | var salt = new byte[32]; 27 | using var random = RandomNumberGenerator.Create(); 28 | random.GetBytes(seed); 29 | random.GetBytes(salt); 30 | using Rfc2898DeriveBytes deriver = new Rfc2898DeriveBytes(seed, salt, 10000, HashAlgorithmName.SHA512); 31 | _key = deriver.GetBytes(32); 32 | } 33 | 34 | public SymmetricCrypt(string algorithm, string password, string salt, int iterations) { 35 | if (algorithm != "AES;CBC;PKCS7;32") { 36 | throw new ArgumentException($"Algorithm not supported {algorithm}"); 37 | } 38 | _algorithm = algorithm; 39 | using Rfc2898DeriveBytes deriver = new Rfc2898DeriveBytes(password, Convert.FromHexString(salt), iterations, HashAlgorithmName.SHA512); 40 | _key = deriver.GetBytes(32); 41 | } 42 | 43 | public byte[] DecryptBytes(string data) { 44 | using var aes = Aes.Create(); 45 | aes.KeySize = _key.Length * 8; 46 | aes.Key = _key; 47 | aes.Mode = CipherMode.CBC; 48 | aes.Padding = PaddingMode.PKCS7; 49 | byte[] dataBytes = Convert.FromBase64String(data); 50 | byte[] iv = new byte[IV_SIZE]; 51 | Array.Copy(dataBytes, 0, iv, 0, IV_SIZE); 52 | aes.IV = iv; 53 | using ICryptoTransform transform = aes.CreateDecryptor(); 54 | using MemoryStream dataStream = new MemoryStream(dataBytes, IV_SIZE, dataBytes.Length - IV_SIZE); 55 | using CryptoStream cryptoStream = new CryptoStream(dataStream, transform, CryptoStreamMode.Read); 56 | using MemoryStream outStream = new MemoryStream(); 57 | cryptoStream.CopyTo(outStream); 58 | return outStream.ToArray(); 59 | } 60 | 61 | public string Decrypt(string data) { 62 | using var aes = Aes.Create(); 63 | aes.KeySize = _key.Length * 8; 64 | aes.Key = _key; 65 | aes.Mode = CipherMode.CBC; 66 | aes.Padding = PaddingMode.PKCS7; 67 | byte[] dataBytes = Convert.FromBase64String(data); 68 | byte[] iv = new byte[IV_SIZE]; 69 | Array.Copy(dataBytes, 0, iv, 0, IV_SIZE); 70 | aes.IV = iv; 71 | using ICryptoTransform transform = aes.CreateDecryptor(); 72 | using MemoryStream dataStream = new MemoryStream(dataBytes, IV_SIZE, dataBytes.Length - IV_SIZE); 73 | using CryptoStream cryptoStream = new CryptoStream(dataStream, transform, CryptoStreamMode.Read); 74 | using var reader = new StreamReader(cryptoStream, Encoding.UTF8); 75 | return reader.ReadToEnd(); 76 | } 77 | 78 | public string Encrypt(string data) { 79 | return Encrypt(Encoding.UTF8.GetBytes(data)); 80 | } 81 | 82 | public string Encrypt(byte[] data) { 83 | using var aes = Aes.Create(); 84 | aes.KeySize = _key.Length * 8; 85 | aes.Key = _key; 86 | aes.Mode = CipherMode.CBC; 87 | aes.Padding = PaddingMode.PKCS7; 88 | aes.GenerateIV(); 89 | Debug.Assert(aes.IV.Length == IV_SIZE); 90 | using MemoryStream dataStream = new MemoryStream(); 91 | dataStream.Write(aes.IV, 0, IV_SIZE); 92 | using ICryptoTransform transform = aes.CreateEncryptor(); 93 | using (CryptoStream cryptoStream = new CryptoStream(dataStream, transform, CryptoStreamMode.Write)) { 94 | cryptoStream.Write(data, 0, data.Length); 95 | } 96 | return Convert.ToBase64String(dataStream.ToArray()); 97 | } 98 | 99 | protected virtual void Dispose(bool disposing) { 100 | if (!disposedValue) { 101 | disposedValue = true; 102 | } 103 | } 104 | 105 | public void Dispose() { 106 | Dispose(true); 107 | } 108 | 109 | public string Algorithm { 110 | get { 111 | return _algorithm; 112 | } 113 | } 114 | 115 | public byte[] Key { 116 | get { 117 | return _key; 118 | } 119 | } 120 | 121 | 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/DataTypes/MimerKey.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.DataTypes { 4 | public class MimerKey { 5 | private JsonObject _json; 6 | 7 | public MimerKey() { 8 | _json = new JsonObject(); 9 | } 10 | 11 | public MimerKey(JsonObject json) { 12 | _json = json; 13 | } 14 | 15 | public Guid Id { 16 | get { 17 | return _json.Guid("id"); 18 | } 19 | set { 20 | _json.Guid("id", value); 21 | } 22 | } 23 | 24 | public Guid UserId { 25 | get { 26 | return _json.Guid("userId"); 27 | } 28 | set { 29 | _json.Guid("userId", value); 30 | } 31 | } 32 | 33 | public Guid Name { 34 | get { 35 | return _json.Guid("name"); 36 | } 37 | set { 38 | _json.Guid("name", value); 39 | } 40 | } 41 | 42 | public string Algorithm { 43 | get { 44 | return _json.String("algorithm"); 45 | } 46 | set { 47 | _json.String("algorithm", value); 48 | } 49 | } 50 | 51 | public string KeyData { 52 | get { 53 | return _json.String("keyData"); 54 | } 55 | set { 56 | _json.String("keyData", value); 57 | } 58 | } 59 | 60 | public string AsymmetricAlgorithm { 61 | get { 62 | return _json.String("asymmetricAlgorithm"); 63 | } 64 | set { 65 | _json.String("asymmetricAlgorithm", value); 66 | } 67 | } 68 | 69 | public string PublicKey { 70 | get { 71 | return _json.String("publicKey"); 72 | } 73 | set { 74 | _json.String("publicKey", value); 75 | } 76 | } 77 | 78 | public string PrivateKey { 79 | get { 80 | return _json.String("privateKey"); 81 | } 82 | set { 83 | _json.String("privateKey", value); 84 | } 85 | } 86 | 87 | public string Metadata { 88 | get { 89 | return _json.String("metadata"); 90 | } 91 | set { 92 | _json.String("metadata", value); 93 | } 94 | } 95 | 96 | 97 | public JsonObject Json() { 98 | return _json; 99 | } 100 | 101 | public string ToJsonString() { 102 | return _json.ToString(); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/DataTypes/MimerNotificationToken.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.DataTypes { 4 | public class MimerNotificationToken : ISignable { 5 | private JsonObject _json; 6 | 7 | public MimerNotificationToken() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | TokenId = Guid.NewGuid(); 11 | } 12 | 13 | public MimerNotificationToken(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Url { 18 | get { 19 | return _json.String("url"); 20 | } 21 | set { 22 | _json.String("url", value); 23 | } 24 | } 25 | 26 | public string UserId { 27 | get { 28 | return _json.String("userId"); 29 | } 30 | set { 31 | _json.String("userId", value); 32 | } 33 | } 34 | 35 | public string Username { 36 | get { 37 | return _json.String("username"); 38 | } 39 | set { 40 | _json.String("username", value); 41 | } 42 | } 43 | 44 | public string PayloadToSign { 45 | get { 46 | return _json.ToFilteredString(SignatureFilter.Default); 47 | } 48 | } 49 | 50 | public void AddSignature(string name, string signature) { 51 | if (!_json.Has("signatures")) { 52 | _json.Array("signatures", new JsonArray()); 53 | } 54 | _json.Array("signatures").Add(new JsonObject() 55 | .String("name", name) 56 | .String("signature", signature) 57 | ); 58 | } 59 | 60 | public string? GetSignature(string name) { 61 | foreach (var signature in _json.Array("signatures").AsObjects()) { 62 | if (signature.String("name") == name) { 63 | return signature.String("signature"); 64 | } 65 | } 66 | return null; 67 | } 68 | 69 | public DateTime TimeStamp { 70 | get { 71 | return _json.DateTime("timestamp"); 72 | } 73 | set { 74 | _json.DateTime("timestamp", value); 75 | } 76 | } 77 | 78 | public Guid TokenId { 79 | get { 80 | return _json.Guid("tokenId"); 81 | } 82 | set { 83 | _json.Guid("tokenId", value); 84 | } 85 | } 86 | 87 | public override string ToString() { 88 | return _json.ToString(true); 89 | } 90 | 91 | public string ToJsonString() { 92 | return _json.ToString(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/DataTypes/MimerUser.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.DataTypes { 4 | public class MimerUser { 5 | private JsonObject _json; 6 | private Guid _stableId; 7 | private long _size; 8 | private long _noteCount; 9 | private long _typeId; 10 | private string _serverConfig = "{}"; 11 | private string _clientConfig = "{}"; 12 | 13 | public MimerUser() { 14 | _json = new JsonObject(); 15 | } 16 | 17 | public MimerUser(JsonObject json, Guid stableId, long size, long noteCount, long typeId, string serverConfig, string clientConfig) { 18 | _json = json; 19 | _stableId = stableId; 20 | _size = size; 21 | _noteCount = noteCount; 22 | _typeId = typeId; 23 | _serverConfig = serverConfig; 24 | _clientConfig = clientConfig; 25 | } 26 | 27 | public string Username { 28 | get { 29 | return _json.String("username"); 30 | } 31 | set { 32 | _json.String("username", value); 33 | } 34 | } 35 | 36 | public string PublicKey { 37 | get { 38 | return _json.String("publicKey"); 39 | } 40 | set { 41 | _json.String("publicKey", value); 42 | } 43 | } 44 | 45 | public string PrivateKey { 46 | get { 47 | return _json.String("privateKey"); 48 | } 49 | set { 50 | _json.String("privateKey", value); 51 | } 52 | } 53 | 54 | public string AsymmetricAlgorithm { 55 | get { 56 | return _json.String("asymmetricAlgorithm"); 57 | } 58 | set { 59 | _json.String("asymmetricAlgorithm", value); 60 | } 61 | } 62 | 63 | public string Salt { 64 | get { 65 | return _json.String("salt"); 66 | } 67 | set { 68 | _json.String("salt", value); 69 | } 70 | } 71 | 72 | public int Iterations { 73 | get { 74 | return _json.Int32("iterations"); 75 | } 76 | set { 77 | _json.Int32("iterations", value); 78 | } 79 | } 80 | 81 | public string Algorithm { 82 | get { 83 | return _json.String("algorithm"); 84 | } 85 | set { 86 | _json.String("algorithm", value); 87 | } 88 | } 89 | 90 | public string PasswordSalt { 91 | get { 92 | return _json.Object("password").String("salt"); 93 | } 94 | set { 95 | if (!_json.Has("password")) { 96 | _json.Object("password", new JsonObject()); 97 | } 98 | _json.Object("password").String("salt", value); 99 | } 100 | } 101 | 102 | public string PasswordHash { 103 | get { 104 | return _json.Object("password").String("hash"); 105 | } 106 | set { 107 | if (!_json.Has("password")) { 108 | _json.Object("password", new JsonObject()); 109 | } 110 | _json.Object("password").String("hash", value); 111 | } 112 | } 113 | 114 | public int PasswordIterations { 115 | get { 116 | return _json.Object("password").Int32("iterations"); 117 | } 118 | set { 119 | if (!_json.Has("password")) { 120 | _json.Object("password", new JsonObject()); 121 | } 122 | _json.Object("password").Int32("iterations", value); 123 | } 124 | } 125 | 126 | public string PasswordAlgorithm { 127 | get { 128 | return _json.Object("password").String("algorithm"); 129 | } 130 | set { 131 | if (!_json.Has("password")) { 132 | _json.Object("password", new JsonObject()); 133 | } 134 | _json.Object("password").String("algorithm", value); 135 | } 136 | } 137 | 138 | public string SymmetricAlgorithm { 139 | get { 140 | return _json.String("symmetricAlgorithm"); 141 | } 142 | set { 143 | _json.String("symmetricAlgorithm", value); 144 | } 145 | } 146 | 147 | public string SymmetricKey { 148 | get { 149 | return _json.String("symmetricKey"); 150 | } 151 | set { 152 | _json.String("symmetricKey", value); 153 | } 154 | } 155 | 156 | public string Data { 157 | get { 158 | return _json.String("data"); 159 | } 160 | set { 161 | _json.String("data", value); 162 | } 163 | } 164 | 165 | public Guid Id { 166 | get { 167 | return _stableId; 168 | } 169 | } 170 | 171 | public long Size { 172 | get { 173 | return _size; 174 | } 175 | } 176 | 177 | public long NoteCount { 178 | get { 179 | return _noteCount; 180 | } 181 | } 182 | 183 | public long TypeId { 184 | get { 185 | return _typeId; 186 | } 187 | } 188 | 189 | public string ServerConfig { 190 | get { 191 | return _serverConfig; 192 | } 193 | } 194 | 195 | public string ClientConfig { 196 | get { 197 | return _clientConfig; 198 | } 199 | } 200 | 201 | public string ToJsonString() { 202 | return _json.ToString(); 203 | } 204 | 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/DataTypes/Note.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.DataTypes { 4 | public class Note { 5 | private JsonObject _json; 6 | private bool _isCache; 7 | 8 | public Note(bool isCache = false) { 9 | _json = new JsonObject(); 10 | Id = Guid.NewGuid(); 11 | _json.Object("items", new JsonObject()); 12 | _isCache = isCache; 13 | } 14 | 15 | public Note(JsonObject json, bool isCache = false) { 16 | _json = json; 17 | isCache = false; 18 | } 19 | 20 | public Guid Id { 21 | get { 22 | return _json.Guid("id"); 23 | } 24 | set { 25 | _json.Guid("id", value); 26 | } 27 | } 28 | 29 | public Guid KeyName { 30 | get { 31 | return _json.Guid("keyName"); 32 | } 33 | set { 34 | _json.Guid("keyName", value); 35 | } 36 | } 37 | public void ClearItems() { 38 | _json.Array("items", new JsonArray()); 39 | } 40 | 41 | public bool Has(string type) { 42 | return _json.Object("items").Has(type); 43 | } 44 | 45 | public NoteItem this[string type] { 46 | get { 47 | if (!_json.Object("items").Has(type)) { 48 | _json.Object("items").Object(type, new JsonObject() 49 | .Int64("version", 0) 50 | .String("type", type) 51 | .Object("data", new JsonObject()) 52 | ); 53 | } 54 | return new NoteItem(_json.Object("items").Object(type)); 55 | } 56 | } 57 | 58 | public long GetVersion(string type) { 59 | if (_json.Object("items").Has(type)) { 60 | return _json.Object("items").Object(type).Int64("version"); 61 | } 62 | return 0; 63 | } 64 | 65 | public JsonObject? GetItem(string type) { 66 | if (!_json.Object("items").Has(type)) { 67 | return null; 68 | } 69 | return _json.Object("items").Object(type); 70 | 71 | } 72 | 73 | public void LoadItem(long version, string type, string data) { 74 | _json.Object("items").Object(type, new JsonObject() 75 | .Int64("version", version) 76 | .String("type", type) 77 | .Object("data", new JsonObject(data)) 78 | ); 79 | } 80 | 81 | public List Items { 82 | get { 83 | var result = new List(); 84 | foreach (var key in _json.Object("items").Keys) { 85 | result.Add(_json.Object("items").Object(key)); 86 | } 87 | return result; 88 | } 89 | } 90 | 91 | public List ChangedItems { 92 | get { 93 | var result = new List(); 94 | foreach (var key in _json.Object("items").Keys) { 95 | JsonObject obj = _json.Object("items").Object(key); 96 | if (obj.Has("changed") && obj.Boolean("changed")) { 97 | result.Add(obj); 98 | } 99 | } 100 | return result; 101 | } 102 | } 103 | 104 | public List Types { 105 | get { 106 | return _json.Object("items").Keys; 107 | } 108 | } 109 | 110 | public bool IsCache { 111 | get { 112 | return _isCache; 113 | } 114 | } 115 | 116 | public override string ToString() { 117 | return _json.ToString(true); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/DataTypes/NoteItem.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.DataTypes { 4 | public class NoteItem { 5 | private JsonObject _json; 6 | 7 | public NoteItem() { 8 | _json = new JsonObject(); 9 | } 10 | 11 | public NoteItem(JsonObject json) { 12 | _json = json; 13 | } 14 | 15 | public long Version { 16 | get { 17 | return _json.Int64("version"); 18 | } 19 | } 20 | 21 | public string Type { 22 | get { 23 | return _json.String("type"); 24 | } 25 | } 26 | 27 | public string String(string name) { 28 | return _json.Object("data").String(name); 29 | } 30 | 31 | public NoteItem String(string name, string value) { 32 | _json.Object("data").String(name, value); 33 | MarkChanged(); 34 | return this; 35 | } 36 | 37 | public string StringOrDefault(string name, string defaultValue) { 38 | return _json.Object("data").StringOrDefault(name, defaultValue); 39 | } 40 | 41 | public DateTime DateTime(string name) { 42 | return _json.Object("data").DateTime(name); 43 | } 44 | 45 | public NoteItem DateTime(string name, DateTime value) { 46 | _json.Object("data").DateTime(name, value); 47 | MarkChanged(); 48 | return this; 49 | } 50 | 51 | public bool Has(string name) { 52 | return _json.Object("data").Has(name); 53 | } 54 | 55 | public NoteItemArray Array(string name) { 56 | return new NoteItemArray(this, _json.Object("data").Array(name)); 57 | } 58 | 59 | public NoteItem Array(string name, JsonArray value) { 60 | _json.Object("data").Array(name, value); 61 | MarkChanged(); 62 | return this; 63 | } 64 | 65 | public void MarkChanged() { 66 | _json.Boolean("changed", true); 67 | } 68 | 69 | public bool Changed { 70 | get { 71 | return _json.Has("changed") && _json.Boolean("changed"); 72 | } 73 | } 74 | 75 | public override string ToString() { 76 | return _json.ToString(true); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/DataTypes/NoteShareInfo.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.DataTypes { 4 | public class NoteShareInfo { 5 | private JsonObject _json; 6 | 7 | public NoteShareInfo() { 8 | _json = new JsonObject(); 9 | } 10 | 11 | public NoteShareInfo(JsonObject json) { 12 | _json = json; 13 | } 14 | 15 | public Guid Id { 16 | get { 17 | return _json.Guid("id"); 18 | } 19 | set { 20 | _json.Guid("id", value); 21 | } 22 | } 23 | 24 | public DateTime Created { 25 | get { 26 | if (!_json.Has("created")) { 27 | return DateTime.MinValue; 28 | } 29 | return _json.DateTime("created"); 30 | } 31 | set { 32 | _json.DateTime("created", value); 33 | } 34 | } 35 | 36 | public string Sender { 37 | get { 38 | return _json.String("sender"); 39 | } 40 | set { 41 | _json.String("sender", value); 42 | } 43 | } 44 | 45 | public string Name { 46 | get { 47 | return _json.StringOrDefault("name", "unknown node"); 48 | } 49 | set { 50 | _json.String("name", value); 51 | } 52 | } 53 | 54 | public Guid NoteId { 55 | get { 56 | return _json.Guid("noteId"); 57 | } 58 | set { 59 | _json.Guid("noteId", value); 60 | } 61 | } 62 | 63 | public Guid KeyName { 64 | get { 65 | return _json.Guid("keyName"); 66 | } 67 | set { 68 | _json.Guid("keyName", value); 69 | } 70 | } 71 | 72 | public string Algorithm { 73 | get { 74 | return _json.String("algorithm"); 75 | } 76 | set { 77 | _json.String("algorithm", value); 78 | } 79 | } 80 | 81 | public string KeyData { 82 | get { 83 | return _json.String("keyData"); 84 | } 85 | set { 86 | _json.String("keyData", value); 87 | } 88 | } 89 | 90 | public string AsymmetricAlgorithm { 91 | get { 92 | return _json.String("asymmetricAlgorithm"); 93 | } 94 | set { 95 | _json.String("asymmetricAlgorithm", value); 96 | } 97 | } 98 | 99 | public string PublicKey { 100 | get { 101 | return _json.String("publicKey"); 102 | } 103 | set { 104 | _json.String("publicKey", value); 105 | } 106 | } 107 | 108 | public string PrivateKey { 109 | get { 110 | return _json.String("privateKey"); 111 | } 112 | set { 113 | _json.String("privateKey", value); 114 | } 115 | } 116 | 117 | public override string ToString() { 118 | return _json.ToString(true); 119 | } 120 | 121 | public string ToJsonString() { 122 | return _json.ToString(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/DataTypes/ShareOffer.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.DataTypes { 4 | public class ShareOffer { 5 | private JsonObject _json; 6 | 7 | 8 | public ShareOffer(JsonObject json) { 9 | _json = json; 10 | } 11 | 12 | public string Sender { 13 | get { 14 | return _json.String("sender"); 15 | } 16 | set { 17 | _json.String("sender", value); 18 | } 19 | } 20 | 21 | public JsonObject Data { 22 | get { 23 | return _json.Object("data"); 24 | } 25 | set { 26 | _json.Object("data", value); 27 | } 28 | } 29 | 30 | public string ToJsonString() { 31 | return _json.ToString(); 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/DataTypes/UserSize.cs: -------------------------------------------------------------------------------- 1 | namespace Mimer.Notes.Model.DataTypes { 2 | public class UserSize { 3 | public long Size { get; private set; } 4 | public long NoteCount { get; private set; } 5 | 6 | public UserSize(long size, long noteCount) { 7 | Size = size; 8 | NoteCount = noteCount; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/DataTypes/UserType.cs: -------------------------------------------------------------------------------- 1 | namespace Mimer.Notes.Model.DataTypes { 2 | public class UserType { 3 | public long Id { get; private set; } 4 | public string Name { get; private set; } 5 | public long MaxTotalBytes { get; private set; } 6 | public long MaxNoteBytes { get; private set; } 7 | public long MaxNoteCount { get; private set; } 8 | public long MaxHistoryEntries { get; private set; } 9 | 10 | public UserType(long id, string name, long maxTotalBytes, long maxNoteBytes, long maxNoteCount, long maxHistoryEntries) { 11 | Id = id; 12 | Name = name; 13 | MaxTotalBytes = maxTotalBytes; 14 | MaxNoteBytes = maxNoteBytes; 15 | MaxNoteCount = maxNoteCount; 16 | MaxHistoryEntries = maxHistoryEntries; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/DataTypes/VersionConflict.cs: -------------------------------------------------------------------------------- 1 | namespace Mimer.Notes.Model.DataTypes { 2 | public class VersionConflict { 3 | public string Type { get; } 4 | public long Expected { get; } 5 | public long Actual { get; } 6 | public VersionConflict(string type, long expected, long actual) { 7 | Type = type; 8 | Expected = expected; 9 | Actual = actual; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/INonRepeatableRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Mimer.Notes.Model { 2 | public interface INonRepeatableRequest { 3 | DateTime TimeStamp { get; } 4 | Guid RequestId { get; } 5 | bool IsValid { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/IRequestObject.cs: -------------------------------------------------------------------------------- 1 | namespace Mimer.Notes.Model { 2 | public interface IRequestObject { 3 | string ToJsonString(); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/IResponseObject.cs: -------------------------------------------------------------------------------- 1 | namespace Mimer.Notes.Model { 2 | public interface IResponseObject { 3 | void SetJson(string json); 4 | string ToJsonString(); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/ISignable.cs: -------------------------------------------------------------------------------- 1 | namespace Mimer.Notes.Model { 2 | public interface ISignable { 3 | string PayloadToSign { get; } 4 | void AddSignature(string name, string signature); 5 | string? GetSignature(string name); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Mimer.Notes.Model.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/BasicRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class BasicRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public BasicRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public BasicRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public string PayloadToSign { 27 | get { 28 | return _json.ToFilteredString(SignatureFilter.Default); 29 | } 30 | } 31 | 32 | public void AddSignature(string name, string signature) { 33 | if (!_json.Has("signatures")) { 34 | _json.Array("signatures", new JsonArray()); 35 | } 36 | _json.Array("signatures").Add(new JsonObject() 37 | .String("name", name) 38 | .String("signature", signature) 39 | ); 40 | } 41 | 42 | public string? GetSignature(string name) { 43 | foreach (var signature in _json.Array("signatures").AsObjects()) { 44 | if (signature.String("name") == name) { 45 | return signature.String("signature"); 46 | } 47 | } 48 | return null; 49 | } 50 | 51 | public DateTime TimeStamp { 52 | get { 53 | return _json.DateTime("timestamp"); 54 | } 55 | set { 56 | _json.DateTime("timestamp", value); 57 | } 58 | } 59 | 60 | public Guid RequestId { 61 | get { 62 | return _json.Guid("requestId"); 63 | } 64 | set { 65 | _json.Guid("requestId", value); 66 | } 67 | } 68 | 69 | public bool IsValid { 70 | get { 71 | return !( 72 | string.IsNullOrWhiteSpace(Username) 73 | ); 74 | } 75 | } 76 | 77 | public override string ToString() { 78 | return _json.ToString(true); 79 | } 80 | 81 | public string ToJsonString() { 82 | return _json.ToString(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/CheckUsernameRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class CheckUsernameRequest : IRequestObject { 5 | private JsonObject _json; 6 | 7 | public CheckUsernameRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public CheckUsernameRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public string Pow { 27 | get { 28 | return _json.String("pow"); 29 | } 30 | set { 31 | _json.String("pow", value); 32 | } 33 | } 34 | 35 | public DateTime TimeStamp { 36 | get { 37 | return _json.DateTime("timestamp"); 38 | } 39 | set { 40 | _json.DateTime("timestamp", value); 41 | } 42 | } 43 | 44 | public Guid RequestId { 45 | get { 46 | return _json.Guid("requestId"); 47 | } 48 | set { 49 | _json.Guid("requestId", value); 50 | } 51 | } 52 | 53 | public bool IsValid { 54 | get { 55 | return !( 56 | string.IsNullOrWhiteSpace(Username) || 57 | string.IsNullOrWhiteSpace(Pow) 58 | ); 59 | } 60 | } 61 | 62 | public override string ToString() { 63 | return _json.ToString(true); 64 | } 65 | 66 | public string ToJsonString() { 67 | return _json.ToString(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/CreateKeyRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class CreateKeyRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public CreateKeyRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public CreateKeyRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public Guid Id { 27 | get { 28 | return _json.Guid("id"); 29 | } 30 | set { 31 | _json.Guid("id", value); 32 | } 33 | } 34 | 35 | public Guid Name { 36 | get { 37 | return _json.Guid("name"); 38 | } 39 | set { 40 | _json.Guid("name", value); 41 | } 42 | } 43 | 44 | public string Algorithm { 45 | get { 46 | return _json.String("algorithm"); 47 | } 48 | set { 49 | _json.String("algorithm", value); 50 | } 51 | } 52 | 53 | public string KeyData { 54 | get { 55 | return _json.String("keyData"); 56 | } 57 | set { 58 | _json.String("keyData", value); 59 | } 60 | } 61 | 62 | public string AsymmetricAlgorithm { 63 | get { 64 | return _json.String("asymmetricAlgorithm"); 65 | } 66 | set { 67 | _json.String("asymmetricAlgorithm", value); 68 | } 69 | } 70 | 71 | public string PublicKey { 72 | get { 73 | return _json.String("publicKey"); 74 | } 75 | set { 76 | _json.String("publicKey", value); 77 | } 78 | } 79 | 80 | public string PrivateKey { 81 | get { 82 | return _json.String("privateKey"); 83 | } 84 | set { 85 | _json.String("privateKey", value); 86 | } 87 | } 88 | 89 | public string Metadata { 90 | get { 91 | return _json.String("metadata"); 92 | } 93 | set { 94 | _json.String("metadata", value); 95 | } 96 | } 97 | 98 | public string PayloadToSign { 99 | get { 100 | return _json.ToFilteredString(SignatureFilter.Default); 101 | } 102 | } 103 | 104 | public void AddSignature(string name, string signature) { 105 | if (!_json.Has("signatures")) { 106 | _json.Array("signatures", new JsonArray()); 107 | } 108 | _json.Array("signatures").Add(new JsonObject() 109 | .String("name", name) 110 | .String("signature", signature) 111 | ); 112 | } 113 | 114 | public string? GetSignature(string name) { 115 | foreach (var signature in _json.Array("signatures").AsObjects()) { 116 | if (signature.String("name") == name) { 117 | return signature.String("signature"); 118 | } 119 | } 120 | return null; 121 | } 122 | 123 | public DateTime TimeStamp { 124 | get { 125 | return _json.DateTime("timestamp"); 126 | } 127 | set { 128 | _json.DateTime("timestamp", value); 129 | } 130 | } 131 | 132 | public Guid RequestId { 133 | get { 134 | return _json.Guid("requestId"); 135 | } 136 | set { 137 | _json.Guid("requestId", value); 138 | } 139 | } 140 | 141 | public bool IsValid { 142 | get { 143 | return !( 144 | string.IsNullOrWhiteSpace(Username) || 145 | string.IsNullOrWhiteSpace(PrivateKey) || 146 | string.IsNullOrWhiteSpace(Algorithm) || 147 | string.IsNullOrWhiteSpace(AsymmetricAlgorithm) || 148 | string.IsNullOrWhiteSpace(PublicKey) || 149 | string.IsNullOrWhiteSpace(PrivateKey) || 150 | string.IsNullOrWhiteSpace(Metadata) || 151 | Id == Guid.Empty || 152 | Name == Guid.Empty 153 | ); 154 | } 155 | } 156 | 157 | public override string ToString() { 158 | return _json.ToString(true); 159 | } 160 | 161 | public string ToJsonString() { 162 | return _json.ToString(); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/CreateUserRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class CreateUserRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public CreateUserRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public CreateUserRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public string PublicKey { 27 | get { 28 | return _json.String("publicKey"); 29 | } 30 | set { 31 | _json.String("publicKey", value); 32 | } 33 | } 34 | 35 | public string PrivateKey { 36 | get { 37 | return _json.String("privateKey"); 38 | } 39 | set { 40 | _json.String("privateKey", value); 41 | } 42 | } 43 | 44 | public string AsymmetricAlgorithm { 45 | get { 46 | return _json.String("asymmetricAlgorithm"); 47 | } 48 | set { 49 | _json.String("asymmetricAlgorithm", value); 50 | } 51 | } 52 | 53 | public string Salt { 54 | get { 55 | return _json.String("salt"); 56 | } 57 | set { 58 | _json.String("salt", value); 59 | } 60 | } 61 | 62 | public int Iterations { 63 | get { 64 | return _json.Int32("iterations"); 65 | } 66 | set { 67 | _json.Int32("iterations", value); 68 | } 69 | } 70 | 71 | public string Algorithm { 72 | get { 73 | return _json.String("algorithm"); 74 | } 75 | set { 76 | _json.String("algorithm", value); 77 | } 78 | } 79 | 80 | public string PasswordSalt { 81 | get { 82 | return _json.Object("password").String("salt"); 83 | } 84 | set { 85 | if (!_json.Has("password")) { 86 | _json.Object("password", new JsonObject()); 87 | } 88 | _json.Object("password").String("salt", value); 89 | } 90 | } 91 | 92 | public string PasswordHash { 93 | get { 94 | return _json.Object("password").String("hash"); 95 | } 96 | set { 97 | if (!_json.Has("password")) { 98 | _json.Object("password", new JsonObject()); 99 | } 100 | _json.Object("password").String("hash", value); 101 | } 102 | } 103 | 104 | public int PasswordIterations { 105 | get { 106 | return _json.Object("password").Int32("iterations"); 107 | } 108 | set { 109 | if (!_json.Has("password")) { 110 | _json.Object("password", new JsonObject()); 111 | } 112 | _json.Object("password").Int32("iterations", value); 113 | } 114 | } 115 | 116 | public string PasswordAlgorithm { 117 | get { 118 | return _json.Object("password").String("algorithm"); 119 | } 120 | set { 121 | if (!_json.Has("password")) { 122 | _json.Object("password", new JsonObject()); 123 | } 124 | _json.Object("password").String("algorithm", value); 125 | } 126 | } 127 | 128 | public string SymmetricAlgorithm { 129 | get { 130 | return _json.String("symmetricAlgorithm"); 131 | } 132 | set { 133 | _json.String("symmetricAlgorithm", value); 134 | } 135 | } 136 | 137 | public string SymmetricKey { 138 | get { 139 | return _json.String("symmetricKey"); 140 | } 141 | set { 142 | _json.String("symmetricKey", value); 143 | } 144 | } 145 | 146 | public string Data { 147 | get { 148 | return _json.String("data"); 149 | } 150 | set { 151 | _json.String("data", value); 152 | } 153 | } 154 | 155 | 156 | public string PayloadToSign { 157 | get { 158 | return _json.ToFilteredString(SignatureFilter.Default); 159 | } 160 | } 161 | 162 | public void AddSignature(string name, string signature) { 163 | if (!_json.Has("signatures")) { 164 | _json.Array("signatures", new JsonArray()); 165 | } 166 | _json.Array("signatures").Add(new JsonObject() 167 | .String("name", name) 168 | .String("signature", signature) 169 | ); 170 | } 171 | 172 | public string? GetSignature(string name) { 173 | foreach (var signature in _json.Array("signatures").AsObjects()) { 174 | if (signature.String("name") == name) { 175 | return signature.String("signature"); 176 | } 177 | } 178 | return null; 179 | } 180 | 181 | public DateTime TimeStamp { 182 | get { 183 | return _json.DateTime("timestamp"); 184 | } 185 | set { 186 | _json.DateTime("timestamp", value); 187 | } 188 | } 189 | 190 | public Guid RequestId { 191 | get { 192 | return _json.Guid("requestId"); 193 | } 194 | set { 195 | _json.Guid("requestId", value); 196 | } 197 | } 198 | 199 | public bool IsValid { 200 | get { 201 | return !( 202 | string.IsNullOrWhiteSpace(Username) || 203 | string.IsNullOrWhiteSpace(PrivateKey) || 204 | string.IsNullOrWhiteSpace(Salt) || 205 | string.IsNullOrWhiteSpace(Algorithm) || 206 | string.IsNullOrWhiteSpace(PasswordSalt) || 207 | string.IsNullOrWhiteSpace(PasswordHash) || 208 | string.IsNullOrWhiteSpace(PasswordAlgorithm) || 209 | string.IsNullOrWhiteSpace(SymmetricAlgorithm) || 210 | string.IsNullOrWhiteSpace(Data) || 211 | string.IsNullOrWhiteSpace(SymmetricKey) 212 | ); 213 | } 214 | } 215 | 216 | public override string ToString() { 217 | return _json.ToString(true); 218 | } 219 | 220 | public string ToJsonString() { 221 | return _json.ToString(); 222 | } 223 | 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/DeleteAccountRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class DeleteAccountRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public DeleteAccountRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public DeleteAccountRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Response { 18 | get { 19 | if (_json.Has("response")) { 20 | return _json.String("response"); 21 | } 22 | return ""; 23 | } 24 | set { 25 | _json.String("response", value); 26 | } 27 | } 28 | 29 | public int HashLength { 30 | get { 31 | if (!_json.Has("hashLength")) { 32 | return 8192; 33 | } 34 | return _json.Int32("hashLength"); 35 | } 36 | set { 37 | _json.Int32("hashLength", value); 38 | } 39 | } 40 | 41 | public string Username { 42 | get { 43 | return _json.String("username"); 44 | } 45 | set { 46 | _json.String("username", value); 47 | } 48 | } 49 | 50 | public string PayloadToSign { 51 | get { 52 | return _json.ToFilteredString(SignatureFilter.Default); 53 | } 54 | } 55 | 56 | public void AddSignature(string name, string signature) { 57 | if (!_json.Has("signatures")) { 58 | _json.Array("signatures", new JsonArray()); 59 | } 60 | _json.Array("signatures").Add(new JsonObject() 61 | .String("name", name) 62 | .String("signature", signature) 63 | ); 64 | } 65 | 66 | public string? GetSignature(string name) { 67 | foreach (var signature in _json.Array("signatures").AsObjects()) { 68 | if (signature.String("name") == name) { 69 | return signature.String("signature"); 70 | } 71 | } 72 | return null; 73 | } 74 | 75 | public DateTime TimeStamp { 76 | get { 77 | return _json.DateTime("timestamp"); 78 | } 79 | set { 80 | _json.DateTime("timestamp", value); 81 | } 82 | } 83 | 84 | public Guid RequestId { 85 | get { 86 | return _json.Guid("requestId"); 87 | } 88 | set { 89 | _json.Guid("requestId", value); 90 | } 91 | } 92 | 93 | public bool IsValid { 94 | get { 95 | return !(string.IsNullOrWhiteSpace(Username)); 96 | } 97 | } 98 | 99 | public override string ToString() { 100 | return _json.ToString(true); 101 | } 102 | 103 | public string ToJsonString() { 104 | return _json.ToString(); 105 | } 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/DeleteKeyRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class DeleteKeyRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public DeleteKeyRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public DeleteKeyRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public Guid Id { 27 | get { 28 | return _json.Guid("id"); 29 | } 30 | set { 31 | _json.Guid("id", value); 32 | } 33 | } 34 | 35 | public string PayloadToSign { 36 | get { 37 | return _json.ToFilteredString(SignatureFilter.Default); 38 | } 39 | } 40 | 41 | public void AddSignature(string name, string signature) { 42 | if (!_json.Has("signatures")) { 43 | _json.Array("signatures", new JsonArray()); 44 | } 45 | _json.Array("signatures").Add(new JsonObject() 46 | .String("name", name) 47 | .String("signature", signature) 48 | ); 49 | } 50 | 51 | public string? GetSignature(string name) { 52 | foreach (var signature in _json.Array("signatures").AsObjects()) { 53 | if (signature.String("name") == name) { 54 | return signature.String("signature"); 55 | } 56 | } 57 | return null; 58 | } 59 | 60 | public DateTime TimeStamp { 61 | get { 62 | return _json.DateTime("timestamp"); 63 | } 64 | set { 65 | _json.DateTime("timestamp", value); 66 | } 67 | } 68 | 69 | public Guid RequestId { 70 | get { 71 | return _json.Guid("requestId"); 72 | } 73 | set { 74 | _json.Guid("requestId", value); 75 | } 76 | } 77 | 78 | public bool IsValid { 79 | get { 80 | return !( 81 | string.IsNullOrWhiteSpace(Username) || 82 | Id == Guid.Empty 83 | ); 84 | } 85 | } 86 | 87 | public override string ToString() { 88 | return _json.ToString(true); 89 | } 90 | 91 | public string ToJsonString() { 92 | return _json.ToString(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/DeleteNoteRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class DeleteNoteRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public DeleteNoteRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public DeleteNoteRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public Guid Id { 27 | get { 28 | return _json.Guid("id"); 29 | } 30 | set { 31 | _json.Guid("id", value); 32 | } 33 | } 34 | 35 | public string PayloadToSign { 36 | get { 37 | return _json.ToFilteredString(SignatureFilter.Default); 38 | } 39 | } 40 | 41 | public void AddSignature(string name, string signature) { 42 | if (!_json.Has("signatures")) { 43 | _json.Array("signatures", new JsonArray()); 44 | } 45 | _json.Array("signatures").Add(new JsonObject() 46 | .String("name", name) 47 | .String("signature", signature) 48 | ); 49 | } 50 | 51 | public string? GetSignature(string name) { 52 | foreach (var signature in _json.Array("signatures").AsObjects()) { 53 | if (signature.String("name") == name) { 54 | return signature.String("signature"); 55 | } 56 | } 57 | return null; 58 | } 59 | 60 | public DateTime TimeStamp { 61 | get { 62 | return _json.DateTime("timestamp"); 63 | } 64 | set { 65 | _json.DateTime("timestamp", value); 66 | } 67 | } 68 | 69 | public Guid RequestId { 70 | get { 71 | return _json.Guid("requestId"); 72 | } 73 | set { 74 | _json.Guid("requestId", value); 75 | } 76 | } 77 | 78 | public bool IsValid { 79 | get { 80 | return !( 81 | string.IsNullOrWhiteSpace(Username) || 82 | Id == Guid.Empty 83 | ); 84 | } 85 | } 86 | 87 | public override string ToString() { 88 | return _json.ToString(true); 89 | } 90 | 91 | public string ToJsonString() { 92 | return _json.ToString(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/DeleteShareRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class DeleteShareRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public DeleteShareRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public DeleteShareRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public Guid Id { 27 | get { 28 | return _json.Guid("id"); 29 | } 30 | set { 31 | _json.Guid("id", value); 32 | } 33 | } 34 | 35 | public string PayloadToSign { 36 | get { 37 | return _json.ToFilteredString(SignatureFilter.Default); 38 | } 39 | } 40 | 41 | public void AddSignature(string name, string signature) { 42 | if (!_json.Has("signatures")) { 43 | _json.Array("signatures", new JsonArray()); 44 | } 45 | _json.Array("signatures").Add(new JsonObject() 46 | .String("name", name) 47 | .String("signature", signature) 48 | ); 49 | } 50 | 51 | public string? GetSignature(string name) { 52 | foreach (var signature in _json.Array("signatures").AsObjects()) { 53 | if (signature.String("name") == name) { 54 | return signature.String("signature"); 55 | } 56 | } 57 | return null; 58 | } 59 | 60 | public DateTime TimeStamp { 61 | get { 62 | return _json.DateTime("timestamp"); 63 | } 64 | set { 65 | _json.DateTime("timestamp", value); 66 | } 67 | } 68 | 69 | public Guid RequestId { 70 | get { 71 | return _json.Guid("requestId"); 72 | } 73 | set { 74 | _json.Guid("requestId", value); 75 | } 76 | } 77 | 78 | public bool IsValid { 79 | get { 80 | return !( 81 | string.IsNullOrWhiteSpace(Username) || 82 | Id == Guid.Empty 83 | ); 84 | } 85 | } 86 | 87 | public override string ToString() { 88 | return _json.ToString(true); 89 | } 90 | 91 | public string ToJsonString() { 92 | return _json.ToString(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/LoginRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class LoginRequest : IRequestObject { 5 | private JsonObject _json; 6 | 7 | public LoginRequest() { 8 | _json = new JsonObject(); 9 | } 10 | 11 | public LoginRequest(JsonObject json) { 12 | _json = json; 13 | } 14 | 15 | public string Username { 16 | get { 17 | return _json.String("username"); 18 | } 19 | set { 20 | _json.String("username", value); 21 | } 22 | } 23 | 24 | public string Response { 25 | get { 26 | return _json.String("response"); 27 | } 28 | set { 29 | _json.String("response", value); 30 | } 31 | } 32 | 33 | public int HashLength { 34 | get { 35 | if (!_json.Has("hashLength")) { 36 | return 8192; 37 | } 38 | return _json.Int32("hashLength"); 39 | } 40 | set { 41 | _json.Int32("hashLength", value); 42 | } 43 | } 44 | 45 | public bool IsValid { 46 | get { 47 | return !( 48 | string.IsNullOrWhiteSpace(Username) || 49 | string.IsNullOrWhiteSpace(Response) 50 | ); 51 | } 52 | } 53 | 54 | public override string ToString() { 55 | return _json.ToString(true); 56 | } 57 | 58 | public string ToJsonString() { 59 | return _json.ToString(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/MultiNoteRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public interface INoteItem { 5 | long Version { get; set; } 6 | string Type { get; set; } 7 | string Data { get; set; } 8 | } 9 | 10 | public class NoteItem : INoteItem { 11 | private JsonObject _json; 12 | 13 | public NoteItem() { 14 | _json = new JsonObject(); 15 | } 16 | 17 | public NoteItem(JsonObject json) { 18 | _json = json; 19 | } 20 | 21 | public string Type { 22 | get { 23 | return _json.String("type"); 24 | } 25 | set { 26 | _json.String("type", value); 27 | } 28 | } 29 | 30 | public long Version { 31 | get { 32 | return _json.Int64("version"); 33 | } 34 | set { 35 | _json.Int64("version", value); 36 | } 37 | } 38 | 39 | public string Data { 40 | get { 41 | return _json.String("data"); 42 | } 43 | set { 44 | _json.String("data", value); 45 | } 46 | } 47 | } 48 | 49 | public class NoteAction { 50 | private JsonObject _json; 51 | 52 | public NoteAction() { 53 | _json = new JsonObject(); 54 | _json.Array("items", new JsonArray()); 55 | } 56 | 57 | public NoteAction(JsonObject json) { 58 | _json = json; 59 | } 60 | 61 | public string Type { 62 | get { 63 | return _json.String("type"); 64 | } 65 | set { 66 | _json.String("type", value); 67 | } 68 | } 69 | 70 | public Guid Id { 71 | get { 72 | return _json.Guid("id"); 73 | } 74 | set { 75 | _json.Guid("id", value); 76 | } 77 | } 78 | 79 | public Guid KeyName { 80 | get { 81 | return _json.Guid("keyName"); 82 | } 83 | set { 84 | _json.Guid("keyName", value); 85 | } 86 | } 87 | 88 | public Guid OldKeyName { 89 | get { 90 | if (_json.Has("oldKeyName")) { 91 | return _json.Guid("oldKeyName"); 92 | } 93 | return Guid.Empty; 94 | } 95 | set { 96 | _json.Guid("oldKeyName", value); 97 | } 98 | } 99 | 100 | public void AddItem(long version, string type, string data) { 101 | _json.Array("items").Add(new JsonObject() 102 | .Int64("version", version) 103 | .String("type", type) 104 | .String("data", data) 105 | ); 106 | } 107 | 108 | public List Items { 109 | get { 110 | return _json.Array("items").AsObjects().Select(json => new NoteItem(json)).ToList(); 111 | } 112 | } 113 | 114 | public JsonObject Json { 115 | get { 116 | return _json; 117 | } 118 | } 119 | 120 | public bool IsValid { 121 | get { 122 | return !( 123 | string.IsNullOrWhiteSpace(Type) || 124 | Id == Guid.Empty || 125 | KeyName == Guid.Empty 126 | ); 127 | } 128 | } 129 | 130 | 131 | } 132 | 133 | public class MultiNoteRequest : ISignable, IRequestObject, INonRepeatableRequest { 134 | private JsonObject _json; 135 | 136 | public MultiNoteRequest() { 137 | _json = new JsonObject(); 138 | _json.Array("actions", new JsonArray()); 139 | TimeStamp = DateTime.UtcNow; 140 | RequestId = Guid.NewGuid(); 141 | } 142 | 143 | public MultiNoteRequest(JsonObject json) { 144 | _json = json; 145 | } 146 | 147 | public string Username { 148 | get { 149 | return _json.String("username"); 150 | } 151 | set { 152 | _json.String("username", value); 153 | } 154 | } 155 | 156 | public void AddAction(NoteAction action) { 157 | _json.Array("actions").Add(action.Json); 158 | } 159 | 160 | public List Actions { 161 | get { 162 | return _json.Array("actions").AsObjects().Select(json => new NoteAction(json)).ToList(); 163 | } 164 | } 165 | 166 | public List KeyNames { 167 | get { 168 | var keys = new HashSet(); 169 | foreach (var action in Actions) { 170 | keys.Add(action.KeyName); 171 | if (action.OldKeyName != Guid.Empty) { 172 | keys.Add(action.OldKeyName); 173 | } 174 | } 175 | return keys.ToList(); 176 | } 177 | } 178 | 179 | public string PayloadToSign { 180 | get { 181 | return _json.ToFilteredString(SignatureFilter.Default); 182 | } 183 | } 184 | 185 | public void AddSignature(string name, string signature) { 186 | if (!_json.Has("signatures")) { 187 | _json.Array("signatures", new JsonArray()); 188 | } 189 | _json.Array("signatures").Add(new JsonObject() 190 | .String("name", name) 191 | .String("signature", signature) 192 | ); 193 | } 194 | 195 | public string? GetSignature(string name) { 196 | foreach (var signature in _json.Array("signatures").AsObjects()) { 197 | if (signature.String("name") == name) { 198 | return signature.String("signature"); 199 | } 200 | } 201 | return null; 202 | } 203 | 204 | public DateTime TimeStamp { 205 | get { 206 | return _json.DateTime("timestamp"); 207 | } 208 | set { 209 | _json.DateTime("timestamp", value); 210 | } 211 | } 212 | 213 | public Guid RequestId { 214 | get { 215 | return _json.Guid("requestId"); 216 | } 217 | set { 218 | _json.Guid("requestId", value); 219 | } 220 | } 221 | 222 | public bool IsValid { 223 | get { 224 | return !string.IsNullOrWhiteSpace(Username) && !Actions.Any(action => !action.IsValid); 225 | } 226 | } 227 | 228 | public override string ToString() { 229 | return _json.ToString(true); 230 | } 231 | 232 | public string ToJsonString() { 233 | return _json.ToString(); 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/PublicKeyRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class PublicKeyRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public PublicKeyRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public PublicKeyRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public string KeyOwnerName { 27 | get { 28 | return _json.String("keyOwnerName"); 29 | } 30 | set { 31 | _json.String("keyOwnerName", value); 32 | } 33 | } 34 | 35 | public string PayloadToSign { 36 | get { 37 | return _json.ToFilteredString(SignatureFilter.Default); 38 | } 39 | } 40 | 41 | public void AddSignature(string name, string signature) { 42 | if (!_json.Has("signatures")) { 43 | _json.Array("signatures", new JsonArray()); 44 | } 45 | _json.Array("signatures").Add(new JsonObject() 46 | .String("name", name) 47 | .String("signature", signature) 48 | ); 49 | } 50 | 51 | public string? GetSignature(string name) { 52 | foreach (var signature in _json.Array("signatures").AsObjects()) { 53 | if (signature.String("name") == name) { 54 | return signature.String("signature"); 55 | } 56 | } 57 | return null; 58 | } 59 | 60 | public DateTime TimeStamp { 61 | get { 62 | return _json.DateTime("timestamp"); 63 | } 64 | set { 65 | _json.DateTime("timestamp", value); 66 | } 67 | } 68 | 69 | public Guid RequestId { 70 | get { 71 | return _json.Guid("requestId"); 72 | } 73 | set { 74 | _json.Guid("requestId", value); 75 | } 76 | } 77 | 78 | public bool IsValid { 79 | get { 80 | return !( 81 | string.IsNullOrWhiteSpace(Username) || 82 | string.IsNullOrWhiteSpace(KeyOwnerName) 83 | ); 84 | } 85 | } 86 | 87 | public override string ToString() { 88 | return _json.ToString(true); 89 | } 90 | 91 | public string ToJsonString() { 92 | return _json.ToString(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/ReadKeyRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class ReadKeyRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public ReadKeyRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public ReadKeyRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public Guid Id { 27 | get { 28 | return _json.Guid("id"); 29 | } 30 | set { 31 | _json.Guid("id", value); 32 | } 33 | } 34 | 35 | public string PayloadToSign { 36 | get { 37 | return _json.ToFilteredString(SignatureFilter.Default); 38 | } 39 | } 40 | 41 | public void AddSignature(string name, string signature) { 42 | if (!_json.Has("signatures")) { 43 | _json.Array("signatures", new JsonArray()); 44 | } 45 | _json.Array("signatures").Add(new JsonObject() 46 | .String("name", name) 47 | .String("signature", signature) 48 | ); 49 | } 50 | 51 | public string? GetSignature(string name) { 52 | foreach (var signature in _json.Array("signatures").AsObjects()) { 53 | if (signature.String("name") == name) { 54 | return signature.String("signature"); 55 | } 56 | } 57 | return null; 58 | } 59 | 60 | public DateTime TimeStamp { 61 | get { 62 | return _json.DateTime("timestamp"); 63 | } 64 | set { 65 | _json.DateTime("timestamp", value); 66 | } 67 | } 68 | 69 | public Guid RequestId { 70 | get { 71 | return _json.Guid("requestId"); 72 | } 73 | set { 74 | _json.Guid("requestId", value); 75 | } 76 | } 77 | 78 | public bool IsValid { 79 | get { 80 | return !( 81 | string.IsNullOrWhiteSpace(Username) || 82 | Id == Guid.Empty 83 | ); 84 | } 85 | } 86 | 87 | public override string ToString() { 88 | return _json.ToString(true); 89 | } 90 | 91 | public string ToJsonString() { 92 | return _json.ToString(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/ReadNoteRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class ReadNoteRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public ReadNoteRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public ReadNoteRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public Guid Id { 27 | get { 28 | return _json.Guid("id"); 29 | } 30 | set { 31 | _json.Guid("id", value); 32 | } 33 | } 34 | 35 | public string Include { 36 | get { 37 | return _json.String("include"); 38 | } 39 | set { 40 | _json.String("include", value); 41 | } 42 | } 43 | 44 | public void AddVersion(string type, long version) { 45 | if (!_json.Has("versions")) { 46 | _json.Array("versions", new JsonArray()); 47 | } 48 | _json.Array("versions").Add(new JsonObject() 49 | .String("type", type) 50 | .Int64("version", version) 51 | ); 52 | } 53 | 54 | public bool isNewer(string type, long version) { 55 | if (!_json.Has("versions")) { 56 | return true; 57 | } 58 | foreach (var item in _json.Array("versions").AsObjects()) { 59 | if (item.String("type") == type) { 60 | return version > item.Int64("version"); 61 | } 62 | } 63 | return true; 64 | } 65 | 66 | public string PayloadToSign { 67 | get { 68 | return _json.ToFilteredString(SignatureFilter.Default); 69 | } 70 | } 71 | 72 | public void AddSignature(string name, string signature) { 73 | if (!_json.Has("signatures")) { 74 | _json.Array("signatures", new JsonArray()); 75 | } 76 | _json.Array("signatures").Add(new JsonObject() 77 | .String("name", name) 78 | .String("signature", signature) 79 | ); 80 | } 81 | 82 | public string? GetSignature(string name) { 83 | foreach (var signature in _json.Array("signatures").AsObjects()) { 84 | if (signature.String("name") == name) { 85 | return signature.String("signature"); 86 | } 87 | } 88 | return null; 89 | } 90 | 91 | public DateTime TimeStamp { 92 | get { 93 | return _json.DateTime("timestamp"); 94 | } 95 | set { 96 | _json.DateTime("timestamp", value); 97 | } 98 | } 99 | 100 | public Guid RequestId { 101 | get { 102 | return _json.Guid("requestId"); 103 | } 104 | set { 105 | _json.Guid("requestId", value); 106 | } 107 | } 108 | 109 | public bool IsValid { 110 | get { 111 | return !( 112 | string.IsNullOrWhiteSpace(Username) || 113 | string.IsNullOrWhiteSpace(Include) || 114 | Id == Guid.Empty 115 | ); 116 | } 117 | } 118 | 119 | public override string ToString() { 120 | return _json.ToString(true); 121 | } 122 | 123 | public string ToJsonString() { 124 | return _json.ToString(); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/ServerNotificationRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class ServerNotificationRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public ServerNotificationRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public ServerNotificationRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public Guid Sender { 18 | get { 19 | return _json.Guid("sender"); 20 | } 21 | set { 22 | _json.Guid("sender", value); 23 | } 24 | } 25 | 26 | public string Recipients { 27 | get { 28 | return _json.String("recipients"); 29 | } 30 | set { 31 | _json.String("recipients", value); 32 | } 33 | } 34 | 35 | public string Type { 36 | get { 37 | return _json.String("type"); 38 | } 39 | set { 40 | _json.String("type", value); 41 | } 42 | } 43 | 44 | public string Payload { 45 | get { 46 | return _json.String("payload"); 47 | } 48 | set { 49 | _json.String("payload", value); 50 | } 51 | } 52 | 53 | public string PayloadToSign { 54 | get { 55 | return _json.ToFilteredString(SignatureFilter.Default); 56 | } 57 | } 58 | 59 | public void AddSignature(string name, string signature) { 60 | if (!_json.Has("signatures")) { 61 | _json.Array("signatures", new JsonArray()); 62 | } 63 | _json.Array("signatures").Add(new JsonObject() 64 | .String("name", name) 65 | .String("signature", signature) 66 | ); 67 | } 68 | 69 | public string? GetSignature(string name) { 70 | foreach (var signature in _json.Array("signatures").AsObjects()) { 71 | if (signature.String("name") == name) { 72 | return signature.String("signature"); 73 | } 74 | } 75 | return null; 76 | } 77 | 78 | public DateTime TimeStamp { 79 | get { 80 | return _json.DateTime("timestamp"); 81 | } 82 | set { 83 | _json.DateTime("timestamp", value); 84 | } 85 | } 86 | 87 | public Guid RequestId { 88 | get { 89 | return _json.Guid("requestId"); 90 | } 91 | set { 92 | _json.Guid("requestId", value); 93 | } 94 | } 95 | 96 | public bool IsValid { 97 | get { 98 | return !( 99 | string.IsNullOrWhiteSpace(Recipients) || 100 | Sender == Guid.Empty || 101 | string.IsNullOrWhiteSpace(Type) || 102 | string.IsNullOrWhiteSpace(Payload) 103 | ); 104 | } 105 | } 106 | 107 | public override string ToString() { 108 | return _json.ToString(true); 109 | } 110 | 111 | public string ToJsonString() { 112 | return _json.ToString(); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/ShareNoteRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class ShareNoteRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public ShareNoteRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public ShareNoteRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public string Recipient { 27 | get { 28 | return _json.String("recipient"); 29 | } 30 | set { 31 | _json.String("recipient", value); 32 | } 33 | } 34 | 35 | public Guid KeyName { 36 | get { 37 | return _json.Guid("keyName"); 38 | } 39 | set { 40 | _json.Guid("keyName", value); 41 | } 42 | } 43 | 44 | public string Data { 45 | get { 46 | return _json.String("data"); 47 | } 48 | set { 49 | _json.String("data", value); 50 | } 51 | } 52 | 53 | public string PayloadToSign { 54 | get { 55 | return _json.ToFilteredString(SignatureFilter.Default); 56 | } 57 | } 58 | 59 | public void AddSignature(string name, string signature) { 60 | if (!_json.Has("signatures")) { 61 | _json.Array("signatures", new JsonArray()); 62 | } 63 | _json.Array("signatures").Add(new JsonObject() 64 | .String("name", name) 65 | .String("signature", signature) 66 | ); 67 | } 68 | 69 | public string? GetSignature(string name) { 70 | foreach (var signature in _json.Array("signatures").AsObjects()) { 71 | if (signature.String("name") == name) { 72 | return signature.String("signature"); 73 | } 74 | } 75 | return null; 76 | } 77 | 78 | public DateTime TimeStamp { 79 | get { 80 | return _json.DateTime("timestamp"); 81 | } 82 | set { 83 | _json.DateTime("timestamp", value); 84 | } 85 | } 86 | 87 | public Guid RequestId { 88 | get { 89 | return _json.Guid("requestId"); 90 | } 91 | set { 92 | _json.Guid("requestId", value); 93 | } 94 | } 95 | 96 | public bool IsValid { 97 | get { 98 | return !( 99 | string.IsNullOrWhiteSpace(Username) || 100 | string.IsNullOrWhiteSpace(Recipient) || 101 | string.IsNullOrWhiteSpace(Data) || 102 | KeyName == Guid.Empty 103 | ); 104 | } 105 | } 106 | 107 | public override string ToString() { 108 | return _json.ToString(true); 109 | } 110 | 111 | public string ToJsonString() { 112 | return _json.ToString(); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/UpdateUserDataRequest .cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class UpdateUserDataRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public UpdateUserDataRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public UpdateUserDataRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string Username { 18 | get { 19 | return _json.String("username"); 20 | } 21 | set { 22 | _json.String("username", value); 23 | } 24 | } 25 | 26 | public string Data { 27 | get { 28 | return _json.String("data"); 29 | } 30 | set { 31 | _json.String("data", value); 32 | } 33 | } 34 | 35 | public string PayloadToSign { 36 | get { 37 | return _json.ToFilteredString(SignatureFilter.Default); 38 | } 39 | } 40 | 41 | public void AddSignature(string name, string signature) { 42 | if (!_json.Has("signatures")) { 43 | _json.Array("signatures", new JsonArray()); 44 | } 45 | _json.Array("signatures").Add(new JsonObject() 46 | .String("name", name) 47 | .String("signature", signature) 48 | ); 49 | } 50 | 51 | public string? GetSignature(string name) { 52 | foreach (var signature in _json.Array("signatures").AsObjects()) { 53 | if (signature.String("name") == name) { 54 | return signature.String("signature"); 55 | } 56 | } 57 | return null; 58 | } 59 | 60 | public DateTime TimeStamp { 61 | get { 62 | return _json.DateTime("timestamp"); 63 | } 64 | set { 65 | _json.DateTime("timestamp", value); 66 | } 67 | } 68 | 69 | public Guid RequestId { 70 | get { 71 | return _json.Guid("requestId"); 72 | } 73 | set { 74 | _json.Guid("requestId", value); 75 | } 76 | } 77 | 78 | public bool IsValid { 79 | get { 80 | return !( 81 | string.IsNullOrWhiteSpace(Username) || 82 | string.IsNullOrWhiteSpace(Data)); 83 | } 84 | } 85 | 86 | public override string ToString() { 87 | return _json.ToString(true); 88 | } 89 | 90 | public string ToJsonString() { 91 | return _json.ToString(); 92 | } 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/UpdateUserRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class UpdateUserRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public UpdateUserRequest() { 8 | _json = new JsonObject(); 9 | TimeStamp = DateTime.UtcNow; 10 | RequestId = Guid.NewGuid(); 11 | } 12 | 13 | public UpdateUserRequest(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public string OldUsername { 18 | get { 19 | return _json.String("oldUsername"); 20 | } 21 | set { 22 | _json.String("oldUsername", value); 23 | } 24 | } 25 | 26 | public string Response { 27 | get { 28 | if (_json.Has("response")) { 29 | return _json.String("response"); 30 | } 31 | return ""; 32 | } 33 | set { 34 | _json.String("response", value); 35 | } 36 | } 37 | 38 | public int HashLength { 39 | get { 40 | if (!_json.Has("hashLength")) { 41 | return 8192; 42 | } 43 | return _json.Int32("hashLength"); 44 | } 45 | set { 46 | _json.Int32("hashLength", value); 47 | } 48 | } 49 | 50 | public string Username { 51 | get { 52 | return _json.String("username"); 53 | } 54 | set { 55 | _json.String("username", value); 56 | } 57 | } 58 | 59 | public string PublicKey { 60 | get { 61 | return _json.String("publicKey"); 62 | } 63 | set { 64 | _json.String("publicKey", value); 65 | } 66 | } 67 | 68 | public string PrivateKey { 69 | get { 70 | return _json.String("privateKey"); 71 | } 72 | set { 73 | _json.String("privateKey", value); 74 | } 75 | } 76 | 77 | public string AsymmetricAlgorithm { 78 | get { 79 | return _json.String("asymmetricAlgorithm"); 80 | } 81 | set { 82 | _json.String("asymmetricAlgorithm", value); 83 | } 84 | } 85 | 86 | public string Salt { 87 | get { 88 | return _json.String("salt"); 89 | } 90 | set { 91 | _json.String("salt", value); 92 | } 93 | } 94 | 95 | public int Iterations { 96 | get { 97 | return _json.Int32("iterations"); 98 | } 99 | set { 100 | _json.Int32("iterations", value); 101 | } 102 | } 103 | 104 | public string Algorithm { 105 | get { 106 | return _json.String("algorithm"); 107 | } 108 | set { 109 | _json.String("algorithm", value); 110 | } 111 | } 112 | 113 | public string PasswordSalt { 114 | get { 115 | return _json.Object("password").String("salt"); 116 | } 117 | set { 118 | if (!_json.Has("password")) { 119 | _json.Object("password", new JsonObject()); 120 | } 121 | _json.Object("password").String("salt", value); 122 | } 123 | } 124 | 125 | public string PasswordHash { 126 | get { 127 | return _json.Object("password").String("hash"); 128 | } 129 | set { 130 | if (!_json.Has("password")) { 131 | _json.Object("password", new JsonObject()); 132 | } 133 | _json.Object("password").String("hash", value); 134 | } 135 | } 136 | 137 | public int PasswordIterations { 138 | get { 139 | return _json.Object("password").Int32("iterations"); 140 | } 141 | set { 142 | if (!_json.Has("password")) { 143 | _json.Object("password", new JsonObject()); 144 | } 145 | _json.Object("password").Int32("iterations", value); 146 | } 147 | } 148 | 149 | public string PasswordAlgorithm { 150 | get { 151 | return _json.Object("password").String("algorithm"); 152 | } 153 | set { 154 | if (!_json.Has("password")) { 155 | _json.Object("password", new JsonObject()); 156 | } 157 | _json.Object("password").String("algorithm", value); 158 | } 159 | } 160 | 161 | public string SymmetricAlgorithm { 162 | get { 163 | return _json.String("symmetricAlgorithm"); 164 | } 165 | set { 166 | _json.String("symmetricAlgorithm", value); 167 | } 168 | } 169 | 170 | public string SymmetricKey { 171 | get { 172 | return _json.String("symmetricKey"); 173 | } 174 | set { 175 | _json.String("symmetricKey", value); 176 | } 177 | } 178 | 179 | public string Data { 180 | get { 181 | return _json.String("data"); 182 | } 183 | set { 184 | _json.String("data", value); 185 | } 186 | } 187 | 188 | 189 | public string PayloadToSign { 190 | get { 191 | return _json.ToFilteredString(SignatureFilter.Default); 192 | } 193 | } 194 | 195 | public void AddSignature(string name, string signature) { 196 | if (!_json.Has("signatures")) { 197 | _json.Array("signatures", new JsonArray()); 198 | } 199 | _json.Array("signatures").Add(new JsonObject() 200 | .String("name", name) 201 | .String("signature", signature) 202 | ); 203 | } 204 | 205 | public string? GetSignature(string name) { 206 | foreach (var signature in _json.Array("signatures").AsObjects()) { 207 | if (signature.String("name") == name) { 208 | return signature.String("signature"); 209 | } 210 | } 211 | return null; 212 | } 213 | 214 | public DateTime TimeStamp { 215 | get { 216 | return _json.DateTime("timestamp"); 217 | } 218 | set { 219 | _json.DateTime("timestamp", value); 220 | } 221 | } 222 | 223 | public Guid RequestId { 224 | get { 225 | return _json.Guid("requestId"); 226 | } 227 | set { 228 | _json.Guid("requestId", value); 229 | } 230 | } 231 | 232 | public bool IsValid { 233 | get { 234 | return !( 235 | string.IsNullOrWhiteSpace(Username) || 236 | string.IsNullOrWhiteSpace(PrivateKey) || 237 | string.IsNullOrWhiteSpace(Salt) || 238 | string.IsNullOrWhiteSpace(Algorithm) || 239 | string.IsNullOrWhiteSpace(PasswordSalt) || 240 | string.IsNullOrWhiteSpace(PasswordHash) || 241 | string.IsNullOrWhiteSpace(PasswordAlgorithm) || 242 | string.IsNullOrWhiteSpace(SymmetricAlgorithm) || 243 | string.IsNullOrWhiteSpace(Data) || 244 | string.IsNullOrWhiteSpace(SymmetricKey)); 245 | } 246 | } 247 | 248 | public override string ToString() { 249 | return _json.ToString(true); 250 | } 251 | 252 | public string ToJsonString() { 253 | return _json.ToString(); 254 | } 255 | 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Requests/WriteNoteRequest.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model.Requests { 4 | public class WriteNoteRequest : ISignable, IRequestObject, INonRepeatableRequest { 5 | private JsonObject _json; 6 | 7 | public WriteNoteRequest() { 8 | _json = new JsonObject(); 9 | _json.Array("items", new JsonArray()); 10 | TimeStamp = DateTime.UtcNow; 11 | RequestId = Guid.NewGuid(); 12 | } 13 | 14 | public WriteNoteRequest(JsonObject json) { 15 | _json = json; 16 | } 17 | 18 | public string Username { 19 | get { 20 | return _json.String("username"); 21 | } 22 | set { 23 | _json.String("username", value); 24 | } 25 | } 26 | 27 | public Guid KeyName { 28 | get { 29 | return _json.Guid("keyName"); 30 | } 31 | set { 32 | _json.Guid("keyName", value); 33 | } 34 | } 35 | 36 | public Guid OldKeyName { 37 | get { 38 | if (_json.Has("oldKeyName")) { 39 | return _json.Guid("oldKeyName"); 40 | } 41 | return Guid.Empty; 42 | } 43 | set { 44 | _json.Guid("oldKeyName", value); 45 | } 46 | } 47 | 48 | public Guid Id { 49 | get { 50 | return _json.Guid("id"); 51 | } 52 | set { 53 | _json.Guid("id", value); 54 | } 55 | } 56 | 57 | public void AddItem(long version, string type, string data) { 58 | _json.Array("items").Add(new JsonObject() 59 | .Int64("version", version) 60 | .String("type", type) 61 | .String("data", data) 62 | ); 63 | } 64 | 65 | public List Items { 66 | get { 67 | if (_json.Has("items")) { 68 | return _json.Array("items").AsObjects().Select(json => new NoteItem(json)).ToList(); 69 | } 70 | return new List(); 71 | } 72 | } 73 | 74 | public string PayloadToSign { 75 | get { 76 | return _json.ToFilteredString(SignatureFilter.Default); 77 | } 78 | } 79 | 80 | public void AddSignature(string name, string signature) { 81 | if (!_json.Has("signatures")) { 82 | _json.Array("signatures", new JsonArray()); 83 | } 84 | _json.Array("signatures").Add(new JsonObject() 85 | .String("name", name) 86 | .String("signature", signature) 87 | ); 88 | } 89 | 90 | public string? GetSignature(string name) { 91 | foreach (var signature in _json.Array("signatures").AsObjects()) { 92 | if (signature.String("name") == name) { 93 | return signature.String("signature"); 94 | } 95 | } 96 | return null; 97 | } 98 | 99 | public DateTime TimeStamp { 100 | get { 101 | return _json.DateTime("timestamp"); 102 | } 103 | set { 104 | _json.DateTime("timestamp", value); 105 | } 106 | } 107 | 108 | public Guid RequestId { 109 | get { 110 | return _json.Guid("requestId"); 111 | } 112 | set { 113 | _json.Guid("requestId", value); 114 | } 115 | } 116 | 117 | public bool IsValid { 118 | get { 119 | return !( 120 | string.IsNullOrWhiteSpace(Username) || 121 | Id == Guid.Empty || 122 | KeyName == Guid.Empty 123 | ); 124 | } 125 | } 126 | 127 | public override string ToString() { 128 | return _json.ToString(true); 129 | } 130 | 131 | public string ToJsonString() { 132 | return _json.ToString(); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/AllKeysResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class KeyInfo { 6 | private JsonObject _json; 7 | 8 | public KeyInfo() { 9 | _json = new JsonObject(); 10 | } 11 | 12 | public Guid Id { 13 | get { 14 | return _json.Guid("id"); 15 | } 16 | set { 17 | _json.Guid("id", value); 18 | } 19 | } 20 | 21 | public Guid Name { 22 | get { 23 | return _json.Guid("name"); 24 | } 25 | set { 26 | _json.Guid("name", value); 27 | } 28 | } 29 | 30 | public string Algorithm { 31 | get { 32 | return _json.String("algorithm"); 33 | } 34 | set { 35 | _json.String("algorithm", value); 36 | } 37 | } 38 | 39 | public string KeyData { 40 | get { 41 | return _json.String("keyData"); 42 | } 43 | set { 44 | _json.String("keyData", value); 45 | } 46 | } 47 | 48 | public string AsymmetricAlgorithm { 49 | get { 50 | return _json.String("asymmetricAlgorithm"); 51 | } 52 | set { 53 | _json.String("asymmetricAlgorithm", value); 54 | } 55 | } 56 | 57 | public string PublicKey { 58 | get { 59 | return _json.String("publicKey"); 60 | } 61 | set { 62 | _json.String("publicKey", value); 63 | } 64 | } 65 | 66 | public string PrivateKey { 67 | get { 68 | return _json.String("privateKey"); 69 | } 70 | set { 71 | _json.String("privateKey", value); 72 | } 73 | } 74 | 75 | public string Metadata { 76 | get { 77 | return _json.String("metadata"); 78 | } 79 | set { 80 | _json.String("metadata", value); 81 | } 82 | } 83 | 84 | public JsonObject Json { 85 | get { 86 | return _json; 87 | } 88 | } 89 | 90 | } 91 | 92 | public class AllKeysResponse : IResponseObject { 93 | private JsonObject _json; 94 | 95 | public AllKeysResponse() { 96 | _json = new JsonObject(); 97 | _json.Array("keys", new JsonArray()); 98 | } 99 | 100 | public AllKeysResponse(JsonObject json) { 101 | _json = json; 102 | } 103 | 104 | public void AddKeyInfo(KeyInfo info) { 105 | _json.Array("keys").Add(info.Json); 106 | } 107 | 108 | public void SetJson(string json) { 109 | _json = new JsonObject(json); 110 | } 111 | 112 | public override string ToString() { 113 | return _json.ToString(true); 114 | } 115 | 116 | public string ToJsonString() { 117 | return _json.ToString(); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/BasicResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class BasicResponse : IResponseObject { 6 | private JsonObject _json; 7 | 8 | public BasicResponse() { 9 | _json = new JsonObject(); 10 | } 11 | 12 | public BasicResponse(JsonObject json) { 13 | _json = json; 14 | } 15 | 16 | public void SetJson(string json) { 17 | _json = new JsonObject(json); 18 | } 19 | 20 | public override string ToString() { 21 | return _json.ToString(true); 22 | } 23 | 24 | public string ToJsonString() { 25 | return _json.ToString(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/CheckUsernameResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class CheckUsernameResponse : IResponseObject { 6 | private JsonObject _json; 7 | 8 | public CheckUsernameResponse() { 9 | _json = new JsonObject(); 10 | } 11 | 12 | public CheckUsernameResponse(JsonObject json) { 13 | _json = json; 14 | } 15 | 16 | public void SetJson(string json) { 17 | _json = new JsonObject(json); 18 | } 19 | 20 | public string Username { 21 | get { 22 | return _json.String("username"); 23 | } 24 | set { 25 | _json.String("username", value); 26 | } 27 | } 28 | 29 | public bool Available { 30 | get { 31 | return _json.Boolean("available"); 32 | } 33 | set { 34 | _json.Boolean("available", value); 35 | } 36 | } 37 | 38 | public bool ProofAccepted { 39 | get { 40 | return _json.Boolean("proofAccepted"); 41 | } 42 | set { 43 | _json.Boolean("proofAccepted", value); 44 | } 45 | } 46 | 47 | public int BitsExpected { 48 | get { 49 | return _json.Int32("bitsExpected"); 50 | } 51 | set { 52 | _json.Int32("bitsExpected", value); 53 | } 54 | } 55 | 56 | public override string ToString() { 57 | return _json.ToString(true); 58 | } 59 | 60 | public string ToJsonString() { 61 | return _json.ToString(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/KeyResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class KeyResponse : IResponseObject { 6 | private JsonObject _json; 7 | 8 | public KeyResponse() { 9 | _json = new JsonObject(); 10 | } 11 | 12 | public KeyResponse(JsonObject json) { 13 | _json = json; 14 | } 15 | 16 | public void SetJson(string json) { 17 | _json = new JsonObject(json); 18 | } 19 | 20 | public Guid Id { 21 | get { 22 | return _json.Guid("id"); 23 | } 24 | set { 25 | _json.Guid("id", value); 26 | } 27 | } 28 | 29 | public Guid Name { 30 | get { 31 | return _json.Guid("name"); 32 | } 33 | set { 34 | _json.Guid("name", value); 35 | } 36 | } 37 | 38 | public string Algorithm { 39 | get { 40 | return _json.String("algorithm"); 41 | } 42 | set { 43 | _json.String("algorithm", value); 44 | } 45 | } 46 | 47 | public string KeyData { 48 | get { 49 | return _json.String("keyData"); 50 | } 51 | set { 52 | _json.String("keyData", value); 53 | } 54 | } 55 | 56 | public string AsymmetricAlgorithm { 57 | get { 58 | return _json.String("asymmetricAlgorithm"); 59 | } 60 | set { 61 | _json.String("asymmetricAlgorithm", value); 62 | } 63 | } 64 | 65 | public string PublicKey { 66 | get { 67 | return _json.String("publicKey"); 68 | } 69 | set { 70 | _json.String("publicKey", value); 71 | } 72 | } 73 | 74 | public string PrivateKey { 75 | get { 76 | return _json.String("privateKey"); 77 | } 78 | set { 79 | _json.String("privateKey", value); 80 | } 81 | } 82 | 83 | public string Metadata { 84 | get { 85 | return _json.String("metadata"); 86 | } 87 | set { 88 | _json.String("metadata", value); 89 | } 90 | } 91 | 92 | public override string ToString() { 93 | return _json.ToString(true); 94 | } 95 | 96 | public string ToJsonString() { 97 | return _json.ToString(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/LoginResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class LoginResponse : IResponseObject { 6 | private JsonObject _json; 7 | 8 | public LoginResponse() { 9 | _json = new JsonObject(); 10 | } 11 | 12 | public LoginResponse(JsonObject json) { 13 | _json = json; 14 | } 15 | 16 | public void SetJson(string json) { 17 | _json = new JsonObject(json); 18 | } 19 | 20 | public Guid UserId { 21 | get { 22 | return _json.Guid("userId"); 23 | } 24 | set { 25 | _json.Guid("userId", value); 26 | } 27 | } 28 | 29 | public string PublicKey { 30 | get { 31 | return _json.String("publicKey"); 32 | } 33 | set { 34 | _json.String("publicKey", value); 35 | } 36 | } 37 | 38 | public string PrivateKey { 39 | get { 40 | return _json.String("privateKey"); 41 | } 42 | set { 43 | _json.String("privateKey", value); 44 | } 45 | } 46 | 47 | public string AsymmetricAlgorithm { 48 | get { 49 | return _json.String("asymmetricAlgorithm"); 50 | } 51 | set { 52 | _json.String("asymmetricAlgorithm", value); 53 | } 54 | } 55 | 56 | public string Salt { 57 | get { 58 | return _json.String("salt"); 59 | } 60 | set { 61 | _json.String("salt", value); 62 | } 63 | } 64 | 65 | public int Iterations { 66 | get { 67 | return _json.Int32("iterations"); 68 | } 69 | set { 70 | _json.Int32("iterations", value); 71 | } 72 | } 73 | 74 | public string Algorithm { 75 | get { 76 | return _json.String("algorithm"); 77 | } 78 | set { 79 | _json.String("algorithm", value); 80 | } 81 | } 82 | 83 | public string SymmetricAlgorithm { 84 | get { 85 | return _json.String("symmetricAlgorithm"); 86 | } 87 | set { 88 | _json.String("symmetricAlgorithm", value); 89 | } 90 | } 91 | 92 | public string SymmetricKey { 93 | get { 94 | return _json.String("symmetricKey"); 95 | } 96 | set { 97 | _json.String("symmetricKey", value); 98 | } 99 | } 100 | 101 | public string Data { 102 | get { 103 | return _json.String("data"); 104 | } 105 | set { 106 | _json.String("data", value); 107 | } 108 | } 109 | 110 | public string Config { 111 | get { 112 | return _json.String("config"); 113 | } 114 | set { 115 | _json.String("config", value); 116 | } 117 | } 118 | 119 | public long Size { 120 | get { 121 | return _json.Int64("size"); 122 | } 123 | set { 124 | _json.Int64("size", value); 125 | } 126 | } 127 | 128 | public long NoteCount { 129 | get { 130 | return _json.Int64("noteCount"); 131 | } 132 | set { 133 | _json.Int64("noteCount", value); 134 | } 135 | } 136 | 137 | public long MaxTotalBytes { 138 | get { 139 | return _json.Int64("maxTotalBytes"); 140 | } 141 | set { 142 | _json.Int64("maxTotalBytes", value); 143 | } 144 | } 145 | 146 | public long MaxNoteBytes { 147 | get { 148 | return _json.Int64("maxNoteBytes"); 149 | } 150 | set { 151 | _json.Int64("maxNoteBytes", value); 152 | } 153 | } 154 | 155 | public long MaxNoteCount { 156 | get { 157 | return _json.Int64("maxNoteCount"); 158 | } 159 | set { 160 | _json.Int64("maxNoteCount", value); 161 | } 162 | } 163 | 164 | public long MaxHistoryEntries { 165 | get { 166 | return _json.Int64("maxHistoryEntries"); 167 | } 168 | set { 169 | _json.Int64("maxHistoryEntries", value); 170 | } 171 | } 172 | 173 | public override string ToString() { 174 | return _json.ToString(true); 175 | } 176 | 177 | public string ToJsonString() { 178 | return _json.ToString(); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/NotificationUrlResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class NotificationUrlResponse : IResponseObject { 6 | private JsonObject _json; 7 | 8 | public NotificationUrlResponse() { 9 | _json = new JsonObject(); 10 | } 11 | 12 | public NotificationUrlResponse(JsonObject json) { 13 | _json = json; 14 | } 15 | 16 | public void SetJson(string json) { 17 | _json = new JsonObject(json); 18 | } 19 | 20 | public string Url { 21 | get { 22 | return _json.String("url"); 23 | } 24 | set { 25 | _json.String("url", value); 26 | } 27 | } 28 | 29 | public string Token { 30 | get { 31 | return _json.String("token"); 32 | } 33 | set { 34 | _json.String("token", value); 35 | } 36 | } 37 | 38 | public override string ToString() { 39 | return _json.ToString(true); 40 | } 41 | 42 | public string ToJsonString() { 43 | return _json.ToString(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/PreLoginResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class PreLoginResponse : IResponseObject { 6 | private JsonObject _json; 7 | 8 | public PreLoginResponse() { 9 | _json = new JsonObject(); 10 | } 11 | 12 | public PreLoginResponse(JsonObject json) { 13 | _json = json; 14 | } 15 | 16 | public void SetJson(string json) { 17 | _json = new JsonObject(json); 18 | } 19 | 20 | public string Salt { 21 | get { 22 | return _json.String("salt"); 23 | } 24 | set { 25 | _json.String("salt", value); 26 | } 27 | } 28 | 29 | public int Iterations { 30 | get { 31 | return _json.Int32("iterations"); 32 | } 33 | set { 34 | _json.Int32("iterations", value); 35 | } 36 | } 37 | 38 | public string Algorithm { 39 | get { 40 | return _json.String("algorithm"); 41 | } 42 | set { 43 | _json.String("algorithm", value); 44 | } 45 | } 46 | 47 | public string Challenge { 48 | get { 49 | return _json.String("challenge"); 50 | } 51 | set { 52 | _json.String("challenge", value); 53 | } 54 | } 55 | 56 | public override string ToString() { 57 | return _json.ToString(true); 58 | } 59 | 60 | public string ToJsonString() { 61 | return _json.ToString(); 62 | } 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/PublicKeyResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class PublicKeyResponse : IResponseObject { 6 | private JsonObject _json; 7 | 8 | public PublicKeyResponse() { 9 | _json = new JsonObject(); 10 | } 11 | 12 | public PublicKeyResponse(JsonObject json) { 13 | _json = json; 14 | } 15 | 16 | public void SetJson(string json) { 17 | _json = new JsonObject(json); 18 | } 19 | 20 | public string AsymmetricAlgorithm { 21 | get { 22 | return _json.String("asymmetricAlgorithm"); 23 | } 24 | set { 25 | _json.String("asymmetricAlgorithm", value); 26 | } 27 | } 28 | 29 | public string PublicKey { 30 | get { 31 | return _json.String("publicKey"); 32 | } 33 | set { 34 | _json.String("publicKey", value); 35 | } 36 | } 37 | 38 | public override string ToString() { 39 | return _json.ToString(true); 40 | } 41 | 42 | public string ToJsonString() { 43 | return _json.ToString(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/ReadNoteResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class ReadNoteResponse : IResponseObject { 6 | private JsonObject _json; 7 | 8 | public ReadNoteResponse() { 9 | _json = new JsonObject(); 10 | _json.Array("items", new JsonArray()); 11 | } 12 | 13 | public ReadNoteResponse(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public void SetJson(string json) { 18 | _json = new JsonObject(json); 19 | } 20 | 21 | public void AddItem(long version, string type, string data) { 22 | _json.Array("items").Add(new JsonObject() 23 | .Int64("version", version) 24 | .String("type", type) 25 | .Boolean("updated", true) 26 | .String("data", data) 27 | ); 28 | } 29 | 30 | public void AddItem(long version, string type) { 31 | _json.Array("items").Add(new JsonObject() 32 | .Int64("version", version) 33 | .String("type", type) 34 | .Boolean("updated", false) 35 | ); 36 | } 37 | 38 | public List Items { 39 | get { 40 | var result = new List(); 41 | if (_json.Has("items")) { 42 | foreach (var item in _json.Array("items").AsObjects()) { 43 | result.Add(item); 44 | } 45 | } 46 | return result; 47 | } 48 | } 49 | 50 | public Guid Id { 51 | get { 52 | return _json.Guid("id"); 53 | } 54 | set { 55 | _json.Guid("id", value); 56 | } 57 | } 58 | 59 | public Guid KeyName { 60 | get { 61 | return _json.Guid("keyName"); 62 | } 63 | set { 64 | _json.Guid("keyName", value); 65 | } 66 | } 67 | 68 | public override string ToString() { 69 | return _json.ToString(true); 70 | } 71 | 72 | public string ToJsonString() { 73 | return _json.ToString(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/ShareOffersResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class ShareOffersResponse : IResponseObject { 6 | private JsonObject _json; 7 | 8 | public ShareOffersResponse() { 9 | _json = new JsonObject(); 10 | _json.Array("offers", new JsonArray()); 11 | } 12 | 13 | public ShareOffersResponse(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public void AddOffer(Guid id, string sender, string data) { 18 | _json.Array("offers").Add(new JsonObject() 19 | .Guid("id", id) 20 | .String("sender", sender) 21 | .String("data", data) 22 | ); 23 | } 24 | 25 | public List Offers { 26 | get { 27 | var result = new List(); 28 | foreach (var share in _json.Array("offers").AsObjects()) { 29 | result.Add(share); 30 | } 31 | return result; 32 | } 33 | } 34 | 35 | public void SetJson(string json) { 36 | _json = new JsonObject(json); 37 | } 38 | 39 | public override string ToString() { 40 | return _json.ToString(true); 41 | } 42 | 43 | public string ToJsonString() { 44 | return _json.ToString(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/UpdateNoteResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | using Mimer.Notes.Model.DataTypes; 4 | 5 | namespace Mimer.Notes.Model.Responses { 6 | public class UpdateNoteResponse : IResponseObject { 7 | private JsonObject _json; 8 | 9 | public UpdateNoteResponse() { 10 | _json = new JsonObject(); 11 | } 12 | 13 | public UpdateNoteResponse(JsonObject json) { 14 | _json = json; 15 | } 16 | 17 | public void SetJson(string json) { 18 | _json = new JsonObject(json); 19 | } 20 | 21 | public void AddVersionConflict(VersionConflict conflict) { 22 | if (!_json.Has("conflicts")) { 23 | _json.Array("conflicts", new JsonArray()); 24 | } 25 | _json.Array("conflicts").Add(new JsonObject() 26 | .String("type", conflict.Type) 27 | .Int64("expected", conflict.Expected) 28 | .Int64("actual", conflict.Actual) 29 | ); 30 | } 31 | 32 | public IReadOnlyList Conflicts { 33 | get { 34 | List result = new List(); 35 | if (_json.Has("conflicts")) { 36 | foreach (var conflict in _json.Array("conflicts").AsObjects()) { 37 | result.Add(new VersionConflict(conflict.String("type"), conflict.Int64("expected"), conflict.Int64("actual"))); 38 | } 39 | } 40 | return result; 41 | } 42 | } 43 | 44 | public bool Success { 45 | get { 46 | return _json.Boolean("success"); 47 | } 48 | set { 49 | _json.Boolean("success", value); 50 | } 51 | } 52 | 53 | public long Size { 54 | get { 55 | return _json.Int64("size"); 56 | } 57 | set { 58 | _json.Int64("size", value); 59 | } 60 | } 61 | 62 | public long NoteCount { 63 | get { 64 | return _json.Int64("noteCount"); 65 | } 66 | set { 67 | _json.Int64("noteCount", value); 68 | } 69 | } 70 | 71 | public override string ToString() { 72 | return _json.ToString(true); 73 | } 74 | 75 | public string ToJsonString() { 76 | return _json.ToString(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/Responses/UserDataResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework.Json; 3 | 4 | namespace Mimer.Notes.Model.Responses { 5 | public class UserDataResponse : IResponseObject { 6 | private JsonObject _json; 7 | 8 | public UserDataResponse() { 9 | _json = new JsonObject(); 10 | } 11 | 12 | public UserDataResponse(JsonObject json) { 13 | _json = json; 14 | } 15 | 16 | public void SetJson(string json) { 17 | _json = new JsonObject(json); 18 | } 19 | 20 | public string Data { 21 | get { 22 | return _json.String("data"); 23 | } 24 | set { 25 | _json.String("data", value); 26 | } 27 | } 28 | 29 | public string Config { 30 | get { 31 | return _json.String("config"); 32 | } 33 | set { 34 | _json.String("config", value); 35 | } 36 | } 37 | 38 | public long Size { 39 | get { 40 | return _json.Int64("size"); 41 | } 42 | set { 43 | _json.Int64("size", value); 44 | } 45 | } 46 | 47 | public long NoteCount { 48 | get { 49 | return _json.Int64("noteCount"); 50 | } 51 | set { 52 | _json.Int64("noteCount", value); 53 | } 54 | } 55 | 56 | public long MaxTotalBytes { 57 | get { 58 | return _json.Int64("maxTotalBytes"); 59 | } 60 | set { 61 | _json.Int64("maxTotalBytes", value); 62 | } 63 | } 64 | 65 | public long MaxNoteBytes { 66 | get { 67 | return _json.Int64("maxNoteBytes"); 68 | } 69 | set { 70 | _json.Int64("maxNoteBytes", value); 71 | } 72 | } 73 | 74 | public long MaxNoteCount { 75 | get { 76 | return _json.Int64("maxNoteCount"); 77 | } 78 | set { 79 | _json.Int64("maxNoteCount", value); 80 | } 81 | } 82 | 83 | public long MaxHistoryEntries { 84 | get { 85 | return _json.Int64("maxHistoryEntries"); 86 | } 87 | set { 88 | _json.Int64("maxHistoryEntries", value); 89 | } 90 | } 91 | 92 | public override string ToString() { 93 | return _json.ToString(true); 94 | } 95 | 96 | public string ToJsonString() { 97 | return _json.ToString(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Mimer.Notes.Model/SignatureFilter.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework.Json; 2 | 3 | namespace Mimer.Notes.Model { 4 | internal class SignatureFilter : IJsonFilter { 5 | public static SignatureFilter Default = new SignatureFilter(); 6 | 7 | public JsonValue Filter(string name, JsonValue item, JsonItem[] path, ref bool remove, object? param) { 8 | if (name == "signatures") { 9 | remove = true; 10 | } 11 | return item; 12 | } 13 | 14 | public JsonValue Filter(int index, JsonValue item, JsonItem[] path, ref bool remove, object? param) { 15 | return item; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Mimer.Notes.Rest.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34408.163 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mimer.Notes.WebApi", "Mimer.Notes.WebApi\Mimer.Notes.WebApi.csproj", "{14324E2D-A6AA-44F3-9D94-D2F64D6D630C}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mimer.Notes.Server", "Mimer.Notes.Server\Mimer.Notes.Server.csproj", "{9E468F76-1E4D-45E4-B231-21FD90262A73}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mimer.Framework", "Mimer.Framework\Mimer.Framework.csproj", "{000E2ECB-0AB4-4F6A-BB29-CAEA49630670}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mimer.Notes.Model", "Mimer.Notes.Model\Mimer.Notes.Model.csproj", "{306A12DD-C1CE-4558-8E1F-04F5436BCBD5}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mimer.Notes.SignalR", "Mimer.Notes.SignalR\Mimer.Notes.SignalR.csproj", "{830805D8-F7B0-43FE-BA89-A97E03C4093F}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {14324E2D-A6AA-44F3-9D94-D2F64D6D630C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {14324E2D-A6AA-44F3-9D94-D2F64D6D630C}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {14324E2D-A6AA-44F3-9D94-D2F64D6D630C}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {14324E2D-A6AA-44F3-9D94-D2F64D6D630C}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {9E468F76-1E4D-45E4-B231-21FD90262A73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {9E468F76-1E4D-45E4-B231-21FD90262A73}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {9E468F76-1E4D-45E4-B231-21FD90262A73}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {9E468F76-1E4D-45E4-B231-21FD90262A73}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {000E2ECB-0AB4-4F6A-BB29-CAEA49630670}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {000E2ECB-0AB4-4F6A-BB29-CAEA49630670}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {000E2ECB-0AB4-4F6A-BB29-CAEA49630670}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {000E2ECB-0AB4-4F6A-BB29-CAEA49630670}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {306A12DD-C1CE-4558-8E1F-04F5436BCBD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {306A12DD-C1CE-4558-8E1F-04F5436BCBD5}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {306A12DD-C1CE-4558-8E1F-04F5436BCBD5}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {306A12DD-C1CE-4558-8E1F-04F5436BCBD5}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {830805D8-F7B0-43FE-BA89-A97E03C4093F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {830805D8-F7B0-43FE-BA89-A97E03C4093F}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {830805D8-F7B0-43FE-BA89-A97E03C4093F}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {830805D8-F7B0-43FE-BA89-A97E03C4093F}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {CF072190-42FA-4045-AB45-CA5DCC33F56E} 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /Mimer.Notes.Server/ChallengeManager.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework; 2 | using Mimer.Notes.Model.Cryptography; 3 | using System.Security.Cryptography; 4 | 5 | namespace Mimer.Notes.Server { 6 | public class ChallengeManager { 7 | private class IssuedChallenge { 8 | public string Username { get; set; } = ""; 9 | public string Challenge { get; set; } = ""; 10 | public DateTime IssueTime { get; set; } 11 | } 12 | private Thread _pruneThread; 13 | private Dictionary _issuedChallenges = new Dictionary(); 14 | 15 | public ChallengeManager() { 16 | _pruneThread = new Thread(ExecutePruneChallenges); 17 | _pruneThread.IsBackground = true; 18 | _pruneThread.Start(); 19 | } 20 | 21 | private IssuedChallenge? GetChallenge(string username) { 22 | IssuedChallenge? issuedChallenge = null; 23 | lock (_issuedChallenges) { 24 | _issuedChallenges.TryGetValue(username, out issuedChallenge); 25 | } 26 | return issuedChallenge; 27 | } 28 | 29 | private void ClearChallenge(string username) { 30 | lock (_issuedChallenges) { 31 | if (_issuedChallenges.ContainsKey(username)) { 32 | _issuedChallenges.Remove(username); 33 | } 34 | } 35 | } 36 | 37 | public string IssueChallenge(string username) { 38 | var challengeBytes = new byte[32]; 39 | using var random = RandomNumberGenerator.Create(); 40 | random.GetBytes(challengeBytes); 41 | var challenge = new IssuedChallenge(); 42 | challenge.Username = username; 43 | challenge.IssueTime = DateTime.UtcNow; 44 | challenge.Challenge = Convert.ToHexString(challengeBytes); 45 | lock (_issuedChallenges) { 46 | _issuedChallenges[username] = challenge; 47 | } 48 | return challenge.Challenge!; 49 | } 50 | 51 | public bool ValidateChallenge(string username, string passwordHash, string response, int hashLength) { 52 | var challenge = GetChallenge(username); 53 | if (challenge != null) { 54 | if (hashLength >= 256 && hashLength * 2 < passwordHash.Length) { 55 | passwordHash = passwordHash.Substring(0, hashLength * 2); 56 | } 57 | var expectedResponse = PasswordHasher.Instance.ComputeResponse(passwordHash, challenge.Challenge); 58 | if (response == expectedResponse) { 59 | ClearChallenge(username); 60 | return true; 61 | } 62 | } 63 | return false; 64 | } 65 | 66 | private void ExecutePruneChallenges() { 67 | var lastPrune = DateTime.UtcNow; 68 | while (true) { 69 | try { 70 | if ((DateTime.UtcNow - lastPrune).TotalMinutes > 10) { 71 | lastPrune = DateTime.UtcNow; 72 | IssuedChallenge[] items; 73 | lock (_issuedChallenges) { 74 | items = _issuedChallenges.Values.ToArray(); 75 | } 76 | foreach (var item in items) { 77 | if ((DateTime.UtcNow - item.IssueTime).TotalMinutes > 10) { 78 | ClearChallenge(item.Username); 79 | } 80 | } 81 | } 82 | } 83 | catch (Exception ex) { 84 | Dev.Log(ex); 85 | Thread.Sleep(1000); 86 | } 87 | Thread.Sleep(100); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Mimer.Notes.Server/DbNote .cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Notes.Model.Requests; 3 | 4 | namespace Mimer.Notes.Server { 5 | public class DbNote { 6 | public Guid Id { get; set; } 7 | public Guid KeyName { get; set; } 8 | public List Items { get; } = new List(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Mimer.Notes.Server/DbNoteItem.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Notes.Model.Requests; 3 | 4 | namespace Mimer.Notes.Server { 5 | 6 | public class DbNoteItem : INoteItem { 7 | public long Version { get; set; } 8 | public string Type { get; set; } 9 | public string Data { get; set; } 10 | 11 | public DbNoteItem() { 12 | Version = 0; 13 | Type = ""; 14 | Data = ""; 15 | } 16 | 17 | public DbNoteItem(long version, string type, string data) { 18 | Version = version; 19 | Type = type; 20 | Data = data; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Mimer.Notes.Server/DbShareOffer.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Mimer.Notes.Server { 3 | public class DbShareOffer { 4 | public Guid Id { get; set; } 5 | public string Sender { get; set; } = ""; 6 | public string Data { get; set; } = ""; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Mimer.Notes.Server/GlobalStatsManager .cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace Mimer.Notes.Server { 8 | public class GlobalStatistic { 9 | public string Id { get; set; } = ""; 10 | public string Type { get; set; } = ""; 11 | public string Action { get; set; } = ""; 12 | public string Key { get; set; } = ""; 13 | public long Value { get; set; } 14 | public DateTime LastActivity { get; set; } 15 | } 16 | 17 | public class ClientInfo { 18 | private static Regex UserAgentPart = new Regex(@"(?\S+)/(?\S+)"); 19 | public string UserAgent { get; set; } 20 | public string MimiriVersion { get; set; } 21 | public string Client { get; set; } 22 | public string Environment { get; set; } 23 | public string BundleVersion { get; set; } 24 | public string HostVersion { get; set; } 25 | public string ElectronVersion { get; set; } 26 | public string ChromeVersion { get; set; } 27 | public string FirefoxVersion { get; set; } 28 | 29 | public ClientInfo(string userAgent, string mimiriVersion) { 30 | UserAgent = userAgent.Trim(); 31 | MimiriVersion = mimiriVersion.Trim(); 32 | if (MimiriVersion.Length > 0) { 33 | var items = MimiriVersion.Split(';'); 34 | var clientEnvironment = items[0].Split('-'); 35 | Client = clientEnvironment[0]; 36 | Environment = clientEnvironment.Length > 1 ? clientEnvironment[1] : ""; 37 | BundleVersion = items[1]; 38 | HostVersion = items[2]; 39 | } 40 | else { 41 | Client = "unknown"; 42 | Environment = "unknown"; 43 | BundleVersion = "2.1.0"; 44 | HostVersion = "2.1.0"; 45 | } 46 | ElectronVersion = ""; 47 | ChromeVersion = ""; 48 | FirefoxVersion = ""; 49 | if (UserAgent.Length > 0) { 50 | foreach (var match in UserAgentPart.Matches(UserAgent).ToList()) { 51 | Dev.Log(match); 52 | if (match.Success) { 53 | if (match.Groups[1].Value == "MimiriNotes" && HostVersion == "2.1.0") { 54 | HostVersion = match.Groups[2].Value; 55 | } 56 | if (match.Groups[1].Value == "Electron") { 57 | ElectronVersion = match.Groups[2].Value; 58 | if (Client == "unknown") { 59 | Client = "Electron"; 60 | } 61 | if (HostVersion == "") { 62 | HostVersion = match.Groups[2].Value; 63 | } 64 | } 65 | if (match.Groups[1].Value == "Chrome") { 66 | ChromeVersion = match.Groups[2].Value; 67 | } 68 | if (match.Groups[1].Value == "Firefox") { 69 | FirefoxVersion = match.Groups[2].Value; 70 | } 71 | } 72 | } 73 | } 74 | if (MimiriVersion.Length == 0) { 75 | MimiriVersion = $"{Client}-{Environment};{BundleVersion};{HostVersion}"; 76 | } 77 | } 78 | 79 | public override string ToString() { 80 | return $"Client: {Client}, Environment: {Environment}, BundleVersion: {BundleVersion}, HostVersion: {HostVersion}, ElectronVersion: {ElectronVersion}"; 81 | } 82 | 83 | } 84 | 85 | class ClientAction { 86 | public ClientInfo Info { get; set; } 87 | public string Action { get; set; } 88 | public ClientAction(ClientInfo info, string action) { 89 | Info = info; 90 | Action = action; 91 | } 92 | } 93 | 94 | public class GlobalStatsManager { 95 | private IMimerDataSource _dataSource; 96 | private Dictionary _stats = new Dictionary(); 97 | private Queue _queue = new Queue(); 98 | 99 | public GlobalStatsManager(IMimerDataSource dataSource) { 100 | _dataSource = dataSource; 101 | Task.Run(Execute); 102 | //var info = new ClientInfo("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) MimiriNotes/2.1.70 Chrome/128.0.6613.84 Electron/32.0.2 Safari/537.36", "Electron-Windows;2.1.100;2.1.70"); 103 | //Dev.Log(info); 104 | } 105 | 106 | private async Task Execute() { 107 | DateTime nextWrite = DateTime.UtcNow.AddSeconds(10); 108 | while (true) { 109 | Thread.Sleep(100); 110 | try { 111 | ProcessQueue(); 112 | if (DateTime.UtcNow > nextWrite) { 113 | await WriteStats(); 114 | nextWrite = DateTime.UtcNow.AddSeconds(10); 115 | } 116 | } 117 | catch (Exception ex) { 118 | if (ex is ThreadAbortException || ex is ThreadInterruptedException) { 119 | throw; 120 | } 121 | Dev.Log(ex); 122 | Thread.Sleep(1000); 123 | } 124 | } 125 | } 126 | 127 | private async Task WriteStats() { 128 | var count = _stats.Count; 129 | if (count > 0) { 130 | await _dataSource.UpdateGlobalStats(_stats.Values); 131 | _stats.Clear(); 132 | } 133 | return count; 134 | } 135 | 136 | private string CreateId(string longIdentifier) { 137 | return Convert.ToHexString(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(longIdentifier))).ToLower(); 138 | } 139 | 140 | private void IncrementStats(string key, string type, string action) { 141 | if (key.Length > 0) { 142 | string identifier = $"{type}:{action}:{key}"; 143 | var now = DateTime.UtcNow; 144 | if (!_stats.ContainsKey(identifier)) { 145 | _stats.Add(identifier, new GlobalStatistic() { Id = CreateId(identifier), Type = type, Action = action, Key = key, Value = 0 }); 146 | } 147 | var stats = _stats[identifier]; 148 | stats.Value++; 149 | stats.LastActivity = now; 150 | } 151 | } 152 | 153 | private void ProcessQueue() { 154 | while (true) { 155 | ClientAction? item; 156 | lock (_queue) { 157 | _queue.TryDequeue(out item); 158 | } 159 | if (item == null) { 160 | break; 161 | } 162 | IncrementStats(item.Info.UserAgent, "user-agent", item.Action); 163 | IncrementStats(item.Info.MimiriVersion, "mimiri-version", item.Action); 164 | IncrementStats(item.Info.Client, "client", item.Action); 165 | IncrementStats(item.Info.Environment, "environment", item.Action); 166 | IncrementStats(item.Info.BundleVersion, "bundle-version", item.Action); 167 | IncrementStats(item.Info.HostVersion, "host-version", item.Action); 168 | IncrementStats(item.Info.ElectronVersion, "electron", item.Action); 169 | IncrementStats(item.Info.ChromeVersion, "chrome", item.Action); 170 | IncrementStats(item.Info.FirefoxVersion, "firefox", item.Action); 171 | } 172 | } 173 | 174 | public void RegisterAction(ClientInfo info, string action) { 175 | lock (_queue) { 176 | _queue.Enqueue(new ClientAction(info, action)); 177 | } 178 | } 179 | 180 | 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /Mimer.Notes.Server/IMimerDataSource.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Notes.Model.DataTypes; 2 | using Mimer.Notes.Model.Requests; 3 | 4 | namespace Mimer.Notes.Server { 5 | public interface IMimerDataSource { 6 | void CreateDatabase(); 7 | List GetUserTypes(); 8 | Task CreateKey(MimerKey data); 9 | Task CreateNote(DbNote note); 10 | Task CreateNoteShareOffer(string sender, string recipient, Guid keyName, string data); 11 | Task CreateUser(MimerUser user); 12 | Task DeleteKey(Guid id); 13 | Task DeleteNote(Guid id); 14 | Task DeleteNoteShareOffer(Guid id); 15 | Task> GetAllKeys(Guid userId); 16 | Task GetKey(Guid id, Guid userId); 17 | Task GetKeyByName(Guid keyName); 18 | Task GetNote(Guid id); 19 | Task> GetShareOffers(string username); 20 | Task GetUser(string username); 21 | Task GetUserSize(Guid userId); 22 | void TearDown(bool keepLogs); 23 | Task?> UpdateNote(DbNote note, Guid oldKeyName); 24 | Task UpdateUser(string oldUsername, MimerUser user); 25 | Task> GetUserIdsByKeyName(Guid keyName); 26 | Task?> MultiApply(List actions, UserStats stats); 27 | Task UpdateUserStats(IEnumerable userStats); 28 | Task UpdateGlobalStats(IEnumerable globalStats); 29 | Task DeleteUser(Guid userId); 30 | } 31 | } -------------------------------------------------------------------------------- /Mimer.Notes.Server/Mimer.Notes.Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Mimer.Notes.Server/RequestValidator.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework; 2 | using Mimer.Notes.Model; 3 | 4 | namespace Mimer.Notes.Server { 5 | 6 | public class RequestValidator { 7 | private class RequestLink { 8 | public Guid Id; 9 | public DateTime TimeStamp; 10 | public volatile RequestLink? Next = null; 11 | } 12 | 13 | private Thread _pruneThread; 14 | private Dictionary _executedRequests = new Dictionary(); 15 | private RequestLink _first; 16 | private object _lock = new object(); 17 | private RequestLink _last; 18 | 19 | public RequestValidator() { 20 | _first = new RequestLink(); 21 | _last = _first; 22 | _pruneThread = new Thread(ExecutePruneRequests); 23 | _pruneThread.IsBackground = true; 24 | _pruneThread.Start(); 25 | } 26 | 27 | private void ExecutePruneRequests() { 28 | var lastPrune = DateTime.UtcNow; 29 | while (true) { 30 | try { 31 | int pruneCount = 0; 32 | if ((DateTime.UtcNow - lastPrune).TotalMinutes > 5) { 33 | lastPrune = DateTime.UtcNow; 34 | // never prune _last 35 | if (_first.Next != null) { 36 | // leave dummy first in place 37 | var prev = _first; 38 | var current = _first.Next; 39 | while (current.Next != null) { 40 | if ((DateTime.UtcNow - current.TimeStamp).TotalMinutes > 21) { 41 | pruneCount++; 42 | prev.Next = current.Next; 43 | lock (_executedRequests) { 44 | _executedRequests.Remove(current.Id); 45 | } 46 | } 47 | else { 48 | // only advance prev if we left current in place 49 | prev = current; 50 | } 51 | current = current.Next; 52 | } 53 | } 54 | } 55 | } 56 | catch (Exception ex) { 57 | Dev.Log(ex); 58 | Thread.Sleep(1000); 59 | } 60 | Thread.Sleep(100); 61 | } 62 | } 63 | 64 | public bool ValidateRequest(INonRepeatableRequest request) { 65 | try { 66 | bool reject = false; 67 | lock (_executedRequests) { 68 | if (_executedRequests.ContainsKey(request.RequestId)) { 69 | reject = true; 70 | } 71 | } 72 | if (reject) { 73 | Dev.Log("Rejected Replay (In List)", request.RequestId, request.TimeStamp); 74 | return false; 75 | } 76 | if ((DateTime.UtcNow - request.TimeStamp).TotalMinutes > 20) { 77 | Dev.Log("Rejected Replay (Old)", request.RequestId, request.TimeStamp); 78 | return false; 79 | } 80 | lock (_executedRequests) { 81 | if (_executedRequests.ContainsKey(request.RequestId)) { 82 | return false; 83 | } 84 | _executedRequests.Add(request.RequestId, request.TimeStamp); 85 | } 86 | lock (_lock) { 87 | _last.Next = new RequestLink { 88 | Id = request.RequestId, 89 | TimeStamp = request.TimeStamp 90 | }; 91 | _last = _last.Next; 92 | } 93 | } 94 | catch (Exception ex) { 95 | Dev.Log(ex); 96 | } 97 | return request.IsValid; 98 | } 99 | 100 | 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Mimer.Notes.Server/UserStatsManager.cs: -------------------------------------------------------------------------------- 1 |  2 | using Mimer.Framework; 3 | 4 | namespace Mimer.Notes.Server { 5 | public class UserStats { 6 | public Guid UserId { get; set; } 7 | public DateTime LastActivity { get; set; } = DateTime.UtcNow; 8 | public long Logins { get; set; } 9 | public long Reads { get; set; } 10 | public long ReadBytes { get; set; } 11 | public long Writes { get; set; } 12 | public long WriteBytes { get; set; } 13 | public long Notifications { get; set; } 14 | public long Creates { get; set; } 15 | public long Deletes { get; set; } 16 | } 17 | 18 | public class UserStatsManager { 19 | private object _lock = new object(); 20 | private IMimerDataSource _dataSource; 21 | private Dictionary _stats = new Dictionary(); 22 | 23 | public UserStatsManager(IMimerDataSource dataSource) { 24 | _dataSource = dataSource; 25 | Task.Run(Execute); 26 | } 27 | 28 | private async Task Execute() { 29 | DateTime nextWrite = DateTime.UtcNow.AddSeconds(10); 30 | while (true) { 31 | Thread.Sleep(100); 32 | try { 33 | if (DateTime.UtcNow > nextWrite) { 34 | await WriteStats(); 35 | nextWrite = DateTime.UtcNow.AddSeconds(10); 36 | } 37 | } 38 | catch (Exception ex) { 39 | if (ex is ThreadAbortException || ex is ThreadInterruptedException) { 40 | throw; 41 | } 42 | Dev.Log(ex); 43 | Thread.Sleep(1000); 44 | } 45 | } 46 | } 47 | 48 | private async Task WriteStats() { 49 | Dictionary stats; 50 | lock (_lock) { 51 | stats = _stats; 52 | _stats = new Dictionary(); 53 | } 54 | if (stats.Count > 0) { 55 | await _dataSource.UpdateUserStats(stats.Values); 56 | } 57 | return stats.Count; 58 | } 59 | 60 | private UserStats GetUserStats(Guid userId) { 61 | lock (_lock) { 62 | if (!_stats.ContainsKey(userId)) { 63 | _stats.Add(userId, new UserStats() { UserId = userId }); 64 | } 65 | return _stats[userId]; 66 | } 67 | } 68 | 69 | public void RegisterLogin(Guid userId) { 70 | var stats = GetUserStats(userId); 71 | lock (stats) { 72 | stats.Logins++; 73 | stats.LastActivity = DateTime.UtcNow; 74 | } 75 | } 76 | 77 | public void RegisterRead(Guid userId, long bytes) { 78 | var stats = GetUserStats(userId); 79 | lock (stats) { 80 | stats.Reads++; 81 | stats.ReadBytes += bytes; 82 | stats.LastActivity = DateTime.UtcNow; 83 | } 84 | } 85 | 86 | public void RegisterWrite(Guid userId, long bytes) { 87 | var stats = GetUserStats(userId); 88 | lock (stats) { 89 | stats.Writes++; 90 | stats.WriteBytes += bytes; 91 | stats.LastActivity = DateTime.UtcNow; 92 | } 93 | } 94 | 95 | public void RegisterCreateNote(Guid userId, long bytes) { 96 | var stats = GetUserStats(userId); 97 | lock (stats) { 98 | stats.Creates++; 99 | stats.Writes++; 100 | stats.WriteBytes += bytes; 101 | stats.LastActivity = DateTime.UtcNow; 102 | } 103 | } 104 | 105 | public void RegisterDeleteNote(Guid userId) { 106 | var stats = GetUserStats(userId); 107 | lock (stats) { 108 | stats.Deletes++; 109 | stats.Writes++; 110 | stats.LastActivity = DateTime.UtcNow; 111 | } 112 | } 113 | 114 | public void RegisterNotification(Guid userId) { 115 | var stats = GetUserStats(userId); 116 | lock (stats) { 117 | stats.Notifications++; 118 | stats.LastActivity = DateTime.UtcNow; 119 | } 120 | } 121 | 122 | public void AddStats(Guid userId, UserStats delta) { 123 | var stats = GetUserStats(userId); 124 | lock (stats) { 125 | stats.LastActivity = DateTime.UtcNow; 126 | stats.Logins += delta.Logins; 127 | stats.Reads += delta.Reads; 128 | stats.ReadBytes += delta.ReadBytes; 129 | stats.Writes += delta.Writes; 130 | stats.WriteBytes += delta.WriteBytes; 131 | stats.Notifications += delta.Notifications; 132 | stats.Creates += delta.Creates; 133 | stats.Deletes += delta.Deletes; 134 | } 135 | } 136 | 137 | 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-ef": { 6 | "version": "8.0.8", 7 | "commands": [ 8 | "dotnet-ef" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/Authentication/MimerAuthenticationHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.Extensions.Options; 3 | using Mimer.Framework.Json; 4 | using Mimer.Notes.Model.Cryptography; 5 | using Mimer.Notes.Model.DataTypes; 6 | using System.Security.Claims; 7 | using System.Text; 8 | using System.Text.Encodings.Web; 9 | 10 | namespace Mimer.Notes.SignalR.Authentication { 11 | 12 | public class MimerAuthenticationSchemeOptions : AuthenticationSchemeOptions { 13 | 14 | } 15 | 16 | public class MimerAuthenticationHandler : AuthenticationHandler { 17 | private static AuthenticationTicket Anonymous = new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(new Claim[0], "none")), "MimerAuth"); 18 | private static CryptSignature _signature; 19 | 20 | static MimerAuthenticationHandler() { 21 | _signature = new CryptSignature("RSA;3072", File.ReadAllText(Path.Combine(NotificationServer.CertPath!, "server.pub"))); 22 | } 23 | 24 | public MimerAuthenticationHandler( 25 | IOptionsMonitor options, 26 | ILoggerFactory logger, 27 | UrlEncoder encoder) : base(options, logger, encoder) { 28 | } 29 | 30 | protected override async Task HandleAuthenticateAsync() { 31 | await Task.Delay(1); 32 | string? auth = Context.Request.Headers["Authorization"]; 33 | if (string.IsNullOrEmpty(auth)) { 34 | auth = Context.Request.Query["access_token"]; 35 | } 36 | if (auth?.StartsWith("Bearer ") ?? false) { 37 | auth = auth.Substring("Bearer ".Length); 38 | } 39 | //Console.WriteLine($"HandleAuthenticateAsync {auth}"); 40 | if (string.IsNullOrEmpty(auth)) { 41 | return AuthenticateResult.Success(Anonymous); 42 | } 43 | else { 44 | var token = new MimerNotificationToken(new JsonObject(Encoding.UTF8.GetString(Convert.FromBase64String(auth)))); 45 | if (_signature.VerifySignature("mimer", token)) { 46 | //Console.WriteLine("signature verified for " + token.UserId); 47 | var claims = new[] { 48 | new Claim(ClaimTypes.Name, token.Username), 49 | new Claim(ClaimTypes.NameIdentifier, token.UserId), 50 | new Claim(ClaimTypes.Role, "User") 51 | }; 52 | var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "MimerAuth")); 53 | var ticket = new AuthenticationTicket(principal, Scheme.Name); 54 | return AuthenticateResult.Success(ticket); 55 | } 56 | return AuthenticateResult.NoResult(); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/Authentication/MimerIdentity.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Principal; 2 | 3 | namespace Mimer.Notes.SignalR.Authentication { 4 | public class MimerIdentity : IIdentity { 5 | private readonly string _name; 6 | 7 | public MimerIdentity(string name) { 8 | _name = name; 9 | } 10 | 11 | public string? AuthenticationType => "mime"; 12 | public bool IsAuthenticated => true; 13 | public string? Name => _name; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/Base/JsonModelBinder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ModelBinding; 2 | using Mimer.Framework.Json; 3 | using System.Text; 4 | 5 | namespace Mimer.Notes.SignalR.Base { 6 | public class JsonModelBinder : IModelBinder { 7 | public async Task BindModelAsync(ModelBindingContext bindingContext) { 8 | using var reader = new StreamReader(bindingContext.ActionContext.HttpContext.Request.Body, Encoding.UTF8); 9 | bindingContext.Result = ModelBindingResult.Success(new JsonObject(await reader.ReadToEndAsync())); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/Base/JsonModelBinderProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ModelBinding; 2 | using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; 3 | using Mimer.Framework.Json; 4 | 5 | namespace Mimer.Notes.SignalR.Base { 6 | public class JsonModelBinderProvider : IModelBinderProvider { 7 | public IModelBinder? GetBinder(ModelBinderProviderContext context) { 8 | if (context.Metadata.ModelType == typeof(JsonObject)) { 9 | return new BinderTypeModelBinder(typeof(JsonModelBinder)); 10 | } 11 | return null; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/Controllers/HealthController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace Mimer.Notes.SignalR.Controllers { 4 | [ApiController] 5 | [Route("/")] 6 | public class HealthController : ControllerBase { 7 | private NotificationServer _server; 8 | 9 | public HealthController(NotificationServer server) { 10 | _server = server; 11 | } 12 | 13 | [HttpGet()] 14 | [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] 15 | public IActionResult Health() { 16 | return Content("OK"); 17 | } 18 | 19 | [HttpHead()] 20 | public IActionResult Head() { 21 | return Ok(); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/Controllers/NotificationController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Mimer.Framework.Json; 3 | using Mimer.Notes.Model.Requests; 4 | 5 | namespace Mimer.Notes.SignalR.Controllers { 6 | [ApiController] 7 | [Route("/api/notification")] 8 | public class NotificationController : ControllerBase { 9 | private NotificationServer _server; 10 | 11 | public NotificationController(NotificationServer server) { 12 | _server = server; 13 | } 14 | 15 | [HttpPost("send")] 16 | public async Task Send([FromBody] JsonObject json) { 17 | await _server.Send(new ServerNotificationRequest(json)); 18 | return Ok(); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/Hubs/NotificationsHub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.SignalR; 3 | using System.Security.Claims; 4 | 5 | namespace Mimer.Notes.SignalR.Hubs { 6 | [Authorize()] 7 | public class NotificationsHub : Hub { 8 | 9 | public override async Task OnConnectedAsync() { 10 | await base.OnConnectedAsync(); 11 | if (Context != null) { 12 | var user = Context.User; 13 | if (user != null) { 14 | var id = user.Claims.FirstOrDefault(claim => claim.Type == ClaimTypes.NameIdentifier)?.Value; 15 | if (id != null) { 16 | NotificationServer.AddClient(id, Context.ConnectionId); 17 | } 18 | } 19 | } 20 | } 21 | 22 | public override async Task OnDisconnectedAsync(Exception? exception) { 23 | await base.OnDisconnectedAsync(exception); 24 | if (Context != null) { 25 | var user = Context.User; 26 | if (user != null) { 27 | var id = user.Claims.FirstOrDefault(claim => claim.Type == ClaimTypes.NameIdentifier)?.Value; 28 | if (id != null) { 29 | NotificationServer.RemoveClient(id, Context.ConnectionId); 30 | } 31 | 32 | } 33 | } 34 | 35 | } 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/Mimer.Notes.SignalR.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Exe 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/NotificationServer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.SignalR; 2 | using Mimer.Framework; 3 | using Mimer.Notes.Model.Cryptography; 4 | using Mimer.Notes.Model.Requests; 5 | using Mimer.Notes.SignalR.Hubs; 6 | 7 | namespace Mimer.Notes.SignalR { 8 | public class NotificationServer { 9 | public static string? CertPath { get; set; } 10 | private static Dictionary> _connectionIds = new Dictionary>(); 11 | private IHubClients _clients; 12 | private CryptSignature _signature; 13 | 14 | public static void AddClient(string userId, string connectionId) { 15 | lock (_connectionIds) { 16 | if (!_connectionIds.ContainsKey(userId)) { 17 | _connectionIds.Add(userId, new List()); 18 | } 19 | if (!_connectionIds[userId].Contains(connectionId)) { 20 | _connectionIds[userId].Add(connectionId); 21 | } 22 | } 23 | } 24 | 25 | public static void RemoveClient(string userId, string connectionId) { 26 | lock (_connectionIds) { 27 | if (_connectionIds.ContainsKey(userId)) { 28 | if (_connectionIds[userId].Contains(connectionId)) { 29 | _connectionIds[userId].Remove(connectionId); 30 | } 31 | if (_connectionIds[userId].Count == 0) { 32 | _connectionIds.Remove(userId); 33 | } 34 | } 35 | } 36 | } 37 | 38 | public NotificationServer(IHubContext hub) { 39 | _clients = hub.Clients; 40 | _signature = new CryptSignature("RSA;3072", File.ReadAllText(Path.Combine(CertPath!, "server.pub"))); 41 | } 42 | 43 | public async Task Send(ServerNotificationRequest request) { 44 | try { 45 | if (_signature.VerifySignature("mimer", request)) { 46 | if (request.Type == "bundle-update") { 47 | await _clients.All.SendAsync("notification", "", request.Type); 48 | } 49 | else { 50 | string[] userIds = request.Recipients.Split(","); 51 | List connectionIds = new List(); 52 | foreach (var userId in userIds) { 53 | lock (_connectionIds) { 54 | if (_connectionIds.ContainsKey(userId)) { 55 | connectionIds.AddRange(_connectionIds[userId]); 56 | } 57 | } 58 | } 59 | if (connectionIds.Count > 0) { 60 | await _clients.Clients(connectionIds).SendAsync("notification", request.Sender, request.Type, request.Payload); 61 | } 62 | } 63 | } 64 | } 65 | catch (Exception ex) { 66 | Dev.Log(ex); 67 | } 68 | } 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/Program.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework; 2 | using Mimer.Notes.SignalR; 3 | using Mimer.Notes.SignalR.Authentication; 4 | using Mimer.Notes.SignalR.Base; 5 | using Mimer.Notes.SignalR.Hubs; 6 | 7 | var builder = WebApplication.CreateBuilder(args); 8 | 9 | NotificationServer.CertPath = builder.Configuration.GetValue("CertPath"); 10 | Dev.SetDebugPath(builder.Configuration.GetValue("LogPath")!); 11 | Dev.Log("Start"); 12 | var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get() ?? []; 13 | 14 | // Add services to the container. 15 | 16 | builder.Services.AddControllers(); 17 | builder.Services.AddSignalR(); 18 | builder.Services.AddEndpointsApiExplorer(); 19 | builder.Services.AddSwaggerGen(); 20 | builder.Services.AddSingleton(); 21 | builder.Services.AddAuthentication() 22 | .AddScheme( 23 | "MimerAuth", 24 | opts => { } 25 | ); 26 | 27 | 28 | builder.Services.AddCors(options => { 29 | options.AddPolicy("Main", 30 | policy => { 31 | policy.WithOrigins(allowedOrigins).AllowAnyMethod().AllowAnyHeader().AllowCredentials(); 32 | }); 33 | }); 34 | 35 | builder.Services.AddControllers(options => { 36 | options.ModelBinderProviders.Insert(0, new JsonModelBinderProvider()); 37 | }); 38 | 39 | var app = builder.Build(); 40 | 41 | // Configure the HTTP request pipeline. 42 | 43 | if (app.Environment.IsDevelopment()) { 44 | app.UseSwagger(); 45 | app.UseSwaggerUI(); 46 | app.UseExceptionHandler(_ => { }); 47 | } 48 | 49 | app.UseRouting(); 50 | app.UseAuthentication(); 51 | app.UseAuthorization(); 52 | app.UseCors("Main"); 53 | app.MapControllers(); 54 | app.MapHub("/notifications"); 55 | 56 | app.Run(); 57 | -------------------------------------------------------------------------------- /Mimer.Notes.SignalR/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-ef": { 6 | "version": "8.0.8", 7 | "commands": [ 8 | "dotnet-ef" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Base/DevExceptionHandler.cs: -------------------------------------------------------------------------------- 1 |  2 | using Microsoft.AspNetCore.Diagnostics; 3 | using Mimer.Framework; 4 | 5 | namespace Mimer.Notes.WebApi.Base 6 | { 7 | public class DevExceptionHandler : IExceptionHandler 8 | { 9 | public ValueTask TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) 10 | { 11 | Dev.Log(exception); 12 | return ValueTask.FromResult(false); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Base/JsonModelBinder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ModelBinding; 2 | using Mimer.Framework.Json; 3 | using System.Text; 4 | 5 | namespace Mimer.Notes.WebApi.Base { 6 | public class JsonModelBinder : IModelBinder { 7 | public async Task BindModelAsync(ModelBindingContext bindingContext) { 8 | using var reader = new StreamReader(bindingContext.ActionContext.HttpContext.Request.Body, Encoding.UTF8); 9 | bindingContext.Result = ModelBindingResult.Success(new JsonObject(await reader.ReadToEndAsync())); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Base/JsonModelBinderProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ModelBinding; 2 | using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; 3 | using Mimer.Framework.Json; 4 | 5 | namespace Mimer.Notes.WebApi.Base { 6 | public class JsonModelBinderProvider : IModelBinderProvider { 7 | public IModelBinder? GetBinder(ModelBinderProviderContext context) { 8 | if (context.Metadata.ModelType == typeof(JsonObject)) { 9 | return new BinderTypeModelBinder(typeof(JsonModelBinder)); 10 | } 11 | return null; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Base/MimerController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Primitives; 3 | using Mimer.Notes.Server; 4 | 5 | namespace Mimer.Notes.WebApi.Base { 6 | public class MimerController : ControllerBase { 7 | 8 | public ClientInfo Info { 9 | get { 10 | string userAgent = ""; 11 | StringValues userAgentValues; 12 | if (this.HttpContext.Request.Headers.TryGetValue("User-Agent", out userAgentValues)) { 13 | userAgent = userAgentValues[0] ?? ""; 14 | } 15 | string mimriVersion = ""; 16 | StringValues mimriVersionValues; 17 | if (this.HttpContext.Request.Headers.TryGetValue("X-Mimiri-Version", out mimriVersionValues)) { 18 | mimriVersion = mimriVersionValues[0] ?? ""; 19 | } 20 | return new ClientInfo(userAgent, mimriVersion); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Controllers/HealthController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Mimer.Notes.Server; 3 | using Mimer.Notes.WebApi.Base; 4 | 5 | namespace Mimer.Notes.WebApi.Controllers { 6 | [ApiController] 7 | [Route("api/health")] 8 | public class HealthController : MimerController { 9 | private MimerServer _server; 10 | 11 | public HealthController(MimerServer server) { 12 | _server = server; 13 | } 14 | 15 | [HttpGet()] 16 | [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] 17 | public IActionResult Get() { 18 | _server.RegisterAction(Info, "health"); 19 | return Ok("OK"); 20 | } 21 | 22 | [HttpHead()] 23 | public IActionResult Head() { 24 | _server.RegisterAction(Info, "health:head"); 25 | return Ok(); 26 | } 27 | 28 | //[HttpGet("error")] 29 | //public IActionResult GetError() { 30 | // throw new Exception("Test Error"); 31 | //} 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Controllers/KeyController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Mimer.Framework.Json; 3 | using Mimer.Notes.Model.Requests; 4 | using Mimer.Notes.Server; 5 | using Mimer.Notes.WebApi.Base; 6 | using System.Text; 7 | 8 | namespace Mimer.Notes.WebApi.Controllers { 9 | [ApiController] 10 | [Route("api/key")] 11 | public class KeyController : MimerController { 12 | private MimerServer _server; 13 | 14 | public KeyController(MimerServer server) { 15 | _server = server; 16 | } 17 | 18 | [HttpPost("create")] 19 | public async Task Create([FromBody] JsonObject json) { 20 | _server.RegisterAction(Info, "key/create"); 21 | var response = await _server.CreateKey(new CreateKeyRequest(json)); 22 | if (response == null) { 23 | return Conflict(); 24 | } 25 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 26 | } 27 | 28 | [HttpPost("read-all")] 29 | public async Task ReadAllKeys([FromBody] JsonObject json) { 30 | _server.RegisterAction(Info, "key/read-all"); 31 | var response = await _server.ReadAllKeys(new BasicRequest(json)); 32 | if (response == null) { 33 | return NotFound(); 34 | } 35 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 36 | } 37 | 38 | [HttpPost("read")] 39 | public async Task Read([FromBody] JsonObject json) { 40 | _server.RegisterAction(Info, "key/read"); 41 | var response = await _server.ReadKey(new ReadKeyRequest(json)); 42 | if (response == null) { 43 | return NotFound(); 44 | } 45 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 46 | } 47 | 48 | [HttpPost("delete")] 49 | public async Task Delete([FromBody] JsonObject json) { 50 | _server.RegisterAction(Info, "key/delete"); 51 | var response = await _server.DeleteKey(new DeleteKeyRequest(json)); 52 | if (response == null) { 53 | return NotFound(); 54 | } 55 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 56 | } 57 | 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Controllers/NoteController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Mimer.Framework.Json; 3 | using Mimer.Notes.Model.Requests; 4 | using Mimer.Notes.Server; 5 | using Mimer.Notes.WebApi.Base; 6 | using System.Text; 7 | 8 | namespace Mimer.Notes.WebApi.Controllers { 9 | [ApiController] 10 | [Route("api/note")] 11 | public class NoteController : MimerController { 12 | private MimerServer _server; 13 | 14 | public NoteController(MimerServer server) { 15 | _server = server; 16 | } 17 | 18 | [HttpPost("multi")] 19 | public async Task Multi([FromBody] JsonObject json) { 20 | _server.RegisterAction(Info, "note/multi"); 21 | var response = await _server.MultiNote(new MultiNoteRequest(json)); 22 | if (response == null) { 23 | return NotFound(); 24 | } 25 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 26 | } 27 | 28 | [HttpPost("create")] 29 | public async Task Create([FromBody] JsonObject json) { 30 | _server.RegisterAction(Info, "note/create"); 31 | var response = await _server.CreateNote(new WriteNoteRequest(json)); 32 | if (response == null) { 33 | return Conflict(); 34 | } 35 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 36 | } 37 | 38 | [HttpPost("read")] 39 | public async Task Read([FromBody] JsonObject json) { 40 | _server.RegisterAction(Info, "note/read"); 41 | var response = await _server.ReadNote(new ReadNoteRequest(json)); 42 | if (response == null) { 43 | return NotFound(); 44 | } 45 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 46 | } 47 | 48 | [HttpPost("update")] 49 | public async Task Update([FromBody] JsonObject json) { 50 | _server.RegisterAction(Info, "note/update"); 51 | var response = await _server.UpdateNote(new WriteNoteRequest(json)); 52 | if (response == null) { 53 | return NotFound(); 54 | } 55 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 56 | } 57 | 58 | [HttpPost("delete")] 59 | public async Task Delete([FromBody] JsonObject json) { 60 | _server.RegisterAction(Info, "note/delete"); 61 | var response = await _server.DeleteNote(new DeleteNoteRequest(json)); 62 | if (response == null) { 63 | return NotFound(); 64 | } 65 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 66 | } 67 | 68 | [HttpPost("share")] 69 | public async Task Share([FromBody] JsonObject json) { 70 | _server.RegisterAction(Info, "note/share"); 71 | var response = await _server.ShareNote(new ShareNoteRequest(json)); 72 | if (response == null) { 73 | return Conflict(); 74 | } 75 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 76 | } 77 | 78 | [HttpPost("share-offers")] 79 | public async Task ReadShareOffers([FromBody] JsonObject json) { 80 | _server.RegisterAction(Info, "note/share-offers"); 81 | var response = await _server.ReadShareOffers(new BasicRequest(json)); 82 | if (response == null) { 83 | return NotFound(); 84 | } 85 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 86 | } 87 | 88 | [HttpPost("share/delete")] 89 | public async Task DeleteShare([FromBody] JsonObject json) { 90 | _server.RegisterAction(Info, "note/share/delete"); 91 | var response = await _server.DeleteShare(new DeleteShareRequest(json)); 92 | if (response == null) { 93 | return NotFound(); 94 | } 95 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 96 | } 97 | 98 | 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Controllers/NotificationController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Mimer.Framework.Json; 3 | using Mimer.Notes.Model.Requests; 4 | using Mimer.Notes.Model.Responses; 5 | using Mimer.Notes.Server; 6 | using Mimer.Notes.WebApi.Base; 7 | using System.Text; 8 | 9 | namespace Mimer.Notes.WebApi.Controllers { 10 | [ApiController] 11 | [Route("api/notification")] 12 | public class NotificationController : MimerController { 13 | private MimerServer _server; 14 | 15 | public NotificationController(MimerServer server) { 16 | _server = server; 17 | } 18 | 19 | [HttpPost("create-url")] 20 | public async Task CreateUrl([FromBody] JsonObject json) { 21 | var response = await _server.CreateNotificationUrl(new BasicRequest(json)); 22 | if (response == null) { 23 | return Conflict(); 24 | } 25 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 26 | } 27 | 28 | [HttpPost("send")] 29 | public IActionResult Send([FromBody] JsonObject json) { 30 | return Content(new BasicResponse().ToJsonString(), "text/plain", Encoding.UTF8); 31 | } 32 | 33 | [HttpGet("notify-update")] 34 | [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] 35 | public async Task NotifyUpdate() { 36 | await _server.NotifyUpdate(); 37 | return Content(new BasicResponse().ToJsonString(), "text/plain", Encoding.UTF8); 38 | } 39 | 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Controllers/TestController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Mimer.Framework.Json; 3 | using Mimer.Notes.Server; 4 | using Mimer.Notes.WebApi.Base; 5 | using System.Text; 6 | 7 | namespace Mimer.Notes.WebApi.Controllers { 8 | [ApiController] 9 | [Route("api/test")] 10 | public class TestController : MimerController { 11 | private class TestContext { 12 | public MimerServer? Server; 13 | public KeyController? Key; 14 | public NoteController? Note; 15 | public UserController? User; 16 | public NotificationController? Notification; 17 | } 18 | 19 | private static Dictionary _testServers = new Dictionary(); 20 | 21 | private TestContext? GetTestContext(string testId) { 22 | lock (_testServers) { 23 | if (_testServers.ContainsKey(testId)) { 24 | return _testServers[testId]; 25 | } 26 | } 27 | return null; 28 | } 29 | 30 | [HttpGet("{testId}/begin")] 31 | [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] 32 | public IActionResult Begin(string testId) { 33 | lock (_testServers) { 34 | if (!_testServers.ContainsKey(testId)) { 35 | var testContext = new TestContext(); 36 | testContext.Server = new MimerServer(testId); 37 | testContext.Key = new KeyController(testContext.Server); 38 | testContext.Note = new NoteController(testContext.Server); 39 | testContext.User = new UserController(testContext.Server); 40 | testContext.Notification = new NotificationController(testContext.Server); 41 | _testServers.Add(testId, testContext); 42 | } 43 | } 44 | return Content("{}", "text/plain", Encoding.UTF8); 45 | } 46 | 47 | [HttpGet("{testId}/end/{keepLogs}")] 48 | [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] 49 | public IActionResult End(string testId, bool keepLogs) { 50 | var testContext = GetTestContext(testId); 51 | if (testContext != null) { 52 | testContext.Server!.TearDown(keepLogs); 53 | lock (_testServers) { 54 | _testServers.Remove(testId); 55 | } 56 | } 57 | return Content("{}", "text/plain", Encoding.UTF8); 58 | } 59 | 60 | // user controller 61 | [HttpPost("{testId}/user/create")] 62 | public async Task UserCreate(string testId, [FromBody] JsonObject json) { 63 | var context = GetTestContext(testId); 64 | if (context == null) { 65 | return NotFound(); 66 | } 67 | return await context.User!.Create(json); 68 | } 69 | 70 | [HttpPost("{testId}/user/update")] 71 | public async Task UserUpdate(string testId, [FromBody] JsonObject json) { 72 | var context = GetTestContext(testId); 73 | if (context == null) { 74 | return NotFound(); 75 | } 76 | return await context.User!.Update(json); 77 | } 78 | 79 | [HttpPost("{testId}/user/update-data")] 80 | public async Task UserUpdateData(string testId, [FromBody] JsonObject json) { 81 | var context = GetTestContext(testId); 82 | if (context == null) { 83 | return NotFound(); 84 | } 85 | return await context.User!.UpdateData(json); 86 | } 87 | 88 | [HttpGet("{testId}/user/pre-login/{username}")] 89 | [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] 90 | public async Task UserPreLogin(string testId, string username) { 91 | var context = GetTestContext(testId); 92 | if (context == null) { 93 | return NotFound(); 94 | } 95 | return await context.User!.PreLogin(username); 96 | } 97 | 98 | [HttpPost("{testId}/user/login")] 99 | public async Task UserLogin(string testId, [FromBody] JsonObject json) { 100 | var context = GetTestContext(testId); 101 | if (context == null) { 102 | return NotFound(); 103 | } 104 | return await context.User!.Login(json); 105 | } 106 | 107 | [HttpPost("{testId}/user/public-key")] 108 | public async Task PublicKey(string testId, [FromBody] JsonObject json) { 109 | var context = GetTestContext(testId); 110 | if (context == null) { 111 | return NotFound(); 112 | } 113 | return await context.User!.PublicKey(json); 114 | } 115 | 116 | // key controller 117 | [HttpPost("{testId}/key/create")] 118 | public async Task KeyCreate(string testId, [FromBody] JsonObject json) { 119 | var context = GetTestContext(testId); 120 | if (context == null) { 121 | return NotFound(); 122 | } 123 | return await context.Key!.Create(json); 124 | } 125 | 126 | [HttpPost("{testId}/key/read-all")] 127 | public async Task KeyAll(string testId, [FromBody] JsonObject json) { 128 | var context = GetTestContext(testId); 129 | if (context == null) { 130 | return NotFound(); 131 | } 132 | return await context.Key!.ReadAllKeys(json); 133 | } 134 | 135 | [HttpPost("{testId}/key/read")] 136 | public async Task KeyRead(string testId, [FromBody] JsonObject json) { 137 | var context = GetTestContext(testId); 138 | if (context == null) { 139 | return NotFound(); 140 | } 141 | return await context.Key!.Read(json); 142 | } 143 | 144 | [HttpPost("{testId}/key/delete")] 145 | public async Task Delete(string testId, [FromBody] JsonObject json) { 146 | var context = GetTestContext(testId); 147 | if (context == null) { 148 | return NotFound(); 149 | } 150 | return await context.Key!.Delete(json); 151 | } 152 | 153 | // note controller 154 | [HttpPost("{testId}/note/create")] 155 | public async Task NoteCreate(string testId, [FromBody] JsonObject json) { 156 | var context = GetTestContext(testId); 157 | if (context == null) { 158 | return NotFound(); 159 | } 160 | return await context.Note!.Create(json); 161 | } 162 | 163 | [HttpPost("{testId}/note/read")] 164 | public async Task NoteRead(string testId, [FromBody] JsonObject json) { 165 | var context = GetTestContext(testId); 166 | if (context == null) { 167 | return NotFound(); 168 | } 169 | return await context.Note!.Read(json); 170 | } 171 | 172 | [HttpPost("{testId}/note/update")] 173 | public async Task NoteUpdate(string testId, [FromBody] JsonObject json) { 174 | var context = GetTestContext(testId); 175 | if (context == null) { 176 | return NotFound(); 177 | } 178 | return await context.Note!.Update(json); 179 | } 180 | 181 | [HttpPost("{testId}/note/delete")] 182 | public async Task NoteDelete(string testId, [FromBody] JsonObject json) { 183 | var context = GetTestContext(testId); 184 | if (context == null) { 185 | return NotFound(); 186 | } 187 | return await context.Note!.Delete(json); 188 | } 189 | 190 | [HttpPost("{testId}/note/multi")] 191 | public async Task NoteMulti(string testId, [FromBody] JsonObject json) { 192 | var context = GetTestContext(testId); 193 | if (context == null) { 194 | return NotFound(); 195 | } 196 | return await context.Note!.Multi(json); 197 | } 198 | 199 | [HttpPost("{testId}/note/share")] 200 | public async Task NoteShare(string testId, [FromBody] JsonObject json) { 201 | var context = GetTestContext(testId); 202 | if (context == null) { 203 | return NotFound(); 204 | } 205 | return await context.Note!.Share(json); 206 | } 207 | 208 | [HttpPost("{testId}/note/share-offers")] 209 | public async Task ShareOffers(string testId, [FromBody] JsonObject json) { 210 | var context = GetTestContext(testId); 211 | if (context == null) { 212 | return NotFound(); 213 | } 214 | return await context.Note!.ReadShareOffers(json); 215 | } 216 | 217 | [HttpPost("{testId}/note/share/delete")] 218 | public async Task NoteDeleteShare(string testId, [FromBody] JsonObject json) { 219 | var context = GetTestContext(testId); 220 | if (context == null) { 221 | return NotFound(); 222 | } 223 | return await context.Note!.DeleteShare(json); 224 | } 225 | 226 | [HttpPost("{testId}/notification/create-url")] 227 | public async Task NotificationsCreateUrl(string testId, [FromBody] JsonObject json) { 228 | var context = GetTestContext(testId); 229 | if (context == null) { 230 | return NotFound(); 231 | } 232 | return await context.Notification!.CreateUrl(json); 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Controllers/UserController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Mimer.Framework.Json; 3 | using Mimer.Notes.Model.Requests; 4 | using Mimer.Notes.Server; 5 | using Mimer.Notes.WebApi.Base; 6 | using System.Text; 7 | 8 | namespace Mimer.Notes.WebApi.Controllers { 9 | [ApiController] 10 | [Route("api/user")] 11 | public class UserController : MimerController { 12 | private MimerServer _server; 13 | 14 | public UserController(MimerServer server) { 15 | _server = server; 16 | } 17 | 18 | [HttpPost("create")] 19 | public async Task Create([FromBody] JsonObject json) { 20 | _server.RegisterAction(Info, "user/create"); 21 | if (json.Has("keyId")) { 22 | json = _server.DecryptRequest(json); 23 | } 24 | var response = await _server.CreateUser(new CreateUserRequest(json)); 25 | if (response != null) { 26 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 27 | } 28 | return Conflict(); 29 | } 30 | 31 | [HttpPost("update")] 32 | public async Task Update([FromBody] JsonObject json) { 33 | _server.RegisterAction(Info, "user/update"); 34 | if (json.Has("keyId")) { 35 | json = _server.DecryptRequest(json); 36 | } 37 | var response = await _server.UpdateUser(new UpdateUserRequest(json)); 38 | if (response != null) { 39 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 40 | } 41 | return Conflict(); 42 | } 43 | 44 | [HttpPost("update-data")] 45 | public async Task UpdateData([FromBody] JsonObject json) { 46 | _server.RegisterAction(Info, "user/update-data"); 47 | var response = await _server.UpdateUserData(new UpdateUserDataRequest(json)); 48 | if (response != null) { 49 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 50 | } 51 | return Conflict(); 52 | } 53 | 54 | [HttpGet("pre-login/{username}")] 55 | [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] 56 | public async Task PreLogin(string username) { 57 | _server.RegisterAction(Info, "user/pre-login"); 58 | var response = await _server.PreLogin(username); 59 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 60 | } 61 | 62 | [HttpPost("login")] 63 | public async Task Login([FromBody] JsonObject json) { 64 | _server.RegisterAction(Info, "user/login"); 65 | var response = await _server.Login(new LoginRequest(json)); 66 | if (response != null) { 67 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 68 | } 69 | return NotFound(); 70 | } 71 | 72 | [HttpPost("get-data")] 73 | public async Task GetUserData([FromBody] JsonObject json) { 74 | _server.RegisterAction(Info, "user/get-data"); 75 | var response = await _server.GetUserData(new BasicRequest(json)); 76 | if (response != null) { 77 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 78 | } 79 | return NotFound(); 80 | } 81 | 82 | [HttpPost("public-key")] 83 | public async Task PublicKey([FromBody] JsonObject json) { 84 | _server.RegisterAction(Info, "user/public-key"); 85 | var response = await _server.GetPublicKey(new PublicKeyRequest(json)); 86 | if (response != null) { 87 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 88 | } 89 | return NotFound(); 90 | } 91 | 92 | [HttpPost("delete")] 93 | public async Task DeleteUser([FromBody] JsonObject json) { 94 | _server.RegisterAction(Info, "user/delete"); 95 | var response = await _server.DeleteUser(new DeleteAccountRequest(json)); 96 | if (response != null) { 97 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 98 | } 99 | return NotFound(); 100 | } 101 | 102 | [HttpPost("available")] 103 | public async Task UsernameAvailable([FromBody] JsonObject json) { 104 | _server.RegisterAction(Info, "user/available"); 105 | var response = await _server.UsernameAvailable(new CheckUsernameRequest(json)); 106 | if (response != null) { 107 | return Content(response.ToJsonString(), "text/plain", Encoding.UTF8); 108 | } 109 | return NotFound(); 110 | } 111 | 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Mimer.Notes.WebApi.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Exe 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/Program.cs: -------------------------------------------------------------------------------- 1 | using Mimer.Framework; 2 | using Mimer.Notes.Server; 3 | using Mimer.Notes.WebApi.Base; 4 | 5 | var builder = WebApplication.CreateBuilder(args); 6 | 7 | //var aes = Aes.Create(); 8 | //aes.GenerateKey(); 9 | 10 | //var rsa = new RSACryptoServiceProvider(4096); 11 | 12 | //JsonObject json = new JsonObject(); 13 | //json.String("aesKey", Convert.ToBase64String(aes.Key)); 14 | //json.String("privateKey", rsa.ExportPkcs8PrivateKeyPem()); 15 | //json.String("publicKey", rsa.ExportSubjectPublicKeyInfoPem()); 16 | 17 | //Console.WriteLine(json.ToString()); 18 | 19 | MimerServer.CertPath = builder.Configuration.GetValue("CertPath"); 20 | MimerServer.WebsocketUrl = builder.Configuration.GetValue("WebsocketUrl"); 21 | MimerServer.NotificationsUrl = builder.Configuration.GetValue("NotificationsUrl"); 22 | Dev.SetDebugPath(builder.Configuration.GetValue("LogPath")!); 23 | Dev.Log("Start"); 24 | MimerServer.DefaultPostgresConnectionString = builder.Configuration.GetConnectionString("Default") ?? ""; 25 | MimerServer.AesKey = Convert.FromBase64String(builder.Configuration.GetValue("AesKey") ?? ""); 26 | var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get() ?? []; 27 | Dev.Log("Allowed Origins:"); 28 | foreach (var allowedOrigin in allowedOrigins) { 29 | Dev.Log(allowedOrigin); 30 | } 31 | 32 | // Add services to the container. 33 | builder.Services.AddControllers(); 34 | builder.Services.AddEndpointsApiExplorer(); 35 | builder.Services.AddSwaggerGen(); 36 | builder.Services.AddExceptionHandler(); 37 | builder.Services.AddSingleton(); 38 | 39 | builder.Services.AddControllers(options => { 40 | options.ModelBinderProviders.Insert(0, new JsonModelBinderProvider()); 41 | }); 42 | 43 | builder.Services.AddCors(options => { 44 | options.AddPolicy("Main", 45 | policy => { 46 | policy.WithOrigins(allowedOrigins).AllowAnyMethod().AllowAnyHeader(); 47 | }); 48 | }); 49 | 50 | var app = builder.Build(); 51 | 52 | 53 | 54 | // Configure the HTTP request pipeline. 55 | if (app.Environment.IsDevelopment()) { 56 | app.UseSwagger(); 57 | app.UseSwaggerUI(); 58 | app.UseExceptionHandler(_ => { }); 59 | } 60 | 61 | app.UseCors("Main"); 62 | app.MapControllers(); 63 | 64 | app.Run(); 65 | -------------------------------------------------------------------------------- /Mimer.Notes.WebApi/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mimiri Server 2 | 3 | Documentation: work in progress 4 | --------------------------------------------------------------------------------