diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java index 4a3f813b0..69d8b9869 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialBuddyStatus.java @@ -311,7 +311,7 @@ public class TutorialBuddyStatus implements LinphoneCoreListener { @Override public void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, - LinphoneContent content, String buffer, int size) { + LinphoneContent content, byte[] buffer, int size) { // TODO Auto-generated method stub } @@ -322,6 +322,4 @@ public class TutorialBuddyStatus implements LinphoneCoreListener { // TODO Auto-generated method stub return 0; } - - } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java index 85f389f70..f5a34746c 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialChatRoom.java @@ -229,7 +229,7 @@ public class TutorialChatRoom implements LinphoneCoreListener, LinphoneChatMessa @Override public void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, - LinphoneContent content, String buffer, int size) { + LinphoneContent content, byte[] buffer, int size) { // TODO Auto-generated method stub } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java index 6b5666cb5..5a80f4824 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialHelloWorld.java @@ -231,7 +231,7 @@ public class TutorialHelloWorld implements LinphoneCoreListener { @Override public void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, - LinphoneContent content, String buffer, int size) { + LinphoneContent content, byte[] buffer, int size) { // TODO Auto-generated method stub } diff --git a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java index cb96f4242..2d2135a62 100644 --- a/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java +++ b/coreapi/help/java/org/linphone/core/tutorials/TutorialRegistration.java @@ -262,7 +262,7 @@ public class TutorialRegistration implements LinphoneCoreListener { @Override public void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, - LinphoneContent content, String buffer, int size) { + LinphoneContent content, byte[] buffer, int size) { // TODO Auto-generated method stub } diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index a770b2ebd..8c11df94c 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -316,7 +316,7 @@ public: fileTransferProgressIndicationId = env->GetMethodID(listenerClass, "fileTransferProgressIndication", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;I)V"); fileTransferSendId = env->GetMethodID(listenerClass, "fileTransferSend", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;Ljava/nio/ByteBuffer;I)I"); - fileTransferRecvId = env->GetMethodID(listenerClass, "fileTransferRecv", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;Ljava/lang/String;I)V"); + fileTransferRecvId = env->GetMethodID(listenerClass, "fileTransferRecv", "(Lorg/linphone/core/LinphoneCore;Lorg/linphone/core/LinphoneChatMessage;Lorg/linphone/core/LinphoneContent;[BI)V"); } ~LinphoneCoreData() { @@ -847,12 +847,16 @@ public: return; } LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_get_user_data(lc); + + jbyteArray jbytes = env->NewByteArray(size); + env->SetByteArrayRegion(jbytes, 0, size, (jbyte*)buff); + env->CallVoidMethod(lcData->listener, lcData->fileTransferRecvId, lcData->core, message ? env->NewObject(lcData->chatMessageClass, lcData->chatMessageCtrId, (jlong)message) : NULL, content ? create_java_linphone_content(env, content) : NULL, - buff ? env->NewStringUTF(buff) : NULL, + jbytes, size); } }; @@ -3926,21 +3930,23 @@ static jobject create_java_linphone_content(JNIEnv *env, const LinphoneContent * jmethodID ctor; jstring jtype, jsubtype, jencoding, jname; jbyteArray jdata = NULL; + jint jsize = 0; contentClass = (jclass)env->NewGlobalRef(env->FindClass("org/linphone/core/LinphoneContentImpl")); - ctor = env->GetMethodID(contentClass,"", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BLjava/lang/String;)V"); + ctor = env->GetMethodID(contentClass,"", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BLjava/lang/String;I)V"); jtype = env->NewStringUTF(content->type); jsubtype = env->NewStringUTF(content->subtype); jencoding = content->encoding ? env->NewStringUTF(content->encoding) : NULL; jname = content->name ? env->NewStringUTF(content->name) : NULL; + jsize = (jint) content->size; if (content->data){ jdata = env->NewByteArray(content->size); env->SetByteArrayRegion(jdata, 0, content->size, (jbyte*)content->data); } - jobject jobj = env->NewObject(contentClass, ctor, jname, jtype, jsubtype, jdata, jencoding); + jobject jobj = env->NewObject(contentClass, ctor, jname, jtype, jsubtype, jdata, jencoding, jsize); env->DeleteGlobalRef(contentClass); return jobj; } diff --git a/coreapi/message_storage.c b/coreapi/message_storage.c index 848c91017..2eed4d25c 100644 --- a/coreapi/message_storage.c +++ b/coreapi/message_storage.c @@ -37,6 +37,49 @@ static inline LinphoneChatMessage* get_transient_message(LinphoneChatRoom* cr, u return NULL; } +/* DB layout: + * | 0 | storage_id + * | 1 | type + * | 2 | subtype + * | 3 | name + * | 4 | encoding + * | 5 | size + * | 6 | data + */ +// Callback for sql request when getting linphone content +static int callback_content(void *data, int argc, char **argv, char **colName) { + LinphoneChatMessage *message = (LinphoneChatMessage *)data; + + if (message->file_transfer_information) { + linphone_content_uninit(message->file_transfer_information); + ms_free(message->file_transfer_information); + message->file_transfer_information = NULL; + } + message->file_transfer_information = (LinphoneContent *)malloc(sizeof(LinphoneContent)); + memset(message->file_transfer_information, 0, sizeof(*(message->file_transfer_information))); + + message->file_transfer_information->type = argv[1] ? ms_strdup(argv[1]) : NULL; + message->file_transfer_information->subtype = argv[2] ? ms_strdup(argv[2]) : NULL; + message->file_transfer_information->name = argv[3] ? ms_strdup(argv[3]) : NULL; + message->file_transfer_information->encoding = argv[4] ? ms_strdup(argv[4]) : NULL; + message->file_transfer_information->size = (size_t) atoi(argv[5]); + + return 0; +} + +static void fetch_content_from_database(sqlite3 *db, LinphoneChatMessage *message, int content_id) { + char* errmsg = NULL; + int ret; + char * buf; + + buf = sqlite3_mprintf("SELECT * FROM content WHERE id = %i", content_id); + ret = sqlite3_exec(db, buf, callback_content, message, &errmsg); + if (ret != SQLITE_OK) { + ms_error("Error in creation: %s.", errmsg); + sqlite3_free(errmsg); + } + sqlite3_free(buf); +} /* DB layout: * | 0 | storage_id @@ -50,6 +93,7 @@ static inline LinphoneChatMessage* get_transient_message(LinphoneChatRoom* cr, u * | 8 | external body url * | 9 | utc timestamp * | 10 | app data text + * | 11 | linphone content */ static void create_chat_message(char **argv, void *data){ LinphoneChatRoom *cr = (LinphoneChatRoom *)data; @@ -90,6 +134,13 @@ static void create_chat_message(char **argv, void *data){ new_message->storage_id=storage_id; new_message->external_body_url= argv[8] ? ms_strdup(argv[8]) : NULL; new_message->appdata = argv[10]? ms_strdup(argv[10]) : NULL; + + if (argv[11] != NULL) { + int id = atoi(argv[11]); + if (id >= 0) { + fetch_content_from_database(cr->lc->db, new_message, id); + } + } } cr->messages_hist=ms_list_prepend(cr->messages_hist,new_message); } @@ -139,24 +190,51 @@ void linphone_sql_request_all(sqlite3* db,const char *stmt, LinphoneCore* lc){ } } +static int linphone_chat_message_store_content(LinphoneChatMessage *msg) { + LinphoneCore *lc = linphone_chat_room_get_lc(msg->chat_room); + int id = -1; + if (lc->db) { + LinphoneContent *content = msg->file_transfer_information; + char *buf = sqlite3_mprintf("INSERT INTO content VALUES(NULL,%Q,%Q,%Q,%Q,%i,%Q);", + content->type, + content->subtype, + content->name, + content->encoding, + content->size, + NULL + ); + linphone_sql_request(lc->db, buf); + sqlite3_free(buf); + id = (unsigned int) sqlite3_last_insert_rowid (lc->db); + } + return id; +} + unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){ LinphoneCore *lc=linphone_chat_room_get_lc(msg->chat_room); - int id=0; + int id = 0; if (lc->db){ + int content_id = -1; + if (msg->file_transfer_information) { + content_id = linphone_chat_message_store_content(msg); + } + char *peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(msg->chat_room)); char *local_contact=linphone_address_as_string_uri_only(linphone_chat_message_get_local_address(msg)); - char *buf=sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%i,%Q);", + char *buf=sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%i,%Q,%i);", local_contact, - peer, - msg->dir, - msg->message, - "-1", /* use UTC field now */ - msg->is_read, - msg->state, - msg->external_body_url, - msg->time, - msg->appdata); + peer, + msg->dir, + msg->message, + "-1", /* use UTC field now */ + msg->is_read, + msg->state, + msg->external_body_url, + msg->time, + msg->appdata, + content_id + ); linphone_sql_request(lc->db,buf); sqlite3_free(buf); ms_free(local_contact); @@ -409,7 +487,7 @@ void linphone_update_table(sqlite3* db) { ms_message("Table already up to date: %s.", errmsg); sqlite3_free(errmsg); } else { - ms_debug("Table updated successfully for URL."); + ms_debug("Table history updated successfully for URL."); } // for UTC timestamp storage @@ -418,7 +496,7 @@ void linphone_update_table(sqlite3* db) { ms_message("Table already up to date: %s.", errmsg); sqlite3_free(errmsg); } else { - ms_debug("Table updated successfully for UTC."); + ms_debug("Table history updated successfully for UTC."); // migrate from old text-based timestamps to unix time-based timestamps linphone_migrate_timestamps(db); } @@ -429,7 +507,32 @@ void linphone_update_table(sqlite3* db) { ms_message("Table already up to date: %s.", errmsg); sqlite3_free(errmsg); } else { - ms_debug("Table updated successfully for app-specific data."); + ms_debug("Table history updated successfully for app-specific data."); + } + + // new field for linphone content storage + ret=sqlite3_exec(db,"ALTER TABLE history ADD COLUMN content INTEGER;",NULL,NULL,&errmsg); + if(ret != SQLITE_OK) { + ms_message("Table already up to date: %s.", errmsg); + sqlite3_free(errmsg); + } else { + ms_debug("Table history updated successfully for content data."); + ret = sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS content (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "type TEXT," + "subtype TEXT," + "name TEXT," + "encoding TEXT," + "size INTEGER," + "data BLOB" + ");", + 0,0,&errmsg); + if(ret != SQLITE_OK) { + ms_error("Error in creation: %s.\n", errmsg); + sqlite3_free(errmsg); + } else { + ms_debug("Table content successfully created."); + } } } diff --git a/java/common/org/linphone/core/LinphoneContent.java b/java/common/org/linphone/core/LinphoneContent.java index acfe84956..eb41c62f7 100644 --- a/java/common/org/linphone/core/LinphoneContent.java +++ b/java/common/org/linphone/core/LinphoneContent.java @@ -31,10 +31,21 @@ public interface LinphoneContent { **/ byte [] getData(); /** - * Get the data size. - * @return the data size. + * Get the expected data size. + * @return the expected data size */ - int getSize(); + int getExpectedSize(); + + /** + * Sets the expected data size + */ + void setExpectedSize(int size); + + /** + * Return the size of the data field + * @return the size of the data field + */ + int getRealSize(); /** * Set the content type, for example "application" diff --git a/java/common/org/linphone/core/LinphoneCoreListener.java b/java/common/org/linphone/core/LinphoneCoreListener.java index 15a5369f1..c82390c48 100644 --- a/java/common/org/linphone/core/LinphoneCoreListener.java +++ b/java/common/org/linphone/core/LinphoneCoreListener.java @@ -208,7 +208,7 @@ public interface LinphoneCoreListener { * @param buffer * @param size */ - void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, String buffer, int size); + void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message, LinphoneContent content, byte[] buffer, int size); /** * Callback to be notified when new data needs to be sent diff --git a/java/impl/org/linphone/core/LinphoneChatRoomImpl.java b/java/impl/org/linphone/core/LinphoneChatRoomImpl.java index e6eb7fc39..d47e4e47e 100644 --- a/java/impl/org/linphone/core/LinphoneChatRoomImpl.java +++ b/java/impl/org/linphone/core/LinphoneChatRoomImpl.java @@ -173,7 +173,7 @@ class LinphoneChatRoomImpl implements LinphoneChatRoom { @Override public LinphoneChatMessage createFileTransferMessage(LinphoneContent content) { synchronized(getCore()) { - return new LinphoneChatMessageImpl(createFileTransferMessage(nativePtr, content.getName(), content.getType(), content.getSubtype(), content.getSize())); + return new LinphoneChatMessageImpl(createFileTransferMessage(nativePtr, content.getName(), content.getType(), content.getSubtype(), content.getRealSize())); } } diff --git a/java/impl/org/linphone/core/LinphoneContentImpl.java b/java/impl/org/linphone/core/LinphoneContentImpl.java index 2c4d8d092..0231fd539 100644 --- a/java/impl/org/linphone/core/LinphoneContentImpl.java +++ b/java/impl/org/linphone/core/LinphoneContentImpl.java @@ -3,6 +3,7 @@ package org.linphone.core; public class LinphoneContentImpl implements LinphoneContent { private String mType, mSubtype, mEncoding, mName; private byte[] mData; + private int mExpectedSize; public LinphoneContentImpl(String type, String subtype, byte data[], String encoding){ mType = type; @@ -10,14 +11,16 @@ public class LinphoneContentImpl implements LinphoneContent { mData = data; mEncoding = encoding; mName = null; + mExpectedSize = 0; } - public LinphoneContentImpl(String name, String type, String subtype, byte data[], String encoding){ + public LinphoneContentImpl(String name, String type, String subtype, byte data[], String encoding, int expectedSize){ mType = type; mSubtype = subtype; mData = data; mEncoding = encoding; mName = name; + mExpectedSize = expectedSize; } @Override @@ -36,9 +39,19 @@ public class LinphoneContentImpl implements LinphoneContent { return new String(mData); return null; } + + @Override + public void setExpectedSize(int size) { + mExpectedSize = size; + } @Override - public int getSize() { + public int getExpectedSize() { + return mExpectedSize; + } + + @Override + public int getRealSize() { if (mData != null) return mData.length; return 0; @@ -91,5 +104,4 @@ public class LinphoneContentImpl implements LinphoneContent { public String getName() { return mName; } - }