/******************************************************************************* * Copyright (c) 2006-2007 Skype Technologies S.A. * Copyright (c) 2011-2012 Gabriel Takeuchi * * Skype Java Api is licensed under either the Apache License, Version 2.0 or * the Eclipse Public License v1.0. * You may use it freely in commercial and non-commercial products. * You may obtain a copy of the licenses at * * the Apache License - http://www.apache.org/licenses/LICENSE-2.0 * the Eclipse Public License - http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Gabriel Takeuchi - DBUS native implementation ******************************************************************************/ #include #include #include #include #include #include #include #include #include #include "skype-service.h" #include "skype-service-object-glue.h" #include "com_skype_connector_linux_SkypeFramework.h" #define SKYPE_FRAMEWORK_CLASS "com/skype/connector/linux/LinuxSkypeFramework" #define SKYPE_STRING_MAX 0x10000 static JNIEnv *currentEnv; static DBusGConnection *dbus_conn = NULL; static DBusGProxy *proxy_send = NULL; static SkypeService *service_object = NULL; static void throwInternalError(JNIEnv *env, char *message); gboolean startsWith(const char * subject, const char * prefix); gboolean endsWith(const char * subject, const char * trail); typedef enum { IssuerNone, IssuerCallback, IssuerCommandReply }IssuerType; DBusGConnection *getSkypeDBusConnection() { GError *error = NULL; if (!dbus_conn) { dbus_conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (!dbus_conn) { if (error) g_error_free(error); return NULL; } } return dbus_conn; } void setupSkypeFrameWork(JNIEnv *env) { if (dbus_conn != NULL) return; g_type_init(); dbus_g_thread_init(); currentEnv = env; dbus_conn = getSkypeDBusConnection(); proxy_send = dbus_g_proxy_new_for_name(dbus_conn, "com.Skype.API", "/com/Skype", "com.Skype.API"); if (!proxy_send) { throwInternalError(env, "Failed to connect to skype"); } service_object = skype_service_new(); dbus_g_object_type_install_info(G_TYPE_FROM_INSTANCE(service_object), &dbus_glib_server_object_object_info); dbus_g_connection_register_g_object(dbus_conn, SKYPE_SERVICE_PATH/*/com/Skype/Client*/, (GObject*)service_object); } JNIEXPORT void JNICALL Java_com_skype_connector_linux_LinuxSkypeFramework_setup0(JNIEnv *env, jclass this) { setupSkypeFrameWork(env); } gboolean stringIsNullOnlyOnOutOfMemoryError(JNIEnv *env, void *value); gboolean isDuplicateChatSentNotification(JNIEnv* env, gchar * skypeNotification, IssuerType type); static void fireNotificationReceived(JNIEnv *env, gchar *skypeNotification, IssuerType type) { if (isDuplicateChatSentNotification(env, skypeNotification, type)) return; jstring notificationString = (*env)->NewStringUTF(env, skypeNotification); if (stringIsNullOnlyOnOutOfMemoryError(env, (void *)notificationString)) { return; } jclass clazz = (*env)->FindClass(env, SKYPE_FRAMEWORK_CLASS); jmethodID method = (*env)->GetStaticMethodID(env, clazz, "fireNotificationReceived", "(Ljava/lang/String;)V"); (*env)->CallStaticVoidMethod(env, clazz, method, notificationString); } gboolean stringIsNullOnlyOnOutOfMemoryError(JNIEnv *env, void *value) { if (value == NULL) { jclass clazz = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); (*env)->ThrowNew(env, clazz, NULL); return TRUE; } else { return FALSE; } } gboolean isDuplicateChatSentNotification(JNIEnv* env, gchar * skypeNotification, IssuerType type) { static char lastReceivedMessage[SKYPE_STRING_MAX]=""; if (type != IssuerCallback) return FALSE; if (strcmp(skypeNotification, lastReceivedMessage) == 0) { if (startsWith(skypeNotification, "CHATMESSAGE") && endsWith(skypeNotification,"STATUS SENT")) { return TRUE; } } strcpy(lastReceivedMessage, skypeNotification); return FALSE; } JNIEnv *eventLoopEnv = NULL; gboolean skype_service_notify_callback(SkypeService *object, gchar *message, GError **error) { fireNotificationReceived(eventLoopEnv, message, IssuerCallback); return TRUE; } GMainLoop *loop=NULL; JNIEXPORT void JNICALL Java_com_skype_connector_linux_LinuxSkypeFramework_runEventLoop0(JNIEnv *env, jclass this) { eventLoopEnv = env; loop = g_main_loop_new ( NULL , FALSE ); g_main_loop_run (loop); g_main_loop_unref(loop); loop=NULL; } JNIEXPORT void JNICALL Java_com_skype_connector_linux_LinuxSkypeFramework_stopEventLoop0(JNIEnv *env, jclass this) { if(loop!=NULL) g_main_loop_quit (loop); if (service_object) g_object_unref(service_object); service_object = NULL; } static gboolean isRunning() { return proxy_send != NULL; } JNIEXPORT jboolean JNICALL Java_com_skype_connector_linux_LinuxSkypeFramework_isRunning0(JNIEnv *env, jclass this) { return isRunning()? JNI_TRUE: JNI_FALSE; } static void sendCommand(JNIEnv *env, const char *commandChars); JNIEXPORT void JNICALL Java_com_skype_connector_linux_LinuxSkypeFramework_sendCommand0(JNIEnv *env, jclass this, jstring command) { jboolean isCopy; const char *commandChars = (*env)->GetStringUTFChars(env, command, &isCopy); sendCommand(env, commandChars); if (isCopy == JNI_TRUE) { (*env)->ReleaseStringUTFChars(env, command, commandChars); } } JNIEXPORT void JNICALL Java_com_skype_connector_linux_LinuxSkypeFramework_closeDisplay0(JNIEnv *env, jclass this) { // NOT NEEDED FOR DBUS IMPLEMENTATION } static void sendCommand(JNIEnv *env, const char *command) { if (!isRunning()) { return; } gchar *str = NULL; GError *error = NULL; gboolean success = dbus_g_proxy_call(proxy_send, "Invoke", &error, G_TYPE_STRING, command, G_TYPE_INVALID, G_TYPE_STRING, &str, G_TYPE_INVALID); if (success) { if (str != NULL) { fireNotificationReceived(env, str, IssuerCommandReply); g_free(str); } return; } if (error) { g_error_free(error); } } static void throwInternalError(JNIEnv *env, char *message) { if (env == NULL) { fprintf(stderr, "%s\n", message); fflush(stderr); return; } jclass clazz = (*env)->FindClass(env, "java/lang/InternalError"); (*env)->ThrowNew(env, clazz, message); } gboolean startsWith(const char * subject, const char * prefix) { return strncmp(subject, prefix, strlen(prefix)) == 0; } gboolean endsWith(const char * subject, const char * trail) { int startMatchIndex = strlen(subject) - strlen(trail); const char * from = subject+startMatchIndex; return strcmp(from, trail) == 0; }