From 313b60c0047dd04ad9698b848347301c5b86f9ad Mon Sep 17 00:00:00 2001 From: Johan Pascal Date: Sun, 16 Apr 2017 18:11:09 +0700 Subject: [PATCH] zid cache migration moved to bzrtp lib - cache migration is now performed in place not to another file - tester of the migration needs to be fixed --- coreapi/linphonecore.c | 278 ++++++----------------------------- coreapi/private.h | 2 - gtk/main.c | 9 +- include/linphone/core.h | 21 +-- tester/message_tester.c | 38 ++++- tester/zidCacheMigration.xml | 2 + 6 files changed, 88 insertions(+), 262 deletions(-) create mode 100644 tester/zidCacheMigration.xml diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 45a996832..65c6250b9 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -28,7 +28,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #ifdef SQLITE_STORAGE_ENABLED #include "sqlite3_bctbx_vfs.h" -/* we need bzrtp.h to setup the zrtp cache only when SQLITE is enabled */ #include "bzrtp/bzrtp.h" #endif @@ -107,6 +106,7 @@ static void set_media_network_reachable(LinphoneCore* lc,bool_t isReachable); static void linphone_core_run_hooks(LinphoneCore *lc); static void linphone_core_uninit(LinphoneCore *lc); static void linphone_core_zrtp_cache_close(LinphoneCore *lc); +static void linphone_core_zrtp_cache_db_init(LinphoneCore *lc, const char *fileName); #include "enum.h" #include "contact_providers_priv.h" @@ -5787,14 +5787,6 @@ static void linphone_core_uninit(LinphoneCore *lc) bctbx_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_unref); lc->call_logs=bctbx_list_free(lc->call_logs); - if(lc->zrtp_secrets_cache != NULL) { - ms_free(lc->zrtp_secrets_cache); - } - - if(lc->zrtp_cache_db_file != NULL) { - ms_free(lc->zrtp_cache_db_file); - } - if(lc->user_certificates_path != NULL) { ms_free(lc->user_certificates_path); } @@ -6215,14 +6207,47 @@ void linphone_core_remove_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook } void linphone_core_set_zrtp_secrets_file(LinphoneCore *lc, const char* file){ - if (lc->zrtp_secrets_cache != NULL) { - ms_free(lc->zrtp_secrets_cache); - } - lc->zrtp_secrets_cache=file ? ms_strdup(file) : NULL; -} + /* shall we perform cache migration ? */ + if (!lp_config_get_int(lc->config,"sip","zrtp_cache_migration_done",FALSE)) { + char *tmpFile = bctbx_malloc(strlen(file)+6); + /* check we have a valid xml cache file given in path */ + FILE *CACHEFD = NULL; + /* load the xml cache */ + if (file != NULL) { + CACHEFD = fopen(file, "rb+"); + xmlDocPtr cacheXml = NULL; + if (CACHEFD) { + size_t cacheSize; + char *cacheString = ms_load_file_content(CACHEFD, &cacheSize); + if (!cacheString) { + ms_warning("Unable to load content of ZRTP ZID cache"); + bctbx_free(tmpFile); + return; + } + cacheString[cacheSize] = '\0'; + cacheSize += 1; + fclose(CACHEFD); + cacheXml = xmlParseDoc((xmlChar*)cacheString); + ms_free(cacheString); + } -const char *linphone_core_get_zrtp_secrets_file(LinphoneCore *lc){ - return lc->zrtp_secrets_cache; + /* create a temporary file for the sqlite base and initialise it */ + sprintf(tmpFile,"%s.tmp", file); + linphone_core_zrtp_cache_db_init(lc, tmpFile); + + /* migrate */ + if (bzrtp_cache_migration((void *)cacheXml, linphone_core_get_zrtp_cache_db(lc), linphone_core_get_identity(lc)) ==0) { + char *bkpFile = bctbx_malloc(strlen(file)+6); + sprintf(bkpFile,"%s.bkp", file); + /* migration went ok, rename the original file and replace it with by the tmp one and set the migration tag in config file */ + if (rename(file, bkpFile)==0 && rename(tmpFile, file)==0) { + lp_config_set_int(lc->config, "sip", "zrtp_cache_migration_done", TRUE); + } + } + } + } else { + linphone_core_zrtp_cache_db_init(lc, file); + } } void *linphone_core_get_zrtp_cache_db(LinphoneCore *lc){ @@ -6243,213 +6268,18 @@ static void linphone_core_zrtp_cache_close(LinphoneCore *lc) { } #ifdef SQLITE_STORAGE_ENABLED -/* this function is called only when the sqlite cache is newly created but still empty(contains tables structures but no data) - * and we have an xml cache - * SQL zid cache associate each local sip:uri its own ZID while XML version used one ZID for the whole device-> use the XML provided ZID - * for the default local sipUri retrieved using linphone_core_get_identity - */ -static void linphone_core_zrtp_cache_migration(LinphoneCore *lc) { - FILE *CACHEFD = NULL; - /* load the xml cache */ - if (lc->zrtp_secrets_cache != NULL) { - CACHEFD = fopen(lc->zrtp_secrets_cache, "rb+"); - if (CACHEFD) { - size_t cacheSize; - xmlDocPtr cacheXml; - char *cacheString = ms_load_file_content(CACHEFD, &cacheSize); - if (!cacheString) { - ms_warning("Unable to load content of ZRTP ZID cache"); - return; - } - cacheString[cacheSize] = '\0'; - cacheSize += 1; - fclose(CACHEFD); - cacheXml = xmlParseDoc((xmlChar*)cacheString); - ms_free(cacheString); - if (cacheXml) { - xmlNodePtr cur; - xmlChar *selfZidHex=NULL; - uint8_t selfZID[12]; - sqlite3 *db = (sqlite3 *)linphone_core_get_zrtp_cache_db(lc); - sqlite3_stmt *sqlStmt = NULL; - const char *selfURI = linphone_core_get_identity(lc); - int ret; - /* parse the cache to get the selfZID and insert it in sqlcache */ - cur = xmlDocGetRootElement(cacheXml); - /* if we found a root element, parse its children node */ - if (cur!=NULL) - { - cur = cur->xmlChildrenNode; - } - selfZidHex = NULL; - while (cur!=NULL) { - if ((!xmlStrcmp(cur->name, (const xmlChar *)"selfZID"))){ /* self ZID found, extract it */ - selfZidHex = xmlNodeListGetString(cacheXml, cur->xmlChildrenNode, 1); - bctbx_str_to_uint8(selfZID, selfZidHex, 24); - break; - } - cur = cur->next; - } - /* did we found a self ZID? */ - if (selfZidHex == NULL) { - ms_warning("ZRTP/LIME cache migration: Failed to parse selfZID"); - return; - } - - /* insert the selfZID in cache, associate it to default local sip:uri in case we have more than one */ - ms_message("ZRTP/LIME cache migration: found selfZID %.24s link it to default URI %s in SQL cache", selfZidHex, selfURI); - xmlFree(selfZidHex); - - ret = sqlite3_prepare_v2(db, "INSERT INTO ziduri (zid,selfuri,peeruri) VALUES(?,?,?);", -1, &sqlStmt, NULL); - if (ret != SQLITE_OK) { - ms_warning("ZRTP/LIME cache migration: Failed to insert selfZID"); - return; - } - sqlite3_bind_blob(sqlStmt, 1, selfZID, 12, SQLITE_TRANSIENT); - sqlite3_bind_text(sqlStmt, 2, selfURI,-1,SQLITE_TRANSIENT); - sqlite3_bind_text(sqlStmt, 3, "self",-1,SQLITE_TRANSIENT); - - ret = sqlite3_step(sqlStmt); - if (ret!=SQLITE_DONE) { - ms_warning("ZRTP/LIME cache migration: Failed to insert selfZID"); - return; - } - sqlite3_finalize(sqlStmt); - - /* loop over all the peer node in the xml cache and get from them : uri(can be more than one), ZID, rs1, rs2, pvs, sndKey, rcvKey, sndSId, rcvSId, sndIndex, rcvIndex, valid */ - /* some of these may be missing(pvs, valid, rs2) but we'll consider them NULL */ - /* aux and pbx secrets were not used, so don't even bother looking for them */ - cur = xmlDocGetRootElement(cacheXml)->xmlChildrenNode; - - while (cur!=NULL) { /* loop on all peer nodes */ - if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))) { /* found a peer node, check if there is a sipURI node in it (other nodes are just ignored) */ - int i; - xmlNodePtr peerNodeChildren = cur->xmlChildrenNode; - xmlChar *nodeContent = NULL; - xmlChar *peerZIDString = NULL; - uint8_t peerZID[12]; - uint8_t peerZIDFound=0; - xmlChar *peerUri[128]; /* array to contain all the peer uris found in one node */ - /* hopefully they won't be more than 128(it would mean some peer has more than 128 accounts and we called all of them...) */ - int peerUriIndex=0; /* index of previous array */ - char *zrtpColNames[] = {"rs1", "rs2", "pvs"}; - uint8_t *zrtpColValues[] = {NULL, NULL, NULL}; - size_t zrtpColExpectedLengths[] = {32,32,1}; - size_t zrtpColLengths[] = {0,0,0}; - - char *limeColNames[] = {"sndKey", "rcvKey", "sndSId", "rcvSId", "sndIndex", "rcvIndex", "valid"}; - uint8_t *limeColValues[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL}; - size_t limeColExpectedLengths[] = {32,32,32,32,4,4,8}; - size_t limeColLengths[] = {0,0,0,0,0,0,0}; - - /* check all the children nodes to retrieve all information we may get */ - while (peerNodeChildren!=NULL && peerUriIndex<128) { - if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"uri")) { /* found a peer an URI node, get the content */ - peerUri[peerUriIndex] = xmlNodeListGetString(cacheXml, peerNodeChildren->xmlChildrenNode, 1); - peerUriIndex++; - } - - if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"ZID")) { - peerZIDString = xmlNodeListGetString(cacheXml, peerNodeChildren->xmlChildrenNode, 1); - bctbx_str_to_uint8(peerZID, peerZIDString, 24); - - peerZIDFound=1; - } - - for (i=0; i<3; i++) { - if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)zrtpColNames[i])) { - nodeContent = xmlNodeListGetString(cacheXml, peerNodeChildren->xmlChildrenNode, 1); - zrtpColValues[i] = (uint8_t *)bctbx_malloc(zrtpColExpectedLengths[i]); - bctbx_str_to_uint8(zrtpColValues[i], nodeContent, 2*zrtpColExpectedLengths[i]); - zrtpColLengths[i]=zrtpColExpectedLengths[i]; - } - } - - for (i=0; i<7; i++) { - if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)limeColNames[i])) { - nodeContent = xmlNodeListGetString(cacheXml, peerNodeChildren->xmlChildrenNode, 1); - limeColValues[i] = (uint8_t *)bctbx_malloc(limeColExpectedLengths[i]); - bctbx_str_to_uint8(limeColValues[i], nodeContent, 2*limeColExpectedLengths[i]); - limeColLengths[i]=limeColExpectedLengths[i]; - } - } - - peerNodeChildren = peerNodeChildren->next; - xmlFree(nodeContent); - nodeContent=NULL; - } - - if (peerUriIndex>0 && peerZIDFound==1) { /* we found at least an uri in this peer node, extract the keys all other informations */ - /* retrieve all the informations */ - - /* loop over all the uri founds */ - for (i=0; inext; - } - } - } - } -} - - -static void linphone_core_zrtp_cache_db_init(LinphoneCore *lc) { +static void linphone_core_zrtp_cache_db_init(LinphoneCore *lc, const char *fileName) { int ret; const char *errmsg; sqlite3 *db; linphone_core_zrtp_cache_close(lc); - ret = _linphone_sqlite3_open(lc->zrtp_cache_db_file, &db); + ret = _linphone_sqlite3_open(fileName, &db); if (ret != SQLITE_OK) { errmsg = sqlite3_errmsg(db); - ms_error("Error in the opening zrtp_cache_db_file(%s): %s.\n", lc->zrtp_cache_db_file, errmsg); + ms_error("Error in the opening zrtp_cache_db_file(%s): %s.\n", fileName, errmsg); sqlite3_close(db); lc->zrtp_cache_db=NULL; return; @@ -6460,33 +6290,17 @@ static void linphone_core_zrtp_cache_db_init(LinphoneCore *lc) { if (ret == BZRTP_CACHE_SETUP || ret == BZRTP_CACHE_UPDATE) { /* After updating schema, database need to be closed/reopenned */ sqlite3_close(db); - _linphone_sqlite3_open(lc->zrtp_cache_db_file, &db); + _linphone_sqlite3_open(fileName, &db); } lc->zrtp_cache_db = db; - - if (ret == BZRTP_CACHE_SETUP && lc->zrtp_secrets_cache != NULL) { - /* we just created the db and we have an old XML version of the cache : migrate */ - linphone_core_zrtp_cache_migration(lc); - } } #else /* SQLITE_STORAGE_ENABLED */ -static void linphone_core_zrtp_cache_db_init(LinphoneCore *lc) { +static void linphone_core_zrtp_cache_db_init(LinphoneCore *lc, const char *fileName) { ms_warning("Tried to open %s as zrtp_cache_db_file, but SQLITE_STORAGE is not enabled", lc->zrtp_cache_db_file); } #endif /* SQLITE_STORAGE_ENABLED */ -void linphone_core_set_zrtp_cache_database_path(LinphoneCore *lc, const char *path) { - if (lc->zrtp_cache_db_file){ - ms_free(lc->zrtp_cache_db_file); - lc->zrtp_cache_db_file = NULL; - } - if (path) { - lc->zrtp_cache_db_file = ms_strdup(path); - linphone_core_zrtp_cache_db_init(lc); - } -} - void linphone_core_set_user_certificates_path(LinphoneCore *lc, const char* path){ char* new_value; new_value = path?ms_strdup(path):NULL; diff --git a/coreapi/private.h b/coreapi/private.h index 8688e5cf0..ba9ec7cd8 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -1056,7 +1056,6 @@ struct _LinphoneCore struct _EchoTester *ect; LinphoneTaskList hooks; /*tasks periodically executed in linphone_core_iterate()*/ LinphoneConference *conf_ctx; - char* zrtp_secrets_cache; /**< zrtp xml filename cache : obsolete, use zrtp_cache_db now, kept to allow cache migration */ char* user_certificates_path; LinphoneVideoPolicy video_policy; time_t network_last_check; @@ -1091,7 +1090,6 @@ struct _LinphoneCore char *chat_db_file; char *logs_db_file; char *friends_db_file; - char *zrtp_cache_db_file; #ifdef SQLITE_STORAGE_ENABLED sqlite3 *zrtp_cache_db; /**< zrtp sqlite cache, used by both zrtp and lime */ sqlite3 *db; diff --git a/gtk/main.c b/gtk/main.c index 394657281..da0e86220 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -253,8 +253,7 @@ gboolean linphone_gtk_get_audio_assistant_option(void){ static void linphone_gtk_init_liblinphone(const char *config_file, const char *factory_config_file, const char *chat_messages_db_file, - const char *call_logs_db_file, const char *friends_db_file, - const char *zrtp_cache_db_file) { + const char *call_logs_db_file, const char *friends_db_file) { LinphoneCoreVTable vtable={0}; gchar *secrets_file=linphone_gtk_get_config_file(SECRETS_FILE); gchar *user_certificates_dir=linphone_gtk_get_config_file(CERTIFICATES_PATH); @@ -291,7 +290,6 @@ static void linphone_gtk_init_liblinphone(const char *config_file, linphone_core_set_user_agent(the_core,"Linphone", LINPHONE_VERSION); linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL); linphone_core_set_zrtp_secrets_file(the_core,secrets_file); /* XML cache is superseeded by the sqlite one, keep it for migration purpose but it shall be removed in future version */ - if (zrtp_cache_db_file) linphone_core_set_zrtp_cache_database_path(the_core, zrtp_cache_db_file); g_free(secrets_file); linphone_core_set_user_certificates_path(the_core,user_certificates_dir); g_free(user_certificates_dir); @@ -2191,7 +2189,7 @@ int main(int argc, char *argv[]){ const char *icon_name=LINPHONE_ICON_NAME; const char *app_name="Linphone"; LpConfig *factory_config; - char *chat_messages_db_file, *call_logs_db_file, *friends_db_file, *zrtp_cache_db_file; + char *chat_messages_db_file, *call_logs_db_file, *friends_db_file; GError *error=NULL; const char *tmp; const char *resources_dir; @@ -2339,8 +2337,7 @@ core_start: chat_messages_db_file=linphone_gtk_message_storage_get_db_file(NULL); call_logs_db_file = linphone_gtk_call_logs_storage_get_db_file(NULL); friends_db_file = linphone_gtk_friends_storage_get_db_file(NULL); - zrtp_cache_db_file = linphone_gtk_zrtp_cache_get_db_file(NULL); - linphone_gtk_init_liblinphone(config_file, factory_config_file, chat_messages_db_file, call_logs_db_file, friends_db_file, zrtp_cache_db_file); + linphone_gtk_init_liblinphone(config_file, factory_config_file, chat_messages_db_file, call_logs_db_file, friends_db_file); g_free(chat_messages_db_file); g_free(call_logs_db_file); g_free(friends_db_file); diff --git a/include/linphone/core.h b/include/linphone/core.h index 13c6c828a..21a34c234 100644 --- a/include/linphone/core.h +++ b/include/linphone/core.h @@ -3655,18 +3655,8 @@ LINPHONE_PUBLIC void linphone_core_refresh_registers(LinphoneCore* lc); * @param[in] lc #LinphoneCore object * @param[in] file The path to the file to use to store the zrtp secrets cache. * @ingroup initializing - * @deprecated cache is now hold as sqlite db, use linphone_core_set_zrtp_cache_database_path to set path to the db and open it */ -LINPHONE_PUBLIC LINPHONE_DEPRECATED void linphone_core_set_zrtp_secrets_file(LinphoneCore *lc, const char* file); - -/** - * Get the path to the file storing the zrtp secrets cache. - * @param[in] lc #LinphoneCore object. - * @return The path to the file storing the zrtp secrets cache. - * @ingroup initializing - * @deprecated cache is now hold as sqlite db, use linphone_core_get_zrtp_cache_db to get a pointer to it - */ -LINPHONE_PUBLIC LINPHONE_DEPRECATED const char *linphone_core_get_zrtp_secrets_file(LinphoneCore *lc); +LINPHONE_PUBLIC void linphone_core_set_zrtp_secrets_file(LinphoneCore *lc, const char* file); /** * Get a pointer to the sqlite db holding zrtp/lime cache @@ -3676,15 +3666,6 @@ LINPHONE_PUBLIC LINPHONE_DEPRECATED const char *linphone_core_get_zrtp_secrets_f */ LINPHONE_PUBLIC void *linphone_core_get_zrtp_cache_db(LinphoneCore *lc); -/** - * Sets the database filename where zrtp cache will be stored. - * If the file does not exist, it will be created. - * @ingroup initializing - * @param lc the linphone core - * @param path filesystem path -**/ -LINPHONE_PUBLIC void linphone_core_set_zrtp_cache_database_path(LinphoneCore *lc, const char *path); - /** * Set the path to the directory storing the user's x509 certificates (used by dtls) * @param[in] lc #LinphoneCore object diff --git a/tester/message_tester.c b/tester/message_tester.c index 7540b266a..7404567a0 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -883,14 +883,18 @@ static int enable_lime_for_message_test(LinphoneCoreManager *marie, LinphoneCore linphone_core_enable_lime(marie->lc, LinphoneLimeMandatory); linphone_core_enable_lime(pauline->lc, LinphoneLimeMandatory); + /* make sure to not trigger the cache migration function */ + lp_config_set_int(marie->lc->config, "sip", "zrtp_cache_migration_done", TRUE); + lp_config_set_int(pauline->lc->config, "sip", "zrtp_cache_migration_done", TRUE); + /* create temporary cache files: setting the database_path will create and initialise the files */ remove(bc_tester_file("tmpZIDCacheMarie.sqlite")); remove(bc_tester_file("tmpZIDCachePauline.sqlite")); filepath = bc_tester_file("tmpZIDCacheMarie.sqlite"); - linphone_core_set_zrtp_cache_database_path(marie->lc, filepath); + linphone_core_set_zrtp_secrets_file(marie->lc, filepath); bc_free(filepath); filepath = bc_tester_file("tmpZIDCachePauline.sqlite"); - linphone_core_set_zrtp_cache_database_path(pauline->lc, filepath); + linphone_core_set_zrtp_secrets_file(pauline->lc, filepath); bc_free(filepath); /* caches are empty, populate them */ @@ -1397,6 +1401,35 @@ static void lime_transfer_message_without_encryption_2(void) { lime_transfer_message_base(FALSE, FALSE, TRUE, FALSE); } +static void lime_cache_migration(void) { + if (lime_is_available()) { + char *xmlCache_filepath = bc_tester_res("zidCacheMigration.xml"); + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + + if (!linphone_core_lime_available(marie->lc)) { + ms_warning("Lime not available, skiping"); + return; + } + + /* make sure lime is enabled */ + linphone_core_enable_lime(marie->lc, LinphoneLimeMandatory); + + /* make sure to trigger the cache migration function */ + lp_config_set_int(marie->lc->config, "sip", "zrtp_cache_migration_done", FALSE); + + /* set the cache path, it will trigger the migration function */ + /* TODO: copy the resource folder xml file to a writable temporary file as it will be erased by sqlite version */ + linphone_core_set_zrtp_secrets_file(marie->lc, xmlCache_filepath); + + /* perform checks on the new cache */ + /* TODO */ + + /* free memory */ + linphone_core_manager_destroy(marie); + } + +} + static void lime_unit(void) { if (lime_is_available()) { LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); @@ -2367,6 +2400,7 @@ test_t message_tests[] = { TEST_ONE_TAG("Lime transfer message from history", lime_transfer_message_from_history, "LIME"), TEST_ONE_TAG("Lime transfer message without encryption", lime_transfer_message_without_encryption, "LIME"), TEST_ONE_TAG("Lime transfer message without encryption 2", lime_transfer_message_without_encryption_2, "LIME"), + TEST_ONE_TAG("Lime cache migration", lime_cache_migration, "LIME"), TEST_ONE_TAG("Lime unitary", lime_unit, "LIME"), TEST_NO_TAG("Database migration", database_migration), TEST_NO_TAG("History range", history_range), diff --git a/tester/zidCacheMigration.xml b/tester/zidCacheMigration.xml new file mode 100644 index 000000000..2414b6f18 --- /dev/null +++ b/tester/zidCacheMigration.xml @@ -0,0 +1,2 @@ + +00112233445566778899aabb99887766554433221100ffeec4274f13a2b6fa05c15ec93158f930e7264b0a893393376dbc80c6eb1cccdc5asip:bob@sip.linphone.org219d9e445d10d4ed64083c7ccbb83a23bc17a97df0af5de4261f3fe026b05b0b747e72a5cc996413cb9fa6e3d18d8b370436e274cd6ba4efc1a4580340af57cadf2bf38e719fa89e17332cf8d5e774ee70d347baa74d16dee01f306c54789869928ce78b0bfc30427a02b1b668b2b3b0496d5664d7e89b75ed292ee97e3fc850496bcc8959337abe5dda11f388384b349d210612f30824268a3753a7afa52ef6df5866dca76315c4sip:bob2@sip.linphone.orgffeeddccbbaa987654321012858b495dfad483af3c088f26d68c4beebc638bd44feae45aea726a771727235esip:bob@sip.linphone.orgb6aac945057bc4466bfe9a23771c6a1b3b8d72ec3e7d8f30ed63cbc5a9479a25bea5ac3225edd0545b816f061a8190370e3ee5160e75404846a34d1580e0c26317ce70fdf12e500294bcb5f2ffef53096761bb1c912b21e972ae03a5a9f05c477e13a20e15a517700f0be0921f74b96d4b4a0c539d5e14d5cdd8706441874ac075e18caa2cfbbf061533dee20c8116dc2c282cae9adfea689b87bc4c6a4e18a846f12e3e7fea39590987654321fedcba5a5a5a5acb6ecc87d1dd87b23f225eec53a26fc541384917623e0c46abab8c0350c6929e92bb03988e8f0ccfefa37a55fd7c5893bea3bfbb27312f49dd9b10d0e3c15fc72315705a5830b98f68458fcd49623144cb34a667512c4d44686aee125bb8b62294c56eea0dd829379263b6da3f6ac0a95388090f168a3568736ca0bd9f8d595fc319ae0d41183fec90afc412d42253c5b456580f7a463c111c7293623b8631f4sip:bob@sip.linphone.org2c46ddcc15f5779e0000000058f095bf01