/*************************************************************************** * Copyright (C) by Tobias Mandrup Johansen * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * 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. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * * For details about the authors of this software, see the AUTHORS file. * ***************************************************************************/ package jolie.net; import cx.ath.matthew.unix.USOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.Vector; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerException; import javax.xml.transform.stream.StreamResult; import jolie.Interpreter; import jolie.lang.parse.ast.types.UInt32; import jolie.lang.parse.ast.types.UInt16; import jolie.lang.parse.ast.types.UInt64; import jolie.lang.parse.ast.InterfaceDefinition; import jolie.net.dbus.DBusSignal; import jolie.net.dbus.Error; import jolie.net.dbus.Message; import jolie.net.dbus.MethodCall; import jolie.net.dbus.MethodReturn; import jolie.net.ports.OutputPort; import jolie.net.ports.Interface; import jolie.net.protocols.ConcurrentCommProtocol; import jolie.runtime.FaultException; import jolie.runtime.InputOperation; import jolie.runtime.InvalidIdException; import jolie.runtime.typing.JolieDBusUtils; import jolie.runtime.typing.OneWayTypeDescription; import jolie.runtime.typing.RequestResponseTypeDescription; import jolie.runtime.typing.Type; import jolie.runtime.Value; import jolie.runtime.ValueVector; import jolie.runtime.VariablePath; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.MessageProtocolVersionException; import org.freedesktop.dbus.Transport; import org.freedesktop.dbus.Variant; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.DOMImplementation; import org.w3c.dom.DocumentType; public class DBusProtocol extends ConcurrentCommProtocol { private HashMap<Long,MethodCall> _inMethodCalls = new HashMap<Long, MethodCall>(); private HashMap<Long,MethodCall> _outMethodCalls = new HashMap<Long,MethodCall>(); private HashMap<Long,CommMessage> _inCommMessage = new HashMap<Long, CommMessage>(); private HashMap<Long,CommMessage> _outSerialMap = new HashMap<Long,CommMessage>(); private HashMap<Long,Integer> _inMap = new HashMap<Long, Integer>(); static final byte ENDIAN = Message.Endian.BIG; private final boolean _inputport; private final UInt32 _nameRequestFlags; private Interpreter _interperter; private final boolean _debug; private boolean _messageBus; private boolean _authenticated; private boolean _introspect; private byte[] _messageBody; private ArrayList<String> _rules; @Override public String name() { return "dbus"; } public DBusProtocol(VariablePath configurationPath,boolean inputport) { super(configurationPath); _inputport = inputport; _debug = checkBooleanParameter("debug"); _interperter = Interpreter.getInstance(); _nameRequestFlags = getUInt32Parameter("nrFlags"); _introspect = checkBooleanParameter("introspect",true); } private Message readMessage(InputStream in) throws IOException { //read the 12 fixed bytes of the message header byte[] buf = new byte[12]; byte[] tbuf; _messageBody = null; HashMap<Byte, Object> dbusHeaders = new HashMap<Byte, Object>(); in.read(buf); /* Parse the details from the header */ byte endian = buf[0]; byte messageType = buf[1]; byte flags = buf[2]; byte version = buf[3]; int bodyLength = (int) Message.demarshallint(buf, 4, endian, 4); int senderCookie = (int) Message.demarshallint(buf, 8, endian, 4); if(_debug){ Interpreter i = Interpreter.getInstance(); i.logInfo("Reading Message" + "\n endian : " + endian + "\n messageType : " + messageType + "\n flags : " + flags + "\n version : " + version + "\n bodyLength : " + bodyLength + "\n senderCookie : " + senderCookie); } // check protocol version. if (version > Message.PROTOCOL) { throw new MessageProtocolVersionException(MessageFormat.format("Protocol version {0} is unsupported", new Object[]{version})); } /* Read the length of the variable header */ tbuf = new byte[4]; in.read(tbuf); /* Parse the variable header length */ int headerlen; headerlen = (int) Message.demarshallint(tbuf, 0, endian, 4); if (0 != headerlen % 8) { headerlen += 8 - (headerlen % 8); } /* Read the variable header */ byte[] headers; headers = new byte[headerlen + 8]; //copy the length of the variable header into header (4 bytes) // the next 4 bytes will be empty/padding System.arraycopy(tbuf, 0, headers, 0, 4); //read headerlength bytes into header starting with offset of 8 bytes. in.read(headers, 8, headerlen); /* parse variable headers */ Object[] headerObjects; try { headerObjects = JolieDBusUtils.extract("a(yv)", headers, endian, new int[]{0, 0}); for (Object o : (Vector<Object>) headerObjects[0]) { dbusHeaders.put((Byte) ((Object[]) o)[0], ((Variant<Object>) ((Object[]) o)[1]).getValue()); } } catch (DBusException e) { throw new IOException("Error parssing DBus variable headers : " + e.toString()); } /* read body */ if (null == _messageBody) { _messageBody = new byte[bodyLength]; } in.read(_messageBody, 0, _messageBody.length); /*parse body*/ Object[] bodyObjects; String sig = (String) dbusHeaders.get(new Byte(Message.HeaderField.SIGNATURE)); if(sig != null && !sig.equals("")){ try { bodyObjects = JolieDBusUtils.extract(sig, _messageBody, endian, new int[]{0, 0}); } catch (DBusException e) { throw new IOException("Error parssing DBus message body : " + e.toString()); } } else { bodyObjects = null; } String path = (String)dbusHeaders.get(Message.HeaderField.PATH); String iface = (String)dbusHeaders.get(Message.HeaderField.INTERFACE); String member = (String)dbusHeaders.get(Message.HeaderField.MEMBER); String errorName = (String)dbusHeaders.get(Message.HeaderField.ERROR_NAME); UInt32 serial = (UInt32)dbusHeaders.get(Message.HeaderField.REPLY_SERIAL); String dest = (String)dbusHeaders.get(Message.HeaderField.DESTINATION); String source = (String)dbusHeaders.get(Message.HeaderField.SENDER); Message msg = null; MethodCall mc; switch (messageType) { case Message.MessageType.METHOD_CALL: if(_debug){ Interpreter.getInstance().logInfo("Message is a method call"); } try{ msg = new MethodCall(source,dest,path,iface,member,endian,flags,sig,bodyObjects); _inMap.put(msg.getSerial(), senderCookie ); } catch (DBusException de){ Interpreter.getInstance().logSevere("Error while parsing message : " + de.toString()); } break; case Message.MessageType.METHOD_RETURN: if(_debug){ Interpreter.getInstance().logInfo("Message is a method return"); } // get the outgoing methodCall. mc = _outMethodCalls.get(serial.longValue()); try { if(bodyObjects == null){ bodyObjects = new Object[0]; } msg = new MethodReturn(mc,endian, sig, bodyObjects); } catch(DBusException de) { _interperter.logSevere(de); } break; case Message.MessageType.SIGNAL: if(_debug){ _interperter.logInfo("Message is a signal"); } if(iface == null || member == null || path == null){ _interperter.logWarning("Invalid signal recieved : " + "\n interface : " + iface + "\n member : " + member + "\n path : " + path); } try { msg = new DBusSignal(source, path, iface, member,endian, sig, bodyObjects); } catch (DBusException de){ _interperter.logSevere(de); } break; case Message.MessageType.ERROR: if(_debug){ Interpreter.getInstance().logInfo("Message is an error"); } try { msg = new Error(dest, errorName, serial.longValue(), sig, bodyObjects); } catch (DBusException de){ _interperter.logSevere(de); } break; } return msg; } @Override public CommMessage recv(InputStream istream, OutputStream ostream) throws IOException { authenticate(istream,ostream); Message msg = readMessage(istream); CommMessage commMessage = null; if(msg instanceof MethodCall){ if (_inputport && channel().parentPort().getInterface().containsOperation(msg.getName())) { try { InputOperation operation = _interperter.getInputOperation(msg.getName()); Type requestType = operation.requestType(); Value bodyObjectsValue = Value.UNDEFINED_VALUE; if (msg.getSig() != null && !msg.getSig().equals("")) { bodyObjectsValue = JolieDBusUtils.extract(msg.getSig(), _messageBody, msg.getEndian(), new int[]{0, 0}, requestType); } commMessage = CommMessage.createRequest(msg.getName(), msg.getPath(), bodyObjectsValue); _inMethodCalls.put(commMessage.id(), (MethodCall) msg); _inCommMessage.put(commMessage.id(), commMessage); } catch (DBusException de) { _interperter.logSevere(de); } catch (InvalidIdException iie) { _interperter.logInfo("Recieved D-Bus method call for unsuported method " + iie); } } else { String output; // if the method and interface matches introspect then create automatic introspection information. if (_introspect && msg.getName().equals("Introspect") && msg.getInterface().equals("org.freedesktop.DBus.Introspectable")) { try { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); DOMImplementation domImpl = docBuilder.getDOMImplementation(); DocumentType doctype = domImpl.createDocumentType("node", "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN", "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"); Document doc = docBuilder.newDocument(); doc.appendChild(doctype); // root elements Element rootElement = doc.createElement("node"); doc.appendChild(rootElement); if (!_inputport) { TreeMap<String, TreeMap<String,OneWayTypeDescription>> interfaces = new TreeMap<String,TreeMap<String,OneWayTypeDescription>>(); TreeMap<String,OneWayTypeDescription> ifaceMap; // get the interface of the outputport. Interface iface = channel().parentOutputPort().getInterface(); Map<String, OneWayTypeDescription> oneway = iface.oneWayOperations(); Iterator itr = oneway.entrySet().iterator(); while (itr.hasNext()) { Map.Entry<String, OneWayTypeDescription> pair = (Map.Entry<String, OneWayTypeDescription>) itr.next(); InterfaceDefinition def = iface.interfaceForOperation(pair.getKey()); String ifaceName = def.name(); ifaceMap = interfaces.get(ifaceName); if(ifaceMap != null){ if(ifaceMap.get(pair.getKey()) == null){ ifaceMap.put(pair.getKey(), pair.getValue()); } } else { TreeMap<String,OneWayTypeDescription> newInterface = new TreeMap<String, OneWayTypeDescription>(); newInterface.put(pair.getKey(), pair.getValue()); interfaces.put(ifaceName, newInterface); } } for(Map.Entry<String,TreeMap<String,OneWayTypeDescription>> set : interfaces.entrySet()){ String interfaceName = set.getKey(); Element interfaceElement = doc.createElement("interface"); interfaceElement.setAttribute("name", interfaceName); rootElement.appendChild(interfaceElement); for(Map.Entry<String,OneWayTypeDescription> setItem : set.getValue().entrySet()){ String signalName = setItem.getKey(); Element signalElement = doc.createElement("signal"); signalElement.setAttribute("name", signalName); Element argumentOut = doc.createElement("arg"); try { argumentOut.setAttribute("type", getDBusSignature(setItem.getValue().requestType())); } catch (DBusException dbe){ _interperter.logSevere(dbe); } signalElement.appendChild(argumentOut); interfaceElement.appendChild(signalElement); } } } else { //inputport TreeMap<String, TreeMap<String,RequestResponseTypeDescription>> interfaces = new TreeMap<String,TreeMap<String,RequestResponseTypeDescription>>(); TreeMap<String,RequestResponseTypeDescription> ifaceMap; Interface iface = channel().parentInputPort().getInterface(); Map<String, RequestResponseTypeDescription> rrDesc = iface.requestResponseOperations(); Iterator itr = rrDesc.entrySet().iterator(); while(itr.hasNext()){ Map.Entry<String,RequestResponseTypeDescription> pair = (Map.Entry<String,RequestResponseTypeDescription>) itr.next(); InterfaceDefinition def = iface.interfaceForOperation(pair.getKey()); String ifaceName = def.name(); ifaceMap = interfaces.get(ifaceName); if(ifaceMap != null){ if(ifaceMap.get(pair.getKey()) == null){ ifaceMap.put(pair.getKey(),pair.getValue()); } } else { TreeMap<String,RequestResponseTypeDescription> newInterface = new TreeMap<String, RequestResponseTypeDescription>(); newInterface.put(pair.getKey(), pair.getValue()); interfaces.put(ifaceName, newInterface); } } //create xml for(Map.Entry<String,TreeMap<String,RequestResponseTypeDescription>> set : interfaces.entrySet()){ String interfaceName = set.getKey(); Element interfaceElement = doc.createElement("interface"); interfaceElement.setAttribute("name", interfaceName); rootElement.appendChild(interfaceElement); for(Map.Entry<String,RequestResponseTypeDescription> setItem : set.getValue().entrySet()){ String methodName = setItem.getKey(); Element methodElement = doc.createElement("method"); methodElement.setAttribute("name", methodName); Element argumentIn = doc.createElement("arg"); Element argumentOut = doc.createElement("arg"); try { argumentIn.setAttribute("type", getDBusSignature(setItem.getValue().requestType())); argumentIn.setAttribute("direction", "in"); argumentOut.setAttribute("type", getDBusSignature(setItem.getValue().responseType())); argumentOut.setAttribute("direction", "out"); } catch (DBusException dbe){ _interperter.logSevere(dbe); } methodElement.appendChild(argumentIn); methodElement.appendChild(argumentOut); interfaceElement.appendChild(methodElement); } rootElement.appendChild(interfaceElement); } } // add introspect information Element interfaceElement = doc.createElement("interface"); interfaceElement.setAttribute("name", "org.freedesktop.DBus.Introspectable"); Element methodElement = doc.createElement("method"); methodElement.setAttribute("name", "Introspect"); Element argumentOut = doc.createElement("arg"); argumentOut.setAttribute("type", "s"); argumentOut.setAttribute("direction", "out"); methodElement.appendChild(argumentOut); interfaceElement.appendChild(methodElement); rootElement.appendChild(interfaceElement); try{ TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"); transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); StringWriter writer = new StringWriter(); transformer.transform(new DOMSource(doc), new StreamResult(writer)); output = writer.getBuffer().toString(); Value returnVal = Value.create(output); CommMessage requestMessage = CommMessage.createRequest(msg.getName(), "/", null, "", Value.UNDEFINED_VALUE); _inMethodCalls.put(requestMessage.id(), (MethodCall) msg); _inCommMessage.put(requestMessage.id(), requestMessage); CommMessage returnMessage = CommMessage.createResponse(requestMessage, returnVal); send(ostream, returnMessage, istream); channel().disposeForInput(); commMessage = null; } catch(TransformerException te){ _interperter.logSevere(te); } } catch (ParserConfigurationException pce) { _interperter.logSevere(pce); } } if (!(_introspect && msg.getName().equals("Introspect") && msg.getInterface().equals("org.freedesktop.DBus.Introspectable"))) { _interperter.logInfo("Recieved D-Bus method call for unsuported method " + msg.getName()); Value bodyObjectsValue = Value.UNDEFINED_VALUE; commMessage = CommMessage.createRequest(msg.getName(), msg.getPath(), bodyObjectsValue); _inMethodCalls.put(commMessage.id(), (MethodCall) msg); _inCommMessage.put(commMessage.id(), commMessage); } } } else if(msg instanceof MethodReturn){ // get the methodCall this is a Return for, and remove it from the outgoing method calls. MethodCall mc = _outMethodCalls.remove(msg.getReplySerial()); if(mc!= null && channel().parentPort().getInterface().containsOperation(mc.getName())) { try { OutputPort out = _interperter.getOutputPort(_outSerialMap.get(msg.getReplySerial()).getDestination()); RequestResponseTypeDescription rrTypeDescription = out.getInterface().requestResponseOperations().get(mc.getName()); Type responseType = rrTypeDescription.responseType(); Value bodyObjectsValue = Value.UNDEFINED_VALUE; if(msg.getSig() != null && !msg.getSig().equals("")){ bodyObjectsValue = JolieDBusUtils.extract(msg.getSig(), _messageBody, msg.getEndian(), new int[]{0, 0},responseType); } // get the calling commMessage. CommMessage requestMessage = _outSerialMap.remove(Long.valueOf(mc.getSerial())); if(requestMessage != null){ commMessage = CommMessage.createResponse(requestMessage, bodyObjectsValue); } } catch (DBusException de){ _interperter.logSevere(de); } catch (UnsupportedOperationException uoe){ _interperter.logSevere(uoe); } catch (InvalidIdException iee){ _interperter.logSevere(iee); } } else { commMessage = CommMessage.createEmptyResponse(CommMessage.createRequest("", "/", Value.UNDEFINED_VALUE)); } } else if(msg instanceof DBusSignal) { if (channel().parentPort().getInterface().containsOperation(msg.getName())) { try{ InputOperation operation = _interperter.getInputOperation(msg.getName()); Type requestType = operation.requestType(); Value bodyObjectsValue = Value.UNDEFINED_VALUE; if(msg.getSig() != null && !msg.getSig().equals("")){ bodyObjectsValue = JolieDBusUtils.extract(msg.getSig(), _messageBody, msg.getEndian(), new int[]{0, 0},requestType); } Interpreter.getInstance().getOneWayOperation(msg.getName()); commMessage = CommMessage.createRequest(msg.getName(), msg.getPath(), bodyObjectsValue); // no storing of request since its a one-way and no return will be sent. } catch(InvalidIdException iie){ // ignore signal since the instance does not support a one-way message for this member/name _interperter.logInfo("Recieved D-Bus Signal for unsuported one-way method: " + iie); } catch (DBusException de) { _interperter.logSevere(de); } } else { commMessage = CommMessage.createRequest("", "/", Value.UNDEFINED_VALUE); } } else if(msg instanceof Error){ MethodCall mc = _outMethodCalls.remove(msg.getReplySerial()); CommMessage comm = _outSerialMap.remove(msg.getReplySerial()); if(comm != null){ try { commMessage = CommMessage.createFaultResponse(comm, new FaultException(msg.getName(), (String)msg.getParameters()[0])); } catch (DBusException dbe){ _interperter.logSevere(dbe); } } else { // probably disapering service consumer that we have sent a reply/error to if(msg.getHeader(Message.HeaderField.ERROR_NAME).equals("org.freedesktop.DBus.Error.ServiceUnknown")){ try { _interperter.logInfo("Service disapeared : " + msg.getParameters()[0].toString()); channel().disposeForInput(); } catch (DBusException dbe){ _interperter.logSevere(dbe); } } } } return commMessage; } @Override public void send(OutputStream ostream, CommMessage message, InputStream istream)throws IOException { authenticate(istream, ostream); DataOutputStream oos = new DataOutputStream(ostream); String sig = getDBusSignature(message.value()); Object[] objects = getObjectArray(message.value()); Message msg = null; CommMessage jmc = _inCommMessage.remove(message.id()); if(jmc != null) { // this is a MethodReturn. Get the original MethodCall MethodCall mc = _inMethodCalls.remove(jmc.id()); //check to se if reply contains a fault if(message.isFault()){ sig = getDBusSignature(message.fault().value()); objects = getObjectArray(message.fault().value()); try{ msg = new Error(mc.getSource(),this.channel().parentInputPort().name() + ".Error",_inMap.remove(mc.getSerial()), sig, objects); } catch(DBusException de) { _interperter.logSevere(de); } } else { try{ msg = new MethodReturn(mc.getSource(), _inMap.remove(mc.getSerial()),Message.Endian.BIG, sig, objects); } catch(DBusException de) { _interperter.logSevere(de); } } } else { //MethodCall or signal if(message.getDestination() != null){ boolean signal = false; try{ OutputPort out = _interperter.getOutputPort(message.getDestination()); OneWayTypeDescription signalTest = out.getInterface().oneWayOperations().get(message.operationName()); if(signalTest != null) { signal = true; } } catch (InvalidIdException iie) { signal = false; } String resourcePath = message.resourcePath(); if(hasParameter("object_path")){ resourcePath = getStringParameter("object_path"); } String destination = message.getDestination(); if(hasParameter("destination")){ destination = getStringParameter("destination"); } try { if(!signal){ msg = new MethodCall(destination, resourcePath, null, message.operationName(),Message.Endian.BIG, (byte) 0, sig, objects); _outMethodCalls.put(msg.getSerial(), (MethodCall)msg); _outSerialMap.put(msg.getSerial(), message); } else { // TODO add interface information. msg = new DBusSignal(null, resourcePath, message.getInterfaceDefinition().name(), message.operationName(),Message.Endian.BIG, sig, objects); } } catch (DBusException dbe) { _interperter.logSevere(dbe); } }// else probably garbage from incomming message/signal to an unsupported method. } if (msg != null){ for (byte[] buf : msg.getWireData()) { if (null == buf) { break; } oos.write(buf); } if (message.isFault()){ _interperter.logInfo("Send fault to : " + msg.getDestination() +" with name : "+ msg.getName() + " is error:" + message.isFault()); } } } @Override public void setup(InputStream istream, OutputStream ostream) throws IOException { if(_inputport){ _messageBus = this.channel().parentInputPort().messageBus(); if(getParameterVector("matchRules") != null){ _rules = new ArrayList<String>(); ValueVector vv = getValueVectorParameter("matchRules"); if(vv != null){ Iterator itr = vv.iterator(); while(itr.hasNext()){ Object rule = itr.next(); if (rule instanceof Value){ _rules.add(((Value)rule).strValue()); } } } } } else { _messageBus = this.channel().parentOutputPort().messageBus(); } authenticate(istream, ostream); //Say hello to the server if(_authenticated){ DataInputStream dis = new DataInputStream(istream); CommMessage comm = CommMessage.createRequest("Hello","/",null,"org.freedesktop.DBus", Value.UNDEFINED_VALUE); Message rply; send(ostream, comm, istream); Object[] parameters; String sig; Value valueObject = null; ostream.flush(); //listen for reply boolean run = true; while(run){ rply = readMessage(dis); if(rply instanceof MethodReturn){ sig = rply.getSig(); try { parameters = rply.getParameters(); valueObject = createValueFromParam(parameters, sig); } catch (DBusException de){ _interperter.logSevere(de); } run = false; } } if(_messageBus && _inputport){ Value v = Value.create(); ValueVector vv = ValueVector.create(); if(_nameRequestFlags != null){ vv.add(Value.create(_nameRequestFlags)); } else { vv.add(Value.create(new UInt32(0L))); } v.children().put("p2", vv); vv = ValueVector.create(); vv.add(Value.create(this.channel().parentInputPort().name())); v.children().put("p1", vv); comm = CommMessage.createRequest("RequestName", "/",null, "org.freedesktop.DBus", v); send(ostream, comm, istream); run = true; while(run){ rply = readMessage(dis); if(rply instanceof MethodReturn){ sig = rply.getSig(); try { parameters = rply.getParameters(); valueObject = createValueFromParam(parameters, sig); } catch (DBusException de){ _interperter.logSevere(de); } run = false; } } //MatchRule setup for(String rule : _rules){ v = Value.create(rule); comm = CommMessage.createRequest("AddMatch", "/",null, "org.freedesktop.DBus", v); send(ostream, comm, istream); run = true; while(run){ rply = readMessage(dis); if(rply instanceof MethodReturn){ sig = rply.getSig(); if(sig != null && !sig.equals("")){ try { parameters = rply.getParameters(); valueObject = createValueFromParam(parameters, sig); } catch (DBusException de){ _interperter.logSevere(de); } } run = false; } } } } ostream.flush(); } else if(!_authenticated){ throw new IOException("DBus authentication failed"); } } private void authenticate(InputStream istream, OutputStream ostream) throws IOException { if (!_authenticated) { if (!_messageBus && _inputport) { _authenticated = dBusAuth(ostream, istream, true); } else { /*input port but not connected to dbus-daemon, (setup method was not executed) */ _authenticated = dBusAuth(ostream, istream, false); } } } private boolean dBusAuth(OutputStream ostream, InputStream istream, boolean server) throws IOException { boolean authenticated; if (ostream instanceof USOutputStream) { USOutputStream usos = (USOutputStream) ostream; if (!server) { if (new Transport.SASL().auth(Transport.SASL.MODE_CLIENT, Transport.SASL.AUTH_EXTERNAL, "", ostream, istream, usos.getSocket())) { authenticated = true; //set credentials usos.getSocket().setPassCred(true); } else { authenticated = false; throw new IOException("Failed to authenticate"); } } else { if (new Transport.SASL().auth(Transport.SASL.MODE_SERVER, Transport.SASL.AUTH_EXTERNAL, null, //TODO fix this. ostream, istream, usos.getSocket())) { //set credentials usos.getSocket().setPassCred(true); authenticated = true; } else { authenticated = false; throw new IOException("Failed to authenticate."); } } } else { authenticated = false; throw new IOException("DBus currently only support unix sockets."); } return authenticated; } private Object[] getObjectArray(Value val) { ArrayList list = new ArrayList(); Map<String, ValueVector> map = new TreeMap(val.children()); if (!map.isEmpty()) { for (Map.Entry<String, ValueVector> entry : map.entrySet()) { ValueVector vv = entry.getValue(); if (vv.size() > 1) { ArrayList internalArray = new ArrayList(); for (int i = 0; i < vv.size(); i++) { Value internalVal = vv.get(i); if (internalVal.hasChildren()) { //children are part of a struct and should be kept as a array internalArray.add(getObjectArray(internalVal)); } else if (internalVal.valueObject() != null) { internalArray.add(internalVal.valueObject()); } } if (list == null) { list = new ArrayList(); } list.add(internalArray.toArray()); } else if (vv.first().hasChildren()) { list.add(getObjectArray(vv.first())); } else { list.add(vv.first().valueObject()); } } } else { list.add(val.valueObject()); } return list.toArray(); } private Iterator getSubTypesIterator(Type t) throws DBusException { Set<Map.Entry<String,Type>> subTypes; List<Map.Entry<String,Type>> subTypesSorted = new LinkedList<Map.Entry<String,Type>>(); java.lang.reflect.Method subTypesMethod; try{ subTypesMethod = Type.class.getDeclaredMethod("subTypeSet"); subTypesMethod.setAccessible(true); subTypes = (Set<Map.Entry<String,Type>>)subTypesMethod.invoke(t); subTypesSorted.addAll(subTypes); Collections.sort(subTypesSorted, new Comparator<Map.Entry<String,Type>>(){ @Override public int compare(Map.Entry<String,Type> o1, Map.Entry<String,Type> o2) { return o1.getKey().compareTo(o2.getKey()); } }); } catch(NoSuchMethodException nme){ throw new DBusException(nme.getMessage()); } catch (IllegalAccessException iae){ throw new DBusException(iae.getMessage()); } catch (InvocationTargetException ite){ throw new DBusException(ite.getMessage()); } if(!subTypesSorted.isEmpty()){ return subTypesSorted.iterator(); } else { return null; } } private jolie.lang.NativeType getNativeType(Type t) throws DBusException{ java.lang.reflect.Method nativeTypeMethod; jolie.lang.NativeType nativeType; try{ nativeTypeMethod = Type.class.getDeclaredMethod("nativeType"); nativeTypeMethod.setAccessible(true); nativeType = (jolie.lang.NativeType)nativeTypeMethod.invoke(t); } catch(NoSuchMethodException nme){ throw new DBusException(nme.getMessage()); } catch (IllegalAccessException iae){ throw new DBusException(iae.getMessage()); } catch (InvocationTargetException ite){ throw new DBusException(ite.getMessage()); } return nativeType; } private jolie.util.Range getRange(Type t) throws DBusException{ java.lang.reflect.Method rangeMethod; jolie.util.Range range; try{ rangeMethod = Type.class.getDeclaredMethod("cardinality"); rangeMethod.setAccessible(true); range = (jolie.util.Range)rangeMethod.invoke(t); } catch(NoSuchMethodException nme){ throw new DBusException(nme.getMessage()); } catch (IllegalAccessException iae){ throw new DBusException(iae.getMessage()); } catch (InvocationTargetException ite){ throw new DBusException(ite.getMessage()); } return range; } private String getDBusSignature(Type t) throws DBusException{ String sig = ""; Iterator subItr = getSubTypesIterator(t); if(subItr == null){ sig += getNativeTypeSignature(getNativeType(t)); } else { while (subItr.hasNext()){ Map.Entry<String,Type>subType = (Map.Entry<String,Type>)subItr.next(); Iterator childrenIterator = getSubTypesIterator(subType.getValue()); if(childrenIterator != null){ sig += "("; sig += getDBusSignature(subType.getValue()); sig += ")"; } else { // no children //check range for array type? jolie.util.Range typeRange = getRange(subType.getValue()); if(typeRange.min() < typeRange.max()){ sig += "a"; sig += getDBusSignature(subType.getValue()); } else { sig += getDBusSignature(subType.getValue()); } } } } return sig; } /** * Gets the dbus signature of a value from Jolie * * @param val the Jolie value implementation object. * @return a signature string corresponding to the Jolie Value object. */ private String getDBusSignature(Value val) { String sig = ""; Map<String, ValueVector> map = new TreeMap(val.children()); if (!map.isEmpty()) { for (Map.Entry<String, ValueVector> entry : map.entrySet()) { ValueVector vv = entry.getValue(); if (vv.size() > 1) { Value internalVal = vv.first(); if (!val.children().isEmpty()) { sig += "a"; if (internalVal.hasChildren() && internalVal.children().size() > 1) { // maybe a dictionary value. if(internalVal.hasChildren() && internalVal.children().size() == 2 && internalVal.children().get("key") != null && internalVal.children().get("value") != null){ sig += "{"; sig += getDBusSignature(internalVal); sig += "}"; } else { sig += "("; sig += getDBusSignature(internalVal); sig += ")"; } } else { sig += getDBusSignature(internalVal); } } else if (internalVal.valueObject() != null) { sig += getDBusValueObjectSignature(internalVal.valueObject()); } } else if (vv.first().hasChildren()) { if(vv.first().children().size() == 2 && vv.first().children().get("key") != null && vv.first().children().get("value") != null){ sig += "{"; sig += getDBusSignature(vv.first()); sig += "}"; } else { sig += "("; sig += getDBusSignature(vv.first()); sig += ")"; } } else { sig += getDBusValueObjectSignature(vv.first().valueObject()); } } } else { sig += getDBusValueObjectSignature(val.valueObject()); } return sig; } private String getDBusValueObjectSignature(Object obj) { String sig = ""; if (obj != null) { if (obj instanceof String) { sig += "s"; } else if (obj instanceof Integer) { sig += "i"; } else if (obj instanceof Double) { sig += "d"; } else if (obj instanceof Boolean) { sig += "b"; } else if (obj instanceof Long) { sig += "x"; } else if (obj instanceof UInt32) { sig +="u"; } else if (obj instanceof UInt16) { sig +="q"; } else if (obj instanceof Short) { sig +="n"; } else if (obj instanceof UInt64) { sig +="t"; } else if (obj instanceof Byte) { sig +="y"; } } return sig; } private String getNativeTypeSignature(jolie.lang.NativeType obj) { String sig = ""; if (obj != null) { if (obj.id().equals("string")) { sig += "s"; } else if (obj.id().equals("int")) { sig += "i"; } else if (obj.id().equals("double")) { sig += "d"; } else if (obj.id().equals("bool")) { sig += "b"; } else if (obj.id().equals("long")) { sig += "x"; } else if (obj.id().equals("uint32")) { sig +="u"; } else if (obj.id().equals("uint16")) { sig +="q"; } else if (obj.id().equals("int16")) { sig +="n"; } else if (obj.id().equals("uint64")) { sig +="t"; } else if (obj.id().equals("byte")) { sig +="y"; } } return sig; } /** * Create a Jolie value object with data from D-Bus. * * @param parameter the object array from DBus. * @param signature the signature of the parameter objects. * @return */ private Value createValueFromParam(Object[] parameters, String signature) { Value root = null; Value val = null; if (signature != null) { for (int i = 0; i < signature.length(); i++) { char sig = signature.charAt(i); switch (sig) { case 's': val = Value.create((String) parameters[i]); break; case 'i': val = Value.create((Integer) parameters[i]); break; case 'd': val = Value.create((Double) parameters[i]); break; case 'b': val = Value.create((Boolean) parameters[i]); break; case 'x': val = Value.create((Long) parameters[i]); break; case 'u': UInt32 uint32 = (UInt32)parameters[i]; val = Value.create(new UInt32(uint32.longValue())); break; default: break; } if (root == null) { root = val; } else { root.add(val); } } return root; } else { return null; } } }