internal static PasswordRecord Load(this IPasswordRecord r, byte[] key)
        {
            var record = new PasswordRecord()
            {
                RecordKey      = key,
                Uid            = r.RecordUid,
                Shared         = r.Shared,
                Owner          = r.Owner,
                ClientModified = r.ClientModifiedTime != 0
                    ? DateTimeOffsetExtensions.FromUnixTimeMilliseconds(r.ClientModifiedTime)
                    : DateTimeOffset.Now,
            };

            var data = r.Data.Base64UrlDecode();

            data = CryptoUtils.DecryptAesV1(data, key);
            using (var ms = new MemoryStream(data))
            {
                var parsedData = (RecordData)DataSerializer.ReadObject(ms);
                record.Title    = parsedData.title;
                record.Login    = parsedData.secret1;
                record.Password = parsedData.secret2;
                record.Link     = parsedData.link;
                record.Notes    = parsedData.notes;
                if (parsedData.custom != null)
                {
                    foreach (var cr in parsedData.custom)
                    {
                        record.Custom.Add(new CustomField
                        {
                            Name  = cr.name,
                            Value = cr.value,
                            Type  = cr.type
                        });
                    }
                }
            }

            if (!string.IsNullOrEmpty(r.Extra))
            {
                var extra = CryptoUtils.DecryptAesV1(r.Extra.Base64UrlDecode(), key);
                using (var ms = new MemoryStream(extra))
                {
                    var parsedExtra = (RecordExtra)ExtraSerializer.ReadObject(ms);
                    if (parsedExtra.files != null && parsedExtra.files.Length > 0)
                    {
                        foreach (var file in parsedExtra.files)
                        {
                            var atta = new AttachmentFile
                            {
                                Id           = file.id,
                                Key          = file.key,
                                Name         = file.name,
                                Title        = file.title ?? "",
                                Type         = file.type ?? "",
                                Size         = file.size ?? 0,
                                LastModified = file.lastModified != null
                                    ? DateTimeOffsetExtensions.FromUnixTimeMilliseconds(file.lastModified.Value)
                                    : DateTimeOffset.Now
                            };
                            if (file.thumbs != null)
                            {
                                atta.Thumbnails = file.thumbs
                                                  .Select(t => new AttachmentFileThumb
                                {
                                    Id   = t.id,
                                    Type = t.type,
                                    Size = t.size ?? 0
                                })
                                                  .ToArray();
                            }

                            record.Attachments.Add(atta);
                        }
                    }

                    if (parsedExtra.fields != null)
                    {
                        foreach (var field in parsedExtra.fields)
                        {
                            var fld = new ExtraField();
                            foreach (var pair in field)
                            {
                                switch (pair.Key)
                                {
                                case "id":
                                    fld.Id = pair.Value.ToString();
                                    break;

                                case "field_type":
                                    fld.FieldType = pair.Value.ToString();
                                    break;

                                case "field_title":
                                    fld.FieldTitle = pair.Value.ToString();
                                    break;

                                default:
                                    fld.Custom[pair.Key] = pair.Value;
                                    break;
                                }
                            }

                            record.ExtraFields.Add(fld);
                        }
                    }
                }
            }

            return(record);
        }
        public static async Task <PasswordRecord> PutRecord(this VaultOnline vault, PasswordRecord record, bool skipData = false, bool skipExtra = true)
        {
            IPasswordRecord existingRecord = null;

            if (!string.IsNullOrEmpty(record.Uid))
            {
                existingRecord = vault.Storage.Records.GetEntity(record.Uid);
            }

            if (existingRecord == null)
            {
                return(await vault.AddRecordToFolder(record));
            }

            var updateRecord = new RecordUpdateRecord
            {
                RecordUid = existingRecord.RecordUid
            };

            var rmd = vault.ResolveRecordAccessPath(updateRecord, true);

            if (rmd != null)
            {
                if (rmd.RecordKeyType == (int)KeyType.NoKey || rmd.RecordKeyType == (int)KeyType.PrivateKey)
                {
                    updateRecord.RecordKey = CryptoUtils.EncryptAesV1(record.RecordKey, vault.Auth.AuthContext.DataKey)
                                             .Base64UrlEncode();
                }
            }

            updateRecord.Revision = existingRecord.Revision;

            if (!skipData)
            {
                var        dataSerializer = new DataContractJsonSerializer(typeof(RecordData), JsonUtils.JsonSettings);
                RecordData existingData   = null;
                try
                {
                    var unencryptedData =
                        CryptoUtils.DecryptAesV1(existingRecord.Data.Base64UrlDecode(), record.RecordKey);
                    using (var ms = new MemoryStream(unencryptedData))
                    {
                        existingData = (RecordData)dataSerializer.ReadObject(ms);
                    }
                }
                catch (Exception e)
                {
                    Trace.TraceError("Decrypt Record: UID: {0}, {1}: \"{2}\"",
                                     existingRecord.RecordUid,
                                     e.GetType().Name,
                                     e.Message);
                }

                var data = record.ExtractRecordData(existingData);
                using (var ms = new MemoryStream())
                {
                    dataSerializer.WriteObject(ms, data);
                    updateRecord.Data = CryptoUtils.EncryptAesV1(ms.ToArray(), record.RecordKey).Base64UrlEncode();
                }
            }

            if (!skipExtra)
            {
                var         extraSerializer = new DataContractJsonSerializer(typeof(RecordExtra), JsonUtils.JsonSettings);
                RecordExtra existingExtra   = null;
                try
                {
                    var unencryptedExtra =
                        CryptoUtils.DecryptAesV1(existingRecord.Extra.Base64UrlDecode(), record.RecordKey);
                    using (var ms = new MemoryStream(unencryptedExtra))
                    {
                        existingExtra = (RecordExtra)extraSerializer.ReadObject(ms);
                    }
                }
                catch (Exception e)
                {
                    Trace.TraceError("Decrypt Record: UID: {0}, {1}: \"{2}\"",
                                     existingRecord.RecordUid,
                                     e.GetType().Name,
                                     e.Message);
                }

                var extra = record.ExtractRecordExtra(existingExtra);
                using (var ms = new MemoryStream())
                {
                    extraSerializer.WriteObject(ms, extra);
                    updateRecord.Extra = CryptoUtils.EncryptAesV1(ms.ToArray(), record.RecordKey).Base64UrlEncode();
                }

                var udata = new RecordUpdateUData();
                var ids   = new HashSet <string>();
                if (record.Attachments != null)
                {
                    foreach (var atta in record.Attachments)
                    {
                        ids.Add(atta.Id);
                        if (atta.Thumbnails != null)
                        {
                            foreach (var thumb in atta.Thumbnails)
                            {
                                ids.Add(thumb.Id);
                            }
                        }
                    }
                }

                udata.FileIds      = ids.ToArray();
                updateRecord.Udata = udata;
            }

            var command = new RecordUpdateCommand
            {
                deviceId      = vault.Auth.Endpoint.DeviceName,
                UpdateRecords = new[] { updateRecord }
            };

            await vault.Auth.ExecuteAuthCommand <RecordUpdateCommand, RecordUpdateResponse>(command);

            await vault.ScheduleSyncDown(TimeSpan.FromSeconds(0));

            return(vault.TryGetRecord(record.Uid, out var r) ? r : record);
        }