From 836927aa902696297febc95132e2c82147c08c03 Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Tue, 14 Jan 2014 17:15:09 +0100 Subject: Expect Invoker and Fedora build fixes --- CMakeLists.txt | 11 +- apps/samples/http2im/http2im.scxml | 3 +- config.h.in | 2 + contrib/cmake/FindFFMPEG.cmake | 10 +- contrib/cmake/FindPHP5.cmake | 4 + contrib/cmake/FindSWI.cmake | 8 +- contrib/cmake/HeaderExists.cmake | 1 + contrib/ctest/CTestCustom.ctest.in | 5 - contrib/src/evws/evws.c | 3 + docs/BUILDING.md | 32 +- src/uscxml/Convenience.h | 27 ++ src/uscxml/Factory.cpp | 17 +- src/uscxml/Interpreter.cpp | 28 +- src/uscxml/Message.h | 17 + src/uscxml/URL.cpp | 1 + src/uscxml/plugins/invoker/CMakeLists.txt | 20 ++ .../plugins/invoker/expect/ExpectInvoker.cpp | 359 +++++++++++++++++++++ src/uscxml/plugins/invoker/expect/ExpectInvoker.h | 79 +++++ src/uscxml/plugins/invoker/im/IMInvoker.cpp | 18 +- .../ioprocessor/basichttp/BasicHTTPIOProcessor.cpp | 40 ++- src/uscxml/server/HTTPServer.cpp | 14 + test/CMakeLists.txt | 55 ++-- test/samples/uscxml/test-expect.scxml | 64 ++++ test/src/test-expect.cpp | 58 ++++ test/src/test-url.cpp | 28 +- 25 files changed, 846 insertions(+), 58 deletions(-) create mode 100644 src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp create mode 100644 src/uscxml/plugins/invoker/expect/ExpectInvoker.h create mode 100644 test/samples/uscxml/test-expect.scxml create mode 100644 test/src/test-expect.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b05df3..789b231 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -672,7 +672,7 @@ endif() set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SHARED}) find_package(FFMPEG) if (FFMPEG_FOUND) - include_directories (${FFMPEG_INCLUDE_DIR}) + include_directories (${FFMPEG_INCLUDE_DIRS}) list (APPEND USCXML_OPT_LIBS ${FFMPEG_LIBRARIES}) # required with static ffmpeg builds # set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-Bsymbolic") @@ -687,6 +687,15 @@ if (LIBICAL_FOUND) endif() set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_ORIG}) +find_package(EXPECT) +find_package(TCL) +if (EXPECT_FOUND AND TCL_FOUND) + include_directories (${EXPECT_INCLUDE_DIR}) + include_directories (${TCL_INCLUDE_PATH}) + list (APPEND USCXML_OPT_LIBS ${EXPECT_LIBRARY}) + list (APPEND USCXML_OPT_LIBS ${TCL_LIBRARY}) +endif() + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SHARED}) find_package(LibPurple) if (LIBPURPLE_FOUND) diff --git a/apps/samples/http2im/http2im.scxml b/apps/samples/http2im/http2im.scxml index a2b2090..369c6a6 100644 --- a/apps/samples/http2im/http2im.scxml +++ b/apps/samples/http2im/http2im.scxml @@ -23,7 +23,7 @@ dump(_event); - + @@ -40,7 +40,6 @@ - here]]> diff --git a/config.h.in b/config.h.in index 666a005..b10be40 100644 --- a/config.h.in +++ b/config.h.in @@ -69,6 +69,8 @@ #cmakedefine OPENSSL_FOUND #cmakedefine OPENSSL_HAS_ELIPTIC_CURVES #cmakedefine EVENT_SSL_FOUND +#cmakedefine EXPECT_FOUND +#cmakedefine TCL_FOUND /** Header files we found */ #cmakedefine HAS_UNISTD_H diff --git a/contrib/cmake/FindFFMPEG.cmake b/contrib/cmake/FindFFMPEG.cmake index 5f64790..261b283 100644 --- a/contrib/cmake/FindFFMPEG.cmake +++ b/contrib/cmake/FindFFMPEG.cmake @@ -160,5 +160,13 @@ IF (FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODE ENDIF() INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFMPEG DEFAULT_MSG FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFMPEG DEFAULT_MSG + FFMPEG_LIBRARIES + FFMPEG_INCLUDE_DIRS + FFMPEG_LIBAVFORMAT_INCLUDE_DIRS + FFMPEG_LIBAVDEVICE_INCLUDE_DIRS + FFMPEG_LIBAVCODEC_INCLUDE_DIRS + FFMPEG_LIBAVUTIL_INCLUDE_DIRS + FFMPEG_LIBSWSCALE_INCLUDE_DIRS +) MARK_AS_ADVANCED(FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIR) diff --git a/contrib/cmake/FindPHP5.cmake b/contrib/cmake/FindPHP5.cmake index d449112..eee8e3f 100644 --- a/contrib/cmake/FindPHP5.cmake +++ b/contrib/cmake/FindPHP5.cmake @@ -1,4 +1,8 @@ if (UNIX) + if (APPLE) + # mavericks broke it - will look into it eventually + return() + endif() if (NOT PHP_CONFIG) find_program(PHP_CONFIG NAMES php-config diff --git a/contrib/cmake/FindSWI.cmake b/contrib/cmake/FindSWI.cmake index 1eb98cd..0953307 100644 --- a/contrib/cmake/FindSWI.cmake +++ b/contrib/cmake/FindSWI.cmake @@ -21,8 +21,12 @@ if (SWI_FOUND) # message("SWI_CFLAGS_OTHER_STATIC: ${SWI_CFLAGS_OTHER_STATIC}") # message(FATAL_ERROR "") - set(SWI_INCLUDE_DIR ${SWI_LIBRARY_DIRS}) - set(SWI_LIBRARY ${SWI_LIBRARIES}) + set(SWI_INCLUDE_DIR ${SWI_INCLUDE_DIRS}) + + FIND_LIBRARY(SWI_LIBRARY + NAMES libswipl swipl + PATHS ${SWI_LIBRARY_DIRS} + ) FIND_PROGRAM(SWI_BINARY swipl) diff --git a/contrib/cmake/HeaderExists.cmake b/contrib/cmake/HeaderExists.cmake index 9d0570e..810ac65 100644 --- a/contrib/cmake/HeaderExists.cmake +++ b/contrib/cmake/HeaderExists.cmake @@ -4,4 +4,5 @@ CHECK_INCLUDE_FILE(string.h HAS_STRING_H) CHECK_INCLUDE_FILE(signal.h HAS_SIGNAL_H) CHECK_INCLUDE_FILE(execinfo.h HAS_EXECINFO_H) CHECK_INCLUDE_FILE(dlfcn.h HAS_DLFCN_H) +CHECK_INCLUDE_FILE(stdint.h HAS_STDINT_H) diff --git a/contrib/ctest/CTestCustom.ctest.in b/contrib/ctest/CTestCustom.ctest.in index e263147..6abfa28 100644 --- a/contrib/ctest/CTestCustom.ctest.in +++ b/contrib/ctest/CTestCustom.ctest.in @@ -11,11 +11,6 @@ set(CTEST_CUSTOM_TESTS_IGNORE "ecma/test178.scxml" "ecma/test230.scxml" - "ecma/test250.scxml" - "ecma/test307.scxml" - "ecma/test313.scxml" - "ecma/test314.scxml" - "ecma/test415.scxml" "ecma/test463.scxml" "ecma/test464.scxml" "ecma/test465.scxml" diff --git a/contrib/src/evws/evws.c b/contrib/src/evws/evws.c index 5dbd8e9..8932b9f 100644 --- a/contrib/src/evws/evws.c +++ b/contrib/src/evws/evws.c @@ -181,6 +181,9 @@ int evws_parse_first_line(struct evws_connection *conn, char *line) { if (line != NULL) return (-1); + (void)method; + (void)version; + if ((conn->uri = strdup(uri)) == NULL) { return (-1); } diff --git a/docs/BUILDING.md b/docs/BUILDING.md index a9096eb..9cd61eb 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -168,7 +168,37 @@ This would be all distributions based on Redhat, e.g. Fedora. $ sudo yum install git cmake cmake-gui gcc-c++ # uscxml required dependencies - $ sudo yum install xml2-devel + $ sudo yum install xml2-devel libcurl-devel + +#### Fedora 20 + +Here is a complete walk-through to get uscxml running on Fedora 20, starting with the net installer. + + # get us git and the developer tools + $ sudo yum install git gcc-c++ cmake + + # uscxml required dependencies + $ sudo yum install libxml2-devel libcurl-devel + +This is sufficient to get uscxml to build. If you want some more functionality, install some more libraries: + + # SWI prolog datamodel + $ sudo yum install pl-devel + + # OpenAL invoker + $ sudo yum install openal-soft-devel libsndfile-devel + + # scenegraph and osgconvert invoker + $ sudo yum install OpenSceneGraph-devel mesa-libGL-devel + + # ffmpeg invoker (add repository from http://rpmfusion.org) + $ sudo yum install ffmpeg-devel ffmpeg-compat-devel + + # calendar invoker + $ sudo yum install libical-devel + + # expect invoker + $ sudo yum install expect-devel ### Console / Make diff --git a/src/uscxml/Convenience.h b/src/uscxml/Convenience.h index 0177476..cfaf617 100644 --- a/src/uscxml/Convenience.h +++ b/src/uscxml/Convenience.h @@ -20,6 +20,7 @@ #ifndef CONVENIENCE_H_LU7GZ6CB #define CONVENIENCE_H_LU7GZ6CB +#include #include namespace uscxml { @@ -69,6 +70,32 @@ inline bool equals(const std::string& a, const std::string& b) { return true; } +inline std::string unescape(const std::string& a) { + std::stringstream b; + // see http://en.cppreference.com/w/cpp/language/escape + + std::string::const_iterator it = a.begin(); + while (it != a.end()) { + char c = *it++; + if (c == '\\' && it != a.end()) { + switch (*it++) { + case '\\': c = '\\'; break; + case '0': c = '\0'; break; + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + } + } + b << c; + } + + return b.str(); +} + // see http://www.cplusplus.com/forum/general/27544/ // Little-endian operating systems: diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp index 7677fd4..c32c6a8 100644 --- a/src/uscxml/Factory.cpp +++ b/src/uscxml/Factory.cpp @@ -72,6 +72,10 @@ # include "uscxml/plugins/invoker/im/IMInvoker.h" # endif +# if (defined EXPECT_FOUND && defined TCL_FOUND) +# include "uscxml/plugins/invoker/expect/ExpectInvoker.h" +# endif + #ifdef OPENAL_FOUND # include "uscxml/plugins/invoker/audio/OpenALInvoker.h" #endif @@ -202,6 +206,13 @@ Factory::Factory() { } #endif +#if (defined EXPECT_FOUND && defined TCL_FOUND) + { + ExpectInvoker* invoker = new ExpectInvoker(); + registerInvoker(invoker); + } +#endif + #if (defined OPENAL_FOUND && (defined LIBSNDFILE_FOUND || defined AUDIOTOOLBOX_FOUND)) { OpenALInvoker* invoker = new OpenALInvoker(); @@ -510,7 +521,11 @@ size_t DataModelImpl::replaceExpressions(std::string& content) { // } else { // ss << data.atom; // } - ss << Data::toJSON(data); + if (data.atom.length() > 0) { + ss << data.atom; + } else { + ss << Data::toJSON(data); + } replacements++; } catch (Event e) { // insert unsubstituted diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 8542bba..db24dc6 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -423,7 +423,15 @@ void InterpreterImpl::start() { } void InterpreterImpl::run(void* instance) { - ((InterpreterImpl*)instance)->interpret(); + try { + ((InterpreterImpl*)instance)->interpret(); + } catch (Event e) { + LOG(ERROR) << e; + } catch(boost::bad_lexical_cast e) { + LOG(ERROR) << "InterpreterImpl::run catched exception: " << e.what(); + } catch (...) { + LOG(ERROR) << "InterpreterImpl::run catched unknown exception"; + } } bool InterpreterImpl::runOnMainThread(int fps, bool blocking) { @@ -1124,8 +1132,10 @@ void InterpreterImpl::invoke(const Arabica::DOM::Node& element) { LOG(INFO) << "Added invoker " << invokeReq.type << " at " << invokeReq.invokeid; try { invoker.invoke(invokeReq); + } catch(boost::bad_lexical_cast e) { + LOG(ERROR) << "Exception caught while sending invoke request to invoker " << invokeReq.invokeid << ": " << e.what(); } catch(...) { - LOG(ERROR) << "Exception caught while sending invoke requst to invoker " << invokeReq.invokeid; + LOG(ERROR) << "Unknown exception caught while sending invoke request to invoker " << invokeReq.invokeid; } if (_dataModel) { try { @@ -1641,7 +1651,13 @@ Arabica::DOM::Node InterpreterImpl::getState(const std::string& sta goto FOUND; LOG(ERROR) << "No state with id " << stateId << " found!"; - + { + Event ev; + ev.name = "error.execution"; + ev.eventType = Event::PLATFORM; + ev.data.compound["cause"] = Data("No state with id " + stateId + " found", Data::VERBATIM); + throw ev; + } FOUND: if (target.size() > 0) { for (int i = 0; i < target.size(); i++) { @@ -1726,8 +1742,10 @@ NodeSet InterpreterImpl::getTargetStates(const Arabica::DOM::Node targetIds = InterpreterImpl::tokenizeIdRefs(ATTR(transition, "target")); for (std::list::const_iterator targetIter = targetIds.begin(); targetIter != targetIds.end(); targetIter++) { Arabica::DOM::Node state = getState(*targetIter); - assert(HAS_ATTR(state, "id")); - targetStates.push_back(state); + if (state) { + assert(HAS_ATTR(state, "id")); + targetStates.push_back(state); + } } return targetStates; } diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h index c444cad..9c17340 100644 --- a/src/uscxml/Message.h +++ b/src/uscxml/Message.h @@ -318,6 +318,23 @@ public: return false; } + static bool getParam(params_t params, const std::string& name, bool& target) { + if (params.find(name) != params.end()) { + target = true; + if (iequals(params.find(name)->second.atom, "false")) { + target = false; + } else if(iequals(params.find(name)->second.atom, "off")) { + target = false; + } else if(iequals(params.find(name)->second.atom, "no")) { + target = false; + } else if(iequals(params.find(name)->second.atom, "0")) { + target = false; + } + return true; + } + return false; + } + template static bool getParam(params_t params, const std::string& name, std::list& target) { if (params.find(name) != params.end()) { std::pair rangeIter = params.equal_range(name); diff --git a/src/uscxml/URL.cpp b/src/uscxml/URL.cpp index 2aaa46b..7948b3b 100644 --- a/src/uscxml/URL.cpp +++ b/src/uscxml/URL.cpp @@ -757,6 +757,7 @@ void URLFetcher::perform() { case CURLM_BAD_HANDLE: case CURLM_BAD_EASY_HANDLE: case CURLE_FILE_COULDNT_READ_FILE: + case CURLE_COULDNT_CONNECT: case CURLM_OUT_OF_MEMORY: case CURLM_INTERNAL_ERROR: case CURLM_BAD_SOCKET: diff --git a/src/uscxml/plugins/invoker/CMakeLists.txt b/src/uscxml/plugins/invoker/CMakeLists.txt index 6dd78ec..607e64e 100644 --- a/src/uscxml/plugins/invoker/CMakeLists.txt +++ b/src/uscxml/plugins/invoker/CMakeLists.txt @@ -95,6 +95,26 @@ else() endif() +# expect invoker + +set(USCXML_INVOKERS "expect ${USCXML_INVOKERS}") +file(GLOB_RECURSE EXPECT_INVOKER + expect/*.cpp + expect/*.h +) +if (BUILD_AS_PLUGINS) + source_group("" FILES EXPECT_INVOKER) + add_library( + invoker_expect SHARED + ${EXPECT_INVOKER} + "../Plugins.cpp") + target_link_libraries(invoker_expect uscxml) + set_target_properties(invoker_expect PROPERTIES FOLDER "Plugin Invoker") +else() + list (APPEND USCXML_FILES ${EXPECT_INVOKER}) +endif() + + # SQLite3 SQL Invoker if (SQLITE3_FOUND) diff --git a/src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp b/src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp new file mode 100644 index 0000000..c66005f --- /dev/null +++ b/src/uscxml/plugins/invoker/expect/ExpectInvoker.cpp @@ -0,0 +1,359 @@ +/** + * @file + * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see . + * @endcond + */ + +#include "ExpectInvoker.h" +#include + +#ifdef BUILD_AS_PLUGINS +#include +#endif + +#include "uscxml/UUID.h" + +#undef USE_TCL_STUBS + +namespace uscxml { + +#ifdef BUILD_AS_PLUGINS +PLUMA_CONNECTOR +bool pluginConnect(pluma::Host& host) { + host.add( new ExpectInvokerProvider() ); + return true; +} +#endif + +Tcl_Interp* ExpectInvoker::_tcl = NULL; + +ExpectInvoker::ExpectInvoker() : _eventQueue(NULL) { +} + +ExpectInvoker::~ExpectInvoker() { + _eventQueue->stop(); + exp_cl +// if (_tcl) { +// Tcl_DeleteInterp(_tcl); +// } +}; + +boost::shared_ptr ExpectInvoker::create(InterpreterImpl* interpreter) { + boost::shared_ptr invoker = boost::shared_ptr(new ExpectInvoker()); + return invoker; +} + +Data ExpectInvoker::getDataModelVariables() { + Data data; + return data; +} + +void ExpectInvoker::send(const SendRequest& req) { + EventContext* ctx = new EventContext(); + ctx->sendReq = req; + ctx->instance = this; + +// LOG(ERROR) << "################ " << req; + + std::string eventId = UUID::getUUID(); + _eventQueue->addEvent(eventId, ExpectInvoker::send, 0, ctx); + +// send(ctx, ""); +} + +void ExpectInvoker::send(void *userdata, const std::string event) { + + EventContext* ctx = (EventContext*)userdata; + if (!ctx) + return; + + if (!ctx->instance) { + delete(ctx); + return; + } + + const SendRequest& req = ctx->sendReq; + + if (iequals(req.name, "expect.match")) { + int nrCases = req.params.size(); + struct exp_case *cases = (struct exp_case*)malloc(sizeof(struct exp_case) * (nrCases + 1)); + memset(cases, 0, sizeof(exp_case) * (nrCases + 1)); + + /** + exp_end: indicates that no more patterns appear. + exp_glob: indicates that the pattern is a glob-style string pattern. + exp_exact: indicates that the pattern is an exact string. + exp_regexp: indicates that the pattern is a regexp-style string pattern. + exp_compiled: indicates that the pattern is a regexp-style string pattern, and that its compiled form is also provided. + exp_null: indicates that the pattern is a null (for debugging purposes, a string pattern must also follow). + */ + + Event::params_t::const_iterator paramIter = req.params.begin(); + int index = 0; + while (paramIter != req.params.end()) { + struct exp_case* expCase = &cases[index]; + size_t colonPos = paramIter->first.find(":"); + if (colonPos != std::string::npos) { + if (paramIter->first.substr(0, colonPos) == "regex") { + expCase->type = exp_regexp; + } else if(paramIter->first.substr(0, colonPos) == "glob") { + expCase->type = exp_glob; + } else if(paramIter->first.substr(0, colonPos) == "exact") { + expCase->type = exp_exact; + } else { + // if we can't make sense of the type + expCase->type = exp_exact; + } + } else { + expCase->type = exp_regexp; + } + + expCase->pattern = strdup(paramIter->second.atom.c_str()); +// LOG(ERROR) << "################ " << expCase->pattern; + + if (expCase->type == exp_regexp) { + expCase->re = TclRegComp(expCase->pattern); + if (expCase->re == NULL) { + LOG(ERROR) << TclGetRegError(); + expCase->type = exp_null; + } + } + expCase->value = index + 1; + paramIter++; + index++; + } + + assert(index == nrCases); + + cases[nrCases].type = exp_end; + + /** + * The functions wait until the output from a process matches one of the + * patterns, a specified time period has passed, or an EOF is seen. + */ + + int rc = 0; + // exp_fexpectv won't return on timeout when called in thread +// rc = exp_fexpectv(ctx->instance->_cmdFP, cases); + rc = exp_expectv(ctx->instance->_cmdFD, cases); + + if (rc == EXP_EOF) { + Event ev; + ev.name = "expect.match.eof"; + ev.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM); + ctx->instance->returnEvent(ev); + } else if (rc == EXP_TIMEOUT) { + Event ev; + ev.name = "expect.match.timeout"; + ev.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM); + ctx->instance->returnEvent(ev); + } else if (rc == EXP_FULLBUFFER) { + Event ev; + ev.name = "expect.match.fullbuffer"; + ev.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM); + ctx->instance->returnEvent(ev); + } else if (rc > 0) { + rc--; // we started at 1 + paramIter = req.params.begin(); + while(rc > 0) { + if (paramIter == req.params.end()) + break; + paramIter++; + rc--; + } + if (paramIter != req.params.end()) { + Event event; + + size_t colonPos = paramIter->first.find(":"); + if (colonPos != std::string::npos) { + std::string eventName = paramIter->first; + event.name = std::string("expect.match.") + eventName.substr(colonPos + 1, eventName.length() - (colonPos + 1)); + event.data.compound["type"] = Data(paramIter->first.substr(0, colonPos), Data::VERBATIM); + + } else { + event.name = std::string("expect.match.") + paramIter->first; + event.data.compound["type"] = Data("regex", Data::VERBATIM); + } + + event.data.compound["pattern"] = Data(paramIter->second.atom, Data::VERBATIM); + event.data.compound["buffer"] = Data(exp_buffer, Data::VERBATIM); + event.data.compound["start"] = Data((int)(exp_match - exp_buffer)); + event.data.compound["end"] = Data((int)(exp_match_end - exp_buffer)); + event.data.compound["match"] = Data(std::string(exp_buffer).substr(exp_match - exp_buffer, exp_match_end - exp_match), Data::VERBATIM); + ctx->instance->returnEvent(event); + } else { + // exp_fexpectl returned gibberish + assert(false); + } + } else { + // exp_fexpectl returned gibberish + assert(false); + } + + // free our memory + for (int i = 0; i < nrCases; i++) { + if (cases[i].pattern != NULL) + free(cases[i].pattern); + if (cases[i].re != NULL) + free(cases[i].re); + } + free(cases); + + } else if (iequals(req.name, "expect.send")) { + std::string toSend = unescape(req.content); + ctx->instance->_interpreter->getDataModel().replaceExpressions(toSend); + fwrite(toSend.c_str(), toSend.length(), 1, ctx->instance->_cmdFP); + } + + delete(ctx); +} + +void ExpectInvoker::cancel(const std::string sendId) { +} + +void ExpectInvoker::invoke(const InvokeRequest& req) { + if (_eventQueue == NULL) { + _eventQueue = new DelayedEventQueue(); + _eventQueue->start(); + } + + EventContext* ctx = new EventContext(); + ctx->invokeReq = req; + ctx->instance = this; + + //_eventQueue->addEvent(req.sendid, ExpectInvoker::invoke, 0, ctx); + invoke(ctx, ""); + +} + +void ExpectInvoker::invoke(void *userdata, const std::string event) { + EventContext* ctx = (EventContext*)userdata; + + if (!ctx) + return; + + if (!ctx->instance) { + delete(ctx); + return; + } + + const InvokeRequest& req = ctx->invokeReq; + + // moved here for thread local storage + if (ctx->instance->_tcl == NULL) { + ctx->instance->_tcl = Tcl_CreateInterp(); + if (ctx->instance->_tcl) { + Tcl_Init(ctx->instance->_tcl); + Expect_Init(ctx->instance->_tcl); + } + ctx->instance->_cmdFP = NULL; + + bool debug = false; + Event::getParam(req.params, "debug", debug); + if (debug) { + exp_is_debugging = 1; + } else { + exp_is_debugging = 0; + } + + int timeout = 20; + Event::getParam(req.params, "timeout", timeout); + exp_timeout = timeout; + + bool logUser = false; + Event::getParam(req.params, "loguser", logUser); + if (logUser) { + exp_loguser = 1; + } else { + exp_loguser = 0; + } + + // exp_interactive = 1; + exp_logfile = 0; + // exp_remove_nulls = 1; + // exp_ttyinit = 1; + + } else { +// assert(false); + } + + char* cmd = NULL; + char** args = NULL; + int nrArgs = 0; + + if (req.params.count("spawn")) { + // get command + std::string command; + Event::getParam(req.params, "spawn", command); + cmd = strdup(command.c_str()); + + // get arguments + nrArgs = req.params.count("argument"); + args = (char**)malloc(sizeof(char*) * nrArgs + 2); + args[0] = strdup(command.c_str()); + + size_t index = 1; + std::pair argIterRange = req.params.equal_range("argument"); + Event::params_t::const_iterator argIter = argIterRange.first; + while(argIter != argIterRange.second) { + args[index] = strdup(argIter->second.atom.c_str()); + argIter++; + index++; + } + args[index] = (char*)0; + } else if(req.params.count("command")) { + + } + + // open socket + ctx->instance->_cmdFD = exp_spawnv(cmd, args); + if (ctx->instance->_cmdFD > 0) { + ctx->instance->_cmdFP = fdopen(ctx->instance->_cmdFD, "r+"); + + if (ctx->instance->_cmdFP) { + // disable buffering + setbuf(ctx->instance->_cmdFP,(char *)0); + Event event; + event.name = "spawn.success"; + ctx->instance->returnEvent(event); + } + } + + if (ctx->instance->_cmdFP == NULL || ctx->instance->_cmdFD <= 0) { + Event event; + event.name = "spawn.failed"; + event.data.compound["cause"] = Data(strerror(errno), Data::VERBATIM); + Tcl_Obj *infoObj = Tcl_GetVar2Ex(_tcl, "errorInfo", NULL, TCL_GLOBAL_ONLY); + if (infoObj) { + event.data.compound["errorInfo"] = Data(Tcl_GetString(infoObj), Data::VERBATIM); + } + + ctx->instance->returnEvent(event); + } + + if (cmd) + free(cmd); + + if (args) { + for (int i = 0; i < nrArgs + 1; i++) { + free(args[i]); + } + free(args); + } + +} + +} \ No newline at end of file diff --git a/src/uscxml/plugins/invoker/expect/ExpectInvoker.h b/src/uscxml/plugins/invoker/expect/ExpectInvoker.h new file mode 100644 index 0000000..7c9861e --- /dev/null +++ b/src/uscxml/plugins/invoker/expect/ExpectInvoker.h @@ -0,0 +1,79 @@ +/** + * @file + * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see . + * @endcond + */ + +#ifndef EXPECTINVOKER_H_W02590F0 +#define EXPECTINVOKER_H_W02590F0 + +#include + +#ifdef BUILD_AS_PLUGINS +#include "uscxml/plugins/Plugins.h" +#endif + +#include +#include +#include + +namespace uscxml { + +class ExpectInvoker : public InvokerImpl { +public: + struct EventContext { + InvokeRequest invokeReq; + SendRequest sendReq; + ExpectInvoker* instance; + }; + + ExpectInvoker(); + virtual ~ExpectInvoker(); + virtual boost::shared_ptr create(InterpreterImpl* interpreter); + + virtual std::set getNames() { + std::set names; + names.insert("expect"); + names.insert("http://uscxml.tk.informatik.tu-darmstadt.de/#expect"); + return names; + } + + virtual Data getDataModelVariables(); + virtual void send(const SendRequest& req); + virtual void cancel(const std::string sendId); + virtual void invoke(const InvokeRequest& req); + +protected: + + static void send(void *userdata, const std::string event); + static void invoke(void *userdata, const std::string event); + + static Tcl_Interp* _tcl; + FILE* _cmdFP; + int _cmdFD; + + DelayedEventQueue* _eventQueue; + +}; + +#ifdef BUILD_AS_PLUGINS +PLUMA_INHERIT_PROVIDER(ExpectInvoker, InvokerImpl); +#endif + +} + + +#endif /* end of include guard: EXPECTINVOKER_H_W02590F0 */ diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.cpp b/src/uscxml/plugins/invoker/im/IMInvoker.cpp index 981f2fe..23594f3 100644 --- a/src/uscxml/plugins/invoker/im/IMInvoker.cpp +++ b/src/uscxml/plugins/invoker/im/IMInvoker.cpp @@ -572,14 +572,6 @@ Data IMInvoker::purpleValueToData(GValue* value) { IMInvoker::IMInvoker() { _account = NULL; - if (!_eventQueue) { - tthread::lock_guard lock(_initMutex); - _eventQueue = new DelayedEventQueue(); - _eventQueue->addEvent("initLibPurple", IMInvoker::initLibPurple, 0, NULL); - _eventQueue->start(); - // make sure to have the shebang initialized when we leave - _initCond.wait(_initMutex); - } } IMInvoker::~IMInvoker() { @@ -593,6 +585,16 @@ IMInvoker::~IMInvoker() { boost::shared_ptr IMInvoker::create(InterpreterImpl* interpreter) { boost::shared_ptr invoker = boost::shared_ptr(new IMInvoker()); + + if (!_eventQueue) { + tthread::lock_guard lock(_initMutex); + _eventQueue = new DelayedEventQueue(); + _eventQueue->addEvent("initLibPurple", IMInvoker::initLibPurple, 0, NULL); + _eventQueue->start(); + // make sure to have the shebang initialized when we leave + _initCond.wait(_initMutex); + } + invoker->_dataModelVars.compound["plugins"] = _pluginData; return invoker; } diff --git a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp index 1b3eff0..7a24c79 100644 --- a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp +++ b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp @@ -21,6 +21,7 @@ #include "uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.h" #include "uscxml/Message.h" +#include "uscxml/DOMUtils.h" #include #include #include @@ -85,8 +86,26 @@ boost::shared_ptr BasicHTTPIOProcessor::create(InterpreterImpl* Data BasicHTTPIOProcessor::getDataModelVariables() { Data data; - assert(_url.length() > 0); + + // we are not connected! + if(_url.length() == 0) + return data; + data.compound["location"] = Data(_url, Data::VERBATIM); + + URL url(_url); + data.compound["host"] = Data(url.host(), Data::VERBATIM); + data.compound["port"] = Data(url.port(), Data::VERBATIM); + data.compound["path"] = Data(url.path(), Data::VERBATIM); + data.compound["scheme"] = Data(url.scheme(), Data::VERBATIM); + + std::vector pathComps = url.pathComponents(); + std::vector::const_iterator pathCompIter = pathComps.begin(); + while(pathCompIter != pathComps.end()) { + data.compound["pathComponens"].array.push_back(Data(*pathCompIter, Data::VERBATIM)); + pathCompIter++; + } + return data; } @@ -94,10 +113,29 @@ bool BasicHTTPIOProcessor::httpRecvRequest(const HTTPServer::Request& req) { Event reqEvent = req; reqEvent.eventType = Event::EXTERNAL; +// std::cout << req.raw << std::endl; + + /** + * If a single instance of the parameter '_scxmleventname' is present, the + * SCXML Processor must use its value as the name of the SCXML event that it + * raises. + */ + // this will call the const subscript operator if (req.data["content"]["_scxmleventname"]) { reqEvent.name = req.data["content"]["_scxmleventname"].atom; } + if (req.data["content"]["content"]) { + reqEvent.content = req.data["content"]["content"].atom; + } + + // check whether we can parse it as XML + if (reqEvent.content.length() > 0) { + NameSpacingParser parser = NameSpacingParser::fromXML(reqEvent.content); + if (!parser.errorsReported()) { + reqEvent.dom = parser.getDocument(); + } + } /// test532 if (reqEvent.name.length() == 0) diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index de78ba9..804b025 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -77,6 +77,12 @@ HTTPServer::HTTPServer(unsigned short port, unsigned short wsPort, SSLConfig* ss _httpHandle = NULL; _wsHandle = NULL; +#ifdef _WIN32 + _wsHandle = NULL; +#else + _wsHandle = 0; +#endif + determineAddress(); unsigned int allowedMethods = @@ -525,6 +531,10 @@ void HTTPServer::replyCallback(evutil_socket_t fd, short what, void *arg) { bool HTTPServer::registerServlet(const std::string& path, HTTPServlet* servlet) { HTTPServer* INSTANCE = getInstance(); + + if (!INSTANCE->_httpHandle) + return true; + tthread::lock_guard lock(INSTANCE->_mutex); // remove trailing and leading slash @@ -560,6 +570,10 @@ bool HTTPServer::registerServlet(const std::string& path, HTTPServlet* servlet) bool HTTPServer::registerServlet(const std::string& path, WebSocketServlet* servlet) { HTTPServer* INSTANCE = getInstance(); + + if (!INSTANCE->_wsHandle) + return true; + tthread::lock_guard lock(INSTANCE->_mutex); // remove trailing and leading slash diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4c906fd..4f1ec70 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,11 +11,11 @@ if (NOT WIN32) endif() add_test(test-execution ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-execution.scxml) -add_test(test-communication ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-communication.scxml) +add_test(test-communication ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser -t5493 ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-communication.scxml) add_test(test-done-data ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-donedata.scxml) if (SWI_FOUND) - add_test(test-prolog-swi ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-prolog.scxml) + add_test(test-prolog-swi ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser -t2345 ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-prolog.scxml) endif() # if (FFMPEG_FOUND) @@ -68,21 +68,21 @@ if (NOT WIN32) add_test(test-arabica-xpath ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-arabica-parsing) set_target_properties(test-arabica-parsing PROPERTIES FOLDER "Tests") - add_executable(test-stress src/test-stress.cpp) - if (BUILD_AS_PLUGINS) - target_link_libraries(test-stress uscxml invoker_dirmon) - else() - target_link_libraries(test-stress uscxml) - endif() - add_test(test-stress ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-stress ${CMAKE_SOURCE_DIR}/test/samples/w3c) - set_target_properties(test-stress PROPERTIES FOLDER "Tests") - - if (LIBPURPLE_FOUND) - add_executable(test-instant-messaging src/test-instant-messaging.cpp) - target_link_libraries(test-instant-messaging uscxml ${LIBPURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${ICONV_LIBRARIES}) - add_test(test-instant-messaging ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-instant-messaging) - set_target_properties(test-instant-messaging PROPERTIES FOLDER "Tests") - endif() + # add_executable(test-stress src/test-stress.cpp) + # if (BUILD_AS_PLUGINS) + # target_link_libraries(test-stress uscxml invoker_dirmon) + # else() + # target_link_libraries(test-stress uscxml) + # endif() + # add_test(test-stress ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-stress ${CMAKE_SOURCE_DIR}/test/samples/w3c) + # set_target_properties(test-stress PROPERTIES FOLDER "Tests") + + # if (LIBPURPLE_FOUND) + # add_executable(test-instant-messaging src/test-instant-messaging.cpp) + # target_link_libraries(test-instant-messaging uscxml ${LIBPURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${ICONV_LIBRARIES}) + # add_test(test-instant-messaging ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-instant-messaging) + # set_target_properties(test-instant-messaging PROPERTIES FOLDER "Tests") + # endif() endif() add_executable(test-url src/test-url.cpp) @@ -123,6 +123,13 @@ set_target_properties(scxml-test-framework-client PROPERTIES FOLDER "Tests") # add_test(test-curl-multi-api ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-completion) # set_target_properties(test-curl-multi-api PROPERTIES FOLDER "Tests") +if (EXPECT_FOUND AND TCL_FOUND) + add_executable(test-expect + src/test-expect.cpp) + target_link_libraries(test-expect uscxml) + set_target_properties(test-expect PROPERTIES FOLDER "Tests") +endif() + add_executable(test-w3c src/test-w3c.cpp) target_link_libraries(test-w3c uscxml) set_target_properties(test-w3c PROPERTIES FOLDER "Tests") @@ -133,15 +140,23 @@ file(GLOB_RECURSE W3C_TESTS foreach( W3C_TEST ${W3C_TESTS} ) string(REGEX MATCH "[^//]+/[^//]+.scxml" TEST_NAME ${W3C_TEST}) - #message("TEST_NAME: ${TEST_NAME}") +# message("TEST_NAME: ${TEST_NAME}") if (NOT TEST_NAME MATCHES ".*sub.*") if (BUILD_TESTS_W3C_ECMA AND TEST_NAME MATCHES "^ecma\\/.*") add_test(${TEST_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-w3c ${W3C_TEST}) - set_tests_properties(${TEST_NAME} PROPERTIES FAIL_REGULAR_EXPRESSION "TEST FAILED") +# set_tests_properties(${TEST_NAME} PROPERTIES FAIL_REGULAR_EXPRESSION "TEST FAILED") + if (TEST_NAME STREQUAL "ecma/test250.scxml") + set_tests_properties(${TEST_NAME} PROPERTIES + FAIL_REGULAR_EXPRESSION "entering final state, invocation was not cancelled") + elseif (TEST_NAME STREQUAL "ecma/test307.scxml") + set_tests_properties(${TEST_NAME} PROPERTIES + FAIL_REGULAR_EXPRESSION "error in state") + endif() + endif() if (BUILD_TESTS_W3C_XPATH AND TEST_NAME MATCHES "^xpath\\/.*") add_test(${TEST_NAME} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-w3c ${W3C_TEST}) - set_tests_properties(${TEST_NAME} PROPERTIES FAIL_REGULAR_EXPRESSION "TEST FAILED") +# set_tests_properties(${TEST_NAME} PROPERTIES FAIL_REGULAR_EXPRESSION "TEST FAILED") endif() endif() endforeach() diff --git a/test/samples/uscxml/test-expect.scxml b/test/samples/uscxml/test-expect.scxml new file mode 100644 index 0000000..abd2a58 --- /dev/null +++ b/test/samples/uscxml/test-expect.scxml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GET /${_name} HTTP/1.1\n\n + + + + + + + + + +
+ Some arbitrary content + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/src/test-expect.cpp b/test/src/test-expect.cpp new file mode 100644 index 0000000..51c3bcb --- /dev/null +++ b/test/src/test-expect.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +int main(int argc, char** argv) { + int rc = 0; + + Tcl_Interp *interp = Tcl_CreateInterp(); + Tcl_FindExecutable(argv[0]); + + if (Tcl_Init(interp) == TCL_ERROR) { + fprintf(stderr,"Tcl_Init failed: %s\n",Tcl_GetStringResult (interp)); + (void) exit(1); + } + + if (Expect_Init(interp) == TCL_ERROR) { + fprintf(stderr,"Expect_Init failed: %s\n",Tcl_GetStringResult (interp)); + (void) exit(1); + } + + exp_loguser = 1; + exp_is_debugging = 1; + exp_timeout = 3; + + FILE *fp; + int ec; +// char* program = "/usr/bin/telnet localhost 80"; +// if (0 > (ec = exp_spawnl("sh","sh","-c",program,(char *)0))) +// exit(0); +// if (NULL == (fp = fdopen(ec,"r+"))) +// exit(0); +// setbuf(fp,(char *)0); + + if (0 > (ec = exp_spawnl("/usr/bin/telnet", "/usr/bin/telnet","localhost", "80", (char *)0))) + exit(0); + if (NULL == (fp = fdopen(ec,"r+"))) + exit(0); + setbuf(fp,(char *)0); + + switch (exp_fexpectl(fp, + exp_glob, "qConnected to", 1, + exp_glob, "qConnection failed", 2, + exp_end)) { + case 1: + printf("SUCCESS!"); + fprintf(fp, "%s\r", "GET /"); + + break; + case 2: + printf("FAIL!"); + break; + + default: + break; + } + exit(EXIT_SUCCESS); +} \ No newline at end of file diff --git a/test/src/test-url.cpp b/test/src/test-url.cpp index e0f8343..9c4d941 100644 --- a/test/src/test-url.cpp +++ b/test/src/test-url.cpp @@ -82,17 +82,23 @@ int main(int argc, char** argv) { #endif { - URL url(argv[0]); - assert(canResolve(argv[0])); - assert(canResolve(url.asString())); - - URL baseUrl = URL::asBaseURL(url); - URL exeUrl(exeName); - exeUrl.toAbsolute(baseUrl); - assert(canResolve(exeUrl.asString())); - std::cout << exeUrl.asString() << std::endl; - exeUrl.download(true); - assert(exeUrl.getInContent().length() > 0); + try { + + URL url(argv[0]); + assert(canResolve(argv[0])); + assert(canResolve(url.asString())); + + URL baseUrl = URL::asBaseURL(url); + URL exeUrl(exeName); + exeUrl.toAbsolute(baseUrl); + assert(canResolve(exeUrl.asString())); + std::cout << exeUrl.asString() << std::endl; + exeUrl.download(true); + assert(exeUrl.getInContent().length() > 0); + + } catch (Event e) { + std::cout << e << std::endl; + } } { -- cgit v0.12