diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-10-07 23:13:54 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-10-07 23:13:54 (GMT) |
commit | 36b5c7614cc896d043ddeebae1cdb4e8e94afe18 (patch) | |
tree | e14ac52189363b252aa8ea10fdd66efef069d665 /src/uscxml | |
parent | 567df9318fff6d1bb570191c33ea68cd6ef88bee (diff) | |
download | uscxml-36b5c7614cc896d043ddeebae1cdb4e8e94afe18.zip uscxml-36b5c7614cc896d043ddeebae1cdb4e8e94afe18.tar.gz uscxml-36b5c7614cc896d043ddeebae1cdb4e8e94afe18.tar.bz2 |
Reduced compile times
- new commandline argument handling
- compiles on windows again
Diffstat (limited to 'src/uscxml')
-rw-r--r-- | src/uscxml/Interpreter.cpp | 167 | ||||
-rw-r--r-- | src/uscxml/Interpreter.h | 47 | ||||
-rw-r--r-- | src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp | 12 | ||||
-rw-r--r-- | src/uscxml/concurrency/eventqueue/DelayedEventQueue.h | 6 | ||||
-rw-r--r-- | src/uscxml/pch.h | 14 | ||||
-rw-r--r-- | src/uscxml/plugins/datamodel/CMakeLists.txt | 106 | ||||
-rw-r--r-- | src/uscxml/plugins/datamodel/ecmascript/TypedArray.h | 1 | ||||
-rw-r--r-- | src/uscxml/plugins/datamodel/ecmascript/v8/V8DOM.h | 1 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/im/IMInvoker.cpp | 412 | ||||
-rw-r--r-- | src/uscxml/plugins/invoker/im/IMInvoker.h | 46 | ||||
-rw-r--r-- | src/uscxml/server/HTTPServer.cpp | 162 | ||||
-rw-r--r-- | src/uscxml/server/HTTPServer.h | 21 |
12 files changed, 765 insertions, 230 deletions
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 7e61ed7..065b61e 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -1,3 +1,4 @@ +#include "uscxml/config.h" #include "uscxml/Common.h" #include "uscxml/Interpreter.h" #include "uscxml/URL.h" @@ -40,6 +41,145 @@ namespace uscxml { using namespace Arabica::XPath; using namespace Arabica::DOM; +void InterpreterOptions::printUsageAndExit(const char* progName) { + printf("%s version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n", progName); + printf("Usage\n"); + printf("\t%s", progName); +#ifdef BUILD_AS_PLUGINS + printf(" [-e pluginPath]"); +#endif + printf("[-v] [-pN] URL\n"); + printf("\n"); + printf("Options\n"); + printf("\t-v : be verbose\n"); + printf("\t-pN : port for HTTP server\n"); + printf("\t-d : write each configuration as a dot file\n"); + printf("\n"); + exit(1); +} + +unsigned int InterpreterOptions::getCapabilities() { + unsigned int capabilities = CAN_NOTHING; + if (withHTTP) + capabilities = capabilities | CAN_GENERIC_HTTP | CAN_BASIC_HTTP; + + return capabilities; +} + +InterpreterOptions InterpreterOptions::fromCmdLine(int argc, char** argv) { + InterpreterOptions options; + struct option longOptions[] = { + {"verbose", no_argument, 0, 'v'}, + {"dot", no_argument, 0, 'd'}, + {"port", required_argument, 0, 't'}, + {"ssl-port", required_argument, 0, 's'}, + {"certificate", required_argument, 0, 'c'}, + {"private-key", required_argument, 0, 0}, + {"public-key", required_argument, 0, 0}, + {"plugin-path", required_argument, 0, 'p'}, + {"loglevel", required_argument, 0, 'l'}, + {"disable-http", no_argument, 0, 0}, + {0, 0, 0, 0} + }; + + opterr = 0; + if (argc < 2) { + options.error = "No SCXML document to evaluate"; + return options; + } + + InterpreterOptions* currOptions = &options; + + // parse global options + int optionInd = 0; + int option; + for (;;) { + option = getopt_long_only(argc, argv, "+vdt:s:c:p:l:", longOptions, &optionInd); + if (option == -1) { + if (optind == argc) + // we are done with parsing + goto DONE_PARSING_CMD; + + std::string url = argv[optind]; + options.interpreters[url] = new InterpreterOptions(); + currOptions = options.interpreters[url]; + + argc -= optind; + argv += optind; + optind = 0; + + if (argc <= 1) + goto DONE_PARSING_CMD; + + } + switch(option) { + // cases without short option + case 0: { + if (boost::equals(longOptions[optionInd].name, "disable-http")) { + currOptions->withHTTP = false; + } else if (boost::equals(longOptions[optionInd].name, "private-key")) { + currOptions->privateKey = optarg; + } else if (boost::equals(longOptions[optionInd].name, "public-key")) { + currOptions->publicKey = optarg; + } + break; + } + // cases with short-hand options + case 'l': + currOptions->logLevel = strTo<unsigned int>(optarg); + break; + case 'p': + currOptions->pluginPath = optarg; + break; + case 'd': + currOptions->useDot = true; + break; + case 'c': + currOptions->certificate = optarg; + break; + case 't': + currOptions->httpPort = strTo<unsigned short>(optarg); + break; + case 's': + currOptions->httpsPort = strTo<unsigned short>(optarg); + break; + case 'v': + currOptions->verbose = true; + break; + case '?': { + std::string param = argv[optind - 1]; + if (boost::starts_with(param, "--")) { + param = param.substr(2, param.length() - 2); + } else if (boost::starts_with(param, "-")) { + param = param.substr(1, param.length() - 1); + } else { + break; + } + boost::trim(param); + + size_t equalPos = param.find("="); + if (equalPos != std::string::npos) { + std::string key = param.substr(0, equalPos); + std::string value = param.substr(equalPos + 1, param.length() - (equalPos + 1)); + currOptions->additionalParameters[key] = value; + } else { + currOptions->additionalParameters[param] = ""; + } + break; + } + default: + break; + } + } + +DONE_PARSING_CMD: + + if (options.interpreters.size() == 0) + options.error = "No SCXML document to evaluate"; + + return options; +} + std::map<std::string, boost::weak_ptr<InterpreterImpl> > Interpreter::_instances; tthread::recursive_mutex Interpreter::_instanceMutex; @@ -1761,26 +1901,15 @@ IOProcessor InterpreterImpl::getIOProcessor(const std::string& type) { return _ioProcessors[type]; } -void InterpreterImpl::setCmdLineOptions(int argc, char** argv) { - char* key = NULL; - char* value = NULL; - for (int i = 0; i < argc; i++) { - if (false) { - } else if (strlen(argv[i]) > 2 && strncmp(&argv[i][0], "-", 1) == 0 && strncmp(&argv[i][1], "-", 1) == 0) { - // longopt - key = &argv[i][2]; - } else if (strlen(argv[i]) > 1 && strncmp(&argv[i][0], "-", 1) == 0 && strncmp(&argv[i][1], "-", 1) != 0) { - // shortopt - key = &argv[i][1]; - } - if (key != NULL) { - if (i + 1 < argc && strncmp(&argv[i + 1][0], "-", 1) != 0) { - value = argv[++i]; - _cmdLineOptions.compound[key] = Data(value, Data::VERBATIM); - } else { - _cmdLineOptions.compound[key] = Data("true"); - } +void InterpreterImpl::setCmdLineOptions(std::map<std::string, std::string> params) { + std::map<std::string, std::string>::iterator paramIter = params.begin(); + while (paramIter != params.end()) { + if (paramIter->second.length() > 0) { + _cmdLineOptions.compound[paramIter->first] = Data(paramIter->second, Data::VERBATIM); + } else { + _cmdLineOptions.compound[paramIter->first] = Data("true"); } + paramIter++; } } diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index aadef72..b8093ab 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -1,7 +1,10 @@ #ifndef RUNTIME_H_SQ1MBKGN #define RUNTIME_H_SQ1MBKGN +// this has to be the first include or MSVC will run amok #include "uscxml/Common.h" +#include "getopt.h" + #include "uscxml/URL.h" #include <boost/algorithm/string.hpp> @@ -61,6 +64,44 @@ enum Capabilities { CAN_GENERIC_HTTP = 2, }; +class InterpreterOptions { +public: + bool useDot; + bool verbose; + bool withHTTP; + bool withHTTPS; + int logLevel; + unsigned short httpPort; + unsigned short httpsPort; + std::string pluginPath; + std::string certificate; + std::string privateKey; + std::string publicKey; + std::map<std::string, InterpreterOptions*> interpreters; + std::map<std::string, std::string> additionalParameters; + + std::string error; + + operator bool() { + return error.length() == 0; + } + + static void printUsageAndExit(const char* progName); + static InterpreterOptions fromCmdLine(int argc, char** argv); + unsigned int getCapabilities(); + +protected: + InterpreterOptions() : + useDot(false), + verbose(false), + withHTTP(true), + withHTTPS(true), + logLevel(0), + httpPort(8080), + httpsPort(8443) + {} +}; + class InterpreterImpl : public boost::enable_shared_from_this<InterpreterImpl> { public: @@ -100,7 +141,7 @@ public: return _baseURI; } - void setCmdLineOptions(int argc, char** argv); + void setCmdLineOptions(std::map<std::string, std::string> params); Data getCmdLineOptions() { return _cmdLineOptions; } @@ -400,8 +441,8 @@ public: return _impl->getNameSpaceInfo(); } - void setCmdLineOptions(int argc, char** argv) { - return _impl->setCmdLineOptions(argc, argv); + void setCmdLineOptions(std::map<std::string, std::string> params) { + return _impl->setCmdLineOptions(params); } Data getCmdLineOptions() { return _impl->getCmdLineOptions(); diff --git a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp index cbb82eb..fe16361 100644 --- a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp +++ b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp @@ -26,14 +26,12 @@ DelayedEventQueue::~DelayedEventQueue() { } void DelayedEventQueue::run(void* instance) { - DelayedEventQueue* THIS = (DelayedEventQueue*)instance; + DelayedEventQueue* INSTANCE = (DelayedEventQueue*)instance; int result; - while(THIS->_isStarted) { - { - //result = event_base_dispatch(THIS->_eventLoop); - result = event_base_loop(THIS->_eventLoop, EVLOOP_NO_EXIT_ON_EMPTY); - (void)result; - } + while(INSTANCE->_isStarted) { + //result = event_base_dispatch(THIS->_eventLoop); + result = event_base_loop(INSTANCE->_eventLoop, EVLOOP_NO_EXIT_ON_EMPTY); + (void)result; } } diff --git a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h index 0b72719..fa76c3f 100644 --- a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h +++ b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h @@ -19,9 +19,9 @@ class DelayedEventQueue { public: enum OpMask { - FD_READ = EV_READ, - FD_WRITE = EV_WRITE, - FD_SIGNAL = EV_SIGNAL + DEQ_READ = EV_READ, + DEQ_WRITE = EV_WRITE, + DEQ_SIGNAL = EV_SIGNAL }; struct callbackData { diff --git a/src/uscxml/pch.h b/src/uscxml/pch.h new file mode 100644 index 0000000..e4ae2d1 --- /dev/null +++ b/src/uscxml/pch.h @@ -0,0 +1,14 @@ +#include "uscxml/Common.h" + +#include <boost/algorithm/string.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/lexical_cast.hpp> + +#include <iostream> +#include <set> +#include <map> +#include <list> +#include <vector> +#include <string> + +#include <DOM/Document.hpp>
\ No newline at end of file diff --git a/src/uscxml/plugins/datamodel/CMakeLists.txt b/src/uscxml/plugins/datamodel/CMakeLists.txt index 5fe82b7..3b6a852 100644 --- a/src/uscxml/plugins/datamodel/CMakeLists.txt +++ b/src/uscxml/plugins/datamodel/CMakeLists.txt @@ -1,63 +1,65 @@ -if (JSC_FOUND AND BUILD_DM_ECMA) - set(USCXML_DATAMODELS "ecmascript(JSC) ${USCXML_DATAMODELS}") - # JavaScriptCore ecmascript datamodel - file(GLOB JSC_DATAMODEL - ecmascript/JavaScriptCore/*.cpp - ecmascript/JavaScriptCore/*.h - ecmascript/*.cpp - ecmascript/*.h - ) - source_group("Datamodel\\jsc" FILES ${JSC_DATAMODEL}) - file(GLOB_RECURSE JSC_DOM - ecmascript/JavaScriptCore/dom/*.cpp - ecmascript/JavaScriptCore/dom/*.h - ) - source_group("Datamodel\\DOM" FILES ${JSC_DOM}) - if (BUILD_AS_PLUGINS) - add_library( - datamodel_jsc SHARED - ${JSC_DATAMODEL} - ${JSC_DOM}) - target_link_libraries(datamodel_jsc - uscxml - ${JSC_LIBRARY}) - set_target_properties(datamodel_jsc PROPERTIES FOLDER "Plugin DataModel") - else() - list (APPEND USCXML_FILES ${JSC_DATAMODEL}) - list (APPEND USCXML_FILES ${JSC_DOM}) - list (APPEND USCXML_OPT_LIBS ${JSC_LIBRARY}) - endif() -else() - -# GOOGLE V8 ecmascript datamodel - set(USCXML_DATAMODELS "ecmascript(V8) ${USCXML_DATAMODELS}") - # set(ENV{V8_SRC} ${CMAKE_SOURCE_DIR}/../v8) - if (V8_FOUND AND BUILD_DM_ECMA) - file(GLOB V8_DATAMODEL - ecmascript/v8/*.cpp - ecmascript/v8/*.h +if (BUILD_DM_ECMA) + if (JSC_FOUND) + set(USCXML_DATAMODELS "ecmascript(JSC) ${USCXML_DATAMODELS}") + # JavaScriptCore ecmascript datamodel + file(GLOB JSC_DATAMODEL + ecmascript/JavaScriptCore/*.cpp + ecmascript/JavaScriptCore/*.h ecmascript/*.cpp ecmascript/*.h ) - source_group("Datamodel\\v8" FILES ${V8_DATAMODEL}) - file(GLOB_RECURSE V8_DOM - ecmascript/v8/dom/*.cpp - ecmascript/v8/dom/*.h + source_group("Datamodel\\jsc" FILES ${JSC_DATAMODEL}) + file(GLOB_RECURSE JSC_DOM + ecmascript/JavaScriptCore/dom/*.cpp + ecmascript/JavaScriptCore/dom/*.h ) - source_group("Datamodel\\v8\\DOM" FILES ${V8_DOM}) - + source_group("Datamodel\\DOM" FILES ${JSC_DOM}) if (BUILD_AS_PLUGINS) add_library( - datamodel_v8 SHARED - ${V8_DATAMODEL} - ${V8_DOM}) - target_link_libraries(datamodel_v8 + datamodel_jsc SHARED + ${JSC_DATAMODEL} + ${JSC_DOM}) + target_link_libraries(datamodel_jsc uscxml - ${V8_LIBRARY}) - set_target_properties(datamodel_v8 PROPERTIES FOLDER "Plugin DataModel") + ${JSC_LIBRARY}) + set_target_properties(datamodel_jsc PROPERTIES FOLDER "Plugin DataModel") else() - list (APPEND USCXML_FILES ${V8_DATAMODEL}) - list (APPEND USCXML_FILES ${V8_DOM}) + list (APPEND USCXML_FILES ${JSC_DATAMODEL}) + list (APPEND USCXML_FILES ${JSC_DOM}) + list (APPEND USCXML_OPT_LIBS ${JSC_LIBRARY}) + endif() + else() + + # GOOGLE V8 ecmascript datamodel + set(USCXML_DATAMODELS "ecmascript(V8) ${USCXML_DATAMODELS}") + # set(ENV{V8_SRC} ${CMAKE_SOURCE_DIR}/../v8) + if (V8_FOUND AND BUILD_DM_ECMA) + file(GLOB V8_DATAMODEL + ecmascript/v8/*.cpp + ecmascript/v8/*.h + ecmascript/*.cpp + ecmascript/*.h + ) + source_group("Datamodel\\v8" FILES ${V8_DATAMODEL}) + file(GLOB_RECURSE V8_DOM + ecmascript/v8/dom/*.cpp + ecmascript/v8/dom/*.h + ) + source_group("Datamodel\\v8\\DOM" FILES ${V8_DOM}) + + if (BUILD_AS_PLUGINS) + add_library( + datamodel_v8 SHARED + ${V8_DATAMODEL} + ${V8_DOM}) + target_link_libraries(datamodel_v8 + uscxml + ${V8_LIBRARY}) + set_target_properties(datamodel_v8 PROPERTIES FOLDER "Plugin DataModel") + else() + list (APPEND USCXML_FILES ${V8_DATAMODEL}) + list (APPEND USCXML_FILES ${V8_DOM}) + endif() endif() endif() endif() diff --git a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h index 460afd4..e7d7e4c 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h +++ b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h @@ -1,6 +1,7 @@ #ifndef TYPEDARRAY_H_99815BLY #define TYPEDARRAY_H_99815BLY +#include "uscxml/Common.h" #include "uscxml/Message.h" #include <string> diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DOM.h b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DOM.h index 43b98ce..f35b13f 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DOM.h +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DOM.h @@ -1,6 +1,7 @@ #ifndef V8DOM_H_LKE1HKJK #define V8DOM_H_LKE1HKJK +#include "uscxml/Common.h" #include "uscxml/Interpreter.h" #include <v8.h> #include <XPath/XPath.hpp> diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.cpp b/src/uscxml/plugins/invoker/im/IMInvoker.cpp index cff9b5f..1e12650 100644 --- a/src/uscxml/plugins/invoker/im/IMInvoker.cpp +++ b/src/uscxml/plugins/invoker/im/IMInvoker.cpp @@ -7,12 +7,13 @@ #endif #define GET_INSTANCE_IN_CALLBACK(account) \ -tthread::lock_guard<tthread::mutex> lock(_accountMutex); \ +tthread::lock_guard<tthread::recursive_mutex> lock(_accountMutex); \ +IMInvoker* inst = NULL;\ if (_accountInstances.find(account) == _accountInstances.end()) { \ LOG(ERROR) << "Callback for unknown account called"; \ - return; \ -} \ -IMInvoker* inst = _accountInstances[account];\ +} else {\ + inst = _accountInstances[account];\ +} namespace uscxml { @@ -35,13 +36,19 @@ PurpleEventLoopUiOps IMInvoker::_uiEventLoopOps = purpleEventTimeoutRemove, purpleEventInputAdd, purpleEventInputRemove, - NULL, //purpleEventInputGetError, + purpleEventInputGetError, purpleEventTimeoutAddSec, NULL, NULL, NULL }; - + +PurpleDebugUiOps IMInvoker::_uiDebugOps = { + purpleDebugPrint, + purpleDebugIsEnabled, + NULL, NULL, NULL, NULL +}; + PurpleAccountUiOps IMInvoker::_uiAccountOps = { accountNotifyAdded, accountStatusChanged, @@ -181,7 +188,7 @@ PurpleWhiteboardUiOps IMInvoker::_uiWhiteboardOps = { }; PurpleCoreUiOps IMInvoker::_uiCoreOps = { - NULL, + purplePrefsInit, NULL, purpleUIInit, NULL, @@ -196,9 +203,39 @@ DelayedEventQueue* IMInvoker::_eventQueue = NULL; tthread::mutex IMInvoker::_initMutex; tthread::condition_variable IMInvoker::_initCond; -tthread::mutex IMInvoker::_accountMutex; +tthread::recursive_mutex IMInvoker::_accountMutex; std::map<PurpleAccount*, IMInvoker*> IMInvoker::_accountInstances; + +void IMInvoker::setupPurpleSignals() { + int handle; + // connection signals + purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, PURPLE_CALLBACK(signedOnCB), NULL); + + // conversation signals + purple_signal_connect(purple_conversations_get_handle(), "conversation-created", &handle, PURPLE_CALLBACK(conversationCreatedCB), NULL); + purple_signal_connect(purple_conversations_get_handle(), "chat-joined", &handle, PURPLE_CALLBACK(chatJoinedCB), NULL); + purple_signal_connect(purple_conversations_get_handle(), "chat-join-failed", &handle, PURPLE_CALLBACK(chatJoinFailedCB), NULL); + purple_signal_connect(purple_conversations_get_handle(), "buddy-typing", &handle, PURPLE_CALLBACK(buddyTypingCB), NULL); + purple_signal_connect(purple_conversations_get_handle(), "buddy-typed", &handle, PURPLE_CALLBACK(buddyTypedCB), NULL); + purple_signal_connect(purple_conversations_get_handle(), "buddy-typing-stopped", &handle, PURPLE_CALLBACK(buddyTypingStoppedCB), NULL); + + // buddy signals + purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", &handle, PURPLE_CALLBACK(buddyEventCB), GINT_TO_POINTER(PURPLE_BUDDY_SIGNON)); + purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", &handle, PURPLE_CALLBACK(buddyEventCB), GINT_TO_POINTER(PURPLE_BUDDY_SIGNOFF)); + purple_signal_connect(purple_blist_get_handle(), "buddy-got-login-time", &handle, PURPLE_CALLBACK(buddyEventCB), GINT_TO_POINTER(PURPLE_BUDDY_SIGNON_TIME)); + purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", &handle, PURPLE_CALLBACK(buddyIdleChangedCB), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", &handle, PURPLE_CALLBACK(buddyStatusChangedCB), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-icon-changed", &handle, PURPLE_CALLBACK(buddyEventCB), GINT_TO_POINTER(PURPLE_BUDDY_ICON)); + purple_signal_connect(purple_blist_get_handle(), "buddy-added", &handle, PURPLE_CALLBACK(buddyAddedCB), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-removed", &handle, PURPLE_CALLBACK(buddyRemovedCB), NULL); + purple_signal_connect(purple_blist_get_handle(), "blist-node-aliased", &handle, PURPLE_CALLBACK(blistNodeAliasedCB), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-caps-changed", &handle, PURPLE_CALLBACK(buddyCapsChangedCB), NULL); + + // xfer signals + purple_signal_connect(purple_xfers_get_handle(), "file-recv-request", &handle, PURPLE_CALLBACK(fileRecvRequestCB), NULL); +} + void IMInvoker::initLibPurple(void *userdata, const std::string event) { _initMutex.lock(); @@ -218,7 +255,6 @@ void IMInvoker::initLibPurple(void *userdata, const std::string event) { purple_debug_set_enabled(false); purple_core_set_ui_ops(&_uiCoreOps); -// purple_eventloop_set_ui_ops(&glib_eventloops); purple_eventloop_set_ui_ops(&_uiEventLoopOps); purple_plugins_add_search_path("/usr/local/lib/purple-3"); @@ -257,64 +293,153 @@ void IMInvoker::initLibPurple(void *userdata, const std::string event) { _initMutex.unlock(); _initCond.notify_all(); +} +// purple event callbacks +void IMInvoker::signedOnCB(PurpleConnection *gc, gpointer null) { + PurpleAccount *account = purple_connection_get_account(gc); + GET_INSTANCE_IN_CALLBACK(account); + if (!inst) + return; + +#if 0 + GSList *buddies = purple_find_buddies(purple_connection_get_account(gc), NULL); + GSList *cur; + for (cur = buddies; cur; cur = cur->next) { + buddyAddedCB((PurpleBuddy *)cur->data); + } + g_slist_free(buddies); +#endif + + // set my status to active + PurpleSavedStatus* status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE); + purple_savedstatus_activate(status); + + Event retEv("im.signed.on"); + inst->returnEvent(retEv); } -void IMInvoker::buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *newstatus) { +void IMInvoker::conversationCreatedCB(PurpleConversation *conv, void *data) {} +void IMInvoker::chatJoinedCB(PurpleConversation *conv, void *data) {} +void IMInvoker::chatJoinFailedCB(PurpleConnection *gc, GHashTable *components) {} +void IMInvoker::buddyTypingCB(PurpleAccount *account, const char *name, void *data) {} +void IMInvoker::buddyTypedCB(PurpleAccount *account, const char *name, void *data) {} +void IMInvoker::buddyTypingStoppedCB(PurpleAccount *account, const char *name, void *data) {} + +void IMInvoker::buddyEventCB(PurpleBuddy *buddy, PurpleBuddyEvent event) { + if (!buddy) + return; + PurpleAccount *account = purple_buddy_get_account(buddy); GET_INSTANCE_IN_CALLBACK(account); + if (!inst) + return; + + switch (event) { + case PURPLE_BUDDY_SIGNOFF: + case PURPLE_BUDDY_SIGNON: { + PurplePresence* presence = purple_buddy_get_presence(buddy); + PurpleStatus* status = purple_presence_get_active_status(presence); + buddyStatusChangedCB(buddy, NULL, status, event); + break; + } + case PURPLE_BUDDY_ICON: + break; + + default: + break; + } - std::string buddyName = purple_buddy_get_name(buddy); - inst->_dataModelVars.compound["buddies"].compound[buddyName] = buddyToData(buddy); } -void IMInvoker::buddyIdleChangeCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle) { +void IMInvoker::buddyIdleChangedCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle, PurpleBuddyEvent event) { +} + +void IMInvoker::buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *newstatus, PurpleBuddyEvent event) { PurpleAccount *account = purple_buddy_get_account(buddy); GET_INSTANCE_IN_CALLBACK(account); -} + + std::string buddyName = purple_buddy_get_name(buddy); + Data buddyData = buddyToData(buddy); + inst->_dataModelVars.compound["buddies"].compound[buddyName] = buddyData; + + Event retEv("im.buddy.status.changed"); + retEv.data = buddyData; + inst->returnEvent(retEv); -void IMInvoker::buddyUpdateIdleCB() { } void IMInvoker::buddyAddedCB(PurpleBuddy* buddy) { PurpleAccount *account = purple_buddy_get_account(buddy); GET_INSTANCE_IN_CALLBACK(account); + if (!inst) + return; + + std::string buddyName = purple_buddy_get_name(buddy); + + Event retEv("im.buddy.added"); + retEv.data.compound["name"] = Data(buddyName, Data::VERBATIM); + inst->returnEvent(retEv); + + buddyStatusChangedCB(buddy, NULL, purple_presence_get_active_status(purple_buddy_get_presence(buddy)), PURPLE_BUDDY_NONE); + } void IMInvoker::buddyRemovedCB(PurpleBuddy* buddy) { PurpleAccount *account = purple_buddy_get_account(buddy); GET_INSTANCE_IN_CALLBACK(account); + std::string buddyName = purple_buddy_get_name(buddy); + + Event retEv("im.buddy.removed"); + retEv.data.compound["name"] = Data(buddyName, Data::VERBATIM); + inst->returnEvent(retEv); + + inst->_dataModelVars.compound["buddies"].compound.erase(buddyName); + } -void IMInvoker::buddyCapsChangedCB(PurpleBuddy* buddy, PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps) { - PurpleAccount *account = purple_buddy_get_account(buddy); - GET_INSTANCE_IN_CALLBACK(account); +void IMInvoker::blistNodeAliasedCB(PurpleBlistNode *node, char *old_alias) { } -gboolean IMInvoker::jabberRcvdPresenceCB(PurpleConnection *gc, const char *type, const char *from, xmlnode *presence) { - PurpleAccount *account = purple_connection_get_account(gc); - GET_INSTANCE_IN_CALLBACK(account); +void IMInvoker::fileRecvRequestCB(PurpleXfer *xfer) { + purple_xfer_set_local_filename(xfer, ""); } -void IMInvoker::buddySignOnOffCB(PurpleBuddy *buddy) { + +void IMInvoker::buddyCapsChangedCB(PurpleBuddy* buddy, PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps) { PurpleAccount *account = purple_buddy_get_account(buddy); - tthread::lock_guard<tthread::mutex> lock(_accountMutex); - if (_accountInstances.find(account) == _accountInstances.end()) { - LOG(ERROR) << "Callback for unknown account called"; - return; - } - IMInvoker* inst = _accountInstances[account]; - - std::string buddyName = purple_buddy_get_name(buddy); - inst->_dataModelVars.compound["buddies"].compound[buddyName] = buddyToData(buddy); + GET_INSTANCE_IN_CALLBACK(account); } -void IMInvoker::signedOnCB(PurpleConnection *gc, gpointer null) { - PurpleSavedStatus* status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE); - purple_savedstatus_activate(status); +Data IMInvoker::statusToData(PurpleStatus *status) { + Data data; + const char* statusName = purple_status_get_name(status); + if (statusName) data.compound["name"] = Data(statusName, Data::VERBATIM); + + PurpleStatusType* statusType = purple_status_get_type(status); + + GList *statusAttrElem; + GList *statusAttrList = purple_status_type_get_attrs(statusType); + PurpleStatusAttr* statusAttr; + for(statusAttrElem = statusAttrList; statusAttrElem; statusAttrElem = statusAttrElem->next) { + statusAttr = (PurpleStatusAttr*)statusAttrElem->data; + const char* statusAttrId = purple_status_attr_get_id(statusAttr); + PurpleValue* statusValue = purple_status_get_attr_value(status, statusAttrId); + if (statusValue) { + data.compound[statusAttrId] = purpleValueToData(statusValue); + } + } + + data.compound["active"] = Data((bool)purple_status_is_active(status)); + data.compound["available"] = Data((bool)purple_status_is_available(status)); + data.compound["exclusive"] = Data((bool)purple_status_is_exclusive(status)); + data.compound["active"] = Data((bool)purple_status_is_active(status)); + data.compound["independent"] = Data((bool)purple_status_is_independent(status)); + data.compound["online"] = Data((bool)purple_status_is_online(status)); + + return data; } - Data IMInvoker::buddyToData(PurpleBuddy *buddy) { Data data; std::string buddyName = purple_buddy_get_name(buddy); @@ -346,53 +471,16 @@ Data IMInvoker::buddyToData(PurpleBuddy *buddy) { for(statusElem = statusList; statusElem; statusElem = statusElem->next) { status = (PurpleStatus*)statusElem->data; const char* statusId = purple_status_get_id(status); - const char* statusName = purple_status_get_name(status); PurpleStatusPrimitive statusPrimitive = purple_primitive_get_type_from_id(statusId); - + // only include active states if(statusPrimitive == PURPLE_STATUS_UNSET || !purple_presence_is_status_primitive_active(presence, statusPrimitive)) continue; - - if (statusName) data.compound["status"].compound[statusId] = Data(statusName, Data::VERBATIM); - - PurpleStatusType* statusType = purple_status_get_type(status); - - GList *statusAttrElem; - GList *statusAttrList = purple_status_type_get_attrs(statusType); - PurpleStatusAttr* statusAttr; - for(statusAttrElem = statusAttrList; statusAttrElem; statusAttrElem = statusAttrElem->next) { - statusAttr = (PurpleStatusAttr*)statusAttrElem->data; - const char* statusAttrId = purple_status_attr_get_id(statusAttr); - const char* statusAttrName = purple_status_attr_get_name(statusAttr); - - PurpleValue* statusAttrValue = purple_status_attr_get_value(statusAttr); - if (statusAttrValue) { - Data purpleValue = purpleValueToData(statusAttrValue); - if (purpleValue) { - data.compound["status"].compound[statusId].compound[statusAttrId].compound["value"] = purpleValue; - if (statusAttrName) { - data.compound["status"].compound[statusId].compound[statusAttrId].compound["name"] = Data(statusAttrName, Data::VERBATIM); - } - } - } - } - - data.compound["status"].compound[statusId].compound["active"] = Data((bool)purple_status_is_active(status)); - data.compound["status"].compound[statusId].compound["available"] = Data((bool)purple_status_is_available(status)); - data.compound["status"].compound[statusId].compound["exclusive"] = Data((bool)purple_status_is_exclusive(status)); - data.compound["status"].compound[statusId].compound["active"] = Data((bool)purple_status_is_active(status)); - data.compound["status"].compound[statusId].compound["independent"] = Data((bool)purple_status_is_independent(status)); - data.compound["status"].compound[statusId].compound["online"] = Data((bool)purple_status_is_online(status)); - - - PurpleValue* statusValue = purple_status_get_attr_value(status, statusId); - if (statusValue) - data.compound["status"].compound[statusId].compound["value"] = purpleValueToData(statusValue); - + data.compound["status"].compound[statusId] = statusToData(status); } } - std::cout << Data::toJSON(data); + return data; } @@ -407,7 +495,6 @@ Data IMInvoker::purpleValueToData(PurpleValue* value) { case PURPLE_TYPE_STRING: if (purple_value_get_string(value)) { data = Data(purple_value_get_string(value), Data::VERBATIM); - std::cout << purple_value_get_string(value) << std::endl; } break; case PURPLE_TYPE_CHAR: @@ -480,7 +567,7 @@ boost::shared_ptr<InvokerImpl> IMInvoker::create(InterpreterImpl* interpreter) { } Data IMInvoker::getDataModelVariables() { - tthread::lock_guard<tthread::mutex> lock(_accountMutex); + tthread::lock_guard<tthread::recursive_mutex> lock(_accountMutex); return _dataModelVars; } @@ -497,7 +584,7 @@ void IMInvoker::send(const SendRequest& req) { void IMInvoker::send(void *userdata, const std::string event) { // we are in the thread that manages all of libpurple EventContext* ctx = (EventContext*)userdata; -#if 0 + if (boost::iequals(ctx->sendReq.name, "im.send")) { if (ctx->instance->_account) { std::string receiver; @@ -510,7 +597,6 @@ void IMInvoker::send(void *userdata, const std::string event) { PurpleConversation* conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, ctx->instance->_account, receiver.c_str()); purple_conv_im_send(purple_conversation_get_im_data(conv), ctx->sendReq.content.c_str()); -#if 0 if (data.binary) { PurpleConnection *gc = purple_account_get_connection(ctx->instance->_account); PurplePlugin *prpl; @@ -521,18 +607,30 @@ void IMInvoker::send(void *userdata, const std::string event) { prpl = purple_connection_get_prpl(gc); prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); -// if (prpl_info->send_file && -// (prpl_info->can_receive_file -// && prpl_info->can_receive_file(gc, receiver.c_str()))) -// prpl_info->send_file(gc, receiver.c_str(), file); - prpl_info->send_raw(gc, data.binary->_data, data.binary->_size); +// if (prpl_info && prpl_info->new_xfer) { +// PurpleXfer* xfer = (prpl_info->new_xfer)(purple_account_get_connection(ctx->instance->_account), receiver.c_str()); +// purple_xfer_set_local_filename(xfer, "/Users/sradomski/Documents/W3C Standards.pdf"); +// purple_xfer_set_filename(xfer, "asdfadsf.pdf"); +// purple_xfer_request(xfer); +// purple_xfer_request_accepted(xfer, "/Users/sradomski/Documents/W3C Standards.pdf"); +// } + + //Set the filename +// purple_xfer_set_local_filename(xfer, [[fileTransfer localFilename] UTF8String]); +// purple_xfer_set_filename(xfer, [[[fileTransfer localFilename] lastPathComponent] UTF8String]); +// xfer->ui_data +// purple_xfer_request(xfer); + + serv_send_file(gc, "sradomski@localhost", "/Users/sradomski/Documents/W3C Standards.pdf"); +// if (prpl_info->send_file && (prpl_info->can_receive_file && prpl_info->can_receive_file(gc, receiver.c_str()))) { +// prpl_info->send_file(gc, receiver.c_str(), "/Users/sradomski/Documents/W3C Standards.pdf"); +// } +// prpl_info->send_raw(gc, data.binary->data, data.binary->size); } } -#endif } } -#endif delete(ctx); } @@ -565,23 +663,8 @@ void IMInvoker::invoke(void *userdata, const std::string event) { _accountInstances[instance->_account] = instance; purple_account_set_password(instance->_account, password.c_str(), NULL, NULL); - purple_account_set_enabled(instance->_account, "uscxml", true); - int handle; - purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, PURPLE_CALLBACK(signedOnCB), NULL); - - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", &handle, PURPLE_CALLBACK(buddySignOnOffCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", &handle, PURPLE_CALLBACK(buddySignOnOffCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", &handle, PURPLE_CALLBACK(buddyStatusChangedCB), NULL); - - purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", &handle, PURPLE_CALLBACK(buddyIdleChangeCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "update-idle", &handle, PURPLE_CALLBACK(buddyUpdateIdleCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-added", &handle, PURPLE_CALLBACK(buddyAddedCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-removed", &handle, PURPLE_CALLBACK(buddyRemovedCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-caps-changed", &handle, PURPLE_CALLBACK(buddyCapsChangedCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "jabber-receiving-presence", &handle, PURPLE_CALLBACK(jabberRcvdPresenceCB), NULL); - delete(ctx); _accountMutex.unlock(); } @@ -616,24 +699,65 @@ guint IMInvoker::purpleEventInputAdd(int fd, PurpleInputCondition cond, PurpleIn short opMask = 0; if (cond & PURPLE_INPUT_READ) - opMask |= DelayedEventQueue::FD_READ; + opMask |= DelayedEventQueue::DEQ_READ; if (cond & PURPLE_INPUT_WRITE) - opMask |= DelayedEventQueue::FD_WRITE; + opMask |= DelayedEventQueue::DEQ_WRITE; guint eventId = g_rand_int(_gRand); +// std::cout << "-- Input add " << eventId << " --------" << fd << std::endl; _eventQueue->addEvent(toStr(eventId), fd, opMask, purpleCallback, ctx, true); return eventId; } gboolean IMInvoker::purpleEventInputRemove(guint handle) { +// std::cout << "-- Input del " << handle << std::endl; _eventQueue->cancelEvent(toStr(handle)); return true; } int IMInvoker::purpleEventInputGetError(int fd, int *error) { - // unused - std::cout << "purpleEventInputGetError" << std::endl; - return 0; + int ret; + socklen_t len; + len = sizeof(*error); + + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, error, &len); + if (!ret && !(*error)) { + /* + * Taken from Fire's FaimP2PConnection.m: + * The job of this function is to detect if the connection failed or not + * There has to be a better way to do this + * + * Any socket that fails to connect will select for reading and writing + * and all reads and writes will fail + * Any listening socket will select for reading, and any read will fail + * So, select for writing, if you can write, and the write fails, not connected + */ + + { + fd_set thisfd; + struct timeval timeout; + + FD_ZERO(&thisfd); + FD_SET(fd, &thisfd); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + select(fd+1, NULL, &thisfd, NULL, &timeout); + if(FD_ISSET(fd, &thisfd)){ + ssize_t length = 0; + char buffer[4] = {0, 0, 0, 0}; + + length = write(fd, buffer, length); + if(length == -1) + { + /* Not connected */ + ret = -1; + *error = ENOTCONN; + } + } + } + } + + return ret; } void IMInvoker::purpleCallback(void *userdata, const std::string event) { @@ -642,16 +766,20 @@ void IMInvoker::purpleCallback(void *userdata, const std::string event) { ctx->function(ctx->data); delete ctx; } else if(ctx->input) { +// std::cout << "operating on " << ctx->inputFD << std::endl; ctx->input(ctx->data, ctx->inputFD, ctx->cond); } } -void IMInvoker::purplePrefsInit(void) {} +void IMInvoker::purplePrefsInit(void) { + purple_prefs_add_bool("/auto-login", false); +} + void IMInvoker::purpleDebugInit(void) {} void IMInvoker::purpleUIInit(void) { purple_accounts_set_ui_ops(&_uiAccountOps); - purple_xfers_set_ui_ops(&_uiXferOps); +// purple_xfers_set_ui_ops(&_uiXferOps); // purple_blist_set_ui_ops(&_uiBuddyOps); // purple_notify_set_ui_ops(&_uiNotifyOps); // purple_privacy_set_ui_ops(&_uiPrivacyOps); @@ -659,6 +787,10 @@ void IMInvoker::purpleUIInit(void) { // purple_connections_set_ui_ops(&_uiConnectOps); // purple_whiteboard_set_ui_ops(&_uiWhiteboardOps); purple_conversations_set_ui_ops(&_uiConvOps); + purple_debug_set_ui_ops(&_uiDebugOps); + + setupPurpleSignals(); + } void IMInvoker::purpleQuit(void) {} @@ -697,7 +829,7 @@ void* IMInvoker::accountRequestAuthorize(PurpleAccount *account, PurpleAccountRequestAuthorizationCb authorize_cb, PurpleAccountRequestAuthorizationCb deny_cb, void *user_data) { - // always accept all requests + // always accept all "may I add you as a buddy?" requests authorize_cb(message, user_data); return user_data; } @@ -788,9 +920,11 @@ void IMInvoker::purpleCancelRemote(PurpleXfer *xfer) { } gssize IMInvoker::purpleWrite(PurpleXfer *xfer, const guchar *buffer, gssize size) { std::cout << "purpleWrite" << std::endl; + return 0; } gssize IMInvoker::purpleRead(PurpleXfer *xfer, guchar **buffer, gssize size) { std::cout << "purpleRead" << std::endl; + return 0; } void IMInvoker::purpleDataNotSent(PurpleXfer *xfer, const guchar *buffer, gsize size) { std::cout << "purpleDataNotSent" << std::endl; @@ -837,34 +971,66 @@ void* IMInvoker::purpleRequestInput(const char *title, const char *primary, gboolean multiline, gboolean masked, gchar *hint, const char *ok_text, GCallback ok_cb, const char *cancel_text, GCallback cancel_cb, - PurpleRequestCommonParameters *cpar, void *user_data) {} + PurpleRequestCommonParameters *cpar, void *user_data) { + return NULL; +} void* IMInvoker::purpleRequestChoice(const char *title, const char *primary, const char *secondary, gpointer default_value, const char *ok_text, GCallback ok_cb, const char *cancel_text, GCallback cancel_cb, PurpleRequestCommonParameters *cpar, - void *user_data, va_list choices) {} + void *user_data, va_list choices) { + return NULL; +} void* IMInvoker::purpleRequestAction(const char *title, const char *primary, const char *secondary, int default_action, PurpleRequestCommonParameters *cpar, void *user_data, - size_t action_count, va_list actions) {} + size_t action_count, va_list actions) { + return NULL; +} void* IMInvoker::purpleRequestWait(const char *title, const char *primary, const char *secondary, gboolean with_progress, PurpleRequestCancelCb cancel_cb, - PurpleRequestCommonParameters *cpar, void *user_data) {} + PurpleRequestCommonParameters *cpar, void *user_data) { + return NULL; +} + +void IMInvoker::purpleRequestWaitUpdate(void *ui_handle, gboolean pulse, gfloat fraction) { -void IMInvoker::purpleRequestWaitUpdate(void *ui_handle, gboolean pulse, gfloat fraction) {} +} void* IMInvoker::purpleRequestFields(const char *title, const char *primary, const char *secondary, PurpleRequestFields *fields, const char *ok_text, GCallback ok_cb, const char *cancel_text, GCallback cancel_cb, - PurpleRequestCommonParameters *cpar, void *user_data) {} + PurpleRequestCommonParameters *cpar, void *user_data) { + return NULL; +} void* IMInvoker::purpleRequestFile(const char *title, const char *filename, gboolean savedialog, GCallback ok_cb, GCallback cancel_cb, - PurpleRequestCommonParameters *cpar, void *user_data) {} + PurpleRequestCommonParameters *cpar, void *user_data) { + // click ok + PurpleXfer *xfer = (PurpleXfer *)user_data; + PurpleXferType xferType = purple_xfer_get_type(xfer); + if (xferType == PURPLE_XFER_RECEIVE) { + ((PurpleRequestFileCb)ok_cb)(user_data, filename); + } else if (xferType == PURPLE_XFER_SEND) { + if (xfer->local_filename != NULL && xfer->filename != NULL) { + ((PurpleRequestFileCb)ok_cb)(user_data, xfer->local_filename); + } else { + ((PurpleRequestFileCb)cancel_cb)(user_data, xfer->local_filename); + } + } + return NULL; +} + void* IMInvoker::purpleRequestFolder(const char *title, const char *dirname, GCallback ok_cb, GCallback cancel_cb, - PurpleRequestCommonParameters *cpar, void *user_data) {} -void IMInvoker::purpleRequestClose(PurpleRequestType type, void *ui_handle) {} + PurpleRequestCommonParameters *cpar, void *user_data) { + return NULL; +} + +void IMInvoker::purpleRequestClose(PurpleRequestType type, void *ui_handle) { + +} // connection ui operations @@ -885,4 +1051,14 @@ void IMInvoker::purpleDrawPont(PurpleWhiteboard *wb, int x, int y, int color, in void IMInvoker::purpleDrawLine(PurpleWhiteboard *wb, int x1, int y1, int x2, int y2, int color, int size) {} void IMInvoker::purpleClearWB(PurpleWhiteboard *wb) {} +// debug ui operations +void IMInvoker::purpleDebugPrint(PurpleDebugLevel level, const char *category, const char *arg_s) { +// std::cout << category << ": " << arg_s << std::endl; +} + +gboolean IMInvoker::purpleDebugIsEnabled(PurpleDebugLevel level, const char *category) { + return true; +} + + }
\ No newline at end of file diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.h b/src/uscxml/plugins/invoker/im/IMInvoker.h index b8a57f6..51af8f8 100644 --- a/src/uscxml/plugins/invoker/im/IMInvoker.h +++ b/src/uscxml/plugins/invoker/im/IMInvoker.h @@ -13,6 +13,20 @@ extern "C" { namespace uscxml { +typedef enum { + PURPLE_BUDDY_NONE = 0x00, /**< No events. */ + PURPLE_BUDDY_SIGNON = 0x01, /**< The buddy signed on. */ + PURPLE_BUDDY_SIGNOFF = 0x02, /**< The buddy signed off. */ + PURPLE_BUDDY_INFO_UPDATED = 0x10, /**< The buddy's information (profile) changed. */ + PURPLE_BUDDY_ICON = 0x40, /**< The buddy's icon changed. */ + PURPLE_BUDDY_MISCELLANEOUS = 0x80, /**< The buddy's service-specific miscalleneous info changed. */ + PURPLE_BUDDY_SIGNON_TIME = 0x11, /**< The buddy's signon time changed. */ + PURPLE_BUDDY_EVIL = 0x12, /**< The buddy's warning level changed. */ + PURPLE_BUDDY_DIRECTIM_CONNECTED = 0x14, /**< Connected to the buddy via DirectIM. */ + PURPLE_BUDDY_DIRECTIM_DISCONNECTED = 0x18, /**< Disconnected from the buddy via DirectIM. */ + PURPLE_BUDDY_NAME = 0x20 /**<Buddy name (UID) changed. */ +} PurpleBuddyEvent; + class IMInvoker : public InvokerImpl { public: struct EventContext { @@ -38,13 +52,14 @@ public: virtual void cancel(const std::string sendId); virtual void invoke(const InvokeRequest& req); -protected: +private: static bool _libPurpleIsInitialized; static Data _pluginData; Data _dataModelVars; static Data buddyToData(PurpleBuddy *buddy); + static Data statusToData(PurpleStatus *status); static Data purpleValueToData(PurpleValue* value); static PurpleAccountUiOps _uiAccountOps; @@ -58,31 +73,39 @@ protected: static PurpleRequestUiOps _uiRequestOps; static PurpleConnectionUiOps _uiConnectOps; static PurpleWhiteboardUiOps _uiWhiteboardOps; + static PurpleDebugUiOps _uiDebugOps; static PurpleRequestFeature _features; static GHashTable* _uiInfo; static GRand* _gRand; - static tthread::mutex _accountMutex; + static tthread::recursive_mutex _accountMutex; static std::map<PurpleAccount*, IMInvoker*> _accountInstances; static tthread::mutex _initMutex; static tthread::condition_variable _initCond; static DelayedEventQueue* _eventQueue; - // event callbacks + // libpurple event callbacks static void signedOnCB(PurpleConnection *gc, gpointer null); - static void buddySignOnOffCB(PurpleBuddy *buddy); - static void buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *oldstatus, PurpleStatus *newstatus); - static void buddyIdleChangeCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle); - static void buddyUpdateIdleCB(); + static void conversationCreatedCB(PurpleConversation *conv, void *data); + static void chatJoinedCB(PurpleConversation *conv, void *data); + static void chatJoinFailedCB(PurpleConnection *gc, GHashTable *components); + static void buddyTypingCB(PurpleAccount *account, const char *name, void *data); + static void buddyTypedCB(PurpleAccount *account, const char *name, void *data); + static void buddyTypingStoppedCB(PurpleAccount *account, const char *name, void *data); + static void buddyIdleChangedCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle, PurpleBuddyEvent event); + static void blistNodeAliasedCB(PurpleBlistNode *node, char *old_alias); + static void buddyEventCB(PurpleBuddy *buddy, PurpleBuddyEvent event); + static void buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *oldstatus, PurpleStatus *newstatus, PurpleBuddyEvent event); static void buddyAddedCB(PurpleBuddy* buddy); static void buddyRemovedCB(PurpleBuddy* buddy); + static void fileRecvRequestCB(PurpleXfer *xfer); static void buddyCapsChangedCB(PurpleBuddy* buddy, PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps); - static gboolean jabberRcvdPresenceCB(PurpleConnection *gc, const char *type, const char *from, xmlnode *presence); - // these are only being called from the delayed queue's thread static void initLibPurple(void *userdata, const std::string event); + static void setupPurpleSignals(); + static void send(void *userdata, const std::string event); static void invoke(void *userdata, const std::string event); @@ -104,6 +127,11 @@ protected: }; static void purpleCallback(void *userdata, const std::string event); + // libpurple debug + static void purpleDebugPrint(PurpleDebugLevel level, const char *category, const char *arg_s); + static gboolean purpleDebugIsEnabled(PurpleDebugLevel level, const char *category); + + // libpurple core operations static void purplePrefsInit(void); static void purpleDebugInit(void); diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index 8799fc2..28b3ba4 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -8,6 +8,8 @@ #include "uscxml/server/HTTPServer.h" #include "uscxml/Message.h" #include "uscxml/Factory.h" + +#include <string> #include <iostream> #include <event2/dns.h> #include <event2/event.h> @@ -17,9 +19,6 @@ #include <event2/http_struct.h> #include <event2/thread.h> -#include <string> -#include <iostream> - #include <glog/logging.h> #include <boost/algorithm/string.hpp> @@ -28,28 +27,38 @@ #include <arpa/inet.h> #endif +#if (defined EVENT_SSL_FOUND && defined OPENSSL_FOUND) +#include <openssl/ssl.h> +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <event2/bufferevent_ssl.h> +#endif + #ifdef BUILD_AS_PLUGINS #include <Pluma/Connector.hpp> #endif namespace uscxml { -HTTPServer::HTTPServer(unsigned short port) { +HTTPServer::HTTPServer(unsigned short port, SSLConfig* sslConf) { _port = port; _base = event_base_new(); _http = evhttp_new(_base); _thread = NULL; - - evhttp_set_allowed_methods(_http, - EVHTTP_REQ_GET | - EVHTTP_REQ_POST | - EVHTTP_REQ_HEAD | - EVHTTP_REQ_PUT | - EVHTTP_REQ_DELETE | - EVHTTP_REQ_OPTIONS | - EVHTTP_REQ_TRACE | - EVHTTP_REQ_CONNECT | - EVHTTP_REQ_PATCH); // allow all methods + + unsigned int allowedMethods = + EVHTTP_REQ_GET | + EVHTTP_REQ_POST | + EVHTTP_REQ_HEAD | + EVHTTP_REQ_PUT | + EVHTTP_REQ_DELETE | + EVHTTP_REQ_OPTIONS | + EVHTTP_REQ_TRACE | + EVHTTP_REQ_CONNECT | + EVHTTP_REQ_PATCH; + + evhttp_set_allowed_methods(_http, allowedMethods); // allow all methods _handle = NULL; while((_handle = evhttp_bind_socket_with_handle(_http, INADDR_ANY, _port)) == NULL) { @@ -57,6 +66,46 @@ HTTPServer::HTTPServer(unsigned short port) { } determineAddress(); +#if (defined EVENT_SSL_FOUND && defined OPENSSL_FOUND) + if (!sslConf) { + _https = NULL; + _sslHandle = NULL; + _sslPort = 0; + } else { + _sslPort = sslConf->port; + + // Initialize OpenSSL + SSL_library_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + _https = evhttp_new(_base); + evhttp_set_allowed_methods(_https, allowedMethods); // allow all methods + + SSL_CTX* ctx = SSL_CTX_new (SSLv23_server_method ()); + SSL_CTX_set_options(ctx, + SSL_OP_SINGLE_DH_USE | + SSL_OP_SINGLE_ECDH_USE | + SSL_OP_NO_SSLv2); + + EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + SSL_CTX_set_tmp_ecdh (ctx, ecdh); + + SSL_CTX_use_certificate_chain_file(ctx, sslConf->publicKey.c_str()); + SSL_CTX_use_PrivateKey_file(ctx, sslConf->privateKey.c_str(), SSL_FILETYPE_PEM); + SSL_CTX_check_private_key(ctx); + + evhttp_set_bevcb(_https, sslBufferEventCallback, ctx); + evhttp_set_gencb(_https, sslGeneralBufferEventCallback, NULL); + + _sslHandle = NULL; + while((_sslHandle = evhttp_bind_socket_with_handle(_https, INADDR_ANY, _sslPort)) == NULL) { + _sslPort++; + } + } +#endif + // evhttp_set_timeout(_http, 5); // generic callback @@ -72,7 +121,7 @@ HTTPServer::~HTTPServer() { HTTPServer* HTTPServer::_instance = NULL; tthread::recursive_mutex HTTPServer::_instanceMutex; -HTTPServer* HTTPServer::getInstance(int port) { +HTTPServer* HTTPServer::getInstance(unsigned short port, SSLConfig* sslConf) { // tthread::lock_guard<tthread::recursive_mutex> lock(_instanceMutex); if (_instance == NULL) { #ifdef _WIN32 @@ -84,12 +133,91 @@ HTTPServer* HTTPServer::getInstance(int port) { #else evthread_use_windows_threads(); #endif - _instance = new HTTPServer(port); + _instance = new HTTPServer(port, sslConf); _instance->start(); } return _instance; } +#if (defined EVENT_SSL_FOUND && defined OPENSSL_FOUND) +// see https://github.com/ppelleti/https-example/blob/master/https-server.c +struct bufferevent* HTTPServer::sslBufferEventCallback(struct event_base *base, void *arg) { + struct bufferevent* r; + SSL_CTX *ctx = (SSL_CTX *) arg; + r = bufferevent_openssl_socket_new (base, + -1, + SSL_new (ctx), + BUFFEREVENT_SSL_ACCEPTING, + BEV_OPT_CLOSE_ON_FREE); + return r; +} + + +void HTTPServer::sslGeneralBufferEventCallback (struct evhttp_request *req, void *arg) { + struct evbuffer *evb = NULL; + const char *uri = evhttp_request_get_uri (req); + struct evhttp_uri *decoded = NULL; + + /* We only handle POST requests. */ + if (evhttp_request_get_command (req) != EVHTTP_REQ_POST) + { evhttp_send_reply (req, 200, "OK", NULL); + return; + } + + printf ("Got a POST request for <%s>\n", uri); + + /* Decode the URI */ + decoded = evhttp_uri_parse (uri); + if (! decoded) + { printf ("It's not a good URI. Sending BADREQUEST\n"); + evhttp_send_error (req, HTTP_BADREQUEST, 0); + return; + } + + /* Decode the payload */ + struct evkeyvalq kv; + memset (&kv, 0, sizeof (kv)); + struct evbuffer *buf = evhttp_request_get_input_buffer (req); + evbuffer_add (buf, "", 1); /* NUL-terminate the buffer */ + char *payload = (char *) evbuffer_pullup (buf, -1); + if (0 != evhttp_parse_query_str (payload, &kv)) + { printf ("Malformed payload. Sending BADREQUEST\n"); + evhttp_send_error (req, HTTP_BADREQUEST, 0); + return; + } + + /* Determine peer */ + char *peer_addr; + ev_uint16_t peer_port; + struct evhttp_connection *con = evhttp_request_get_connection (req); + evhttp_connection_get_peer (con, &peer_addr, &peer_port); + + /* Extract passcode */ + const char *passcode = evhttp_find_header (&kv, "passcode"); + char response[256]; + evutil_snprintf (response, sizeof (response), + "Hi %s! I %s your passcode.\n", peer_addr, + (0 == strcmp (passcode, "R23") + ? "liked" + : "didn't like")); + evhttp_clear_headers (&kv); /* to free memory held by kv */ + + /* This holds the content we're sending. */ + evb = evbuffer_new (); + + evhttp_add_header (evhttp_request_get_output_headers (req), + "Content-Type", "application/x-yaml"); + evbuffer_add (evb, response, strlen (response)); + + evhttp_send_reply (req, 200, "OK", evb); + + if (decoded) + evhttp_uri_free (decoded); + if (evb) + evbuffer_free (evb); +} +#endif + /** * This callback is registered for all HTTP requests */ diff --git a/src/uscxml/server/HTTPServer.h b/src/uscxml/server/HTTPServer.h index 3e0d91c..141ce7e 100644 --- a/src/uscxml/server/HTTPServer.h +++ b/src/uscxml/server/HTTPServer.h @@ -26,6 +26,14 @@ public: } }; + class SSLConfig { + public: + SSLConfig() : port(8443) {} + std::string privateKey; + std::string publicKey; + unsigned short port; + }; + class Reply { public: Reply(Request req) : status(200), type(req.data.compound["type"].atom), curlReq(req.curlReq) {} @@ -41,7 +49,7 @@ public: evhttp_request* httpReq; }; - static HTTPServer* getInstance(int port = 8080); + static HTTPServer* getInstance(unsigned short port = 8080, SSLConfig* sslConf = NULL); static std::string getBaseURL(); static void reply(const Reply& reply); @@ -58,7 +66,7 @@ private: }; }; - HTTPServer(unsigned short port); + HTTPServer(unsigned short port, SSLConfig* sslConf = NULL); ~HTTPServer(); void start(); @@ -90,6 +98,15 @@ private: bool _isRunning; friend class HTTPServlet; + +#if (defined EVENT_SSL_FOUND && defined OPENSSL_FOUND) + struct evhttp* _https; + struct evhttp_bound_socket* _sslHandle; + unsigned short _sslPort; + + static struct bufferevent* sslBufferEventCallback(struct event_base *base, void *arg); + static void sslGeneralBufferEventCallback (struct evhttp_request *req, void *arg); +#endif }; class HTTPServlet { |