diff --git a/CMakeLists.txt b/CMakeLists.txt index 723b0de75adc14f027ddbbb8bfb3c68c5fbe44ea..d0f53f1aea2577d5f0e54a59a478d972a8cb68e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ option(ENABLE_TOOLS "Turn on or off compilation of tools." YES) option(ENABLE_TUNNEL "Turn on compilation of tunnel support." NO) option(ENABLE_TUTORIALS "Enable compilation of tutorials." YES) option(ENABLE_UNIT_TESTS "Enable compilation of unit tests." YES) +option(ENABLE_UPDATE_CHECK "Enable update check." NO) option(ENABLE_VIDEO "Build with video support." YES) cmake_dependent_option(ENABLE_ASSISTANT "Turn on assistant compiling." YES "ENABLE_GTK_UI" NO) option(ENABLE_DEBUG_LOGS "Turn on or off debug level logs." NO) diff --git a/config.h.cmake b/config.h.cmake index fe8fd97690c10f16c4f21d629bff52772c86cb55..5de3f36a30207dc3d2992889eba08d95cef5d853 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -47,3 +47,4 @@ #cmakedefine HAVE_LIBUDEV_H 0 #cmakedefine HAVE_LIME #cmakedefine ENABLE_NLS 1 +#cmakedefine ENABLE_UPDATE_CHECK 1 diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 0f6ce1e5771e2f45f708244bee3be1ccacdfedc6..f4671904afba50c5209d9eba00ae680638e0fa1a 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -401,6 +401,15 @@ void linphone_core_cbs_set_call_created(LinphoneCoreCbs *cbs, LinphoneCoreCbsCal cbs->vtable->call_created = cb; } +LinphoneCoreCbsVersionUpdateCheckResultReceivedCb linphone_core_cbs_get_version_update_check_result_received(LinphoneCoreCbs *cbs) { + return cbs->vtable->version_update_check_result_received; +} + +void linphone_core_cbs_set_version_update_check_result_received(LinphoneCoreCbs *cbs, LinphoneCoreCbsVersionUpdateCheckResultReceivedCb cb) { + cbs->vtable->version_update_check_result_received = cb; +} + + typedef belle_sip_object_t_vptr_t LinphoneCore_vptr_t; BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneCore); BELLE_SIP_INSTANCIATE_VPTR(LinphoneCore, belle_sip_object_t, @@ -7245,3 +7254,143 @@ bool_t linphone_core_is_content_type_supported(const LinphoneCore *lc, const cha void linphone_core_add_content_type_support(LinphoneCore *lc, const char *content_type) { sal_add_content_type_support(lc->sal, content_type); } + +#ifdef ENABLE_UPDATE_CHECK +static void update_check_process_terminated(LinphoneCore *lc, LinphoneVersionUpdateCheckResult result, const char *version, const char *url) { + linphone_core_notify_version_update_check_result_received(lc, result, version, url); + if (lc->update_check_http_listener) { + belle_sip_object_unref(lc->update_check_http_listener); + lc->update_check_http_listener = NULL; + } + bctbx_free(lc->update_check_current_version); + lc->update_check_current_version = NULL; +} + +typedef struct _parsed_version_st { + int major; + int minor; + int patch; +} parsed_version_t; + +static int compare_parsed_versions(parsed_version_t current_version, parsed_version_t last_version) { + if (last_version.major > current_version.major) return 1; + if (last_version.minor > current_version.minor) return 1; + if (last_version.patch > current_version.patch) return 1; + return -1; +} + +static void parse_version(const char *version, parsed_version_t *parsed_version) { + char *copy = bctbx_strdup(version); + char *ptr = copy; + char *next; + memset(parsed_version, 0, sizeof(parsed_version_t)); + next = strchr(ptr, '.'); + parsed_version->major = atoi(ptr); + ptr = next + 1; + next = strchr(ptr, '.'); + parsed_version->minor = atoi(ptr); + if (next != NULL) { + ptr = next + 1; + parsed_version->patch = atoi(ptr); + } + bctbx_free(copy); +} + +static bool_t update_is_available(const char *current_version, const char *last_version) { + parsed_version_t current_parsed_version; + parsed_version_t last_parsed_version; + parse_version(current_version, ¤t_parsed_version); + parse_version(last_version, &last_parsed_version); + return (compare_parsed_versions(current_parsed_version, last_parsed_version) > 0) ? TRUE : FALSE; +} + +static void update_check_process_response_event(void *ctx, const belle_http_response_event_t *event) { + LinphoneCore *lc = (LinphoneCore *)ctx; + + if (belle_http_response_get_status_code(event->response) == 200) { + belle_sip_message_t *message = BELLE_SIP_MESSAGE(event->response); + char *body = bctbx_strdup(belle_sip_message_get_body(message)); + char *last_version = body; + char *url = strchr(body, '\t'); + char *ptr; + if (url == NULL) { + update_check_process_terminated(lc, LinphoneVersionUpdateCheckError, NULL, NULL); + bctbx_free(body); + return; + } + *url = '\0'; + url++; + ptr = strrchr(url, '\r'); + if (ptr != NULL) *ptr = '\0'; + ptr = strrchr(url, '\n'); + if (ptr != NULL) *ptr = '\0'; + if (update_is_available(lc->update_check_current_version, last_version)) { + update_check_process_terminated(lc, LinphoneVersionUpdateCheckNewVersionAvailable, last_version, url); + } else { + update_check_process_terminated(lc, LinphoneVersionUpdateCheckUpToDate, NULL, NULL); + } + bctbx_free(body); + } else { + update_check_process_terminated(lc, LinphoneVersionUpdateCheckError, NULL, NULL); + } +} + +static void update_check_process_io_error(void *ctx, const belle_sip_io_error_event_t *event) { + LinphoneCore *lc = (LinphoneCore *)ctx; + update_check_process_terminated(lc, LinphoneVersionUpdateCheckError, NULL, NULL); +} + +static void update_check_process_timeout(void *ctx, const belle_sip_timeout_event_t *event) { + LinphoneCore *lc = (LinphoneCore *)ctx; + update_check_process_terminated(lc, LinphoneVersionUpdateCheckError, NULL, NULL); +} + +static void update_check_process_auth_requested(void *ctx, belle_sip_auth_event_t *event) { + LinphoneCore *lc = (LinphoneCore *)ctx; + update_check_process_terminated(lc, LinphoneVersionUpdateCheckError, NULL, NULL); +} +#endif /* ENABLE_UPDATE_CHECK */ + +void linphone_core_check_for_update(LinphoneCore *lc, const char *current_version) { +#ifdef ENABLE_UPDATE_CHECK + int err; + bool_t is_desktop = FALSE; + const char *platform = NULL; + const char *version_check_url_root = lp_config_get_string(lc->config, "misc", "version_check_url_root", NULL); + + if (version_check_url_root != NULL) { + belle_http_request_listener_callbacks_t belle_request_listener = { 0 }; + belle_http_request_t *request; + belle_generic_uri_t *uri; + char *version_check_url; + MSList *item; + MSList *platform_tags = ms_factory_get_platform_tags(linphone_core_get_ms_factory(lc)); + + for (item = platform_tags; item != NULL; item = ms_list_next(item)) { + const char *tag = (const char *)item->data; + if (strcmp(tag, "win32") == 0) platform = "windows"; + else if (strcmp(tag, "apple") == 0) platform = "macosx"; + else if (strcmp(tag, "linux") == 0) platform = "linux"; + else if (strcmp(tag, "desktop") == 0) is_desktop = TRUE; + } + if ((is_desktop == FALSE) || (platform == NULL)) { + ms_warning("Update checking is not supported on this platform"); + return; + } + version_check_url = bctbx_strdup_printf("%s/%s/RELEASE", version_check_url_root, platform); + uri = belle_generic_uri_parse(version_check_url); + ms_message("Checking for new version at: %s", version_check_url); + bctbx_free(version_check_url); + + belle_request_listener.process_response = update_check_process_response_event; + belle_request_listener.process_auth_requested = update_check_process_auth_requested; + belle_request_listener.process_io_error = update_check_process_io_error; + belle_request_listener.process_timeout = update_check_process_timeout; + + lc->update_check_current_version = bctbx_strdup(current_version); + lc->update_check_http_listener = belle_http_request_listener_create_from_callbacks(&belle_request_listener, lc); + request = belle_http_request_create("GET", uri, belle_sip_header_create("User-Agent", linphone_core_get_user_agent(lc)), NULL); + err = belle_http_provider_send_request(lc->http_provider, request, lc->update_check_http_listener); + } +#endif +} diff --git a/coreapi/private.h b/coreapi/private.h index 4d0c38caf8ff60e91b3997aed71b9a0a0d238e9f..6a29177e3fddd22edee086fb65cd9cd30094f106 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -1110,6 +1110,10 @@ struct _LinphoneCore belle_http_provider_t *http_provider; belle_tls_crypto_config_t *http_crypto_config; belle_http_request_listener_t *provisioning_http_listener; +#ifdef ENABLE_UPDATE_CHECK + belle_http_request_listener_t *update_check_http_listener; + char *update_check_current_version; +#endif MSList *tones; LinphoneReason chat_deny_code; char *file_transfer_server; @@ -1922,6 +1926,7 @@ void linphone_core_notify_log_collection_upload_progress_indication(LinphoneCore void linphone_core_notify_friend_list_created(LinphoneCore *lc, LinphoneFriendList *list); void linphone_core_notify_friend_list_removed(LinphoneCore *lc, LinphoneFriendList *list); void linphone_core_notify_call_created(LinphoneCore *lc, LinphoneCall *call); +void linphone_core_notify_version_update_check_result_received(LinphoneCore *lc, LinphoneVersionUpdateCheckResult result, const char *version, const char *url); void set_mic_gain_db(AudioStream *st, float gain); void set_playback_gain_db(AudioStream *st, float gain); diff --git a/coreapi/vtables.c b/coreapi/vtables.c index d48342b36d7944b462eb0f0e6f639e0f7d1291fb..ca9960ef9092aa3759add56b61920abf9123bb90 100644 --- a/coreapi/vtables.c +++ b/coreapi/vtables.c @@ -316,6 +316,11 @@ void linphone_core_notify_call_created(LinphoneCore *lc, LinphoneCall *call) { cleanup_dead_vtable_refs(lc); } +void linphone_core_notify_version_update_check_result_received(LinphoneCore *lc, LinphoneVersionUpdateCheckResult result, const char *version, const char *url) { + NOTIFY_IF_EXIST(version_update_check_result_received, lc, result, version, url); + cleanup_dead_vtable_refs(lc); +} + static VTableReference * v_table_reference_new(LinphoneCoreCbs *cbs, bool_t internal){ VTableReference *ref=ms_new0(VTableReference,1); ref->valid=TRUE; diff --git a/include/linphone/callbacks.h b/include/linphone/callbacks.h index b9dd4b7a10ef2031df635ffd48ff2ae85fbef2d4..067d6ca11c0224ab10b4f72abed714560a892b2c 100644 --- a/include/linphone/callbacks.h +++ b/include/linphone/callbacks.h @@ -486,6 +486,14 @@ typedef void (*LinphoneCoreCbsFriendListRemovedCb) (LinphoneCore *lc, LinphoneFr */ typedef LinphoneCoreCbsFriendListRemovedCb LinphoneCoreFriendListRemovedCb; +/** + * Callback prototype for reporting the result of a version update check. + * @param[in] lc LinphoneCore object + * @param[in] result The result of the version update check + * @param[in] url The url where to download the new version if the result is LinphoneVersionUpdateCheckNewVersionAvailable + */ +typedef void (*LinphoneCoreCbsVersionUpdateCheckResultReceivedCb) (LinphoneCore *lc, LinphoneVersionUpdateCheckResult result, const char *version, const char *url); + /** * @} **/ diff --git a/include/linphone/core.h b/include/linphone/core.h index 434993a1d6ea1c0131ee9f2fa54cfcfa01104707..eec6369ccbf7bb65d22d5f213b467f53e06edf2e 100644 --- a/include/linphone/core.h +++ b/include/linphone/core.h @@ -113,6 +113,13 @@ LINPHONE_PUBLIC LinphonePlayer *linphone_core_create_local_player(LinphoneCore * **/ LINPHONE_PUBLIC LinphoneInfoMessage *linphone_core_create_info_message(LinphoneCore*lc); +/** + * Checks if a new version of the application is available. + * @param lc LinphoneCore object + * @param current_version The current version of the application + */ +LINPHONE_PUBLIC void linphone_core_check_for_update(LinphoneCore *lc, const char *current_version); + /** * @} */ @@ -208,6 +215,7 @@ typedef struct _LinphoneCoreVTable{ LinphoneCoreFriendListCreatedCb friend_list_created; LinphoneCoreFriendListRemovedCb friend_list_removed; LinphoneCoreCbsCallCreatedCb call_created; + LinphoneCoreCbsVersionUpdateCheckResultReceivedCb version_update_check_result_received; void *user_data; /**<User data associated with the above callbacks */ } LinphoneCoreVTable; @@ -652,6 +660,20 @@ LINPHONE_PUBLIC void linphone_core_cbs_set_call_created(LinphoneCoreCbs *cbs, Li */ LINPHONE_PUBLIC LinphoneCoreCbsCallCreatedCb linphone_core_cbs_get_call_created(LinphoneCoreCbs *cbs); +/** + * Set the version update check result callback. + * @param[in] cbs LinphoneCoreCbs object + * @param[in] cb The callback to use + */ +LINPHONE_PUBLIC void linphone_core_cbs_set_version_update_check_result_received(LinphoneCoreCbs *cbs, LinphoneCoreCbsVersionUpdateCheckResultReceivedCb cb); + +/** + * Get the version update check result callback. + * @param[in] cbs LinphoneCoreCbs object + * @return The current callback + */ +LINPHONE_PUBLIC LinphoneCoreCbsVersionUpdateCheckResultReceivedCb linphone_core_cbs_get_version_update_check_result_received(LinphoneCoreCbs *cbs); + /** * @} **/ diff --git a/include/linphone/types.h b/include/linphone/types.h index dbfcf8ef9bda62be5bd854f0ff39d018aaeaf850..1a99dc630f5f0fb5515eccdf2ff7970fa5c08d54 100644 --- a/include/linphone/types.h +++ b/include/linphone/types.h @@ -1148,6 +1148,16 @@ typedef enum _LinphoneUpnpState { */ typedef struct _LinphoneVcard LinphoneVcard; +/** + * Enum describing the result of a version update check. + * @ingroup misc + */ +typedef enum _LinphoneVersionUpdateCheckResult { + LinphoneVersionUpdateCheckUpToDate, + LinphoneVersionUpdateCheckNewVersionAvailable, + LinphoneVersionUpdateCheckError +} LinphoneVersionUpdateCheckResult; + /** * The LinphoneVideoDefinition object represents a video definition, eg. its width and its height. * @ingroup media_parameters