diff --git a/examples/test.c b/examples/test.c index bb9f059..b2a1507 100644 --- a/examples/test.c +++ b/examples/test.c @@ -9,7 +9,6 @@ static char *fgets_no_trailing_white(char * restrict str, int size, FILE * restr s[strlen(s)-1] = '\0'; return s && *s ? s : NULL; } - static void interaction_handler(sxupdate_t handle, enum sxupdate_step step, void (*resume)(sxupdate_t, enum sxupdate_action)) { switch(step) { @@ -125,8 +124,11 @@ int main(int argc, const char *argv[]) { sxupdate_set_url(sxu, url); if(have_custom_header) sxupdate_add_header(sxu, header_name, header_value); - if(sxupdate_execute(sxu)) - fprintf(stderr, "Error: %s\n", sxupdate_err_msg(sxu)); + if(sxupdate_execute(sxu)) { + char *err_msg = sxupdate_err_msg(sxu); + fprintf(stderr, "Error: %s\n", err_msg ? err_msg : "Unknown"); + free(err_msg); + } } sxupdate_delete(sxu); } diff --git a/include/api.h b/include/api.h index 7fbfafb..9ffd09e 100644 --- a/include/api.h +++ b/include/api.h @@ -13,7 +13,7 @@ enum sxupdate_status { sxupdate_status_memory, sxupdate_status_bad_url, sxupdate_status_invalid, /* invalid option value */ - sxupdate_status_parse /* parse error */ + sxupdate_status_parse /* parse error */ }; enum sxupdate_step { @@ -128,9 +128,9 @@ enum sxupdate_status sxupdate_add_header(sxupdate_t handle, const char *header_n enum sxupdate_status sxupdate_execute(sxupdate_t handle); /*** - * Retrieve the last error message, if any [NOT YET IMPLEMENTED] + * Retrieve the last error message. Caller must free the returned string, if any */ -const char *sxupdate_err_msg(sxupdate_t handle); +char *sxupdate_err_msg(sxupdate_t handle); #ifndef NO_SIGNATURE #include diff --git a/src/api.c b/src/api.c index ac0b492..7f21834 100644 --- a/src/api.c +++ b/src/api.c @@ -222,35 +222,23 @@ static size_t sxupdate_parse_chunk(char *ptr, size_t size, size_t nmemb, void *h return len; } -/*** - * Fetch and parse the metadata from a file - */ -static enum sxupdate_status sxupdate_fetch_from_file(sxupdate_t handle, - void (*next)(sxupdate_t, enum sxupdate_status) - ) { -#define SXUPDATE_FETCH_FROM_FILE_BUFF_SIZE 1024 - enum sxupdate_status stat = sxupdate_status_ok; - const char *filename = handle->url + strlen(SXUPDATE_FILE_PREFIX); - if(handle->verbosity) - sxupdate_verbose("Fetching version info from local file %s", filename); - FILE *f = fopen(filename, "rb"); - if(f) { - char buff[SXUPDATE_FETCH_FROM_FILE_BUFF_SIZE]; - size_t len; - while(stat == sxupdate_status_ok && (len = fread(buff, 1, sizeof(buff), f)) > 0) - stat = sxupdate_parse(handle, buff, len); - fclose(f); - } - +static enum sxupdate_status sxupdate_after_parse(sxupdate_t handle, enum sxupdate_status stat, + void (*next)(sxupdate_t, enum sxupdate_status) + ) { // finish parsing - if(stat == sxupdate_status_ok) - stat = sxupdate_parse_finish(handle); + if(stat == sxupdate_status_ok) { + if(handle->parser.scanned_bytes == 0) { + handle->err_msg = "Unable to connect. Please check your credentials and try again"; + stat = sxupdate_status_error; + } else + stat = sxupdate_parse_finish(handle); + } next(handle, stat); return stat; } /*** - * Fetch and parse the metadata from the network using curl + * Fetch and parse the metadata from file or network using curl */ static enum sxupdate_status sxupdate_fetch_from_curl(sxupdate_t handle, struct curl_slist *http_headers, @@ -264,7 +252,7 @@ static enum sxupdate_status sxupdate_fetch_from_curl(sxupdate_t handle, curl_easy_setopt(curl, CURLOPT_URL, handle->url); // set custom headers - if(http_headers) + if(http_headers && !sxupdate_url_is_file(handle->url)) curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_headers); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, handle); @@ -283,17 +271,23 @@ static enum sxupdate_status sxupdate_fetch_from_curl(sxupdate_t handle, // execute if(handle->verbosity) sxupdate_verbose("Fetching version info from %s", handle->url); + CURLcode res = curl_easy_perform(curl); + + if((sxupdate_url_is_file(handle->url))) + handle->http_code = 200; + else + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &handle->http_code); + if(res != CURLE_OK) { sxupdate_printerr("Error connecting to %s:\n %s", handle->url, curl_easy_strerror(res)); stat = sxupdate_status_error; - } + } else if(!(handle->http_code >= 200 && handle->http_code < 300)) + stat = sxupdate_status_error; + curl_easy_cleanup(curl); - // finish parsing - if(stat == sxupdate_status_ok) - stat = sxupdate_parse_finish(handle); - next(handle, stat); + stat = sxupdate_after_parse(handle, stat, next); } return stat; @@ -312,12 +306,8 @@ enum sxupdate_status sxupdate_fetch_and_parse(sxupdate_t handle, ) { enum sxupdate_status stat = sxupdate_status_error; if(handle->url - && (stat = sxupdate_parse_init(handle)) == sxupdate_status_ok) { // initialize parser - if(handle->url_is_file) - stat = sxupdate_fetch_from_file(handle, next); - else - stat = sxupdate_fetch_from_curl(handle, http_headers, next); - } + && (stat = sxupdate_parse_init(handle)) == sxupdate_status_ok) + stat = sxupdate_fetch_from_curl(handle, http_headers, next); return stat; } @@ -379,15 +369,6 @@ static enum sxupdate_status sxupdate_download(sxupdate_t handle, } else resolved_url = version->enclosure.url; - if(sxupdate_url_is_file(resolved_url) == 2) { - *save_path_p = strdup(resolved_url + strlen(SXUPDATE_FILE_PREFIX)); - if(resolved_url != version->enclosure.url) - free(resolved_url); - if(verbosity) - sxupdate_verbose("Using local file %s", *save_path_p ? *save_path_p : "memory error!"); - return sxupdate_status_ok; - } - enum sxupdate_status stat = sxupdate_status_error; char *save_path = sxupdate_get_installer_download_path(version->enclosure.filename); @@ -425,14 +406,15 @@ static enum sxupdate_status sxupdate_download(sxupdate_t handle, // connect and download CURLcode res = curl_easy_perform(curl); - + if((sxupdate_url_is_file(resolved_url))) + handle->http_code = 200; + else + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &handle->http_code); + stat = sxupdate_status_error; if(res != CURLE_OK) sxupdate_printerr("Error connecting to %s:\n %s", resolved_url, curl_easy_strerror(res)); - else { - // ensure saved_path has executable permissions - if(!sxupdate_set_execute_permission(save_path)) - stat = sxupdate_status_ok; - } + else if(handle->http_code >= 200 && handle->http_code < 300) + stat = sxupdate_status_ok; if(verbosity > 2) sxupdate_verbose("cleaning up curl call"); curl_easy_cleanup(curl); @@ -457,6 +439,10 @@ static void sxupdate_resume(sxupdate_t handle, enum sxupdate_action action) { if(action == sxupdate_action_proceed && handle->step == sxupdate_step_have_newer_version) { char *downloaded_file_path; stat = sxupdate_download(handle, &downloaded_file_path); + if(stat == sxupdate_status_ok && + // ensure saved_path has executable permissions + sxupdate_set_execute_permission(downloaded_file_path)) + stat = sxupdate_status_error; if(stat == sxupdate_status_ok) { // TO DO: check download file size @@ -473,6 +459,8 @@ static void sxupdate_resume(sxupdate_t handle, enum sxupdate_action action) { static void sxupdate_after_fetch_and_parse(sxupdate_t handle, enum sxupdate_status stat) { if(stat == sxupdate_status_ok) { + handle->http_code = 0; + // check if this version is newer if(sxupdate_version_cmp(handle->latest_version.version, handle->get_current_version(), handle->verbosity) > 0) handle->step = sxupdate_step_have_newer_version; @@ -511,9 +499,22 @@ SXUPDATE_API enum sxupdate_status sxupdate_execute(sxupdate_t handle) { } /*** - * Retrieve the last error message, if any [NOT YET IMPLEMENTED] + * Retrieve the last error message. Caller must free */ -SXUPDATE_API const char *sxupdate_err_msg(sxupdate_t handle) { - (void)(handle); - return "to do: sxupdate_err_msg()"; +SXUPDATE_API char *sxupdate_err_msg(sxupdate_t handle) { + if(handle->err_msg) + return strdup(handle->err_msg); + if(handle->http_code > 0 && (handle->http_code >= 400 || handle->http_code < 200)) { + switch(handle->http_code) { + case 401: + return strdup("Unauthorized. Please check your Hub credentials and try again"); + default: + { + char *s = malloc(100); + snprintf(s, 100, "Http response code %li", handle->http_code); + return s; + } + } + } + return strdup("Unexpected error"); } diff --git a/src/internal.h b/src/internal.h index 9768ba8..d770ff0 100644 --- a/src/internal.h +++ b/src/internal.h @@ -16,6 +16,7 @@ struct sxupdate_data { struct { yajl_status stat; struct yajl_helper_parse_state st; + size_t scanned_bytes; } parser; struct sxupdate_semantic_version (*get_current_version)(); sxupdate_interaction_handler interaction_handler; @@ -25,6 +26,7 @@ struct sxupdate_data { struct sxupdate_string_list *installer_args, **installer_args_next; struct curl_slist *http_headers; + long http_code; // curl response struct sxupdate_version latest_version; #ifndef NO_SIGNATURE @@ -35,6 +37,8 @@ struct sxupdate_data { } latest_version_internal; #endif + const char *err_msg; + unsigned char verbosity; unsigned char url_is_file:1; diff --git a/src/parse.c b/src/parse.c index 800b446..2066a23 100644 --- a/src/parse.c +++ b/src/parse.c @@ -198,9 +198,11 @@ static int sxupdate_parse_ok(sxupdate_t handle) { */ enum sxupdate_status sxupdate_parse(sxupdate_t handle, const char *data, size_t len) { if(handle->parser.stat == yajl_status_ok - && (handle->parser.stat = yajl_parse(handle->parser.st.yajl, (const unsigned char *)data, len)) == yajl_status_ok) + && (handle->parser.stat = yajl_parse(handle->parser.st.yajl, (const unsigned char *)data, len)) == yajl_status_ok) { + handle->parser.scanned_bytes += len; return sxupdate_status_ok; - return sxupdate_status_error; + } + return sxupdate_status_parse; } enum sxupdate_status sxupdate_parse_finish(sxupdate_t handle) {