file_upload
- CSharp
- Java
- Swift
- Python
- Go
#region Send
/// <summary>
/// Sends the file up to the server
/// </summary>
/// <param name="path">The file path.</param>
/// <param name="useMte">Whether or not to use MTE when sending file.</param>
/// <param name="encoderState">The current encoder state.</param>
/// <param name="decoderState">The current decoder state.</param>
/// <param name="clientid">The client id.</param>
/// <param name="authHeader">The JWT auth header.</param>
/// <returns>System.Int32.</returns>
public ResponseModel<UploadResponse> Send(string path,
bool useMte,
string encoderState,
string decoderState,
string clientid,
string authHeader)
{
ResponseModel<UploadResponse> uploadResponse = new()
{
Data = new UploadResponse
{
EncoderState = encoderState,
DecoderState = decoderState
}
};
try
{
//------------------------
// Create default encoder
//------------------------
MteMkeEnc encoder = new MteMkeEnc();
//------------------------------
// Get file info and create url
//------------------------------
FileInfo file = new FileInfo(path);
string urlType = (useMte) ? "mte" : "nomte";
string fileUrl = Path.Combine($"{Constants.RestAPIName}/FileUpload/",
urlType + "?name=" + file.Name);
//-----------------------------------
// Create file stream and webRequest
//-----------------------------------
_fileReader = new FileStream(path, FileMode.Open, FileAccess.Read);
_webRequest = (HttpWebRequest)WebRequest.Create(fileUrl);
_webRequest.Method = "POST";
if (useMte)
{
//--------------------------------------------------
// If we are using the MTE adjust the content length
//--------------------------------------------------
int additionalBytes = encoder.EncryptFinishBytes();
long finalLength = (long)(_fileReader.Length + additionalBytes);
_webRequest.ContentLength = finalLength;
}
else
{
//-------------------------------------------------------------
// Regular request will have the file length as content length
//-------------------------------------------------------------
_webRequest.ContentLength = _fileReader.Length;
}
_webRequest.Timeout = 600000;
//-------------------------
// Add client id to header if not null
//-------------------------
if (!string.IsNullOrWhiteSpace(clientid))
{
_webRequest.Headers.Add(Constants.ClientIdHeader, clientid);
}
if (!string.IsNullOrWhiteSpace(authHeader))
{
if (authHeader.StartsWith("Bearer"))
authHeader = authHeader.Substring("Bearer ".Length);
_webRequest.Headers.Add("Authorization", "Bearer " + authHeader);
}
_webRequest.Credentials = CredentialCache.DefaultCredentials;
_webRequest.AllowWriteStreamBuffering = false;
_requestStream = _webRequest.GetRequestStream();
long fileSize = _fileReader.Length;
long remainingBytes = fileSize;
int numberOfBytesRead = 0, done = 0;
if (useMte)
{
//----------------------------
// Restore encoder from state
//----------------------------
MteStatus status = encoder.RestoreStateB64(encoderState);
if (status != MteStatus.mte_status_success)
{
uploadResponse.Success = false;
uploadResponse.ResultCode = Constants.RC_MTE_STATE_RETRIEVAL;
uploadResponse.Message = $"Failed to restore MTE encoder engine. Status: "
+ " {encoder.GetStatusName(status)} / {encoder.GetStatusDescription(status)}";
return uploadResponse;
}
//----------------------------
// start the chunking session
//----------------------------
status = encoder.StartEncrypt();
if (status != MteStatus.mte_status_success)
{
uploadResponse.Success = false;
uploadResponse.ResultCode = Constants.RC_MTE_ENCODE_EXCEPTION;
uploadResponse.Message = "Failed to start encode chunk. Status: "
+ encoder.GetStatusName(status) + " / "
+ encoder.GetStatusDescription(status);
return uploadResponse;
}
}
//------------------------
// Break up files to send
//------------------------
while (numberOfBytesRead < fileSize)
{
byte[] fileData;
SetByteArray(out fileData, remainingBytes);
done = _fileReader.Read(fileData, 0, fileData.Length);
if (useMte)
{
//------------------------------------------------------------
// Encode the data in place - encoded data put back in buffer
//------------------------------------------------------------
MteStatus chunkStatus = encoder.EncryptChunk(fileData, 0, fileData.Length);
if (chunkStatus != MteStatus.mte_status_success)
{
uploadResponse.Success = false;
uploadResponse.ResultCode = Constants.RC_MTE_ENCODE_EXCEPTION;
uploadResponse.Message = "Failed to encode chunk. Status: "
+ encoder.GetStatusName(chunkStatus) + " / "
+ encoder.GetStatusDescription(chunkStatus);
return uploadResponse;
}
}
//-----------------------------
// Write the data to the stream
//-----------------------------
_requestStream.Write(fileData, 0, fileData.Length);
numberOfBytesRead += done;
remainingBytes -= done;
}
if (useMte)
{
//----------------------------
// Finish the chunking session
//----------------------------
byte[] finalEncodedChunk = encoder.FinishEncrypt(out MteStatus finishStatus);
if (finishStatus != MteStatus.mte_status_success)
{
uploadResponse.Success = false;
uploadResponse.ResultCode = Constants.RC_MTE_ENCODE_EXCEPTION;
uploadResponse.Message = "Failed to finish encode chunk. Status: "
+ encoder.GetStatusName(finishStatus) + " / "
+ encoder.GetStatusDescription(finishStatus);
return uploadResponse;
}
//------------------------------------
// Append the final data to the stream
//------------------------------------
_requestStream.Write(finalEncodedChunk, 0, finalEncodedChunk.Length);
//-----------------------
// Save the encoderState
//-----------------------
uploadResponse.Data.EncoderState = encoder.SaveStateB64();
}
//------------------
// Get the response.
//------------------
WebResponse response = _webRequest.GetResponse();
//---------------------
// get the return text
//---------------------
using Stream data = response.GetResponseStream();
using var reader = new StreamReader(data);
string text = reader.ReadToEnd();
ResponseModel<byte[]> textResponse =
JsonSerializer.Deserialize<ResponseModel<byte[]>>(text,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (!textResponse.Success)
{
//-----------------------------------
// Check if we need to "re-handshake"
//-----------------------------------
if (textResponse.ResultCode.Equals(Constants.RC_MTE_STATE_NOT_FOUND,
StringComparison.InvariantCultureIgnoreCase))
{
//-------------------------------------------------------------------------
// the server does not have this client's state - we should "re-handshake"
//-------------------------------------------------------------------------
var handshakeRessponse = HandshakeWithServer(clientid);
//-----------------------------------------------------------
// return response, if successful give message to try again.
//-----------------------------------------------------------
uploadResponse.Success = handshakeRessponse.Success;
uploadResponse.Message = (uploadResponse.Success)
? "Server lost MTE state, client needed to handshake "
+ "again, handshake successful, please try again."
: handshakeRessponse.Message;
uploadResponse.ResultCode = handshakeRessponse.ResultCode;
uploadResponse.access_token = handshakeRessponse.access_token;
return uploadResponse;
}
}
if (useMte)
{
//------------------
// Restroe decoder
//------------------
MteMkeDec decoder = new MteMkeDec();
MteStatus status = decoder.RestoreStateB64(decoderState);
if (status != MteStatus.mte_status_success)
{
uploadResponse.Success = false;
uploadResponse.ResultCode = Constants.RC_MTE_STATE_RETRIEVAL;
uploadResponse.Message = $"Failed to restore the MTE decoder engine. Status:"
+ " {decoder.GetStatusName(status)} / {decoder.GetStatusDescription(status)}";
return uploadResponse;
}
//----------------------------
// start the chunking session
//----------------------------
status = decoder.StartDecrypt();
if (status != MteStatus.mte_status_success)
{
throw new Exception("Failed to start decode chunk. Status: "
+ decoder.GetStatusName(status) + " / "
+ decoder.GetStatusDescription(status));
}
//-----------------
// Decode the data
//-----------------
byte[] decodedChunk = decoder.DecryptChunk(textResponse.Data);
var clearBytes = decoder.FinishDecrypt(out MteStatus finalStatus);
if (clearBytes == null) { clearBytes = new byte[0]; }
if (finalStatus != MteStatus.mte_status_success)
{
throw new Exception("Failed to finish decode. Status: "
+ decoder.GetStatusName(finalStatus) + " / "
+ decoder.GetStatusDescription(finalStatus));
}
//---------------------
// Set decoded message
//---------------------
byte[] decodedMessage = new byte[decodedChunk.Length + clearBytes.Length];
Buffer.BlockCopy(decodedChunk, 0, decodedMessage, 0, decodedChunk.Length);
Buffer.BlockCopy(clearBytes, 0, decodedMessage, decodedChunk.Length, clearBytes.Length);
//----------------------------
// Return the server response
//----------------------------
uploadResponse.Data.ServerResponse = Encoding.UTF8.GetString(decodedMessage);
//------------------------
// Save the decoder state
//------------------------
uploadResponse.Data.DecoderState = decoder.SaveStateB64();
}
else
{
//----------------------------
// Return the server response
//----------------------------
uploadResponse.Data.ServerResponse = Encoding.UTF8.GetString(textResponse.Data);
}
//-----------------------------
// update the jwt/access_token
//-----------------------------
uploadResponse.access_token = textResponse.access_token;
}
catch (Exception e)
{
uploadResponse.Message = $"Exception uploading file to server. Ex: {e.Message}";
uploadResponse.ResultCode = Constants.RC_UPLOAD_EXCEPTION;
uploadResponse.Success = false;
}
return uploadResponse;
}
#endregion
#region SetByteArray
/// <summary>
/// Sets the byte array.
/// </summary>
/// <param name="fileData">The file data.</param>
/// <param name="bytesLeft">The bytes left.</param>
private void SetByteArray(out byte[] fileData, long bytesLeft)
{
fileData = bytesLeft < _maxChunkSize ? new byte[bytesLeft] : new byte[_maxChunkSize];
}
#endregion
//---------------------------------------------------
// Login and Handshake previous to this
//---------------------------------------------------
// Exerpt from file upload file
// See full FileUploader.java file for entire code
//---------------------------------------------------
//---------
// Set JWT
//---------
_jwt = loginResponse.access_token;
//---------------------------------------
// Allow multiple uploads till user ends
//---------------------------------------
while (true) {
//----------------------
// Prompt for file path
//----------------------
System.out.print("Enter full path to file to upload: ");
String filename = br.readLine();
File textFile = new File(filename);
String textFileName = textFile.getName();
//---------------
// Set url to API
//---------------
String url = Constants.RestAPIName
+ "/FileUpload/"
+ mteURL
+ "?name=" + textFileName;
String charset = "UTF-8";
//---------------------
// Open url connection
//---------------------
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Accept-Charset", charset);
//------------------------------------------------
// set the content type based on using MTE or not
//------------------------------------------------
String contentType = (useMte)
? "application/octet-stream;charset="
: "text/plain;charset=";
connection.setRequestProperty("Content-Type", contentType + charset);
//-------------------------
// Add client id to header
//-------------------------
connection.setRequestProperty(Constants.ClientIdHeader, clientId);
//----------------------------------
// Add Authentication if jwt is set
//----------------------------------
if(_jwt!=null && _jwt !="") {
if(_jwt.startsWith("Bearer")) {
_jwt = _jwt.substring(0, "Bearer ".length());
}
connection.setRequestProperty(Constants.AuthHeader, "Bearer " + _jwt);
}
//---------------------------
// Create MKE Encoder object
//---------------------------
MteMkeEnc mkeEncoder = new MteMkeEnc();
//----------------------------
// If use MTE restore encoder
//----------------------------
if (useMte) {
MteStatus encoderStatus = mkeEncoder.restoreStateB64(handshake.EncoderState);
if (encoderStatus != MteStatus.mte_status_success) {
System.out.println("Error restoring the encoder mte state for Client "
+ clientId + ": "
+ MteBase.getStatusDescription(encoderStatus));
throw new Exception("Error restoring the encoder mte state for Client "
+ clientId + ": "
+ MteBase.getStatusDescription(encoderStatus));
}
//-----------------------------
// Initialize chunking session
//-----------------------------
encoderStatus = mkeEncoder.startEncrypt();
if (encoderStatus != MteStatus.mte_status_success) {
throw new Exception("Failed to start encode chunk. Status: "
+ MteBase.getStatusName(encoderStatus)
+ " / " + MteBase.getStatusDescription(encoderStatus));
}
}
//-----------------
// Set buffer size
//-----------------
byte[] dataBuffer = new byte[1024];
//--------------------
// Set out put stream
//--------------------
try (OutputStream output = connection.getOutputStream();
) {
//--------------------------------
// Write the actual file contents
//--------------------------------
FileInputStream inputStream = new FileInputStream(textFile);
//-----------------------------------------
// read contents of file into input stream
//-----------------------------------------
int bytesRead;
while ((bytesRead = inputStream.read(dataBuffer)) != -1) {
if (useMte) {
//------------------------------------------------------------
// Encode the data in place - encoded data put back in buffer
//------------------------------------------------------------
MteStatus chunkStatus = mkeEncoder.encryptChunk(dataBuffer, 0, bytesRead);
if (chunkStatus != MteStatus.mte_status_success) {
throw new Exception("Failed to encode chunk. Status: "
+ MteBase.getStatusName(chunkStatus)
+ " / " + MteBase.getStatusDescription(chunkStatus));
}
}
//------------------------
// Write to output writer
//------------------------
output.write(dataBuffer, 0, bytesRead);
}
//----------------------
// finalize mte session
//----------------------
if (useMte) {
MteBase.ArrStatus finalEncodedChunk = mkeEncoder.finishEncrypt();
if (finalEncodedChunk.status != MteStatus.mte_status_success) {
//-----------------------------------------------
// First close inputStream to prevent memory leak
//-----------------------------------------------
inputStream.close();
throw new Exception("Failed to finish encode chunk. Status: "
+ MteBase.getStatusName(finalEncodedChunk.status) + " / "
+ MteBase.getStatusDescription(finalEncodedChunk.status));
}
//-------------------------------------
// Write final encoded chunk to output
//-------------------------------------
output.write(finalEncodedChunk.arr);
//----------------------------
// save updated encoder state
//----------------------------
handshake.EncoderState = mkeEncoder.saveStateB64();
}
//-------------------------
// close/flush output
// close inputStream
//-------------------------
output.flush();
inputStream.close();
}
//---------------------------------------------------------------
// Request is lazily fired whenever you need to obtain response.
//---------------------------------------------------------------
int responseCode = ((HttpURLConnection) connection).getResponseCode();
//---------------
// Get inStream
//---------------
InputStream in = new BufferedInputStream(connection.getInputStream());
int bytesReadInstream;
ByteArrayOutputStream inBuffer = new ByteArrayOutputStream();
//-----------------------
// Iterate through input
//-----------------------
while ((bytesReadInstream = in.read(dataBuffer, 0, dataBuffer.length)) != -1) {
inBuffer.write(dataBuffer, 0, bytesReadInstream);
}
//--------------------------------
// Flush buffer and set to string
//--------------------------------
inBuffer.flush();
String text = inBuffer.toString();
//-------------------------------
// Deserialize to response model
//-------------------------------
Type serverResponseType = new TypeToken<ResponseModel<byte[]>>() {
}.getType();
ResponseModel<byte[]> serverResponse = _gson.fromJson(text, serverResponseType);
String finalData = "";
//--------------------------------------------------
// If we get a successful response decoder response
//--------------------------------------------------
if (serverResponse.Success) {
//-----------
// Update JWT
//-----------
_jwt = serverResponse.access_token;
//----------------
// Create decoder
//----------------
MteMkeDec mkeDecoder = new MteMkeDec();
if (useMte) {
//-----------------
// Restore Decoder
//-----------------
MteStatus decoderStatus = mkeDecoder.restoreStateB64(handshake.DecoderState);
if (decoderStatus != MteStatus.mte_status_success) {
System.out.println("Error restoring the decoder mte state for Client "
+ clientId + ": "
+ MteBase.getStatusDescription(decoderStatus));
throw new Exception("Error restoring the decoder mte state for Client "
+ clientId + ": "
+ MteBase.getStatusDescription(decoderStatus));
}
//------------------------
// Start chunking session
//------------------------
decoderStatus = mkeDecoder.startDecrypt();
if (decoderStatus != MteStatus.mte_status_success) {
throw new Exception(
"Failed to start decode chunk. Status: " + MteBase.getStatusName(decoderStatus) + " / "
+ MteBase.getStatusDescription(decoderStatus));
}
}
if (useMte) {
//-----------------------------------------------
// We know the response is short from the server
// calling decryptChunk only one time then final
//-----------------------------------------------
byte[] decodedBytes = mkeDecoder.decryptChunk(serverResponse.Data);
//-----------------------------
// Finish the chunking session
//-----------------------------
MteBase.ArrStatus finalEncodedChunk = mkeDecoder.finishDecrypt();
if(finalEncodedChunk.status != MteStatus.mte_status_success)
{
throw new Exception("Failed to finish decode chunk. Status: "
+ MteBase.getStatusName(finalEncodedChunk.status)+ " / "
+ MteBase.getStatusDescription(finalEncodedChunk.status));
}
//-----------------------------------------------------------------------
// Check if there is additional bytes if not initialize empty byte array
//-----------------------------------------------------------------------
if(finalEncodedChunk.arr == null) { finalEncodedChunk.arr = new byte[0]; }
//---------------
// Concat bytes
//---------------
byte[] finalBytes =
new byte[decodedBytes.length + finalEncodedChunk.arr.length];
System.arraycopy(decodedBytes,
0,
finalBytes,
0,
decodedBytes.length);
System.arraycopy(finalEncodedChunk.arr,
0,
finalBytes,
decodedBytes.length,
finalEncodedChunk.arr.length);
//------------------------------------
// Convert final byte array to string
//------------------------------------
finalData = new String(finalBytes, StandardCharsets.UTF_8);
//----------------------
// Update Decoder State
//----------------------
handshake.DecoderState = mkeDecoder.saveStateB64();
} else {
//----------------------------------
// else just get the response string
//----------------------------------
finalData = new String(serverResponse.Data, StandardCharsets.UTF_8);
}
} else {
//-----------------------------
// else show the error message
//-----------------------------
finalData = serverResponse.Message;
}
//-----------------------------------
// Output response code and response
//-----------------------------------
System.out.println(responseCode); // Should be 200
System.out.println(finalData);
// ---------------------------------------
// Prompt user to run tasks again or end
// ---------------------------------------
System.out.println("Would you like to upload another file? (y/n)");
String sendAdditional = br.readLine();
if (sendAdditional != null && sendAdditional.equalsIgnoreCase("n")) {
break;
}
}
// cont...
// Additional functions and delegate methods for file upload as well
// as the following FileStreamUpload.swift class
func uploadStream(_ filename: String) {
let filePath = FileManager.default.currentDirectoryPath + "/\(filename)"
guard let urlEncodedFilename = filename.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
print("Unable to urlEncode \(filename)")
exit(EXIT_FAILURE)
}
let connectionModel = ConnectionModel(url: Settings.serverUrl,
method: Constants.POST,
route: "api/mke/uploadstream?name=\(urlEncodedFilename)",
payload: nil,
contentType: "text/plain; charset=utf-8",
clientId: Settings.clientId,
mteVersion: MteBase.getVersion())
fileStreamUpload.upload(connectionModel: connectionModel, filePath: filePath, mteHelper: mteHelper)
}
// MARK: Stream Upload Delegates
func didUploadToServer(success: Bool, filename: String) {
print("\nServer reports that the upload of \(filename) was successful.\n")
exit(EXIT_SUCCESS)
}
func uploadDidFail(message: String) {
print("Server reports that the upload Failed. Error: \(message)\n")
exit(EXIT_FAILURE)
}
FileStreamUpload.swift
import Foundation
protocol StreamUploadDelegate: AnyObject {
func didUploadToServer(success: Bool, filename: String)
func uploadDidFail(message: String)
}
class FileStreamUpload: NSObject, URLSessionDelegate, StreamDelegate, URLSessionStreamDelegate, URLSessionDataDelegate {
weak var streamUploadDelegate: StreamUploadDelegate?
var fileHandle: FileHandle!
var mteHelper: MTEHelper!
lazy var session: URLSession = URLSession(configuration: .default,
delegate: self,
delegateQueue: .main)
struct Streams {
let input: InputStream
let output: OutputStream
}
lazy var boundStreams: Streams = {
var inputOrNil: InputStream? = nil
var outputOrNil: OutputStream? = nil
Stream.getBoundStreams(withBufferSize: Settings.chunkSize,
inputStream: &inputOrNil,
outputStream: &outputOrNil)
guard let input = inputOrNil, let output = outputOrNil else {
fatalError("On return of `getBoundStreams`, both `inputStream` and `outputStream` will contain non-nil streams.")
}
// configure and open output stream
output.delegate = self
output.schedule(in: .current, forMode: .default)
output.open()
return Streams(input: input, output: output)
}()
func upload(connectionModel: ConnectionModel, filePath: String, mteHelper: MTEHelper) {
self.mteHelper = mteHelper
guard let fileUrl = URL(string: filePath) else {
return
}
if FileManager.default.fileExists(atPath: fileUrl.path) {
do {
fileHandle = try FileHandle(forReadingFrom: fileUrl)
} catch {
print("Unable to read from file. Error: \(error.localizedDescription)")
}
}
let url = URL(string: String(format: "%@%@", connectionModel.url, connectionModel.route))
var request = URLRequest(url: url!,
cachePolicy: .reloadIgnoringLocalCacheData,
timeoutInterval: 10)
request.httpMethod = connectionModel.method
request.httpBody = connectionModel.payload
request.setValue(connectionModel.contentType, forHTTPHeaderField: "Content-Type")
request.setValue(connectionModel.clientId, forHTTPHeaderField: "x-client-id")
request.setValue(connectionModel.mteVersion, forHTTPHeaderField: "x-mte-version")
session.uploadTask(withStreamedRequest: request).resume()
}
func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) {
completionHandler(boundStreams.input)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
// We'll convert the response data to a String
let dataStr = String(decoding: data, as: UTF8.self)
// and notify via delegate
streamUploadDelegate?.didUploadToServer(success: true, filename: dataStr)
}
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
guard aStream == boundStreams.output else {
return
}
var buffer = [UInt8]()
if eventCode.contains(.hasSpaceAvailable) {
do {
let encoder = try mteHelper.startEncrypt()
buffer = [UInt8](fileHandle.readData(ofLength: Settings.chunkSize))
while !(buffer.isEmpty) {
try mteHelper.encryptChunk(encoder: encoder, buffer: &buffer)
self.boundStreams.output.write(buffer, maxLength: buffer.count)
// read the next chunk
buffer = [UInt8](fileHandle.readData(ofLength: Settings.chunkSize))
}
let finalBuffer = try mteHelper.finishEncrypt(encoder: encoder)
self.boundStreams.output.write(finalBuffer, maxLength: finalBuffer.count)
self.boundStreams.output.close()
} catch {
print("Upload failed. Error: \(error.localizedDescription)")
}
}
if eventCode.contains(.errorOccurred) {
print("Upload error occurred. Closing Streams. Error: \(eventCode.rawValue)")
streamUploadDelegate?.uploadDidFail(message: "Upload to Server failed.")
self.boundStreams.output.close()
self.boundStreams.input.close()
do {
try fileHandle.close()
} catch {
print("Unable to Close fileHandle. Error: \(error.localizedDescription)")
}
}
if eventCode.contains(.endEncountered) {
print("EndOfStream encountered")
do {
self.boundStreams.input.close()
try fileHandle.close()
} catch {
print("Unable to Close fileHandle. Error: \(error.localizedDescription)")
}
}
}
}
#---------------------------------------------------
# Login and Handshake previous to this.
#---------------------------------------------------
# Exerpt from file upload file.
# See full UploadFile.py file for entire code.
#---------------------------------------------------
def send(self, path, use_mte, encoder_state, decoder_state, client_id) -> ResponseModel:
"""
Sends the file up to the server.
"""
upload_response = ResponseModel()
upload_response.data = UploadResponse()
upload_response.data.encoder_state = encoder_state
upload_response.data.decoder_state = decoder_state
# Create default Encoder.
encoder = MteMkeEnc.fromdefault()
# Get file info and create url.
url_type = "mte" if use_mte else "nomte"
file_url = os.path.join(Constants().REST_API_NAME + "/FileUpload/",
url_type + "?name=" + os.path.basename(path))
# Create file stream and array.
file_reader = open(path, 'rb')
web_request = bytearray()
# Get size of file by reading the bytes.
file_size = 0
while True:
file_bytes = file_reader.read(UploadFile().MAX_CHUNK_SIZE)
file_size += len(file_bytes)
if len(file_bytes) == 0:
break
file_reader.close()
content_length = file_size
if use_mte:
# If we are using the MTE, adjust the content length.
content_length += encoder.encrypt_finish_bytes()
remaining_bytes = file_size
number_of_bytes_read = 0
if use_mte:
# Restore Encoder from state.
status = encoder.restore_state_b64(encoder_state)
if status != MteStatus.mte_status_success:
upload_response.success = False
upload_response.result_code = Constants().RC_MTE_STATE_RETRIEVAL
upload_response.message = "Failed to restore MTE Encoder engine. Status: {0} / {1}".format(
encoder.get_status_name(status), encoder.get_status_description(status))
return upload_response
# Start the chunking session.
status = encoder.start_encrypt()
if status != MteStatus.mte_status_success:
upload_response.success = False
upload_response.result_code = Constants().RC_MTE_ENCODE_EXCEPTION
upload_response.message = "Failed to start encode chunk. Status: {0} / {1}".format(
encoder.get_status_name(status), encoder.get_status_description(status))
return upload_response
file_reader = open(path, 'rb')
# Read all bytes from the file.
while number_of_bytes_read < file_size:
file_bytes = file_reader.read(UploadFile().MAX_CHUNK_SIZE)
if use_mte:
# Encode the data in place - encoded data is put back in place in the buffer.
chunk_status = encoder.encrypt_chunk(file_bytes)
if chunk_status != MteStatus.mte_status_success:
upload_response.success = False
upload_response.result_code = Constants().RC_MTE_ENCODE_EXCEPTION
upload_response.message = "Failed to encode chunk. Status: {0} / {1}".format(
encoder.get_status_name(chunk_status), encoder.get_status_description(chunk_status))
return upload_response
# Write the data to the array.
if len(file_bytes) > 0:
web_request.extend(file_bytes)
number_of_bytes_read += len(file_bytes)
remaining_bytes -= len(file_bytes)
if use_mte:
# Finish the chunking session.
final_encoded_chunk, finish_status = encoder.finish_encrypt()
if finish_status != MteStatus.mte_status_success:
upload_response.success = False
upload_response.result_code = Constants().RC_MTE_ENCODE_EXCEPTION
upload_response.message = "Failed to finish encode chunk. Status: {0} / {1}".format(
encoder.get_status_name(finish_status), encoder.get_status_description(finish_status))
return upload_response
# Append the final data to the array.
if len(final_encoded_chunk) > 0:
web_request.extend(final_encoded_chunk)
# Save the Encoder state.
upload_response.data.encoder_state = encoder.save_state_b64()
# Get the response.
headers = {
"Content-Type": "application/json",
"accept": "*/*",
"Content-Length": str(content_length),
Constants().CLIENT_ID_HEADER: client_id
}
api_handshake_response = requests.post(
url=file_url, data=web_request, headers=headers)
text_response = api_handshake_response.json()
# Get the return text.
if text_response['Success'] == False:
# Check if we need to re-handshake.
if text_response['ResultCode'] == Constants().RC_MTE_STATE_NOT_FOUND:
# The server does not thave this client's state - we should re-handshake.
handshake_response = self.handshake_with_server(client_id)
# Return response, if successful give message to try again.
upload_response.data = handshake_response
upload_response.message = "Server lost MTE state, client needed to handshake again, handshake successful, please try again."
return upload_response
if use_mte:
# Restore Decoder.
decoder = MteMkeDec.fromdefault()
status = decoder.restore_state_b64(decoder_state)
if status != MteStatus.mte_status_success:
upload_response.success = False
upload_response.result_code = Constants().RC_MTE_STATE_RETRIEVAL
upload_response.message = "Failed to restore MTE Decoder engine. Status: {0} / {1}".format(
decoder.get_status_name(status), decoder.get_status_description(status))
return upload_response
# Start the chunking session.
status = decoder.start_decrypt()
if status != MteStatus.mte_status_success:
upload_response.success = False
upload_response.result_code = Constants().RC_MTE_DECODE_EXCEPTION
upload_response.message = "Failed to start decode chunk. Status: {0} / {1}".format(
decoder.get_status_name(status), decoder.get_status_description(status))
return upload_response
# Decode the data.
decoded_chunk = decoder.decrypt_chunk(
base64.b64decode(text_response['Data']))
clear_bytes, final_status = decoder.finish_decrypt()
if clear_bytes == NULL:
clear_bytes = bytearray(1)
if final_status != MteStatus.mte_status_success:
upload_response.success = False
upload_response.result_code = Constants().RC_MTE_DECODE_EXCEPTION
upload_response.message = "Failed to finish decode chunk. Status: {0} / {1}".format(
decoder.get_status_name(final_status), decoder.get_status_description(final_status))
return upload_response
# Set decoded message.
decoded_message = decoded_chunk + clear_bytes
# Return the server response.
upload_response.data.server_response = decoded_message
# Save the Decoder state.
upload_response.data.decoder_state = decoder.save_state_b64()
else:
# Return the server response.
upload_response.data.server_response = base64.b64decode(
text_response['Data'])
# Update the jwt/access_token
upload_response.access_token = text_response['access_token']
upload_response.success = True
return upload_response
# continued...
/**
* Upload file method
* Allows user to upload files to API
* Must enter in full path to file
*
* Uses MTE MKE Add-on
*/
func UploadFile(clientId string) (out int, err error) {
//------------------------------
// Loop till user chooses to end
for {
//------------------------------------
// Prompting message for file to copy
fmt.Print("Please enter path of file to upload\n")
reader := bufio.NewReader(os.Stdin)
fPath, _ := reader.ReadString('\n')
//---------------------------
// Take off carriage return
fPath = strings.Replace(fPath, "\n", "", -1)
fPath = strings.Replace(fPath, "\r", "", -1)
//--------------------------------
// Check to make sure file exists
_, err = os.Stat(fPath)
if err != nil {
fmt.Printf("Path does not exist! %s", err)
return errorPathDoesNotExist, err
}
//---------------------------
// Create MTE MKE from state
encoder := mte.NewMkeEncDef()
defer encoder.Destroy()
if useMte {
encoderStatus := encoder.RestoreStateB64(encoderState)
if encoderStatus != mte.Status_mte_status_success {
errorMessage := "Encoder restore error ("
+ mte.GetStatusName(encoderStatus) + "): "
+ mte.GetStatusDescription(encoderStatus)
fmt.Println(errorMessage)
return int(encoderStatus), errors.New(errorMessage)
}
//---------------------
// Initialize Chunking
encoderStatus = encoder.StartEncrypt()
if encoderStatus != mte.Status_mte_status_success {
errorMessage := "MTE Encoder StartEncrypt error ("
+ mte.GetStatusName(encoderStatus) + "): "
+ mte.GetStatusDescription(encoderStatus)
fmt.Println(errorMessage)
return int(encoderStatus), errors.New(errorMessage)
}
}
//----------------------------
// Open file and retrieve info
file, _ := os.Open(fPath)
fi, _ := file.Stat()
defer file.Close()
//----------
// Set URI
var route string
if useMte {
route = fileUploadMteRoute
} else {
route = fileUploadNoMteRoute
}
uri := restAPIName + route + fi.Name()
//-------------------------
// Calculate content length
totalSize := fi.Size()
//------------------------------------------------------------
// If we are using the MTE add additional length to totalSize
if useMte {
totalSize += int64(encoder.EncryptFinishBytes())
}
//-------------------------
// Use pipe to pass request
rd, wr := io.Pipe()
defer rd.Close()
go func() {
defer wr.Close()
//-------------
// Write file
buf := make([]byte, chunkSize)
for {
n, err := file.Read(buf)
if err != nil {
if errors.Is(err, io.EOF) {
if useMte {
//-----------------------------
// End of the file reached
// Finish the chunking session
//-----------------------------
finishEncode, status := encoder.FinishEncrypt()
if status != mte.Status_mte_status_success {
fmt.Fprintf(os.Stderr, "Encode finish error (%v): %v\n",
mte.GetStatusName(status), mte.GetStatusDescription(status))
}
//-------------------------------------------------
// If there are bytes to write, write them to file
//-------------------------------------------------
if finishEncode != nil {
if _, err := wr.Write(finishEncode); err != nil {
fmt.Printf("Error trying to write to file %s, err: %s",
fi.Name(), err)
}
}
//--------------------
// Save state Encoder
encoderState = encoder.SaveStateB64()
}
}
break
}
//---------------------------------------
// If we are using MTE encrypt the chunk
if useMte {
if n < chunkSize {
buf = buf[:n]
}
//-----------------------------------------------------------
// Encrypt the chunk
encoderStatus := encoder.EncryptChunk(buf)
if encoderStatus != mte.Status_mte_status_success {
fmt.Fprintf(os.Stderr, "Encode error (%v): %v\n",
mte.GetStatusName(encoderStatus), mte.GetStatusDescription(encoderStatus))
break
}
}
_, _ = wr.Write(buf[:n])
}
}()
//--------------------------
// Construct request with rd
req, _ := http.NewRequest("POST", uri, rd)
//------------------------------------------------------
// If we have an access token add authentication header
if access_token != "" {
// Create a Bearer string by appending string access token
var bearer = "Bearer " + access_token
// Add authorization header to the req
req.Header.Add("Authorization", bearer)
}
req.Header.Set(clientIdHeader, clientId)
req.ContentLength = totalSize
//-----------------
// Process request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err.Error())
return errorReadingResponse, err
} else {
body := &bytes.Buffer{}
_, _ = body.ReadFrom(resp.Body)
defer resp.Body.Close()
//------------------------------------------
// Marshal json response to Response object
hrBytes, _ := ioutil.ReadAll(body)
var serverResponse ResponseModel[string]
json.Unmarshal(hrBytes, &serverResponse)
if !serverResponse.Success {
errorMessage := "Error back from server: "
+ serverResponse.Message + " Code: " + strconv.Itoa(errorFromServer)
fmt.Println(errorMessage)
return errorFromServer, errors.New(errorMessage)
}
//------------------
// Set access token
access_token = serverResponse.access_token
var decodedText []byte
//-----------------------------------------------------
// Decode the response message if we are using the MTE
decoder := mte.NewMkeDecDef()
defer decoder.Destroy()
if useMte {
//--------------------------------------------
// Base64 Decode server response to []byte
encodedDatab64 :=
make([]byte, base64.StdEncoding.DecodedLen(len(serverResponse.Data)))
n, err := base64.StdEncoding.Decode(encodedDatab64, []byte(serverResponse.Data))
if err != nil {
errorMessage := "Error base64 decode encoded data: "
+ err.Error() + " Code: " + strconv.Itoa(errorDecodingData)
fmt.Println(errorMessage)
return errorDecodingData, errors.New(errorMessage)
}
encodedData := encodedDatab64[:n]
//----------------------------
// Restore the Decoder state
decoderStatus := decoder.RestoreStateB64(decoderState)
if decoderStatus != mte.Status_mte_status_success {
errorMessage := "Decoder restore error ("
+ mte.GetStatusName(decoderStatus) + "): "
+ mte.GetStatusDescription(decoderStatus)
fmt.Println(errorMessage)
return int(decoderStatus), errors.New(errorMessage)
}
//---------------------
// Initialize Chunking
decoderStatus = decoder.StartDecrypt()
if decoderStatus != mte.Status_mte_status_success {
errorMessage := "MTE Decoder StartDecrypt error ("
+ mte.GetStatusName(decoderStatus) + "): "
+ mte.GetStatusDescription(decoderStatus)
fmt.Println(errorMessage)
return int(decoderStatus), errors.New(errorMessage)
}
//-----------------------------------------------------------
// The response is going to be short don't need to loop
//-----------------------------------------------------------
// Decrypt the chunk
decodedData := decoder.DecryptChunk(encodedData)
if decodedData == nil {
errorMessage := "Decode error."
fmt.Println(errorMessage)
return errorDecodingData, errors.New(errorMessage)
}
//-----------------------
// Finish Decode chunk
finishDecodeChunk, decoderStatus := decoder.FinishDecrypt()
if decoderStatus != mte.Status_mte_status_success {
//--------------------------------------------------------
// Decode finish decrypt unsuccessful and cannot continue
errorMessage := "MTE Decoder FinishDecrypt error ("
+ mte.GetStatusName(decoderStatus) + "): "
+ mte.GetStatusDescription(decoderStatus)
fmt.Println(errorMessage)
return int(decoderStatus), errors.New(errorMessage)
}
//--------------------------------------------------
// Check if there are additional bytes; if so, append
if finishDecodeChunk != nil {
decodedText = append(decodedData[:], finishDecodeChunk[:]...)
} else {
decodedText = decodedData
}
//-------------------
// Save Decoder state
decoderState = decoder.SaveStateB64()
} else {
//-------------------------------
// Base64 Decode response string
decodedText, err = base64.StdEncoding.DecodeString(string(serverResponse.Data))
if err != nil {
errorMessage := "Error base64 decoding string"
fmt.Println(errorMessage)
return errorBase64Decoding, errors.New(errorMessage)
}
}
//-------------------------------
// Print out response from server
fmt.Println("Response from server: " + string(decodedText))
}
//------------------------------------------------
// Prompt user if they want to upload another file
fmt.Print("\nWould you like to upload another file (y/n)?\n")
qReader := bufio.NewReader(os.Stdin)
uploadAgain, _ := qReader.ReadString('\n')
//---------------------------
// Take off carriage return
uploadAgain = strings.Replace(uploadAgain, "\n", "", -1)
uploadAgain = strings.Replace(uploadAgain, "\r", "", -1)
//----------------------
// Check user response
if strings.ToLower(uploadAgain) == "n" {
fmt.Println("Program stopped.")
return endProgram, nil
}
}
}