diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-09-25 13:24:11 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-09-25 13:24:11 (GMT) |
commit | 567df9318fff6d1bb570191c33ea68cd6ef88bee (patch) | |
tree | 72e310c422cea117abe859a6bddf0b613a2db589 | |
parent | be3c180fec71866a91b5f9297708d581bc1d6435 (diff) | |
download | uscxml-567df9318fff6d1bb570191c33ea68cd6ef88bee.zip uscxml-567df9318fff6d1bb570191c33ea68cd6ef88bee.tar.gz uscxml-567df9318fff6d1bb570191c33ea68cd6ef88bee.tar.bz2 |
More work on IMInvoker, renamed Blob attributes and some XPath datamodel fixes
21 files changed, 1409 insertions, 465 deletions
diff --git a/apps/samples/map/SpatialMapTicker.java b/apps/samples/map/SpatialMapTicker.java new file mode 100644 index 0000000..9947ffb --- /dev/null +++ b/apps/samples/map/SpatialMapTicker.java @@ -0,0 +1,160 @@ +/** + * Compile as: + * $ javac -cp .:/usr/local/share/umundo/java/umundo.jar SpatialMapTicker.java + * + * Run as: + * $ java -cp .:/usr/local/share/umundo/java/umundo.jar SpatialMapTicker + */ +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedList; +import java.util.Random; + +import org.umundo.core.Greeter; +import org.umundo.core.Message; +import org.umundo.core.Node; +import org.umundo.core.Publisher; + +public class SpatialMapTicker { + + Node node; + Publisher pub; + Greeter greeter; + ArrayList<Sensor> sensors = new ArrayList<Sensor>(); + static ArrayList<SensorMessage> messages = new ArrayList<SensorMessage>(); + static Random random = new Random(System.currentTimeMillis()); + + public class SensorMessage { + public String message; + public int severity; + public SensorMessage(String message, int severity) { + this.message = message; + this.severity = severity; + } + public SensorMessage(String message) { + this.message = message; + this.severity = 3; + } + } + + public class Sensor { + @Override + public String toString() { + return "Sensor [id=" + id + ", lat=" + lat + ", lon=" + lon + + ", html=" + getHTML() + "]"; + } + public String id = ""; + public Double lat = new Double(0); + public Double lon = new Double(0); + LinkedList<SensorMessage> messages = new LinkedList<SensorMessage>(); + + public void addMessage(SensorMessage message) { + if (messages.size() > 15) + messages.removeLast(); + messages.addFirst(message); + } + + public String getHTML() { + StringBuilder sb = new StringBuilder(); + for (SensorMessage message : messages) { + sb.append(message.severity); + sb.append(": "); + sb.append(message.message); + sb.append("<br />"); + } + return sb.toString(); + } + } + + public class SensorGreeter extends Greeter { + public void welcome(Publisher publisher, String nodeId, String subId) { + // send all sensors to new subscribers + for (Sensor sensor : sensors) { + Message msg = new Message(); //Message.toSubscriber(subId); + msg.putMeta("id", sensor.id); + msg.putMeta("lat", sensor.lat.toString()); + msg.putMeta("lon", sensor.lon.toString()); + msg.putMeta("html", sensor.getHTML()); + pub.send(msg); + } + } + + @Override + public void farewell(Publisher arg0, String nodeId, String subId) { + } + + } + + public SpatialMapTicker() { + node = new Node(); + pub = new Publisher("map/tick"); + greeter = new SensorGreeter(); + pub.setGreeter(greeter); + node.addPublisher(pub); + + double latCenter = 59.32; + double lonCenter = 18.08; + + int nrSensors = 15; //(int) (Math.random() * 20); + for (int i = 0; i < nrSensors; i++) { + Sensor sensor = new Sensor(); + double latOffset = (Math.random() - 0.5) * 0.3; + double lonOffset = (Math.random() - 0.5) * 0.3; + + sensor.id = "Sensor #" + i; + sensor.lat = latCenter + latOffset; + sensor.lon = lonCenter + lonOffset; + sensors.add(sensor); + } + } + + public static void main(String[] args) { + SpatialMapTicker ticker = new SpatialMapTicker(); + ticker.run(); + } + + private void run() { + messages.add(new SensorMessage("Oil pressure threshold exceeded")); + messages.add(new SensorMessage("Equipment is on fire")); + messages.add(new SensorMessage("Error #32 in diagnostics unit")); + messages.add(new SensorMessage("Unauthorized startup")); + messages.add(new SensorMessage("Tire pressure too low")); + messages.add(new SensorMessage("Error #145 in diagnostics unit")); + messages.add(new SensorMessage("Unit was moved out of construction site area")); + messages.add(new SensorMessage("Hydraulic pressure exceeding safety limits")); + messages.add(new SensorMessage("Drivers seat belts are not fastened!")); + messages.add(new SensorMessage("Battery recharge cycles exceeded")); + messages.add(new SensorMessage("Unit operated outside recommended paramters")); + + while (true) { + try { + Thread.sleep((long) (Math.random() * 300) + 100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Sensor sensor = sensors.get(random.nextInt(sensors.size())); + SensorMessage fault = messages.get(random.nextInt(messages.size())); + + Date now = new Date(); + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss z"); + String nowString = sdf.format(now); + sensor.addMessage(fault); + +// System.out.println("Publishing " + sensor); + + Message msg = new Message(); + msg.putMeta("id", sensor.id); + msg.putMeta("lat", sensor.lat.toString()); + msg.putMeta("lon", sensor.lon.toString()); + msg.putMeta("html", sensor.getHTML()); + msg.putMeta("time", nowString); + msg.putMeta("timeStamp", Long.toString(now.getTime())); + msg.putMeta("message", sensor.messages.getFirst().message); + msg.putMeta("severity", Integer.toString(random.nextInt(10))); + pub.send(msg); + } + } + +} diff --git a/apps/samples/map/click.wav b/apps/samples/map/click.wav Binary files differnew file mode 100644 index 0000000..e11b0b7 --- /dev/null +++ b/apps/samples/map/click.wav diff --git a/apps/samples/map/map.html b/apps/samples/map/map.html deleted file mode 100644 index 01dc92b..0000000 --- a/apps/samples/map/map.html +++ /dev/null @@ -1,30 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html> -<head> - <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> - <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dijit/themes/tundra/tundra.css"> - <!-- link rel="stylesheet" href="map.css" --> - <script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script> - <style type="text/css"> - </style> - - <script type="text/javascript"> - </script> - - <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.3/dojo/dojo.js"></script> - <script type="text/javascript" src="map.js"></script> - - <script type="text/javascript"> - require(["dojo/domReady!", "dojo"], function(dom, dojo) { - var map = new Map("map"); - }); - </script> - </head> - <body class="tundra"> - <table> - <tr><td> - <div id="map" /> - </td></tr> - </table> - </body> -</html> diff --git a/apps/samples/map/map.js b/apps/samples/map/map.js deleted file mode 100644 index 6794875..0000000 --- a/apps/samples/map/map.js +++ /dev/null @@ -1,158 +0,0 @@ -function Map(element) { - - // private attributes - var self = this; - - // private instanceId - if (!Map.instances) - Map.instances = 0; - var instanceId = Map.instances++; - - // public attributes - this.coords = {}; - - - require([ - "dojo/ready", - "dojo/dom-construct", - "dojo/_base/window", - "dojo/dom", - "dojox/geo/openlayers/Map", - "dojox/geo/openlayers/GfxLayer", - "dojox/geo/openlayers/Layer", - "dojox/geo/openlayers/GeometryFeature", - "dojox/geo/openlayers/Point", - "dojox/geo/openlayers/LineString", - "dojox/geo/openlayers/WidgetFeature", - "dojox/charting/widget/Chart", - "dojox/charting/widget/Chart2D", - "dojox/charting/plot2d/Pie", - "dojox/charting/themes/PlotKit/blue" - ], function( - ready, - domConstruct, - win, - dom, - Map, - GfxLayer, - Layer, - GeometryFeature, - Point, - LineString, - WidgetFeature, - Chart, - Chart2D, - Pie, - blue - ) { - ready(function(){ - - if (typeof(element) === 'string') { - element = dom.byId(element); - } - - element.style.width = "450px"; - element.style.height = "350px"; - - var options = { - baseLayerName: "WorldMap", - baseLayerType : dojox.geo.openlayers.BaseLayerType.OSM, - //baseLayerUrl: "http://localhost/mapserver/mapserv.cgi?map=./world.map", - baseLayerOptions: { - layers: ['contry','state','city','town','highway'] - }, - touchHandler: false, - accessible: true - }; - - // Available base layers: http://dojotoolkit.org/reference-guide/1.7/dojox/geo/openlayers.html#id5 - self.map = new Map(element, options); - - // This is New York location - var ny = { - latitude : 49.877648, - longitude : 8.654762 - }; - - var people = [ { - name : 'Dirk', - y : 49.877848, - x : 8.653762 - }, { - name : 'Stefan', - y : 49.877348, - x : 8.655462 - } ]; - - var div = domConstruct.create("div", {}, win.body()); - //var div = domConstruct.create("div", {}); - var chart = new Chart({ - margins : { - l : 0, - r : 0, - t : 0, - b : 0 - } - }, div); - - var c = chart.chart; - c.addPlot("default", { - type : "Pie", - radius : 50, - labelOffset : 100, - fontColor : "black", - fontSize : 20 - }); - - var ser = [ 2, 8, 12, 3 ]; - c.addSeries("Series", ser); - c.setTheme(blue); - c.render(); - c.theme.plotarea.fill = undefined; - - var descr = { - longitude : ny.longitude, - latitude : ny.latitude, - widget : chart, - width : 120, - height : 120 - }; - feat3 = new WidgetFeature(descr); - - // create a GfxLayer - var layer = new GfxLayer(); - - var point = new Point({ - x:ny.longitude, - y:ny.latitude - }); - // create a GeometryFeature - var feat = new GeometryFeature(point); - // set the shape properties, fill and stroke - feat.setFill([ 0, 128, 128 ]); - feat.setStroke([ 0, 0, 0 ]); - feat.setShapeProperties({ - r : 8 - }); - - var pts = new LineString(people); - // create a GeometryFeature - var feat2 = new GeometryFeature(pts); - // set the shape stroke property - feat2.setStroke([ 0, 0, 0 ]); - - // add the feature to the layer - layer.addFeature(feat); - layer.addFeature(feat2); - layer.addFeature(feat3); - // add layer to the map - self.map.addLayer(layer); - - // fit to New York with 0.1 degrees extent - self.map.fitTo({ - position : [ ny.longitude, ny.latitude ], - extent : 0.001 - }); - }); - }); -} diff --git a/apps/samples/map/spoken-map-ticker.scxml b/apps/samples/map/spoken-map-ticker.scxml new file mode 100644 index 0000000..147f890 --- /dev/null +++ b/apps/samples/map/spoken-map-ticker.scxml @@ -0,0 +1,78 @@ +<scxml datamodel="ecmascript" name="mapticker" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns:vxml="http://www.w3.org/2001/vxml" + xmlns="http://www.w3.org/2005/07/scxml"> + + <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" /> + + <state id="start"> + <!-- + Invoke an external HTML browser + --> + <invoke type="xhtml" id="xhtml1"> + <content src="spoken-map-ticker.xhtml" /> + <finalize> + <!-- <script>dump(_event);</script> --> + </finalize> + </invoke> + + <!-- + Invoke a VoiceXML browser + --> + <!-- invoke type="vxml" id="vxml" / --> + + <!-- + Invoke a uMundo component for pub/sub ticker subscription + --> + <invoke type="umundo" id="umundo"> + <param name="channel" expr="'map/tick'" /> + <finalize> + <script>dump(_event);</script> + </finalize> + </invoke> + + <invoke type="openal" id="audio"></invoke> + + <state id="idle"> + <!-- + Map in XHTML invoker was moved -> update OpenAL listener position + --> + <transition event="map.center" target="idle"> + <send event="move.listener" target="#_audio"> + <param name="x" expr="_event.data.lon" /> + <param name="y" expr="1" /> + <param name="z" expr="_event.data.lat" /> + </send> + </transition> + + <!-- + Ticker message was received + -> Send to XHTML invoker to display + -> Play clicking noise from respective direction + -> Speak content if severity exceeds a given threshold + --> + <transition target="idle" event="umundo.rcvd"> + <send target="#_xhtml1"> + <content>${_event.data}</content> + </send> + <send target="#_audio" event="play" delay="0ms"> + <param name="src" expr="'click.wav'" /> + <param name="x" expr="_event.data.lon" /> + <param name="y" expr="0" /> + <param name="z" expr="_event.data.lat" /> + </send> + + <if cond="_event.data.message && _event.data.severity > 8"> + <send target="#_vxml"> + <content> + <vxml:prompt> + ${_event.data.message} + </vxml:prompt> + </content> + </send> + </if> + + </transition> + </state> + </state> +</scxml>
\ No newline at end of file diff --git a/apps/samples/map/spoken-map-ticker.xhtml b/apps/samples/map/spoken-map-ticker.xhtml new file mode 100644 index 0000000..ace8724 --- /dev/null +++ b/apps/samples/map/spoken-map-ticker.xhtml @@ -0,0 +1,240 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <!-- space in between script is required as <script/> is not valid HTML to most browsers --> + <script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.js"> </script> + <script type="text/javascript" src="http://www.openlayers.org/api/OpenLayers.js"> </script> + <script type="text/javascript" src="http://epikur.local/~sradomski/CometSession.js"> </script> + <script type="text/javascript" src="https://raw.github.com/jchavannes/jquery-timer/master/jquery.timer.js"> </script> + + <style type="text/css"> + html, body, #map { + width: 100%; + height: 100%; + margin: 0; + } + .olPopup { + border-radius: 8px; + } + div.olControlOverviewMapElement { + background-color: #666; + } + div.olMap { + width: 180px; + height: 120px; + margin: 0; + } + div.timeRuler { + font-family:Courier,Arial,Helvetica,sans-serif; + font-size: 1.2em; + font-weight: bold; + background-color: #eee; + } + div#message { + overflow: auto; + height: 100%; + font-family:Courier,Arial,Helvetica,sans-serif; + font-size: 0.6em; + } + + div#message p { + padding: 0px; + margin: 4px 2px; + } + + </style> + + <!-- script type="text/javascript" src="http://localhost/~sradomski/CometSession.js"></script --> + <script type="text/javascript"> +//<![CDATA[ + $(document).ready(function() { + var scxml = new CometSession({ + server: document.URL, + onEvent: function(response, message) { + if (message) { + console.log(message); + + if (!markers[message.id]) { + addMarker(message); + } else { + for (i = 0; i < map.popups.length; i++) { + if (map.popups[i].feature.id === message.id) { + map.popups[i].setContentHTML("<h3>" + message.id + "</h3>" + message.html); + } + } + markers[message.id].marker.opacity = 1.0; + markers[message.id].marker.setOpacity(1.0); + + markers[message.id].marker.timer.play(); + markers[message.id].feature.popupContentHTML = "<h3>" + message.id + "</h3>" + message.html; + } + + if (message.message && message.message.length > 0) { + var messageDiv = $("#message")[0]; + if (message.timeStamp > lastUpdateTime + 3000) { + var timeRuler = document.createElement("div"); + timeRuler.setAttribute("class", "timeRuler"); + timeRuler.innerHTML = message.time; // + messageDiv.innerHTML; + messageDiv.appendChild(timeRuler); + lastUpdateTime = message.timeStamp; + } + + console.log(message.timeStamp); + console.log(lastUpdateTime); + + var messagePara = document.createElement("p"); + if (message.severity > 7) { + messagePara.setAttribute("style", "color:red; font-weight:bold;"); + } + messagePara.innerHTML += message.message; // + messageDiv.innerHTML; + + messageDiv.appendChild(messagePara); + messagePara.addEventListener("mousedown", function() { + var marker = markers[message.id].marker; + marker.events.triggerEvent("mousedown"); + }); + messagePara.addEventListener("mouseover", function() { + messagePara.style.backgroundColor = "#eee"; + }); + messagePara.addEventListener("mouseout", function() { + messagePara.style.backgroundColor = "#fff"; + }); + + if (!stopScrolling) { + messageDiv.scrollTop = messageDiv.scrollHeight; + } + } + } + } + }); + + // var map = new OpenLayers.Map("demoMap"); + // map.addLayer(new OpenLayers.Layer.OSM()); + // map.zoomToMaxExtent(); + + var markers = {}; + var lastUpdateTime = 0; + var stopScrolling = false; + + var fromProjection = new OpenLayers.Projection("EPSG:4326"); // Transform from WGS 1984 + var toProjection = new OpenLayers.Projection("EPSG:900913"); // to Spherical Mercator Projection + + function onMoveEvent() { + var position = map.getCenter().transform(toProjection, fromProjection); + if (Math.random() > 0.7) { // do not publish every move event + scxml.send('map.center', { + 'zoom': map.getZoom(), + 'lon': position.lon, + 'lat': position.lat }); + } + } + + function onMoveEndEvent() { + var position = map.getCenter().transform(toProjection, fromProjection); + scxml.send('map.center', { + 'zoom': map.getZoom(), + 'lon': position.lon, + 'lat': position.lat }); + } + + function addMarker(message) { + var feature = new OpenLayers.Feature( + markerLayer, + new OpenLayers.LonLat(message.lon, message.lat).transform(fromProjection, toProjection)); + feature.closeBox = true; + feature.popupClass = OpenLayers.Class(OpenLayers.Popup.Anchored, { + 'autoSize': true, +// 'maxSize': new OpenLayers.Size(300,200) + }); + feature.data.popupContentHTML = "<h3>" + message.id + "</h3>" + message.html; + feature.data.overflow = "auto"; + feature.data.id = message.id; + var marker = feature.createMarker(); + + marker.timer = $.timer(function() { + if (marker.opacity > 0.5) { + marker.opacity -= 0.02; + marker.setOpacity(marker.opacity); + } else { + marker.timer.stop(); + } + }, 100); + + marker.opacity = 1.0; + marker.setOpacity(marker.opacity); + marker.timer.play(); + + markerClick = function(evt) { + if (this.popup == null) { + this.popup = this.createPopup(this.closeBox); + map.addPopup(this.popup); + this.popup.show(); + } else { + this.popup.toggle(); + } + currentPopup = this.popup; + OpenLayers.Event.stop(evt); + }; + + marker.events.register("mousedown", feature, markerClick); + markers[message.id] = { + 'marker': marker, + 'feature': feature + }; + markerLayer.addMarker(marker); + } + // see also: http://php-baustelle.de/openlayers/step-by-step/ + var map = new OpenLayers.Map('map', { + eventListeners: { + "move": onMoveEvent, + "moveend": onMoveEndEvent, + } + }); + + var mapnik = new OpenLayers.Layer.OSM(); + var markerLayer = new OpenLayers.Layer.Markers("Markers"); +// var attribution = new OpenLayers.Layer.Vector("Attribution", {attribution:"for Smart Vortex"}); + + map.addLayer(mapnik); +// map.addLayer(attribution); + map.addLayer(markerLayer); // has to be the last layer to receive events! + + map.addControl(new OpenLayers.Control.LayerSwitcher()); + // see also http://dev.openlayers.org/releases/OpenLayers-2.12/examples/mousewheel-interval.html + map.addControl(new OpenLayers.Control.Navigation( + {dragPanOptions: {enableKinetic: true}} + )); + + var overview = new OpenLayers.Control.OverviewMap({ + maximized: true, + minRatio: 200, + maxRatio: 300, + }); + map.addControl(overview); + + // stockholm + var position = new OpenLayers.LonLat(18.08,59.32).transform(fromProjection, toProjection); +// var position = new OpenLayers.LonLat(0,0).transform(fromProjection, toProjection); + var zoom = 11; + map.setCenter(position, zoom); + + // var message = { + // 'id': "12", + // 'lat': 59.454441065280534, + // 'lon': 18.062356332194543, + // 'html': '<p>asdf</p>' + // }; + +// addMarker(message); + }); +//]]> + </script> + </head> + <body> + <table width="100%" height="100%"> + <tr> + <td width="20%" height="100%"><div id="message" onmouseover="stopScrolling = true" onmouseout="stopScrolling = false"></div></td> + <td width="80%" height="100%"><div id="map"></div></td> + </tr> + </table> + </body> +</html>
\ No newline at end of file diff --git a/src/uscxml/Convenience.h b/src/uscxml/Convenience.h new file mode 100644 index 0000000..ad45e2c --- /dev/null +++ b/src/uscxml/Convenience.h @@ -0,0 +1,33 @@ +#ifndef CONVENIENCE_H_LU7GZ6CB +#define CONVENIENCE_H_LU7GZ6CB + +namespace uscxml { + inline bool isnan(double x) { + return x != x; + } + + // see http://stackoverflow.com/questions/228005/alternative-to-itoa-for-converting-integer-to-string-c + template <typename T> std::string toStr(T tmp) { + std::ostringstream out; + out.precision(std::numeric_limits<double>::digits10 + 1); + out << tmp; + return out.str(); + } + + template <typename T> T strTo(std::string tmp) { + T output; + std::istringstream in(tmp); + in >> output; + return output; + } + + inline bool isNumeric( const char* pszInput, int nNumberBase) { + std::string base = ".-0123456789ABCDEF"; + std::string input = pszInput; + return (input.find_first_not_of(base.substr(0, nNumberBase + 2)) == std::string::npos); + } + +} + + +#endif /* end of include guard: CONVENIENCE_H_LU7GZ6CB */ diff --git a/src/uscxml/Factory.h b/src/uscxml/Factory.h index b815712..617e461 100644 --- a/src/uscxml/Factory.h +++ b/src/uscxml/Factory.h @@ -2,6 +2,7 @@ #define FACTORY_H_5WKLGPRB #include "uscxml/Message.h" +#include "uscxml/Convenience.h" #ifdef BUILD_AS_PLUGINS #include "Pluma/Pluma.hpp" @@ -14,31 +15,6 @@ namespace uscxml { -inline bool isnan(double x) { - return x != x; -} - -// see http://stackoverflow.com/questions/228005/alternative-to-itoa-for-converting-integer-to-string-c -template <typename T> std::string toStr(T tmp) { - std::ostringstream out; - out.precision(std::numeric_limits<double>::digits10 + 1); - out << tmp; - return out.str(); -} - -template <typename T> T strTo(std::string tmp) { - T output; - std::istringstream in(tmp); - in >> output; - return output; -} - -inline bool isNumeric( const char* pszInput, int nNumberBase) { - std::string base = ".-0123456789ABCDEF"; - std::string input = pszInput; - return (input.find_first_not_of(base.substr(0, nNumberBase + 2)) == std::string::npos); -} - class InterpreterImpl; class ExecutableContentImpl { diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp index ef542d7..0a686d5 100644 --- a/src/uscxml/Message.cpp +++ b/src/uscxml/Message.cpp @@ -22,27 +22,27 @@ namespace uscxml { static int _dataIndentation = 1; Blob::~Blob() { - free(_data); + free(data); } -Blob::Blob(size_t size) { - _data = (char*)malloc(size); - memset(_data, 0, size); - _size = size; +Blob::Blob(size_t _size) { + data = (char*)malloc(_size); + memset(data, 0, _size); + size = _size; } -Blob::Blob(void* data, size_t size, bool adopt) { +Blob::Blob(void* _data, size_t _size, bool adopt) { if (adopt) { - _data = (char*)data; + data = (char*)_data; } else { - _data = (char*)malloc(size); - memcpy(_data, data, size); + data = (char*)malloc(_size); + memcpy(data, _data, _size); } - _size = size; + size = _size; } -Data::Data(const char* data, size_t size, bool adopt) { - binary = boost::shared_ptr<Blob>(new Blob((void*)data, size, adopt)); +Data::Data(const char* _data, size_t _size, bool adopt) { + binary = boost::shared_ptr<Blob>(new Blob((void*)_data, _size, adopt)); } Data::Data(const Arabica::DOM::Node<std::string>& dom) { diff --git a/src/uscxml/Message.h b/src/uscxml/Message.h index 9ae6ea1..bc92180 100644 --- a/src/uscxml/Message.h +++ b/src/uscxml/Message.h @@ -14,6 +14,8 @@ #include <boost/lexical_cast.hpp> #include <inttypes.h> +#include "uscxml/Convenience.h" + #define TAGNAME(elem) ((Arabica::DOM::Element<std::string>)elem).getTagName() #define LOCALNAME(elem) ((Arabica::DOM::Element<std::string>)elem).getLocalName() #define ATTR(elem, attr) ((Arabica::DOM::Element<std::string>)elem).getAttribute(attr) @@ -26,8 +28,9 @@ public: ~Blob(); Blob(size_t size); Blob(void* data, size_t size, bool adopt = false); - char* _data; - size_t _size; + char* data; + size_t size; + std::string mimetype; }; class Data { @@ -40,6 +43,15 @@ public: Data() : type(INTERPRETED) {} Data(const std::string& atom_, Type type_ = INTERPRETED) : atom(atom_), type(type_) {} Data(const char* data, size_t size, bool adopt); + Data(bool atom_) : type(INTERPRETED) { + if (atom_) { + atom = "true"; + } else { + atom = "false"; + } + } + template <typename T> Data(T value) : atom(toStr(value)), type(INTERPRETED) {} + explicit Data(const Arabica::DOM::Node<std::string>& dom); virtual ~Data() {} diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp index 62c61e0..176bddf 100644 --- a/src/uscxml/interpreter/InterpreterDraft6.cpp +++ b/src/uscxml/interpreter/InterpreterDraft6.cpp @@ -56,8 +56,9 @@ void InterpreterDraft6::interpret() { for (unsigned int i = 0; i < dataElems.size(); i++) { // do not process data elements of nested documents from invokers if (!getAncestorElement(dataElems[i], _xmlNSPrefix + "invoke")) - if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE) + if (dataElems[i].getNodeType() == Node_base::ELEMENT_NODE) { initializeData(Element<std::string>(dataElems[i])); + } } } else if(_dataModel) { // initialize current data elements diff --git a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.cpp b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.cpp index d24639a..8d126e9 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.cpp +++ b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.cpp @@ -4,14 +4,14 @@ #define DATAVIEW_TYPED_GET(type) \ type retVal;\ -if (index + _start + sizeof(type) > _buffer->_size)\ +if (index + _start + sizeof(type) > _buffer->size)\ return 0;\ -memcpy(&retVal, _buffer->_data + (_start + index), sizeof(type)); +memcpy(&retVal, _buffer->data + (_start + index), sizeof(type)); #define DATAVIEW_TYPED_SET(type) \ -if (index + _start + sizeof(type) > _buffer->_size)\ +if (index + _start + sizeof(type) > _buffer->size)\ return;\ -memcpy(_buffer->_data + (_start + index), &value, sizeof(type)); +memcpy(_buffer->data + (_start + index), &value, sizeof(type)); namespace uscxml { @@ -29,26 +29,26 @@ ArrayBuffer::ArrayBuffer(void* data, unsigned int size) { unsigned long ArrayBuffer::getByteLength() { if (!_buffer) return 0; - return _buffer->_size; + return _buffer->size; } ArrayBuffer ArrayBuffer::slice(long begin, long end) { if (!_buffer) { return ArrayBuffer(0); } - unsigned int realBegin = (begin + _buffer->_size) % _buffer->_size; - unsigned int realEnd = (end + _buffer->_size) % _buffer->_size; + unsigned int realBegin = (begin + _buffer->size) % _buffer->size; + unsigned int realEnd = (end + _buffer->size) % _buffer->size; if (realEnd < realBegin) { return ArrayBuffer(0); } ArrayBuffer arrBuffer(realEnd - realBegin); - memcpy(arrBuffer._buffer->_data, _buffer->_data + realBegin, realEnd - realBegin); + memcpy(arrBuffer._buffer->data, _buffer->data + realBegin, realEnd - realBegin); return arrBuffer; } ArrayBuffer ArrayBuffer::slice(long begin) { - return slice(begin, _buffer->_size); + return slice(begin, _buffer->size); } bool ArrayBuffer::isView(void*) { @@ -65,25 +65,25 @@ ArrayBuffer ArrayBufferView::getBuffer() { DataView::DataView(ArrayBuffer* buffer, unsigned long byteOffset, unsigned long byteLength) { _start = byteOffset; - if (_start > buffer->_buffer->_size) + if (_start > buffer->_buffer->size) return; _end = _start + byteLength; - if (_end > buffer->_buffer->_size) + if (_end > buffer->_buffer->size) return; _buffer = buffer->_buffer; } DataView::DataView(ArrayBuffer* buffer , unsigned long byteOffset) { _start = byteOffset; - _end = buffer->_buffer->_size; - if (_start > buffer->_buffer->_size) + _end = buffer->_buffer->size; + if (_start > buffer->_buffer->size) return; _buffer = buffer->_buffer; } DataView::DataView(ArrayBuffer* buffer) { _start = 0; - _end = (buffer->_buffer->_size); + _end = (buffer->_buffer->size); _buffer = buffer->_buffer; } diff --git a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h index 5584721..460afd4 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h +++ b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h @@ -199,7 +199,7 @@ public: _start = byteOffset / sizeof(S); _end = _start + length; - if (_end > buffer->_buffer->_size / sizeof(S)) + if (_end > buffer->_buffer->size / sizeof(S)) return; _buffer = buffer->_buffer; @@ -209,12 +209,12 @@ public: return; _start = byteOffset / sizeof(S); - _end = buffer->_buffer->_size / sizeof(S); + _end = buffer->_buffer->size / sizeof(S); _buffer = buffer->_buffer; } TypedArray(uscxml::ArrayBuffer* buffer) { _start = 0; - _end = (buffer->_buffer->_size) / sizeof(S); + _end = (buffer->_buffer->size) / sizeof(S); _buffer = buffer->_buffer; } @@ -225,7 +225,7 @@ public: _start = byteOffset / sizeof(S); _end = _start + length; - if (_end > buffer->_size / sizeof(S)) + if (_end > buffer->size / sizeof(S)) return; _buffer = buffer; @@ -271,7 +271,7 @@ public: if (index >= getLength()) return static_cast<T>(0); S retVal; - memcpy(&retVal, _buffer->_data + (_start + index) * sizeof(S), sizeof(S)); + memcpy(&retVal, _buffer->data + (_start + index) * sizeof(S), sizeof(S)); return retVal; } @@ -280,7 +280,7 @@ public: * Sets the element at the given numeric index to the given value. */ void set(unsigned long index, T value) { - memcpy(_buffer->_data + (_start + index) * sizeof(S), &value, sizeof(S)); + memcpy(_buffer->data + (_start + index) * sizeof(S), &value, sizeof(S)); } /** @@ -298,13 +298,13 @@ public: void set(TypedArray<T, S>* value, unsigned long offset) { if (!_buffer) return; - if (offset * sizeof(S) + value->getByteLength() > _buffer->_size) + if (offset * sizeof(S) + value->getByteLength() > _buffer->size) return; unsigned long otherOffset = value->_start * sizeof(S); // will this work if we use the same buffer? - memcpy(_buffer->_data + (_start + offset) * sizeof(S), value->_buffer->_data + otherOffset, value->getByteLength()); + memcpy(_buffer->data + (_start + offset) * sizeof(S), value->_buffer->data + otherOffset, value->getByteLength()); } void set(TypedArray<T, S>* value) { @@ -330,7 +330,7 @@ public: return; if (sizeof(T) == sizeof(S)) { - memcpy(_buffer->_data + offset, (void*)&data[0], data.size() * sizeof(S)); + memcpy(_buffer->data + offset, (void*)&data[0], data.size() * sizeof(S)); } else { S* buffer = (S*)malloc(data.size() * sizeof(S)); typename std::vector<T>::const_iterator dataIter = data.begin(); @@ -340,7 +340,7 @@ public: dataIter++; i++; } - memcpy(_buffer->_data + offset, buffer, data.size() * sizeof(S)); + memcpy(_buffer->data + offset, buffer, data.size() * sizeof(S)); free (buffer); } } diff --git a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp index f863bb7..ee74a5f 100644 --- a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp +++ b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp @@ -204,6 +204,33 @@ void XPathDataModel::setEvent(const Event& event) { Data XPathDataModel::getStringAsData(const std::string& content) { Data data; + XPathValue<std::string> result = _xpath.evaluate_expr(content, _doc); + + std::stringstream ss; + + switch (result.type()) { + case ANY: + break; + case BOOL: + ss << result.asBool(); + break; + case NUMBER: + ss << result.asNumber(); + break; + case STRING: + ss << result.asString(); + break; + case NODE_SET: + NodeSet<std::string> ns = result.asNodeSet(); + for (int i = 0; i < ns.size(); i++) { + ss << ns[i]; + } + ss << result; + break; + } + + data.atom = ss.str(); + data.type = Data::VERBATIM; return data; } @@ -308,6 +335,7 @@ void XPathDataModel::eval(const Arabica::DOM::Element<std::string>& scriptElem, } bool XPathDataModel::isDeclared(const std::string& expr) { + return true; try { return _varResolver.isDeclared(expr) || evalAsBool(expr); } catch(...) { @@ -461,14 +489,13 @@ void XPathDataModel::init(const Element<std::string>& dataElem, Element<std::string> container = _doc.createElement("data"); container.setAttribute("id", location); + if (node) { - if (node) { - Node<std::string> data = node; - while (data) { - Node<std::string> dataClone = _doc.importNode(data, true); - container.appendChild(dataClone); - data = data.getNextSibling(); - } + Node<std::string> data = node; + while (data) { + Node<std::string> dataClone = _doc.importNode(data, true); + container.appendChild(dataClone); + data = data.getNextSibling(); } } else if (content.length() > 0) { Text<std::string> textNode = _doc.createTextNode(Interpreter::spaceNormalize(content)); @@ -497,8 +524,6 @@ void XPathDataModel::init(const Element<std::string>& dataElem, } catch (SyntaxException e) { throw Event("error.execution", Event::PLATFORM); } - } else { - LOG(ERROR) << "data element has no content"; } _datamodel.appendChild(container); @@ -740,7 +765,7 @@ NodeSetVariableResolver::resolveVariable(const std::string& namepaceUri, } void NodeSetVariableResolver::setVariable(const std::string& name, const NodeSet<std::string>& value) { -#if VERBOSE +#if 0 std::cout << std::endl << "Setting " << name << ":" << std::endl; for (int i = 0; i < value.size(); i++) { std::cout << value[i].getNodeType() << " | " << value[i] << std::endl; @@ -748,9 +773,26 @@ void NodeSetVariableResolver::setVariable(const std::string& name, const NodeSet std::cout << std::endl; #endif _variables[name] = value; +#if 0 + std::map<std::string, Arabica::XPath::NodeSet<std::string> >::iterator varIter = _variables.begin(); + while (varIter != _variables.end()) { + std::cout << varIter->first << ":" << std::endl; + for (int i = 0; i < varIter->second.size(); i++) { + std::cout << varIter->second[i].getNodeType() << " | " << varIter->second[i] << std::endl; + } + varIter++; + } +#endif } bool NodeSetVariableResolver::isDeclared(const std::string& name) { +#if 0 + std::map<std::string, Arabica::XPath::NodeSet<std::string> >::iterator varIter = _variables.begin(); + while (varIter != _variables.end()) { + std::cout << varIter->first << std::endl; + varIter++; + } +#endif return _variables.find(name) != _variables.end(); } diff --git a/src/uscxml/plugins/element/file/FileElement.cpp b/src/uscxml/plugins/element/file/FileElement.cpp index 2b48aba..e551ad7 100644 --- a/src/uscxml/plugins/element/file/FileElement.cpp +++ b/src/uscxml/plugins/element/file/FileElement.cpp @@ -78,8 +78,8 @@ void FileElement::enterElement(const Arabica::DOM::Node<std::string>& node) { } else { Data data = _interpreter->getDataModel().getStringAsData(ATTR(node, "contentexpr")); if (data.binary) { - content = data.binary->_data; - contentSize = data.binary->_size; + content = data.binary->data; + contentSize = data.binary->size; } else if (data.atom.length() > 0) { contentStr = data.atom; } diff --git a/src/uscxml/plugins/element/respond/RespondElement.cpp b/src/uscxml/plugins/element/respond/RespondElement.cpp index 9da3002..699a3a0 100644 --- a/src/uscxml/plugins/element/respond/RespondElement.cpp +++ b/src/uscxml/plugins/element/respond/RespondElement.cpp @@ -67,7 +67,7 @@ void RespondElement::enterElement(const Arabica::DOM::Node<std::string>& node) { if (contentData.atom.length() > 0) { httpReply.content = contentData.atom; } else if (contentData.binary) { - httpReply.content = std::string(contentData.binary->_data, contentData.binary->_size); + httpReply.content = std::string(contentData.binary->data, contentData.binary->size); } else { httpReply.content = Data::toJSON(contentData); } diff --git a/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp b/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp index e637f8b..36fcc1b 100644 --- a/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp +++ b/src/uscxml/plugins/invoker/ffmpeg/FFMPEGInvoker.cpp @@ -370,14 +370,14 @@ void FFMPEGInvoker::writeVideoFrame(EncodingContext* ctx, AVFormatContext *oc, A } uint32_t headerOffset = 0; - headerOffset += image->_data[10] << 0; - headerOffset += image->_data[11] << 8; - headerOffset += image->_data[12] << 16; - headerOffset += image->_data[13] << 24; + headerOffset += image->data[10] << 0; + headerOffset += image->data[11] << 8; + headerOffset += image->data[12] << 16; + headerOffset += image->data[13] << 24; // std::cout << headerOffset + (c->width * c->height) << " / " << image->_size << std::endl; - ret = avpicture_fill(&ctx->src_picture, (uint8_t*)(image->_data + headerOffset), BMP_FORMAT, c->width, c->height); + ret = avpicture_fill(&ctx->src_picture, (uint8_t*)(image->data + headerOffset), BMP_FORMAT, c->width, c->height); if (ret < 0) { fprintf(stderr, "Could not fill image from given bitmap\n"); @@ -386,7 +386,7 @@ void FFMPEGInvoker::writeVideoFrame(EncodingContext* ctx, AVFormatContext *oc, A (const uint8_t * const *)ctx->src_picture.data, ctx->src_picture.linesize, 0, c->height, ctx->dst_picture.data, ctx->dst_picture.linesize); } else { - avpicture_fill(&ctx->dst_picture, (uint8_t*)image->_data, c->pix_fmt, c->width, c->height); + avpicture_fill(&ctx->dst_picture, (uint8_t*)image->data, c->pix_fmt, c->width, c->height); } if (oc->oformat->flags & AVFMT_RAWPICTURE) { diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.cpp b/src/uscxml/plugins/invoker/im/IMInvoker.cpp index a7fd146..cff9b5f 100644 --- a/src/uscxml/plugins/invoker/im/IMInvoker.cpp +++ b/src/uscxml/plugins/invoker/im/IMInvoker.cpp @@ -1,12 +1,22 @@ #include "IMInvoker.h" #include <glog/logging.h> +#include "uscxml/UUID.h" #ifdef BUILD_AS_PLUGINS #include <Pluma/Connector.hpp> #endif -namespace uscxml { +#define GET_INSTANCE_IN_CALLBACK(account) \ +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];\ + +namespace uscxml { + #ifdef BUILD_AS_PLUGINS PLUMA_CONNECTOR bool connect(pluma::Host& host) { @@ -25,16 +35,14 @@ PurpleEventLoopUiOps IMInvoker::_uiEventLoopOps = purpleEventTimeoutRemove, purpleEventInputAdd, purpleEventInputRemove, - purpleEventInputGetError, + NULL, //purpleEventInputGetError, purpleEventTimeoutAddSec, - - /* padding */ NULL, NULL, NULL }; -PurpleAccountUiOps IMInvoker::_accountUIOps = { +PurpleAccountUiOps IMInvoker::_uiAccountOps = { accountNotifyAdded, accountStatusChanged, accountRequestAdd, @@ -44,66 +52,139 @@ PurpleAccountUiOps IMInvoker::_accountUIOps = { NULL, NULL }; - -/*** Conversation uiops ***/ -static void -null_write_conv(PurpleConversation *conv, const char *who, const char *alias, - const char *message, PurpleMessageFlags flags, time_t mtime) -{ - const char *name; - if (alias && *alias) - name = alias; - else if (who && *who) - name = who; - else - name = NULL; - - printf("(%s) %s %s: %s\n", purple_conversation_get_name(conv), - purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)), - name, message); -} -static PurpleConversationUiOps null_conv_uiops = +PurpleBlistUiOps IMInvoker::_uiBuddyOps = { + purpleNewList, + purpleNewNode, + purpleShow, + NULL, // purpleUpdate, + NULL, // purpleRemove, + NULL, // purpleDestroy, + NULL, // purpleSetVisible, + purpleRequestAddBuddy, + purpleRequestAddChat, + purpleRequestAddGroup, + NULL, // purpleSaveNode, + NULL, // purpleRemoveNode, + NULL, // purpleSaveAccount, +}; + +// file transfer +PurpleXferUiOps IMInvoker::_uiXferOps = { + purpleNewXfer, + purpleDestroy, + purpleAddXfer, + purpleUpdateProgress, + purpleCancelLocal, + purpleCancelRemote, + purpleWrite, + purpleRead, + purpleDataNotSent, + purpleAddThumbnail +}; + +// connection info +PurpleConnectionUiOps IMInvoker::_uiConnectOps = { + purpleConnectProgress, + purpleConnected, + purpleDisonnected, + purpleNotice, + purpleNetworkConnected, + purpleNetworkDisconnected, + purpleReportDisconnect, + NULL, + NULL, + NULL + +}; + +//libpurple conversation operations +PurpleConversationUiOps IMInvoker::_uiConvOps = { - NULL, /* create_conversation */ - NULL, /* destroy_conversation */ - NULL, /* write_chat */ - NULL, /* write_im */ - null_write_conv, /* write_conv */ - NULL, /* chat_add_users */ - NULL, /* chat_rename_user */ - NULL, /* chat_remove_users */ - NULL, /* chat_update_user */ - NULL, /* present */ - NULL, /* has_focus */ - NULL, /* custom_smiley_add */ - NULL, /* custom_smiley_write */ - NULL, /* custom_smiley_close */ - NULL, /* send_confirm */ + NULL, //purpleCreateConversation, + NULL, //purpleDestroyConversation, + NULL, //purpleWriteChat, + NULL, //purpleWriteIm, + purpleWriteConv, + NULL, //purpleChatRenameUser, + NULL, //purpleChatRemoveUsers, + NULL, //purpleChatUpdateUser, + NULL, //purplePresentConversation, + NULL, //purpleHasFocus, + NULL, //purpleCustomSmileyAdd, + NULL, //purpleCustomSmileyWrite, + NULL, //purpleCustomSmileyClose, + NULL, //purpleSendConfirm, NULL, NULL, NULL, NULL }; -static void -null_ui_init(void) -{ - /** - * This should initialize the UI components for all the modules. Here we - * just initialize the UI for conversations. - */ - purple_conversations_set_ui_ops(&null_conv_uiops); -} +PurpleNotifyUiOps IMInvoker::_uiNotifyOps = { + purpeNotifyMessage, + purpeNotifyEmail, + purpeNotifyEmails, + purpeNotifyFormatted, + purpeNotifySearchResults, + purpeNotifySearchResultsNewRows, + purpeNotifyUserInfo, + purpeNotifyURI, + purpeNotifyClose, + NULL, + NULL, + NULL, + NULL +}; -static PurpleCoreUiOps null_core_uiops = -{ +PurplePrivacyUiOps IMInvoker::_uiPrivacyOps = { + purplePermitAdded, + purplePermitRemoved, + purpleDebyAdded, + purpleDenyRemoved, + NULL, + NULL, NULL, + NULL +}; + +PurpleRequestFeature IMInvoker::_features; +PurpleRequestUiOps IMInvoker::_uiRequestOps = { + _features, + purpleRequestInput, + purpleRequestChoice, + purpleRequestAction, + purpleRequestWait, + purpleRequestWaitUpdate, + purpleRequestFields, + purpleRequestFile, + purpleRequestFolder, + purpleRequestClose, NULL, - null_ui_init, NULL, + NULL, + NULL +}; + +PurpleWhiteboardUiOps IMInvoker::_uiWhiteboardOps = { + purpleCreateWB, + purpleDestroyWB, + purpleSetDimensions, + purpleSetBrush, + purpleDrawPont, + purpleDrawLine, + purpleClearWB, + NULL, + NULL, + NULL, + NULL +}; - /* padding */ +PurpleCoreUiOps IMInvoker::_uiCoreOps = { + NULL, + NULL, + purpleUIInit, + NULL, NULL, NULL, NULL, @@ -112,8 +193,22 @@ static PurpleCoreUiOps null_core_uiops = DelayedEventQueue* IMInvoker::_eventQueue = NULL; +tthread::mutex IMInvoker::_initMutex; +tthread::condition_variable IMInvoker::_initCond; + +tthread::mutex IMInvoker::_accountMutex; +std::map<PurpleAccount*, IMInvoker*> IMInvoker::_accountInstances; + void IMInvoker::initLibPurple(void *userdata, const std::string event) { + _initMutex.lock(); + _uiInfo = g_hash_table_new(g_str_hash, g_str_equal); +// g_hash_table_insert(_uiInfo, "name", (char*)"uscxml"); +// g_hash_table_insert(_uiInfo, "version", "0.0.3"); +// g_hash_table_insert(_uiInfo, "website", "http://uscxml.tk.informatik.tu-darmstadt.de"); +// g_hash_table_insert(_uiInfo, "dev_website", "http://uscxml.tk.informatik.tu-darmstadt.de"); +// g_hash_table_insert(_uiInfo, "client_type", "pc"); + _gRand = g_rand_new(); /* Set a custom user directory (optional) */ @@ -122,7 +217,7 @@ void IMInvoker::initLibPurple(void *userdata, const std::string event) { /* We do not want any debugging for now to keep the noise to a minimum. */ purple_debug_set_enabled(false); - purple_core_set_ui_ops(&null_core_uiops); + purple_core_set_ui_ops(&_uiCoreOps); // purple_eventloop_set_ui_ops(&glib_eventloops); purple_eventloop_set_ui_ops(&_uiEventLoopOps); @@ -160,63 +255,222 @@ void IMInvoker::initLibPurple(void *userdata, const std::string event) { } + _initMutex.unlock(); + _initCond.notify_all(); + } -void IMInvoker::send(void *userdata, const std::string event) { - EventContext* ctx = (EventContext*)userdata; - delete(ctx); +void IMInvoker::buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *newstatus) { + PurpleAccount *account = purple_buddy_get_account(buddy); + GET_INSTANCE_IN_CALLBACK(account); + + std::string buddyName = purple_buddy_get_name(buddy); + inst->_dataModelVars.compound["buddies"].compound[buddyName] = buddyToData(buddy); } -static void -signed_on(PurpleConnection *gc, gpointer null) -{ +void IMInvoker::buddyIdleChangeCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle) { + PurpleAccount *account = purple_buddy_get_account(buddy); + GET_INSTANCE_IN_CALLBACK(account); +} + +void IMInvoker::buddyUpdateIdleCB() { +} + +void IMInvoker::buddyAddedCB(PurpleBuddy* buddy) { + PurpleAccount *account = purple_buddy_get_account(buddy); + GET_INSTANCE_IN_CALLBACK(account); +} + +void IMInvoker::buddyRemovedCB(PurpleBuddy* buddy) { + PurpleAccount *account = purple_buddy_get_account(buddy); + GET_INSTANCE_IN_CALLBACK(account); +} + +void IMInvoker::buddyCapsChangedCB(PurpleBuddy* buddy, PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps) { + PurpleAccount *account = purple_buddy_get_account(buddy); + GET_INSTANCE_IN_CALLBACK(account); +} + +gboolean IMInvoker::jabberRcvdPresenceCB(PurpleConnection *gc, const char *type, const char *from, xmlnode *presence) { PurpleAccount *account = purple_connection_get_account(gc); - printf("Account connected: %s %s\n", purple_account_get_username(account), purple_account_get_protocol_id(account)); + GET_INSTANCE_IN_CALLBACK(account); } -void IMInvoker::invoke(void *userdata, const std::string event) { - EventContext* ctx = (EventContext*)userdata; - IMInvoker* instance = ctx->instance; +void IMInvoker::buddySignOnOffCB(PurpleBuddy *buddy) { + 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 username; - Event::getParam(ctx->invokeReq.params, "username", username); - std::string protocolId; - Event::getParam(ctx->invokeReq.params, "protocol", protocolId); - std::string password; - Event::getParam(ctx->invokeReq.params, "password", password); + std::string buddyName = purple_buddy_get_name(buddy); + inst->_dataModelVars.compound["buddies"].compound[buddyName] = buddyToData(buddy); +} + +void IMInvoker::signedOnCB(PurpleConnection *gc, gpointer null) { + PurpleSavedStatus* status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE); + purple_savedstatus_activate(status); +} - instance->_account = purple_account_new(username.c_str(), protocolId.c_str()); - purple_account_set_password(instance->_account, password.c_str(), NULL, NULL); - purple_accounts_set_ui_ops(&_accountUIOps); +Data IMInvoker::buddyToData(PurpleBuddy *buddy) { + Data data; + std::string buddyName = purple_buddy_get_name(buddy); - purple_account_set_enabled(instance->_account, "uscxml", true); + if (purple_buddy_get_name(buddy)) data.compound["name"] = Data(purple_buddy_get_name(buddy), Data::VERBATIM); + if (purple_buddy_get_alias(buddy)) data.compound["alias"] = Data(purple_buddy_get_alias(buddy), Data::VERBATIM); + if (purple_buddy_get_alias_only(buddy)) data.compound["aliasOnly"] = Data(purple_buddy_get_alias_only(buddy), Data::VERBATIM); + if (purple_buddy_get_server_alias(buddy)) data.compound["server"] = Data(purple_buddy_get_server_alias(buddy), Data::VERBATIM); - PurpleSavedStatus* status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE); - purple_savedstatus_activate(status); - - int handle; - purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, - PURPLE_CALLBACK(signed_on), NULL); + PurpleGroup* group = purple_buddy_get_group(buddy); + if (group) { + if (purple_group_get_name(group)) data.compound["group"] = Data(purple_group_get_name(group), Data::VERBATIM); + } - delete(ctx); + PurpleBuddyIcon* icon = purple_buddy_get_icon(buddy); + if (icon) { + size_t iconSize = 0; + gconstpointer iconData = purple_buddy_icon_get_data(icon, &iconSize); + data.compound["icon"] = Data((char*)iconData, iconSize, false); + } + + PurplePresence* presence = purple_buddy_get_presence(buddy); + + if (presence) { + GList *statusElem; + GList *statusList = purple_presence_get_statuses(presence); + PurpleStatus* status; + + 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); + + } + + } + std::cout << Data::toJSON(data); + return data; } +Data IMInvoker::purpleValueToData(PurpleValue* value) { + Data data; + switch (purple_value_get_type(value)) { + case PURPLE_TYPE_BOOLEAN: + if (purple_value_get_boolean(value)) + data = Data("true"); + data = Data("false"); + break; + 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: + Data(purple_value_get_char(value)); + break; + case PURPLE_TYPE_UCHAR: + Data(purple_value_get_uchar(value)); + break; + case PURPLE_TYPE_SHORT: + Data(purple_value_get_short(value)); + break; + case PURPLE_TYPE_USHORT: + Data(purple_value_get_ushort(value)); + break; + case PURPLE_TYPE_INT: + Data(purple_value_get_int(value)); + break; + case PURPLE_TYPE_UINT: + Data(purple_value_get_uint(value)); + break; + case PURPLE_TYPE_LONG: + Data(purple_value_get_long(value)); + break; + case PURPLE_TYPE_ULONG: + Data(purple_value_get_ulong(value)); + break; + case PURPLE_TYPE_INT64: + Data(purple_value_get_int64(value)); + break; + case PURPLE_TYPE_UINT64: + Data(purple_value_get_uint64(value)); + break; + case PURPLE_TYPE_OBJECT: + case PURPLE_TYPE_POINTER: + case PURPLE_TYPE_ENUM: + case PURPLE_TYPE_BOXED: + case PURPLE_TYPE_UNKNOWN: + case PURPLE_TYPE_SUBTYPE: + LOG(ERROR) << "purple thingy not supported"; + break; + } + return data; +} + IMInvoker::IMInvoker() { _account = NULL; if (!_eventQueue) { + tthread::lock_guard<tthread::mutex> 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() { if (_account) { + _accountMutex.lock(); + _accountInstances.erase(_account); purple_account_destroy(_account); + _accountMutex.unlock(); } }; @@ -226,17 +480,62 @@ boost::shared_ptr<InvokerImpl> IMInvoker::create(InterpreterImpl* interpreter) { } Data IMInvoker::getDataModelVariables() { - return _pluginData; + tthread::lock_guard<tthread::mutex> lock(_accountMutex); + return _dataModelVars; } void IMInvoker::send(const SendRequest& req) { EventContext* ctx = new EventContext(); ctx->sendReq = req; ctx->instance = this; - _eventQueue->addEvent(req.sendid, IMInvoker::invoke, 0, ctx); + + std::string eventId = UUID::getUUID(); + _eventQueue->addEvent(eventId, IMInvoker::send, 0, ctx); return; } +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; + Event::getParam(ctx->sendReq.params, "receiver", receiver); + + Data data; + Event::getParam(ctx->sendReq.params, "data", data); + + // purple_conv_im_send + 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; + PurplePluginProtocolInfo *prpl_info; + + + if (gc) { + 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); + } + + } +#endif + } + } +#endif + delete(ctx); +} + void IMInvoker::cancel(const std::string sendId) { } @@ -249,6 +548,44 @@ void IMInvoker::invoke(const InvokeRequest& req) { return; } +void IMInvoker::invoke(void *userdata, const std::string event) { + _accountMutex.lock(); + + EventContext* ctx = (EventContext*)userdata; + IMInvoker* instance = ctx->instance; + + std::string username; + Event::getParam(ctx->invokeReq.params, "username", username); + std::string protocolId; + Event::getParam(ctx->invokeReq.params, "protocol", protocolId); + std::string password; + Event::getParam(ctx->invokeReq.params, "password", password); + + instance->_account = purple_account_new(username.c_str(), protocolId.c_str()); + _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(); +} + guint IMInvoker::purpleEventTimeoutAdd(guint interval, GSourceFunc function, gpointer data) { PurpleEventContext* ctx = new PurpleEventContext(); ctx->function = function; @@ -294,6 +631,7 @@ gboolean IMInvoker::purpleEventInputRemove(guint handle) { } int IMInvoker::purpleEventInputGetError(int fd, int *error) { + // unused std::cout << "purpleEventInputGetError" << std::endl; return 0; } @@ -310,7 +648,18 @@ void IMInvoker::purpleCallback(void *userdata, const std::string event) { void IMInvoker::purplePrefsInit(void) {} void IMInvoker::purpleDebugInit(void) {} -void IMInvoker::purpleUIInit(void) {} + +void IMInvoker::purpleUIInit(void) { + purple_accounts_set_ui_ops(&_uiAccountOps); + purple_xfers_set_ui_ops(&_uiXferOps); +// purple_blist_set_ui_ops(&_uiBuddyOps); +// purple_notify_set_ui_ops(&_uiNotifyOps); +// purple_privacy_set_ui_ops(&_uiPrivacyOps); +// purple_request_set_ui_ops(&_uiRequestOps); +// purple_connections_set_ui_ops(&_uiConnectOps); +// purple_whiteboard_set_ui_ops(&_uiWhiteboardOps); + purple_conversations_set_ui_ops(&_uiConvOps); +} void IMInvoker::purpleQuit(void) {} GHashTable* IMInvoker::purpleGetUIInfo(void) { @@ -357,5 +706,183 @@ void IMInvoker::accountCloseRequest(void *ui_handle) { std::cout << "accountCloseRequest" << std::endl; } +//libpurple conversation operations +void IMInvoker::purpleCreateConversation(PurpleConversation *conv) {} +void IMInvoker::purpleDestroyConversation(PurpleConversation *conv) {} +void IMInvoker::purpleWriteChat(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime) {} +void IMInvoker::purpleWriteIm(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime) {} +void IMInvoker::purpleWriteConv(PurpleConversation *conv, const char *name, const char *alias, const char *message, PurpleMessageFlags flags, time_t mtime) { + const char *who; + if (alias && *alias) + who = alias; + else if (name && *name) + who = name; + else + who = NULL; + + printf("(%s) %s %s: %s\n", purple_conversation_get_name(conv), + purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)), + who, message); +} +void IMInvoker::purpleChatAddUsers(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals) {} +void IMInvoker::purpleChatRenameUser(PurpleConversation *conv, const char *old_name, const char *new_name, const char *new_alias) {} +void IMInvoker::purpleChatRemoveUsers(PurpleConversation *conv, GList *users) {} +void IMInvoker::purpleChatUpdateUser(PurpleConversation *conv, const char *user) {} +void IMInvoker::purplePresentConversation(PurpleConversation *conv) {} +gboolean IMInvoker::purpleHasFocus(PurpleConversation *conv) { + return true; +} +gboolean IMInvoker::purpleCustomSmileyAdd(PurpleConversation *conv, const char *smile, gboolean remote) { + return true; +} +void IMInvoker::purpleCustomSmileyWrite(PurpleConversation *conv, const char *smile, const guchar *data, gsize size) {} +void IMInvoker::purpleCustomSmileyClose(PurpleConversation *conv, const char *smile) {} +void IMInvoker::purpleSendConfirm(PurpleConversation *conv, const char *message) {} + +// buddy operations +void IMInvoker::purpleNewList(PurpleBuddyList *list) { + std::cout << "purpleNewList" << std::endl; +} +void IMInvoker::purpleNewNode(PurpleBlistNode *node) { + std::cout << "purpleNewNode" << std::endl; +} +void IMInvoker::purpleShow(PurpleBuddyList *list) { + std::cout << "purpleShow" << std::endl; +} +void IMInvoker::purpleUpdate(PurpleBuddyList *list, PurpleBlistNode *node) { +} +void IMInvoker::purpleRemove(PurpleBuddyList *list, PurpleBlistNode *node) {} +void IMInvoker::purpleDestroy(PurpleBuddyList *list) {} +void IMInvoker::purpleSetVisible(PurpleBuddyList *list, gboolean show) {} +void IMInvoker::purpleRequestAddBuddy(PurpleAccount *account, const char *username, const char *group, const char *alias) { + std::cout << "purpleRequestAddBuddy" << std::endl; +} +void IMInvoker::purpleRequestAddChat(PurpleAccount *account, PurpleGroup *group, const char *alias, const char *name) { + std::cout << "purpleRequestAddChat" << std::endl; +} +void IMInvoker::purpleRequestAddGroup(void) { + std::cout << "purpleRequestAddGroup" << std::endl; +} +void IMInvoker::purpleSaveNode(PurpleBlistNode *node) {} +void IMInvoker::purpleRemoveNode(PurpleBlistNode *node) {} +void IMInvoker::purpleSaveAccount(PurpleAccount *account) {} + +// file transfer operations +void IMInvoker::purpleNewXfer(PurpleXfer *xfer) { + std::cout << "purpleNewXfer" << std::endl; +} +void IMInvoker::purpleDestroy(PurpleXfer *xfer) { + std::cout << "purpleDestroy" << std::endl; +} +void IMInvoker::purpleAddXfer(PurpleXfer *xfer) { + std::cout << "purpleAddXfer" << std::endl; +} +void IMInvoker::purpleUpdateProgress(PurpleXfer *xfer, double percent) { + std::cout << "purpleUpdateProgress" << std::endl; +} +void IMInvoker::purpleCancelLocal(PurpleXfer *xfer) { + std::cout << "purpleCancelLocal" << std::endl; +} +void IMInvoker::purpleCancelRemote(PurpleXfer *xfer) { + std::cout << "purpleCancelRemote" << std::endl; +} +gssize IMInvoker::purpleWrite(PurpleXfer *xfer, const guchar *buffer, gssize size) { + std::cout << "purpleWrite" << std::endl; +} +gssize IMInvoker::purpleRead(PurpleXfer *xfer, guchar **buffer, gssize size) { + std::cout << "purpleRead" << std::endl; +} +void IMInvoker::purpleDataNotSent(PurpleXfer *xfer, const guchar *buffer, gsize size) { + std::cout << "purpleDataNotSent" << std::endl; +} +void IMInvoker::purpleAddThumbnail(PurpleXfer *xfer, const gchar *formats) { + std::cout << "purpleAddThumbnail" << std::endl; +} + +// notification operations +void* IMInvoker::purpeNotifyMessage(PurpleNotifyMsgType type, const char *title, const char *primary, const char *secondary, PurpleRequestCommonParameters *cpar) { + return NULL; +} +void* IMInvoker::purpeNotifyEmail(PurpleConnection *gc, const char *subject, const char *from, const char *to, const char *url) { + return NULL; +} +void* IMInvoker::purpeNotifyEmails(PurpleConnection *gc, size_t count, gboolean detailed, const char **subjects, const char **froms, const char **tos, const char **urls) { + return NULL; +} +void* IMInvoker::purpeNotifyFormatted(const char *title, const char *primary, const char *secondary, const char *text) { + return NULL; +} +void* IMInvoker::purpeNotifySearchResults(PurpleConnection *gc, const char *title, const char *primary, const char *secondary, PurpleNotifySearchResults *results, gpointer user_data) { + return NULL; +} +void IMInvoker::purpeNotifySearchResultsNewRows(PurpleConnection *gc, PurpleNotifySearchResults *results, void *data) {} +void* IMInvoker::purpeNotifyUserInfo(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info) { + return NULL; +} +void* IMInvoker::purpeNotifyURI(const char *uri) { + return NULL; +} +void IMInvoker::purpeNotifyClose(PurpleNotifyType type, void *ui_handle) {} + +// privacy ui operations +void IMInvoker::purplePermitAdded(PurpleAccount *account, const char *name) {} +void IMInvoker::purplePermitRemoved(PurpleAccount *account, const char *name) {} +void IMInvoker::purpleDebyAdded(PurpleAccount *account, const char *name) {} +void IMInvoker::purpleDenyRemoved(PurpleAccount *account, const char *name) {} + +// request ui operations +void* IMInvoker::purpleRequestInput(const char *title, const char *primary, + const char *secondary, const char *default_value, + 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) {} +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* 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) {} +void* IMInvoker::purpleRequestWait(const char *title, const char *primary, + const char *secondary, gboolean with_progress, + PurpleRequestCancelCb cancel_cb, + PurpleRequestCommonParameters *cpar, void *user_data) {} + +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) {} +void* IMInvoker::purpleRequestFile(const char *title, const char *filename, + gboolean savedialog, GCallback ok_cb, GCallback cancel_cb, + PurpleRequestCommonParameters *cpar, void *user_data) {} +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) {} + + +// connection ui operations +void IMInvoker::purpleConnectProgress(PurpleConnection *gc, const char *text, size_t step, size_t step_count) {} +void IMInvoker::purpleConnected(PurpleConnection *gc) {} +void IMInvoker::purpleDisonnected(PurpleConnection *gc) {} +void IMInvoker::purpleNotice(PurpleConnection *gc, const char *text) {} +void IMInvoker::purpleNetworkConnected(void) {} +void IMInvoker::purpleNetworkDisconnected(void) {} +void IMInvoker::purpleReportDisconnect(PurpleConnection *gc, PurpleConnectionError reason, const char *text) {} + +// whiteboard ui operations +void IMInvoker::purpleCreateWB(PurpleWhiteboard *wb) {} +void IMInvoker::purpleDestroyWB(PurpleWhiteboard *wb) {} +void IMInvoker::purpleSetDimensions(PurpleWhiteboard *wb, int width, int height) {} +void IMInvoker::purpleSetBrush(PurpleWhiteboard *wb, int size, int color) {} +void IMInvoker::purpleDrawPont(PurpleWhiteboard *wb, int x, int y, int color, int size) {} +void IMInvoker::purpleDrawLine(PurpleWhiteboard *wb, int x1, int y1, int x2, int y2, int color, int size) {} +void IMInvoker::purpleClearWB(PurpleWhiteboard *wb) {} + }
\ 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 fb02e46..b8a57f6 100644 --- a/src/uscxml/plugins/invoker/im/IMInvoker.h +++ b/src/uscxml/plugins/invoker/im/IMInvoker.h @@ -3,9 +3,9 @@ #include <uscxml/Interpreter.h> -//extern "C" { +extern "C" { #include <libpurple/purple.h> -//} +} #ifdef BUILD_AS_PLUGINS #include "uscxml/plugins/Plugins.h" @@ -41,14 +41,46 @@ public: protected: static bool _libPurpleIsInitialized; static Data _pluginData; - static PurpleAccountUiOps _accountUIOps; + + Data _dataModelVars; + + static Data buddyToData(PurpleBuddy *buddy); + static Data purpleValueToData(PurpleValue* value); + + static PurpleAccountUiOps _uiAccountOps; static PurpleEventLoopUiOps _uiEventLoopOps; static PurpleCoreUiOps _uiCoreOps; + static PurpleConversationUiOps _uiConvOps; + static PurpleBlistUiOps _uiBuddyOps; + static PurpleXferUiOps _uiXferOps; + static PurpleNotifyUiOps _uiNotifyOps; + static PurplePrivacyUiOps _uiPrivacyOps; + static PurpleRequestUiOps _uiRequestOps; + static PurpleConnectionUiOps _uiConnectOps; + static PurpleWhiteboardUiOps _uiWhiteboardOps; + + static PurpleRequestFeature _features; static GHashTable* _uiInfo; static GRand* _gRand; + static tthread::mutex _accountMutex; + static std::map<PurpleAccount*, IMInvoker*> _accountInstances; + static tthread::mutex _initMutex; + static tthread::condition_variable _initCond; static DelayedEventQueue* _eventQueue; + // 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 buddyAddedCB(PurpleBuddy* buddy); + static void buddyRemovedCB(PurpleBuddy* buddy); + 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 send(void *userdata, const std::string event); @@ -79,31 +111,130 @@ protected: static void purpleQuit(void); static GHashTable* purpleGetUIInfo(void); + //libpurple conversation operations + static void purpleCreateConversation(PurpleConversation *conv); + static void purpleDestroyConversation(PurpleConversation *conv); + static void purpleWriteChat(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime); + static void purpleWriteIm(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime); + static void purpleWriteConv(PurpleConversation *conv, const char *name, const char *alias, const char *message, PurpleMessageFlags flags, time_t mtime); + static void purpleChatAddUsers(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals); + static void purpleChatRenameUser(PurpleConversation *conv, const char *old_name, const char *new_name, const char *new_alias); + static void purpleChatRemoveUsers(PurpleConversation *conv, GList *users); + static void purpleChatUpdateUser(PurpleConversation *conv, const char *user); + static void purplePresentConversation(PurpleConversation *conv); + static gboolean purpleHasFocus(PurpleConversation *conv); + static gboolean purpleCustomSmileyAdd(PurpleConversation *conv, const char *smile, gboolean remote); + static void purpleCustomSmileyWrite(PurpleConversation *conv, const char *smile, const guchar *data, gsize size); + static void purpleCustomSmileyClose(PurpleConversation *conv, const char *smile); + static void purpleSendConfirm(PurpleConversation *conv, const char *message); + + // buddy operations + static void purpleNewList(PurpleBuddyList *list); + static void purpleNewNode(PurpleBlistNode *node); + static void purpleShow(PurpleBuddyList *list); + static void purpleUpdate(PurpleBuddyList *list, PurpleBlistNode *node); + static void purpleRemove(PurpleBuddyList *list, PurpleBlistNode *node); + static void purpleDestroy(PurpleBuddyList *list); + static void purpleSetVisible(PurpleBuddyList *list, gboolean show); + static void purpleRequestAddBuddy(PurpleAccount *account, const char *username, const char *group, const char *alias); + static void purpleRequestAddChat(PurpleAccount *account, PurpleGroup *group, const char *alias, const char *name); + static void purpleRequestAddGroup(void); + static void purpleSaveNode(PurpleBlistNode *node); + static void purpleRemoveNode(PurpleBlistNode *node); + static void purpleSaveAccount(PurpleAccount *account); + + // file transfer operations + static void purpleNewXfer(PurpleXfer *xfer); + static void purpleDestroy(PurpleXfer *xfer); + static void purpleAddXfer(PurpleXfer *xfer); + static void purpleUpdateProgress(PurpleXfer *xfer, double percent); + static void purpleCancelLocal(PurpleXfer *xfer); + static void purpleCancelRemote(PurpleXfer *xfer); + static gssize purpleWrite(PurpleXfer *xfer, const guchar *buffer, gssize size); + static gssize purpleRead(PurpleXfer *xfer, guchar **buffer, gssize size); + static void purpleDataNotSent(PurpleXfer *xfer, const guchar *buffer, gsize size); + static void purpleAddThumbnail(PurpleXfer *xfer, const gchar *formats); + + // notification operations + static void* purpeNotifyMessage(PurpleNotifyMsgType type, const char *title, const char *primary, const char *secondary, PurpleRequestCommonParameters *cpar); + static void* purpeNotifyEmail(PurpleConnection *gc, const char *subject, const char *from, const char *to, const char *url); + static void* purpeNotifyEmails(PurpleConnection *gc, size_t count, gboolean detailed, const char **subjects, const char **froms, const char **tos, const char **urls); + static void* purpeNotifyFormatted(const char *title, const char *primary, const char *secondary, const char *text); + static void* purpeNotifySearchResults(PurpleConnection *gc, const char *title, const char *primary, const char *secondary, PurpleNotifySearchResults *results, gpointer user_data); + static void purpeNotifySearchResultsNewRows(PurpleConnection *gc, PurpleNotifySearchResults *results, void *data); + static void* purpeNotifyUserInfo(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info); + static void* purpeNotifyURI(const char *uri); + static void purpeNotifyClose(PurpleNotifyType type, void *ui_handle); + // account operations - static void accountNotifyAdded(PurpleAccount *account, - const char *remote_user, - const char *id, - const char *alias, - const char *message); - static void accountStatusChanged(PurpleAccount *account, - PurpleStatus *status); - static void accountRequestAdd(PurpleAccount *account, - const char *remote_user, - const char *id, - const char *alias, - const char *message); - static void* accountRequestAuthorize(PurpleAccount *account, - const char *remote_user, - const char *id, - const char *alias, - const char *message, - gboolean on_list, - PurpleAccountRequestAuthorizationCb authorize_cb, - PurpleAccountRequestAuthorizationCb deny_cb, - void *user_data); + static void accountNotifyAdded(PurpleAccount *account, const char *remote_user, const char *id, const char *alias, const char *message); + static void accountStatusChanged(PurpleAccount *account, PurpleStatus *status); + static void accountRequestAdd(PurpleAccount *account, const char *remote_user, const char *id, const char *alias, const char *message); + static void* accountRequestAuthorize(PurpleAccount *account, const char *remote_user, const char *id, const char *alias, const char *message, gboolean on_list, PurpleAccountRequestAuthorizationCb authorize_cb, PurpleAccountRequestAuthorizationCb deny_cb, void *user_data); static void accountCloseRequest(void *ui_handle); + // privacy ui operations + static void purplePermitAdded(PurpleAccount *account, const char *name); + static void purplePermitRemoved(PurpleAccount *account, const char *name); + static void purpleDebyAdded(PurpleAccount *account, const char *name); + static void purpleDenyRemoved(PurpleAccount *account, const char *name); + + // request ui operations + static void* purpleRequestInput(const char *title, const char *primary, + const char *secondary, const char *default_value, + 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); + static void* 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); + static void* 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); + static void* purpleRequestWait(const char *title, const char *primary, + const char *secondary, gboolean with_progress, + PurpleRequestCancelCb cancel_cb, + PurpleRequestCommonParameters *cpar, void *user_data); + + static void purpleRequestWaitUpdate(void *ui_handle, gboolean pulse, gfloat fraction); + static void* 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); + static void* purpleRequestFile(const char *title, const char *filename, + gboolean savedialog, GCallback ok_cb, GCallback cancel_cb, + PurpleRequestCommonParameters *cpar, void *user_data); + static void* purpleRequestFolder(const char *title, const char *dirname, + GCallback ok_cb, GCallback cancel_cb, + PurpleRequestCommonParameters *cpar, void *user_data); + static void purpleRequestClose(PurpleRequestType type, void *ui_handle); + + // connection ui operations + static void purpleConnectProgress(PurpleConnection *gc, const char *text, size_t step, size_t step_count); + static void purpleConnected(PurpleConnection *gc); + static void purpleDisonnected(PurpleConnection *gc); + static void purpleNotice(PurpleConnection *gc, const char *text); + static void purpleNetworkConnected(void); + static void purpleNetworkDisconnected(void); + static void purpleReportDisconnect(PurpleConnection *gc, PurpleConnectionError reason, const char *text); + + + // whiteboard ui operations + static void purpleCreateWB(PurpleWhiteboard *wb); + static void purpleDestroyWB(PurpleWhiteboard *wb); + static void purpleSetDimensions(PurpleWhiteboard *wb, int width, int height); + static void purpleSetBrush(PurpleWhiteboard *wb, int size, int color); + static void purpleDrawPont(PurpleWhiteboard *wb, int x, int y, int color, int size); + static void purpleDrawLine(PurpleWhiteboard *wb, int x1, int y1, int x2, int y2, int color, int size); + static void purpleClearWB(PurpleWhiteboard *wb); + + PurpleAccount* _account; }; diff --git a/test/samples/uscxml/applications/3dviewer.scxml b/test/samples/uscxml/applications/3dviewer.scxml deleted file mode 100644 index 90970d3..0000000 --- a/test/samples/uscxml/applications/3dviewer.scxml +++ /dev/null @@ -1,84 +0,0 @@ -<scxml - datamodel="ecmascript" - xmlns="http://www.w3.org/2005/07/scxml" - xmlns:scenegraph="http://uscxml.tk.informatik.tu-darmstadt.de/scenegraph.xsd"> - <datamodel> - <data id="localCam" /> - <data id="foo" /> - </datamodel> - <state id="start"> - <!-- setup all invokers --> - - <!-- 20ms heartbeat --> - <invoke type="heartbeat" id="heartbeat.20ms"> - <param name="interval" expr="'20ms'" /> - </invoke> - - <!-- receive files --> - <invoke type="umundo" id="umundo.files"> - <param name="domain" expr="''" /> - <param name="channel" expr="'umundo.files'" /> - <param name="types" expr="'../proto/3DViewer'" /> - </invoke> - - <!-- publish / subscribe to remote pose --> - <invoke type="umundo" id="umundo.pose"> - <param name="domain" expr="''" /> - <param name="channel" expr="'umundo.pose'" /> - <param name="types" expr="'../proto/3DViewer'" /> - </invoke> - - <!-- 3D viewer - This is only a template for nodes inserted per javascript - --> - <invoke type="scenegraph"> - <content> - <scenegraph:display id="localDisplay" width="50%" height="50%"> - <scenegraph:viewport> - <scenegraph:camera id="localCam"> - </scenegraph:camera> - <scenegraph:translation> - <scenegraph:rotation> - <scenegraph:scale> - <scenegraph:node /> - </scenegraph:scale> - </scenegraph:rotation> - </scenegraph:translation> - </scenegraph:viewport> - </scenegraph:display> - </content> - </invoke> - <state id="sendPose"> - <transition target="sendPose" event="heartbeat.20ms" /> - <onentry> - <script> - localCam = document.evaluate("//scenegraph:display[@id='localDisplay']").asNodeSet(); - print(localCam[0].getAttribute("pitch")); - </script> - <send target="#_umundo.pose" event="umundo.pose"> - <param name="type" expr="'SceneSetup'" /> - <content> -({ - 'camPose': { - 'euler': { - 'pitch': localCam.getAttribute("pitch"), - 'roll': localCam.getAttribute("roll"), - 'yaw': localCam.getAttribute("yaw"), - } - }, - 'modelPose': { - 'euler': { - 'pitch': localCam.getAttribute("pitch"), - 'roll': localCam.getAttribute("roll"), - 'yaw': localCam.getAttribute("yaw"), - } - }, - 'modelName': "foo", - 'bandName': "foo" -}) - </content> - </send> - </onentry> - </state> - </state> -</scxml>
\ No newline at end of file diff --git a/test/samples/uscxml/test-instant-messaging.scxml b/test/samples/uscxml/test-instant-messaging.scxml index 569ec59..6ca39d4 100644 --- a/test/samples/uscxml/test-instant-messaging.scxml +++ b/test/samples/uscxml/test-instant-messaging.scxml @@ -1,4 +1,14 @@ <scxml datamodel="ecmascript"> + <script> +//<![CDATA[ + someBinaryData = new ArrayBuffer(256); + var view = new Uint8Array(someBinaryData); + for (var i = 0; i < view.length; i++) { + view[i] = i; + } +//]]> + </script> + <script src="http://uscxml.tk.informatik.tu-darmstadt.de/scripts/dump.js" /> <state id="idle"> <onentry> @@ -13,7 +23,13 @@ <state id="dump"> <transition event="dump" target="dump"> - <script>dump(_invokers['im']);</script> + <send event="dump" delay="1000ms" /> + <script>//dump(_invokers['im']);</script> + <send target="#_im" event="im.send"> + <param name="receiver" expr="'sradomski@localhost'" /> + <param name="data" expr="someBinaryData" /> + <content>Hey There!</content> + </send> </transition> </state> </state> |