Update with working state

* Update with working state
This commit is contained in:
abraunegg 2024-03-31 09:03:58 +11:00
parent a3050bca54
commit c1a619f82f
4 changed files with 67 additions and 39 deletions

View file

@ -201,6 +201,7 @@ class CurlEngine {
bool keepAlive; bool keepAlive;
ulong dnsTimeout; ulong dnsTimeout;
CurlResponse response; CurlResponse response;
File uploadFile;
this() { this() {
http = HTTP(); http = HTTP();
@ -210,6 +211,8 @@ class CurlEngine {
~this() { ~this() {
object.destroy(http); object.destroy(http);
object.destroy(response); object.destroy(response);
if (uploadFile.isOpen())
uploadFile.close();
} }
void initialise(ulong dnsTimeout, ulong connectTimeout, ulong dataTimeout, ulong operationTimeout, int maxRedirects, bool httpsDebug, string userAgent, bool httpProtocol, ulong userRateLimit, ulong protocolVersion, bool keepAlive=true) { void initialise(ulong dnsTimeout, ulong connectTimeout, ulong dataTimeout, ulong operationTimeout, int maxRedirects, bool httpsDebug, string userAgent, bool httpProtocol, ulong userRateLimit, ulong protocolVersion, bool keepAlive=true) {
@ -337,10 +340,25 @@ class CurlEngine {
} }
} }
void setFile(File* file, ulong offsetSize) { void setFile(string filepath, string contentRange, ulong offset, ulong offsetSize) {
setResponseHolder(null); setResponseHolder(null);
// open file as read-only in binary mode
uploadFile = File(filepath, "rb");
if (contentRange.empty) {
offsetSize = uploadFile.size();
} else {
addRequestHeader("Content-Range", contentRange);
uploadFile.seek(offset);
}
// Setup progress bar to display
http.onProgress = delegate int(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) {
return 0;
};
addRequestHeader("Content-Type", "application/octet-stream"); addRequestHeader("Content-Type", "application/octet-stream");
http.onSend = data => file.rawRead(data).length; http.onSend = data => uploadFile.rawRead(data).length;
http.contentLength = offsetSize; http.contentLength = offsetSize;
} }
@ -362,9 +380,6 @@ class CurlEngine {
CurlResponse download(string originalFilename, string downloadFilename) { CurlResponse download(string originalFilename, string downloadFilename) {
setResponseHolder(null); setResponseHolder(null);
// Threshold for displaying download bar
long thresholdFileSize = 4 * 2^^20; // 4 MiB
// open downloadFilename as write in binary mode // open downloadFilename as write in binary mode
auto file = File(downloadFilename, "wb"); auto file = File(downloadFilename, "wb");
@ -403,6 +418,12 @@ class CurlEngine {
}; };
http.contentLength = 0; http.contentLength = 0;
response = null; response = null;
// close file if open
if (uploadFile.isOpen()){
// close open file
uploadFile.close();
}
} }
void shutdown() { void shutdown() {

View file

@ -208,13 +208,38 @@ final class ItemDatabase {
if (e.msg == "database is locked") { if (e.msg == "database is locked") {
addLogEntry(); addLogEntry();
addLogEntry("ERROR: The 'onedrive' application is already running - please check system process list for active application instances"); addLogEntry("ERROR: The 'onedrive' application is already running - please check system process list for active application instances");
addLogEntry(" - Use 'sudo ps aufxw | grep onedrive' to potentially determine acive running process", ["verbose"]); addLogEntry(" - Use 'sudo ps aufxw | grep onedrive' to potentially determine acive running process");
addLogEntry(); addLogEntry();
} else { } else {
// A different error .. detail the message, detail the actual SQLite Error Code to assist with troubleshooting // A different error .. detail the message, detail the actual SQLite Error Code to assist with troubleshooting
addLogEntry(); addLogEntry();
addLogEntry("ERROR: An internal database error occurred: " ~ e.msg ~ " (SQLite Error Code: " ~ to!string(e.errorCode) ~ ")"); addLogEntry("ERROR: An internal database error occurred: " ~ e.msg ~ " (SQLite Error Code: " ~ to!string(e.errorCode) ~ ")");
addLogEntry(); addLogEntry();
// Give the user some additional information and pointers on this error
// The below list is based on user issue / discussion reports since 2018
switch (e.errorCode) {
case 7: // SQLITE_NOMEM
addLogEntry("The operation could not be completed due to insufficient memory. Please close unnecessary applications to free up memory and try again.");
break;
case 10: // SQLITE_IOERR
addLogEntry("A disk I/O error occurred. This could be due to issues with the storage medium (e.g., disk full, hardware failure, filesystem corruption). Please check your disk's health using a disk utility tool, ensure there is enough free space, and check the filesystem for errors.");
break;
case 11: // SQLITE_CORRUPT
addLogEntry("The database file appears to be corrupt. This could be due to incomplete or failed writes, hardware issues, or unexpected interruptions during database operations. Please perform a --resync operation.");
break;
case 14: // SQLITE_CANTOPEN
addLogEntry("The database file could not be opened. Please check that the database file exists, has the correct permissions, and is not being blocked by another process or security software.");
break;
case 26: // SQLITE_NOTADB
addLogEntry("The file attempted to be opened does not appear to be a valid SQLite database, or it may have been corrupted to a point where it's no longer recognizable. Please check your application configuration directory and/or perform a --resync operation.");
break;
default:
addLogEntry("An unexpected error occurred. Please consult the application documentation or support to resolve this issue.");
break;
}
// Blank line before exit
addLogEntry();
} }
return; return;
} }

View file

@ -392,12 +392,10 @@ class OneDriveApi {
string authFilesString = appConfig.getValueString("auth_files"); string authFilesString = appConfig.getValueString("auth_files");
string authResponseString = appConfig.getValueString("auth_response"); string authResponseString = appConfig.getValueString("auth_response");
// Is authResponseString not empty
if (!authResponseString.empty) { if (!authResponseString.empty) {
// read the response from authResponseString // read the response from authResponseString
response = cast(char[]) authResponseString; response = cast(char[]) authResponseString;
} else if (authFilesString != "") { } else if (authFilesString != "") {
// authResponseString empty .. is authFilesString not empty
string[] authFiles = authFilesString.split(":"); string[] authFiles = authFilesString.split(":");
string authUrl = authFiles[0]; string authUrl = authFiles[0];
string responseUrl = authFiles[1]; string responseUrl = authFiles[1];
@ -422,7 +420,6 @@ class OneDriveApi {
exit(-1); exit(-1);
} }
// Add logging that we are waiting for auth elements to be available
addLogEntry("Client requires authentication before proceeding. Waiting for --auth-files elements to be available."); addLogEntry("Client requires authentication before proceeding. Waiting for --auth-files elements to be available.");
while (!exists(responseUrl)) { while (!exists(responseUrl)) {
@ -529,7 +526,6 @@ class OneDriveApi {
// Return all the items that are shared with the user // Return all the items that are shared with the user
// https://docs.microsoft.com/en-us/graph/api/drive-sharedwithme // https://docs.microsoft.com/en-us/graph/api/drive-sharedwithme
JSONValue getSharedWithMe() { JSONValue getSharedWithMe() {
checkAccessTokenExpired();
return get(sharedWithMeUrl); return get(sharedWithMeUrl);
} }
@ -683,7 +679,6 @@ class OneDriveApi {
} }
JSONValue createSubscription(string notificationUrl, SysTime expirationDateTime) { JSONValue createSubscription(string notificationUrl, SysTime expirationDateTime) {
checkAccessTokenExpired();
string driveId = appConfig.getValueString("drive_id"); string driveId = appConfig.getValueString("drive_id");
string url = subscriptionUrl; string url = subscriptionUrl;
@ -772,7 +767,6 @@ class OneDriveApi {
// Private OneDrive API Functions // Private OneDrive API Functions
private void addIncludeFeatureRequestHeader(string[string]* headers) { private void addIncludeFeatureRequestHeader(string[string]* headers) {
addLogEntry("Adding 'Include-Feature=AddToOneDrive' API request header as 'sync_business_shared_items' config option is enabled", ["debug"]); addLogEntry("Adding 'Include-Feature=AddToOneDrive' API request header as 'sync_business_shared_items' config option is enabled", ["debug"]);
(*headers)["Prefer"] = "Include-Feature=AddToOneDrive"; (*headers)["Prefer"] = "Include-Feature=AddToOneDrive";
} }
@ -919,10 +913,11 @@ class OneDriveApi {
} }
private void performDelete(const(char)[] url, string[string] requestHeaders=null, string callingFunction=__FUNCTION__, int lineno=__LINE__) { private void performDelete(const(char)[] url, string[string] requestHeaders=null, string callingFunction=__FUNCTION__, int lineno=__LINE__) {
bool validateJSONResponse = false;
oneDriveErrorHandlerWrapper((CurlResponse response) { oneDriveErrorHandlerWrapper((CurlResponse response) {
connect(HTTP.Method.del, url, false, response, requestHeaders); connect(HTTP.Method.del, url, false, response, requestHeaders);
return curlEngine.execute(); return curlEngine.execute();
}, callingFunction, lineno); }, validateJSONResponse, callingFunction, lineno);
} }
private void downloadFile(const(char)[] url, string filename, long fileSize, string callingFunction=__FUNCTION__, int lineno=__LINE__) { private void downloadFile(const(char)[] url, string filename, long fileSize, string callingFunction=__FUNCTION__, int lineno=__LINE__) {
@ -1067,55 +1062,42 @@ class OneDriveApi {
} }
private JSONValue get(string url, bool skipToken = false, string[string] requestHeaders=null, string callingFunction=__FUNCTION__, int lineno=__LINE__) { private JSONValue get(string url, bool skipToken = false, string[string] requestHeaders=null, string callingFunction=__FUNCTION__, int lineno=__LINE__) {
bool validateJSONResponse = true;
return oneDriveErrorHandlerWrapper((CurlResponse response) { return oneDriveErrorHandlerWrapper((CurlResponse response) {
connect(HTTP.Method.get, url, skipToken, response, requestHeaders); connect(HTTP.Method.get, url, skipToken, response, requestHeaders);
return curlEngine.execute(); return curlEngine.execute();
}, callingFunction, lineno); }, validateJSONResponse, callingFunction, lineno);
} }
private JSONValue patch(const(char)[] url, const(char)[] patchData, string[string] requestHeaders=null, const(char)[] contentType = "application/json", string callingFunction=__FUNCTION__, int lineno=__LINE__) { private JSONValue patch(const(char)[] url, const(char)[] patchData, string[string] requestHeaders=null, const(char)[] contentType = "application/json", string callingFunction=__FUNCTION__, int lineno=__LINE__) {
bool validateJSONResponse = true;
return oneDriveErrorHandlerWrapper((CurlResponse response) { return oneDriveErrorHandlerWrapper((CurlResponse response) {
connect(HTTP.Method.patch, url, false, response, requestHeaders); connect(HTTP.Method.patch, url, false, response, requestHeaders);
curlEngine.setContent(contentType, patchData); curlEngine.setContent(contentType, patchData);
return curlEngine.execute(); return curlEngine.execute();
}, callingFunction, lineno); }, validateJSONResponse, callingFunction, lineno);
} }
private JSONValue post(const(char)[] url, const(char)[] postData, bool skipToken = false, const(char)[] contentType = "application/json", string callingFunction=__FUNCTION__, int lineno=__LINE__) { private JSONValue post(const(char)[] url, const(char)[] postData, bool skipToken = false, const(char)[] contentType = "application/json", string callingFunction=__FUNCTION__, int lineno=__LINE__) {
bool validateJSONResponse = true;
return oneDriveErrorHandlerWrapper((CurlResponse response) { return oneDriveErrorHandlerWrapper((CurlResponse response) {
connect(HTTP.Method.post, url, skipToken, response); connect(HTTP.Method.post, url, skipToken, response);
curlEngine.setContent(contentType, postData); curlEngine.setContent(contentType, postData);
return curlEngine.execute(); return curlEngine.execute();
}, callingFunction, lineno); }, validateJSONResponse, callingFunction, lineno);
} }
private JSONValue put(const(char)[] url, string filepath, bool skipToken=false, string contentRange=null, ulong offset=0, ulong offsetSize=0, string callingFunction=__FUNCTION__, int lineno=__LINE__) { private JSONValue put(const(char)[] url, string filepath, bool skipToken=false, string contentRange=null, ulong offset=0, ulong offsetSize=0, string callingFunction=__FUNCTION__, int lineno=__LINE__) {
bool validateJSONResponse = true;
return oneDriveErrorHandlerWrapper((CurlResponse response) { return oneDriveErrorHandlerWrapper((CurlResponse response) {
string[string] requestHeaders; connect(HTTP.Method.put, url, skipToken, response);
// open file as read-only in binary mode curlEngine.setFile(filepath, contentRange, offset, offsetSize);
auto file = File(filepath, "rb");
if (!contentRange.empty)
file.seek(offset);
// function scopes
scope(exit) {
// close file if open
if (file.isOpen()){
// close open file
file.close();
}
}
if (!contentRange.empty)
requestHeaders["Content-Range"] = contentRange;
else
offsetSize = file.size;
connect(HTTP.Method.put, url, skipToken, response, requestHeaders);
curlEngine.setFile(&file, offsetSize);
return curlEngine.execute(); return curlEngine.execute();
}, callingFunction, lineno); }, validateJSONResponse, callingFunction, lineno);
} }
// Wrapper function for all requests to OneDrive API // Wrapper function for all requests to OneDrive API
// throws a OneDriveException // throws OneDriveException
private JSONValue oneDriveErrorHandlerWrapper(CurlResponse delegate(CurlResponse response) executer, bool validateJSONResponse, string callingFunction, int lineno) { private JSONValue oneDriveErrorHandlerWrapper(CurlResponse delegate(CurlResponse response) executer, bool validateJSONResponse, string callingFunction, int lineno) {
int maxRetryCount = 10; int maxRetryCount = 10;
int retryAttempts = 0; int retryAttempts = 0;

View file

@ -4847,11 +4847,11 @@ class SyncEngine {
onlinePathData = foundDirectoryJSONItem; onlinePathData = foundDirectoryJSONItem;
} else { } else {
// No 'search item matches found' - raise a 404 so that the exception handling will take over to create the folder // No 'search item matches found' - raise a 404 so that the exception handling will take over to create the folder
throw new OneDriveException(404, "Name not found via search", null); throw new OneDriveException(404, "Name not found via search");
} }
} else { } else {
// No 'search item matches found' - raise a 404 so that the exception handling will take over to create the folder // No 'search item matches found' - raise a 404 so that the exception handling will take over to create the folder
throw new OneDriveException(404, "Name not found via search", null); throw new OneDriveException(404, "Name not found via search");
} }
} }
} catch (OneDriveException exception) { } catch (OneDriveException exception) {