diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index aa3bdf3..a01be4c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -42,7 +42,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
- dotnet-version: 6.0.x
+ dotnet-version: '8.0.x'
- uses: microsoft/setup-msbuild@v2
- uses: nuget/setup-nuget@v2
diff --git a/Commander/App.config b/Commander/App.config
index 931b343..6f06adb 100644
--- a/Commander/App.config
+++ b/Commander/App.config
@@ -21,6 +21,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Commander/Commander.csproj b/Commander/Commander.csproj
index 1aae3ba..67e69c2 100644
--- a/Commander/Commander.csproj
+++ b/Commander/Commander.csproj
@@ -69,15 +69,15 @@
-
- ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll
+
+ ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll
..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
diff --git a/Commander/packages.config b/Commander/packages.config
index cc90140..b658a16 100644
--- a/Commander/packages.config
+++ b/Commander/packages.config
@@ -4,7 +4,7 @@
-
+
-
+
\ No newline at end of file
diff --git a/KeeperSdk/KeeperSdk.csproj b/KeeperSdk/KeeperSdk.csproj
index 3a9edd1..27d3907 100644
--- a/KeeperSdk/KeeperSdk.csproj
+++ b/KeeperSdk/KeeperSdk.csproj
@@ -50,6 +50,13 @@
+
+
+
+
+
+ 4.5.2
+
diff --git a/KeeperSdk/vault/FileAttachment.cs b/KeeperSdk/vault/FileAttachment.cs
index 57ff190..aa6b011 100644
--- a/KeeperSdk/vault/FileAttachment.cs
+++ b/KeeperSdk/vault/FileAttachment.cs
@@ -11,15 +11,17 @@
using System.Text;
using System.Threading.Tasks;
using Google.Protobuf;
+using Org.BouncyCastle.Utilities.Zlib;
+using System.Net.Http;
+using System.Runtime.CompilerServices;
+using System.Net.Http.Headers;
-namespace KeeperSecurity.Vault
-{
+namespace KeeperSecurity.Vault {
///
/// Creates an attachment upload task.
///
- public class AttachmentUploadTask : IAttachmentUploadTask
- {
+ public class AttachmentUploadTask : IAttachmentUploadTask {
///
/// Initializes a new instance of class.
///
@@ -50,8 +52,7 @@ public AttachmentUploadTask(Stream attachmentStream, IThumbnailUploadTask thumbn
///
/// Creates a file attachment upload task.
///
- public class FileAttachmentUploadTask : AttachmentUploadTask, IDisposable
- {
+ public class FileAttachmentUploadTask : AttachmentUploadTask, IDisposable {
///
/// Initializes a new instance of class.
///
@@ -60,25 +61,16 @@ public class FileAttachmentUploadTask : AttachmentUploadTask, IDisposable
public FileAttachmentUploadTask(string fileName, IThumbnailUploadTask thumbnail = null)
: base(null, thumbnail)
{
- if (File.Exists(fileName))
- {
- Name = Path.GetFileName(fileName);
- Title = Name;
- try
- {
- MimeType = MimeTypes.MimeTypeMap.GetMimeType(Path.GetExtension(fileName));
- }
- catch
- {
- // ignored
- }
-
- Stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
- }
- else
- {
- Trace.TraceError("FileAttachmentUploadTask: fileName: \"{0}\" not found.", fileName);
+ if (!File.Exists(fileName)) {
+ throw new Exception($"Cannot open file \"{fileName}\"");
}
+ Name = Path.GetFileName(fileName);
+ Title = Name;
+ try {
+ MimeType = MimeTypes.MimeTypeMap.GetMimeType(Path.GetExtension(fileName));
+ } catch {/*ignored*/}
+
+ Stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
}
public void Dispose()
@@ -87,47 +79,39 @@ public void Dispose()
}
}
- public partial class VaultOnline : IVaultFileAttachment
- {
+ public partial class VaultOnline : IVaultFileAttachment {
///
public IEnumerable RecordAttachments(KeeperRecord record)
{
- switch (record)
- {
- case PasswordRecord password:
- if (password.Attachments != null)
- {
- foreach (var atta in password.Attachments)
- {
- yield return atta;
- }
+ switch (record) {
+ case PasswordRecord password:
+ if (password.Attachments != null) {
+ foreach (var atta in password.Attachments) {
+ yield return atta;
}
+ }
- break;
-
- case TypedRecord typed:
- var fileRef = typed.Fields
- .Where(x => x.FieldName == "fileRef")
- .OfType>().FirstOrDefault();
- if (fileRef != null)
- {
- foreach (var fileUid in fileRef.Values)
- {
- if (TryGetKeeperRecord(fileUid, out var kr))
- {
- if (kr is FileRecord fr)
- {
- yield return fr;
- }
+ break;
+
+ case TypedRecord typed:
+ var fileRef = typed.Fields
+ .Where(x => x.FieldName == "fileRef")
+ .OfType>().FirstOrDefault();
+ if (fileRef != null) {
+ foreach (var fileUid in fileRef.Values) {
+ if (TryGetKeeperRecord(fileUid, out var kr)) {
+ if (kr is FileRecord fr) {
+ yield return fr;
}
}
}
+ }
- break;
+ break;
- case FileRecord file:
- yield return file;
- break;
+ case FileRecord file:
+ yield return file;
+ break;
}
}
@@ -136,15 +120,12 @@ public IEnumerable RecordAttachments(KeeperRecord record)
public async Task DownloadAttachment(KeeperRecord record, string attachment, Stream destination)
{
var atta = RecordAttachments(record)
- .Where(x =>
- {
- if (string.IsNullOrEmpty(attachment))
- {
+ .Where(x => {
+ if (string.IsNullOrEmpty(attachment)) {
return true;
}
- if (attachment == x.Id || attachment == x.Name || attachment == x.Title)
- {
+ if (attachment == x.Id || attachment == x.Name || attachment == x.Title) {
return true;
}
@@ -153,23 +134,21 @@ public async Task DownloadAttachment(KeeperRecord record, string attachment, Str
})
.FirstOrDefault();
- if (atta == null)
- {
- throw new KeeperInvalidParameter("Vault::DownloadAttachment", "attachment", attachment, "not found");
+ if (atta == null) {
+ throw new KeeperInvalidParameter("Vault::DownloadAttachment", "attachment", attachment, "not found");
}
- switch (atta)
- {
- case AttachmentFile attachmentFile:
- await DownloadAttachmentFile(record.Uid, attachmentFile, destination);
- break;
+ switch (atta) {
+ case AttachmentFile attachmentFile:
+ await DownloadAttachmentFile(record.Uid, attachmentFile, destination);
+ break;
- case FileRecord fileRecord:
- await DownloadFile(fileRecord, destination);
- break;
+ case FileRecord fileRecord:
+ await DownloadFile(fileRecord, destination);
+ break;
- default:
- throw new KeeperInvalidParameter("Vault::DownloadAttachment", "attachment", atta.GetType().Name, "attachment type is not supported");
+ default:
+ throw new KeeperInvalidParameter("Vault::DownloadAttachment", "attachment", atta.GetType().Name, "attachment type is not supported");
}
}
@@ -178,17 +157,16 @@ public async Task DownloadAttachment(KeeperRecord record, string attachment, Str
///
public async Task UploadAttachment(KeeperRecord record, IAttachmentUploadTask uploadTask)
{
- switch (record)
- {
- case PasswordRecord password:
- await UploadPasswordAttachment(password, uploadTask);
- break;
-
- case TypedRecord typed:
- await UploadTypedAttachment(typed, uploadTask);
- break;
- default:
- throw new KeeperInvalidParameter("Vault::UploadAttachment", "record", record.GetType().Name, "unsupported record type");
+ switch (record) {
+ case PasswordRecord password:
+ await UploadPasswordAttachment(password, uploadTask);
+ break;
+
+ case TypedRecord typed:
+ await UploadTypedAttachment(typed, uploadTask);
+ break;
+ default:
+ throw new KeeperInvalidParameter("Vault::UploadAttachment", "record", record.GetType().Name, "unsupported record type");
}
}
@@ -196,33 +174,26 @@ public async Task UploadAttachment(KeeperRecord record, IAttachmentUploadTask up
public async Task DeleteAttachment(KeeperRecord record, string attachmentId)
{
var deleted = false;
- switch (record)
- {
- case PasswordRecord password:
- if (password.Attachments != null)
- {
- var atta = password.Attachments.FirstOrDefault(x => x.Id == attachmentId);
- if (atta != null)
- {
- deleted = password.Attachments.Remove(atta);
- }
- }
-
- break;
- case TypedRecord typed:
- var fileRef = typed.Fields
- .Where(x => x.FieldName == "fileRef")
- .OfType>().FirstOrDefault();
- if (fileRef != null)
- {
- deleted = fileRef.Values.Remove(attachmentId);
+ switch (record) {
+ case PasswordRecord password:
+ if (password.Attachments != null) {
+ var atta = password.Attachments.FirstOrDefault(x => x.Id == attachmentId);
+ if (atta != null) {
+ deleted = password.Attachments.Remove(atta);
}
-
- break;
+ }
+ break;
+ case TypedRecord typed:
+ var fileRef = typed.Fields
+ .Where(x => x.FieldName == "fileRef")
+ .OfType>().FirstOrDefault();
+ if (fileRef != null) {
+ deleted = fileRef.Values.Remove(attachmentId);
+ }
+ break;
}
- if (deleted)
- {
+ if (deleted) {
await UpdateRecord(record, false);
}
@@ -234,19 +205,16 @@ public async Task DeleteAttachment(KeeperRecord record, string attachmentI
///
public async Task DownloadFile(FileRecord fileRecord, Stream destination)
{
- var rq = new Records.FilesGetRequest
- {
+ var rq = new Records.FilesGetRequest {
ForThumbnails = false
};
rq.RecordUids.Add(ByteString.CopyFrom(fileRecord.Uid.Base64UrlDecode()));
var rs = await Auth.ExecuteAuthRest(
"vault/files_download", rq);
var fileResult = rs.Files[0];
- if (fileResult.Status != Records.FileGetResult.FgSuccess)
- {
+ if (fileResult.Status != Records.FileGetResult.FgSuccess) {
var status = fileResult.Status.ToString().ToSnakeCase();
- if (status.StartsWith("fg_"))
- {
+ if (status.StartsWith("fg_")) {
status = status.Substring(3);
}
@@ -255,15 +223,11 @@ public async Task DownloadFile(FileRecord fileRecord, Stream destination)
var request = WebRequest.Create(new Uri(fileResult.Url));
- using (var response = (HttpWebResponse) await request.GetResponseAsync())
- {
- using (var stream = response.GetResponseStream())
- {
+ using (var response = (HttpWebResponse) await request.GetResponseAsync()) {
+ using (var stream = response.GetResponseStream()) {
var transform = new DecryptAesV2Transform(fileRecord.RecordKey);
- using (var decodeStream = new CryptoStream(stream, transform, CryptoStreamMode.Read))
- {
- if (destination != null)
- {
+ using (var decodeStream = new CryptoStream(stream, transform, CryptoStreamMode.Read)) {
+ if (destination != null) {
await decodeStream.CopyToAsync(destination);
}
}
@@ -274,10 +238,9 @@ public async Task DownloadFile(FileRecord fileRecord, Stream destination)
///
public async Task DownloadAttachmentFile(string recordUid, AttachmentFile attachment, Stream destination)
{
- var command = new RequestDownloadCommand
- {
+ var command = new RequestDownloadCommand {
RecordUid = recordUid,
- FileIDs = new[] { attachment .Id}
+ FileIDs = new[] { attachment.Id }
};
this.ResolveRecordAccessPath(command);
var rs = await this.Auth.ExecuteAuthCommand(command);
@@ -285,111 +248,60 @@ public async Task DownloadAttachmentFile(string recordUid, AttachmentFile attach
var download = rs.Downloads[0];
var request = WebRequest.Create(new Uri(download.Url));
using (var response = (HttpWebResponse) await request.GetResponseAsync())
- using (var stream = response.GetResponseStream())
- {
+ using (var stream = response.GetResponseStream()) {
var transform = new DecryptAesV1Transform(attachment.Key.Base64UrlDecode());
- using (var decodeStream = new CryptoStream(stream, transform, CryptoStreamMode.Read))
- {
- if (destination != null)
- {
+ using (var decodeStream = new CryptoStream(stream, transform, CryptoStreamMode.Read)) {
+ if (destination != null) {
await decodeStream.CopyToAsync(destination);
}
}
}
}
- internal static async Task UploadSingleFile(UploadParameters upload, Stream source)
+ internal static async Task UploadSingleFile(UploadParameters upload, Stream inputStream, IWebProxy proxy = null)
{
- var boundary = "----------" + DateTime.Now.Ticks.ToString("x");
- var boundaryBytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary);
-
- var request = (HttpWebRequest) WebRequest.Create(new Uri(upload.Url));
- request.Method = "POST";
- request.ContentType = "multipart/form-data; boundary=" + boundary;
-
- using (var requestStream = await Task.Factory.FromAsync(request.BeginGetRequestStream, request.EndGetRequestStream, null))
- {
- const string parameterTemplate = "\r\nContent-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
- if (upload.Parameters != null)
- {
- foreach (var pair in upload.Parameters)
- {
- await requestStream.WriteAsync(boundaryBytes, 0, boundaryBytes.Length);
- var formItem = string.Format(parameterTemplate, pair.Key, pair.Value);
- var formItemBytes = Encoding.UTF8.GetBytes(formItem);
- await requestStream.WriteAsync(formItemBytes, 0, formItemBytes.Length);
- }
- }
-
- await requestStream.WriteAsync(boundaryBytes, 0, boundaryBytes.Length);
- const string fileTemplate = "\r\nContent-Disposition: form-data; name=\"{0}\"\r\nContent-Type: application/octet-stream\r\n\r\n";
- var fileItem = string.Format(fileTemplate, upload.FileParameter);
- var fileBytes = Encoding.UTF8.GetBytes(fileItem);
- await requestStream.WriteAsync(fileBytes, 0, fileBytes.Length);
-
- await source.CopyToAsync(requestStream);
-
- await requestStream.WriteAsync(boundaryBytes, 0, boundaryBytes.Length);
- var trailer = Encoding.ASCII.GetBytes("--\r\n");
- await requestStream.WriteAsync(trailer, 0, trailer.Length);
- }
-
- HttpWebResponse response;
- try
- {
- response = (HttpWebResponse) await Task.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null);
- if ((int) response.StatusCode != upload.SuccessStatusCode)
- {
- throw new KeeperInvalidParameter("Vault::UploadSingleFile", "StatusCode", response.StatusCode.ToString(), "not success");
- }
+ var content = new MultipartFormDataContent();
+ foreach (var pair in upload.Parameters) content.Add(new StringContent(pair.Value), pair.Key);
+ var fileContent = new StreamContent(inputStream);
+ fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
+ content.Add(fileContent, upload.FileParameter);
+ var httpMessageHandler = new HttpClientHandler();
+ if (proxy != null) {
+ httpMessageHandler.Proxy = proxy;
}
- catch (WebException e)
- {
- response = (HttpWebResponse) e.Response;
- if (response == null || response.ContentType != "application/xml") throw;
- using (var stream = new MemoryStream())
- {
- var srcStream = response.GetResponseStream();
- if (srcStream == null) throw;
- await srcStream.CopyToAsync(stream);
- Trace.TraceError(Encoding.UTF8.GetString(stream.ToArray()));
- }
-
- throw;
+ using (var httpClient = new HttpClient(httpMessageHandler, true)) {
+ var rs = await httpClient.PostAsync(upload.Url, content);
+ if ((int) rs.StatusCode != upload.SuccessStatusCode)
+ throw new Exception($"File upload HTTP error: {rs.StatusCode}");
}
}
private async Task UploadPasswordAttachment(PasswordRecord record, IAttachmentUploadTask uploadTask)
{
var fileStream = uploadTask.Stream;
- if (fileStream == null)
- {
+ if (fileStream == null) {
throw new KeeperInvalidParameter("Vault::UploadAttachment", "uploadTask", "GetStream()", "null");
}
var thumbStream = uploadTask.Thumbnail?.Stream;
- var command = new RequestUploadCommand
- {
+ var command = new RequestUploadCommand {
FileCount = 1,
ThumbnailCount = thumbStream != null ? 1 : 0
};
var rs = await Auth.ExecuteAuthCommand(command);
- if (rs.FileUploads == null || rs.FileUploads.Length < 1)
- {
+ if (rs.FileUploads == null || rs.FileUploads.Length < 1) {
throw new KeeperInvalidParameter("Vault::UploadAttachment", "request_upload", "file_uploads", "empty");
}
var fileUpload = rs.FileUploads[0];
UploadParameters thumbUpload = null;
- if (rs.ThumbnailUploads != null && rs.ThumbnailUploads.Length > 0)
- {
+ if (rs.ThumbnailUploads != null && rs.ThumbnailUploads.Length > 0) {
thumbUpload = rs.ThumbnailUploads[0];
}
var key = CryptoUtils.GenerateEncryptionKey();
- var atta = new AttachmentFile
- {
+ var atta = new AttachmentFile {
Id = fileUpload.FileId,
Name = uploadTask.Name,
Title = uploadTask.Title,
@@ -398,33 +310,26 @@ private async Task UploadPasswordAttachment(PasswordRecord record, IAttachmentUp
LastModified = DateTimeOffset.Now,
};
var transform = new EncryptAesV1Transform(key);
- using (var cryptoStream = new CryptoStream(fileStream, transform, CryptoStreamMode.Read))
- {
+ using (var cryptoStream = new CryptoStream(fileStream, transform, CryptoStreamMode.Read)) {
await UploadSingleFile(fileUpload, cryptoStream);
atta.Size = transform.EncryptedBytes;
}
- if (thumbUpload != null && thumbStream != null)
- {
- try
- {
+ if (thumbUpload != null && thumbStream != null) {
+ try {
transform = new EncryptAesV1Transform(key);
- using (var cryptoStream = new CryptoStream(thumbStream, transform, CryptoStreamMode.Read))
- {
+ using (var cryptoStream = new CryptoStream(thumbStream, transform, CryptoStreamMode.Read)) {
await UploadSingleFile(thumbUpload, cryptoStream);
}
- var thumbnail = new AttachmentFileThumb
- {
+ var thumbnail = new AttachmentFileThumb {
Id = thumbUpload.FileId,
Type = uploadTask.Thumbnail.MimeType,
Size = uploadTask.Thumbnail.Size
};
- var ts = new[] {thumbnail};
+ var ts = new[] { thumbnail };
atta.Thumbnails = atta.Thumbnails == null ? ts : atta.Thumbnails.Concat(ts).ToArray();
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
Trace.TraceError("Upload Thumbnail: {0}: \"{1}\"", e.GetType().Name, e.Message);
}
}
@@ -437,13 +342,11 @@ private async Task UploadPasswordAttachment(PasswordRecord record, IAttachmentUp
private async Task UploadTypedAttachment(TypedRecord record, IAttachmentUploadTask uploadTask)
{
var fileStream = uploadTask.Stream;
- if (fileStream == null)
- {
+ if (fileStream == null) {
throw new KeeperInvalidParameter("Vault::UploadAttachment", "uploadTask", "GetStream()", "null");
}
- var fileData = new RecordFileData
- {
+ var fileData = new RecordFileData {
Type = uploadTask.MimeType,
Name = uploadTask.Name,
Title = uploadTask.Title,
@@ -454,10 +357,8 @@ private async Task UploadTypedAttachment(TypedRecord record, IAttachmentUploadTa
};
var fileKey = CryptoUtils.GenerateEncryptionKey();
byte[] encryptedThumb = null;
- if (uploadTask.Thumbnail != null)
- {
- using (var ts = new MemoryStream())
- {
+ if (uploadTask.Thumbnail != null) {
+ using (var ts = new MemoryStream()) {
await uploadTask.Stream.CopyToAsync(ts);
await ts.FlushAsync();
var thumbBytes = ts.ToArray();
@@ -469,86 +370,67 @@ private async Task UploadTypedAttachment(TypedRecord record, IAttachmentUploadTa
var tempFile = Path.GetTempFileName();
var transform = new EncryptAesV2Transform(fileKey);
using (var encryptedFile = File.OpenWrite(tempFile))
- using (var cryptoStream = new CryptoStream(uploadTask.Stream, transform, CryptoStreamMode.Read))
- {
+ using (var cryptoStream = new CryptoStream(uploadTask.Stream, transform, CryptoStreamMode.Read)) {
await cryptoStream.CopyToAsync(encryptedFile);
fileData.Size = transform.EncryptedBytes;
}
var fileInfo = new FileInfo(tempFile);
var fileUid = CryptoUtils.GenerateUid();
- var fileRq = new Records.File
- {
+ var fileRq = new Records.File {
RecordUid = ByteString.CopyFrom(fileUid.Base64UrlDecode()),
RecordKey = ByteString.CopyFrom(CryptoUtils.EncryptAesV2(fileKey, Auth.AuthContext.DataKey)),
Data = ByteString.CopyFrom(CryptoUtils.EncryptAesV2(JsonUtils.DumpJson(fileData), fileKey)),
FileSize = fileInfo.Length,
ThumbSize = encryptedThumb?.Length ?? 0,
};
- var rq = new Records.FilesAddRequest
- {
+ var rq = new Records.FilesAddRequest {
ClientTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
rq.Files.Add(fileRq);
var fileRs = await Auth.ExecuteAuthRest("vault/files_add", rq);
var uploadRs = fileRs.Files[0];
- var fileUpload = new UploadParameters
- {
+ var fileUpload = new UploadParameters {
Url = uploadRs.Url,
FileParameter = "file",
SuccessStatusCode = uploadRs.SuccessStatusCode,
- Parameters = JsonUtils.ParseJson>(Encoding.UTF8.GetBytes(uploadRs.Parameters))
+ Parameters = JsonUtils.ParseJson>(Encoding.UTF8.GetBytes(uploadRs.Parameters))
};
- if (record.LinkedKeys == null)
- {
+ if (record.LinkedKeys == null) {
record.LinkedKeys = new Dictionary();
}
record.LinkedKeys[fileUid] = fileKey;
- try
- {
- using (var cryptoStream = File.OpenRead(tempFile))
- {
+ try {
+ using (var cryptoStream = File.OpenRead(tempFile)) {
await UploadSingleFile(fileUpload, cryptoStream);
}
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
Trace.TraceError("Upload Thumbnail: {0}: \"{1}\"", e.GetType().Name, e.Message);
}
- if (encryptedThumb != null && !string.IsNullOrEmpty(uploadRs.ThumbnailParameters))
- {
- var thumbUpload = new UploadParameters
- {
+ if (encryptedThumb != null && !string.IsNullOrEmpty(uploadRs.ThumbnailParameters)) {
+ var thumbUpload = new UploadParameters {
Url = uploadRs.Url,
FileParameter = "thumb",
SuccessStatusCode = uploadRs.SuccessStatusCode,
- Parameters = JsonUtils.ParseJson>(Encoding.UTF8.GetBytes(uploadRs.ThumbnailParameters))
+ Parameters = JsonUtils.ParseJson>(Encoding.UTF8.GetBytes(uploadRs.ThumbnailParameters))
};
- try
- {
- using (var cryptoStream = new MemoryStream(encryptedThumb))
- {
+ try {
+ using (var cryptoStream = new MemoryStream(encryptedThumb)) {
await UploadSingleFile(thumbUpload, cryptoStream);
}
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
Trace.TraceError("Upload Thumbnail: {0}: \"{1}\"", e.GetType().Name, e.Message);
}
}
var facade = new TypedRecordFacade(record);
- if (facade.Fields.FileRef != null)
- {
+ if (facade.Fields.FileRef != null) {
var uids = facade.Fields.FileRef.Values;
- if (uids.Count > 0 && string.IsNullOrEmpty(uids[0]))
- {
+ if (uids.Count > 0 && string.IsNullOrEmpty(uids[0])) {
uids[0] = fileUid;
- }
- else
- {
+ } else {
uids.Add(fileUid);
}
}
diff --git a/KeeperSdk/vault/VaultCommands.cs b/KeeperSdk/vault/VaultCommands.cs
index b892dcf..094e2eb 100644
--- a/KeeperSdk/vault/VaultCommands.cs
+++ b/KeeperSdk/vault/VaultCommands.cs
@@ -588,7 +588,7 @@ internal class UploadParameters
public string FileParameter;
[DataMember(Name = "parameters")]
- public IDictionary Parameters;
+ public IDictionary Parameters;
}
diff --git a/PowerCommander/AttachmentCommands.ps1 b/PowerCommander/AttachmentCommands.ps1
index f858725..2d0df5c 100644
--- a/PowerCommander/AttachmentCommands.ps1
+++ b/PowerCommander/AttachmentCommands.ps1
@@ -9,7 +9,7 @@ function Copy-KeeperFileAttachment {
.Folder
Keeper Folder
- .Record
+ .Record
Keeper Record
.Parameter Path
@@ -32,7 +32,7 @@ function Copy-KeeperFileAttachment {
$Path = '.'
}
$records = $null
- if ($Record) {
+ if ($Record) {
$r = Get-KeeperRecord $record
if ($r) {
$records = @()
@@ -87,7 +87,7 @@ function Copy-KeeperFileAttachment {
$fileStream = $newFile.OpenWrite()
try {
$vault.DownloadAttachment($keeperRecord, $atta.Id, $fileStream).GetAwaiter().GetResult() | Out-Null
- }
+ }
finally {
$fileStream.Dispose()
}
@@ -102,7 +102,7 @@ function Copy-KeeperFileAttachmentToStream {
.Synopsis
Get Attachment as stream
- .Record
+ .Record
Keeper Record Uid
.AttachmentName
@@ -126,3 +126,33 @@ function Copy-KeeperFileAttachmentToStream {
[KeeperSecurity.Vault.VaultOnline]$vault = getVault
$vault.DownloadAttachment($keeperRecord, $AttachmentName, $Stream).GetAwaiter().GetResult() | Out-Null
}
+
+function Copy-FileToKeeperRecord {
+ <#
+ .Synopsis
+ Upload file attachment to a record
+
+ .Record
+ Keeper Record Uid
+
+ .Filename
+ File path
+ #>
+
+ [CmdletBinding()]
+ Param (
+ [Parameter(Mandatory = $true)][string] $Record,
+ [Parameter(Position = 0, Mandatory = $true)][string] $Filename
+ )
+
+ $keeperRecord = Get-KeeperRecord $Record
+ if ($keeperRecord.Length -ne 1) {
+ Write-Error "Record `"$Record`" was not found" -ErrorAction Stop
+ }
+ [KeeperSecurity.Vault.VaultOnline]$vault = getVault
+
+ $path = Resolve-Path $Filename -ErrorAction Stop
+ $uploadTask = New-Object -TypeName KeeperSecurity.Vault.FileAttachmentUploadTask -ArgumentList $path.Path, $null
+
+ $vault.UploadAttachment($keeperRecord, $uploadTask).GetAwaiter().GetResult() | Out-Null
+}
diff --git a/PowerCommander/Google.Protobuf.dll b/PowerCommander/Google.Protobuf.dll
index 2c139d8..d49c513 100644
Binary files a/PowerCommander/Google.Protobuf.dll and b/PowerCommander/Google.Protobuf.dll differ
diff --git a/PowerCommander/KeeperSdk.dll b/PowerCommander/KeeperSdk.dll
index a627396..dd25d0f 100644
Binary files a/PowerCommander/KeeperSdk.dll and b/PowerCommander/KeeperSdk.dll differ
diff --git a/PowerCommander/PowerCommander.psd1 b/PowerCommander/PowerCommander.psd1
index 626bf49..caa722b 100644
--- a/PowerCommander/PowerCommander.psd1
+++ b/PowerCommander/PowerCommander.psd1
@@ -11,7 +11,7 @@
RootModule = 'PowerCommander.psm1'
# Version number of this module.
- ModuleVersion = '0.9.10'
+ ModuleVersion = '0.9.11'
# Supported PSEditions
CompatiblePSEditions = @('Desktop')
@@ -84,7 +84,7 @@
'Revoke-KeeperSharedFolderAccess', 'Get-KeeperAvailableTeam', 'Move-KeeperRecordOwnership', 'Get-KeeperSecretManagerApp',
'Add-KeeperSecretManagerApp', 'Grant-KeeperSecretManagerFolderAccess', 'Revoke-KeeperSecretManagerFolderAccess',
'Add-KeeperSecretManagerClient', 'Remove-KeeperSecretManagerClient', 'New-KeeperOneTimeShare', 'Get-KeeperOneTimeShare',
- 'Remove-KeeperOneTimeShare', 'Copy-KeeperFileAttachment', 'Copy-KeeperFileAttachmentToStream'
+ 'Remove-KeeperOneTimeShare', 'Copy-KeeperFileAttachment', 'Copy-KeeperFileAttachmentToStream', 'Copy-FileToKeeperRecord'
)
# Cmdlets to export from this module
@@ -112,7 +112,7 @@
LicenseUri = 'https://github.com/Keeper-Security/keeper-sdk-dotnet/blob/master/LICENSE'
ProjectUri = 'https://github.com/Keeper-Security/keeper-sdk-dotnet'
IconUri = 'https://keeper-email-images.s3.amazonaws.com/common/powershell.png'
- ReleaseNotes = "Add-KeeperRecord cmdlet help"
+ ReleaseNotes = "Copy-FileToKeeperRecord cmdlet help"
}
}
diff --git a/PowerCommander/PowerCommander.psm1 b/PowerCommander/PowerCommander.psm1
index ba462fd..72e5e69 100644
--- a/PowerCommander/PowerCommander.psm1
+++ b/PowerCommander/PowerCommander.psm1
@@ -55,7 +55,7 @@ Export-ModuleMember -Function Get-KeeperSecretManagerApp, Add-KeeperSecretManage
Revoke-KeeperSecretManagerFolderAccess, Add-KeeperSecretManagerClient, Remove-KeeperSecretManagerClient
Export-ModuleMember -Alias ksm, ksm-create, ksm-share, ksm-unshare, ksm-addclient, ksm-rmclient
-Export-ModuleMember -Function Copy-KeeperFileAttachment, Copy-KeeperFileAttachmentToStream
+Export-ModuleMember -Function Copy-KeeperFileAttachment, Copy-KeeperFileAttachmentToStream, Copy-FileToKeeperRecord
Export-ModuleMember -Alias kda
# function Test-Keeper {
diff --git a/PowerCommander/README.md b/PowerCommander/README.md
index 1854fba..263ff66 100644
--- a/PowerCommander/README.md
+++ b/PowerCommander/README.md
@@ -30,6 +30,7 @@ To run the PowerCommander module from the source copy PowerCommander\ directory
| Show-TwoFactorCode | 2fa | Display Two Factor Code
| Copy-KeeperFileAttachment | kda | Download file attachments
| Copy-KeeperFileAttachmentToStream | | Download file attachement to stream
+| Copy-FileToKeeperRecord | | Upload file attachment to a record
### Sharing Cmdlets
| Cmdlet name | Alias | Description
diff --git a/PowerCommander/System.Buffers.dll b/PowerCommander/System.Buffers.dll
index fb2911d..f2d83c5 100644
Binary files a/PowerCommander/System.Buffers.dll and b/PowerCommander/System.Buffers.dll differ
diff --git a/PowerCommander/System.Memory.dll b/PowerCommander/System.Memory.dll
index 3423680..bdfc501 100644
Binary files a/PowerCommander/System.Memory.dll and b/PowerCommander/System.Memory.dll differ
diff --git a/PowerCommander/System.Runtime.CompilerServices.Unsafe.dll b/PowerCommander/System.Runtime.CompilerServices.Unsafe.dll
index d99e9f9..3156239 100644
Binary files a/PowerCommander/System.Runtime.CompilerServices.Unsafe.dll and b/PowerCommander/System.Runtime.CompilerServices.Unsafe.dll differ