1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2013-2014 Licq developers <licq-dev@googlegroups.com>
4  *
5  * Licq is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * Licq is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Licq; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include "dbusinterface.h"
21 
22 #include <boost/shared_ptr.hpp>
23 #include <cassert>
24 #include <cstdarg>
25 #include <cstring>
26 #include <dbus/dbus.h>
27 #include <list>
28 #include <map>
29 #include <poll.h>
30 
31 #include <licq/logging/log.h>
32 #include <licq/mainloop.h>
33 
34 namespace LicqDbus
35 {
36 
37 /**
38  * Object to handle a DBusWatch in the main loop
39  */
40 class WatchHandler : public Licq::MainLoopCallback
41 {
42 public:
43   typedef boost::shared_ptr<WatchHandler> Ptr;
44 
45   WatchHandler(int id, Licq::MainLoop& mainLoop, DBusConnection* conn, DBusWatch* watch);
46   ~WatchHandler();
47   void toggle();
48 
49 private:
50   // From MainLoopCallback
51   void rawFileEvent(int id, int fd, int revents);
52 
53   int myId;
54   Licq::MainLoop& myMainLoop;
55   DBusConnection* myConn;
56   DBusWatch* myWatch;
57 };
58 
59 
60 /**
61  * Object to handle a DBusTimeout in the main loop
62  */
63 class TimeoutHandler : public Licq::MainLoopCallback
64 {
65 public:
66   typedef boost::shared_ptr<TimeoutHandler> Ptr;
67 
68   TimeoutHandler(int id, Licq::MainLoop& mainLoop, DBusConnection* conn, DBusTimeout* timeout);
69   ~TimeoutHandler();
70   void toggle();
71 
72 private:
73   // From MainLoopCallback
74   void timeoutEvent(int id);
75 
76   int myId;
77   Licq::MainLoop& myMainLoop;
78   DBusConnection* myConn;
79   DBusTimeout* myTimeout;
80 };
81 
82 
83 typedef std::map<DBusWatch*,WatchHandler::Ptr> WatchMap;
84 typedef std::map<DBusTimeout*,TimeoutHandler::Ptr> TimeoutMap;
85 
86 
87 /**
88  * DbusInterface internals
89  */
90 class DbusInterface::Private
91 {
92 public:
Private(DbusInterface * interface,Licq::MainLoop & mainLoop,DbusCallback * callback,bool systemBus)93   Private(DbusInterface* interface, Licq::MainLoop& mainLoop, DbusCallback* callback, bool systemBus)
94     : myInterface(interface), myMainLoop(mainLoop), myCallback(callback), mySystemBus(systemBus)
95   { /* Empty */ }
96 
97   // Callbacks from libdbus
98   static dbus_bool_t addWatchCb(DBusWatch* watch, void* data);
99   static void removeWatchCb(DBusWatch* watch, void* data);
100   static void toggleWatchCb(DBusWatch* watch, void* data);
101   static dbus_bool_t addTimeoutCb(DBusTimeout* timeout, void* data);
102   static void removeTimeoutCb(DBusTimeout* timeout, void* data);
103   static void toggleTimeoutCb(DBusTimeout* timeout, void* data);
104   static DBusHandlerResult handleMessage(DBusConnection* connection, DBusMessage* message, void* data);
105 
106   static bool addMsgArgs(DBusMessageIter* args, const char* fmt, va_list ap);
107 
108   DbusInterface* myInterface;
109   Licq::MainLoop& myMainLoop;
110   DbusCallback* myCallback;
111   DBusConnection* myConn;
112   WatchMap myWatches;
113   TimeoutMap myTimeouts;
114   int myNextWatchId;
115   int myNextTimeoutId;
116   bool mySystemBus;
117 };
118 
119 } // namespace LicqDbus
120 
121 
122 using namespace LicqDbus;
123 
124 
dbusConnected()125 void DbusCallback::dbusConnected()
126 { /* Empty */ }
127 
dbusDisconnected()128 void DbusCallback::dbusDisconnected()
129 { /* Empty */ }
130 
dbusMethod(const char * path,const char * iface,const char * member,DBusMessage *,DBusMessageIter *,const char *)131 int DbusCallback::dbusMethod(const char* path, const char* iface, const char* member,
132       DBusMessage* /*msgref*/, DBusMessageIter* /*argref*/, const char* /*fmt*/)
133 {
134   Licq::gLog.info("Unhandled D-Bus method call '%s' to '%s' interface '%s'", member, path, iface);
135   return DbusInterface::ErrorUnknownInterface;
136 }
137 
dbusSignal(const char * path,const char * iface,const char * member,DBusMessage *,DBusMessageIter *,const char *)138 void DbusCallback::dbusSignal(const char* path, const char* iface, const char* member,
139       DBusMessage* /*msgref*/, DBusMessageIter* /*argref*/, const char* /*fmt*/)
140 {
141   Licq::gLog.info("Unhandled D-Bus signal call '%s' to '%s' interface '%s'", member, path, iface);
142 }
143 
144 
WatchHandler(int id,Licq::MainLoop & mainLoop,DBusConnection * conn,DBusWatch * watch)145 WatchHandler::WatchHandler(int id, Licq::MainLoop& mainLoop, DBusConnection* conn, DBusWatch* watch)
146   : myId(id),
147     myMainLoop(mainLoop),
148     myConn(conn),
149     myWatch(watch)
150 {
151   toggle();
152 }
153 
~WatchHandler()154 WatchHandler::~WatchHandler()
155 {
156   myMainLoop.removeCallback(this);
157 }
158 
toggle()159 void WatchHandler::toggle()
160 {
161   if (dbus_watch_get_enabled(myWatch))
162   {
163     int fd = dbus_watch_get_unix_fd(myWatch);
164     unsigned int flags = dbus_watch_get_flags(myWatch);
165 
166     int events = 0;
167     if (flags & DBUS_WATCH_READABLE)
168       events |= POLLIN;
169     if (flags & DBUS_WATCH_WRITABLE)
170       events |= POLLOUT;
171 
172     myMainLoop.addRawFile(fd, this, events, myId);
173   }
174   else
175   {
176     myMainLoop.removeCallback(this);
177   }
178 }
179 
rawFileEvent(int,int,int revents)180 void WatchHandler::rawFileEvent(int /*id*/, int /*fd*/, int revents)
181 {
182   // Convert returned poll events to flags used by dbus watch
183   unsigned int flags = 0;
184   if (revents & POLLIN) flags |= DBUS_WATCH_READABLE;
185   if (revents & POLLOUT) flags |= DBUS_WATCH_WRITABLE;
186   if (revents & POLLERR) flags |= DBUS_WATCH_ERROR;
187   if (revents & POLLHUP) flags |= DBUS_WATCH_HANGUP;
188 
189   dbus_watch_handle(myWatch, flags);
190 
191   while (dbus_connection_dispatch(myConn) == DBUS_DISPATCH_DATA_REMAINS)
192   { /* empty */ }
193 }
194 
addWatchCb(DBusWatch * watch,void * data)195 dbus_bool_t DbusInterface::Private::addWatchCb(DBusWatch* watch, void* data)
196 {
197   DbusInterface::Private* d = static_cast<DbusInterface::Private*>(data);
198 
199   assert(d->myWatches.count(watch) == 0);
200 
201   // Reset counter if no other watches exist
202   if (d->myWatches.empty())
203     d->myNextWatchId = 1;
204 
205   d->myWatches.insert(std::make_pair(watch,
206       new WatchHandler(d->myNextWatchId++, d->myMainLoop, d->myConn, watch)));
207 
208   return true;
209 }
210 
removeWatchCb(DBusWatch * watch,void * data)211 void DbusInterface::Private::removeWatchCb(DBusWatch* watch, void* data)
212 {
213   DbusInterface::Private* d = static_cast<DbusInterface::Private*>(data);
214   d->myWatches.erase(watch);
215 }
216 
toggleWatchCb(DBusWatch * watch,void * data)217 void DbusInterface::Private::toggleWatchCb(DBusWatch* watch, void* data)
218 {
219   DbusInterface::Private* d = static_cast<DbusInterface::Private*>(data);
220 
221   WatchMap::iterator iter(d->myWatches.find(watch));
222   assert(iter != d->myWatches.end());
223 
224   iter->second->toggle();
225 }
226 
227 
TimeoutHandler(int id,Licq::MainLoop & mainLoop,DBusConnection * conn,DBusTimeout * timeout)228 TimeoutHandler::TimeoutHandler(int id, Licq::MainLoop& mainLoop, DBusConnection* conn, DBusTimeout* timeout)
229   : myId(id),
230     myMainLoop(mainLoop),
231     myConn(conn),
232     myTimeout(timeout)
233 {
234   toggle();
235 }
236 
~TimeoutHandler()237 TimeoutHandler::~TimeoutHandler()
238 {
239   myMainLoop.removeCallback(this);
240 }
241 
toggle()242 void TimeoutHandler::toggle()
243 {
244   if (dbus_timeout_get_enabled(myTimeout))
245   {
246     myMainLoop.addTimeout(dbus_timeout_get_interval(myTimeout), this, myId, false);
247   }
248   else
249   {
250     myMainLoop.removeCallback(this);
251   }
252 }
253 
timeoutEvent(int)254 void TimeoutHandler::timeoutEvent(int /*id*/)
255 {
256   dbus_timeout_handle(myTimeout);
257 
258   while (dbus_connection_dispatch(myConn) == DBUS_DISPATCH_DATA_REMAINS)
259   { /* empty */ }
260 }
261 
addTimeoutCb(DBusTimeout * timeout,void * data)262 dbus_bool_t DbusInterface::Private::addTimeoutCb(DBusTimeout* timeout, void* data)
263 {
264   DbusInterface::Private* d = static_cast<DbusInterface::Private*>(data);
265 
266   assert(d->myTimeouts.count(timeout) == 0);
267 
268   // Reset counter if no other timeouts exist
269   if (d->myTimeouts.empty())
270     d->myNextTimeoutId = 1;
271 
272   d->myTimeouts.insert(std::make_pair(timeout,
273       new TimeoutHandler(d->myNextTimeoutId++, d->myMainLoop, d->myConn, timeout)));
274 
275   return true;
276 }
277 
removeTimeoutCb(DBusTimeout * timeout,void * data)278 void DbusInterface::Private::removeTimeoutCb(DBusTimeout* timeout, void* data)
279 {
280   DbusInterface::Private* d = static_cast<DbusInterface::Private*>(data);
281   d->myTimeouts.erase(timeout);
282 }
283 
toggleTimeoutCb(DBusTimeout * timeout,void * data)284 void DbusInterface::Private::toggleTimeoutCb(DBusTimeout* timeout, void* data)
285 {
286   DbusInterface::Private* d = static_cast<DbusInterface::Private*>(data);
287 
288   TimeoutMap::iterator iter(d->myTimeouts.find(timeout));
289   assert(iter != d->myTimeouts.end());
290 
291   iter->second->toggle();
292 }
293 
handleMessage(DBusConnection *,DBusMessage * message,void * data)294 DBusHandlerResult DbusInterface::Private::handleMessage(DBusConnection* /*connection*/, DBusMessage* message, void* data)
295 {
296   // Don't forward D-Bus daemon messages to application
297   if (dbus_message_has_path(message, "/org/freedesktop/DBus"))
298     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
299 
300   DbusInterface::Private* d = static_cast<DbusInterface::Private*>(data);
301 
302   if (d->myCallback == NULL)
303   {
304     Licq::gLog.info("Unhandled D-Bus message");
305     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
306   }
307 
308   const char* path = dbus_message_get_path(message);
309   const char* iface = dbus_message_get_interface(message);
310   const char* member = dbus_message_get_member(message);
311 
312   const char* signature = NULL;
313   DBusMessageIter args;
314   if (dbus_message_iter_init(message, &args))
315     signature = dbus_message_get_signature(message);
316 
317   int type = dbus_message_get_type(message);
318   int error;
319   switch (type)
320   {
321     case DBUS_MESSAGE_TYPE_METHOD_CALL:
322       if (iface == NULL)
323         error = DbusInterface::ErrorUnknownInterface;
324       else if (member == NULL)
325         error = DbusInterface::ErrorUnknownMethod;
326       else if (path == NULL)
327         error = DbusInterface::ErrorUnknownObject;
328       else if (strcmp(iface, "org.freedesktop.DBus.Introspectable") == 0 && strcmp(member, "Introspect") == 0)
329       {
330         std::string data(d->myCallback->dbusIntrospect(path));
331         if (data.empty())
332           error = DbusInterface::ErrorUnknownObject;
333         else
334         {
335           data.insert(0,
336               "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
337                   "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">"
338               "<node>"
339                 "<interface name=\"org.freedesktop.DBus.Introspectable\">"
340                   "<method name=\"Introspect\">"
341                     "<arg name=\"xml_data\" type=\"s\" direction=\"out\"/>"
342                   "</method>"
343                 "</interface>"
344                 "<interface name=\"org.freedesktop.DBus.Peer\">"
345                   "<method name=\"Ping\"/>"
346                   "<method name=\"GetMachineId\">"
347                     "<arg name=\"machine_uuid\" type=\"s\" direction=\"out\"/>"
348                   "</method>"
349                 "</interface>" );
350           data.append("</node>");
351           d->myInterface->sendReply(message, "s", data.c_str());
352           error = DbusInterface::MethodReplied;
353         }
354       }
355       else
356         error = d->myCallback->dbusMethod(path, iface, member, message, signature == NULL ? NULL : &args, signature);
357       break;
358     case DBUS_MESSAGE_TYPE_SIGNAL:
359       d->myCallback->dbusSignal(path, iface, member, message, signature == NULL ? NULL : &args, signature);
360       error = DbusInterface::MethodReplied;
361       break;
362     default:
363       Licq::gLog.info("Unhandled D-Bus message type %i", type);
364       // Only method calls should be replied to
365       error = DbusInterface::MethodReplied;
366   }
367   if (error != DbusInterface::MethodReplied)
368     d->myInterface->sendError(message, error, NULL);
369 
370   return DBUS_HANDLER_RESULT_HANDLED;
371 }
372 
373 
encodeObjectPathPart(const std::string & s)374 std::string DbusInterface::encodeObjectPathPart(const std::string& s)
375 {
376   // Object paths may only contain "[A-Z][a-z][0-9]_"
377   // Use underscore as escape character similar to percent character in URIs
378   static const char allowedChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
379 
380   std::string ret;
381 
382   size_t lastpos = 0;
383   size_t pos;
384   while ((pos = s.find_first_not_of(allowedChars, lastpos)) != std::string::npos)
385   {
386     // Add all allowed characters before position
387     ret.append(s, lastpos, pos-lastpos);
388 
389     // Add extra character escaped
390     ret += '_';
391     ret += allowedChars[(s[pos] >> 4) & 0x0f];
392     ret += allowedChars[s[pos] & 0x0f];
393 
394     lastpos = pos+1;
395   }
396   // Add remaining chararcters
397   ret.append(s, lastpos, std::string::npos);
398 
399   return ret;
400 }
401 
decodeObjectPathPart(const std::string & s)402 std::string DbusInterface::decodeObjectPathPart(const std::string& s)
403 {
404   std::string ret;
405   size_t lastpos = 0;
406   size_t pos;
407   // Find all escape characters (e.g. underscores)
408   while ((pos = s.find('_', lastpos)) <= s.size() - 3)
409   {
410     // Add all characters before this escape
411     ret.append(s, lastpos, pos-lastpos);
412 
413     // Get the two hex digits after escape character
414     char c1 = s[pos+1];
415     char c2 = s[pos+2];
416     if (c1 < '0' || (c1 > '9' && c1 < 'A') || c1 > 'F' ||
417         c2 < '0' || (c2 > '9' && c2 < 'A') || c2 > 'F')
418     {
419       // Not a valid escape, keep underscore unmodified
420       ret += '_';
421       lastpos = pos + 1;
422       continue;
423     }
424 
425     // Translate to raw byte
426     ret += (char)(((c1 - '0' - (c1 > '9' ? 7 : 0)) << 4) + (c2 - '0' - (c2 > '9' ? 7 : 0)));
427     lastpos = pos + 3;
428   }
429 
430   // Add remaining characters
431   ret.append(s, lastpos, std::string::npos);
432 
433   return ret;
434 }
435 
DbusInterface(Licq::MainLoop & mainLoop,DbusCallback * callback,bool systemBus)436 DbusInterface::DbusInterface(Licq::MainLoop& mainLoop, DbusCallback* callback, bool systemBus)
437   : myPrivate(new Private(this, mainLoop, callback, systemBus))
438 {
439   LICQ_D();
440   d->myConn = NULL;
441 }
442 
~DbusInterface()443 DbusInterface::~DbusInterface()
444 {
445   disconnect();
446   delete myPrivate;
447 }
448 
connect()449 bool DbusInterface::connect()
450 {
451   LICQ_D();
452 
453   if (d->myConn != NULL)
454   {
455     if (dbus_connection_get_is_connected(d->myConn))
456       return true;
457 
458     // Lost connection, drop it and make a new
459     disconnect();
460   }
461 
462   // Connect to DBus
463   DBusError e;
464   dbus_error_init(&e);
465   d->myConn = dbus_bus_get_private(d->mySystemBus ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &e);
466 
467   if (dbus_error_is_set(&e))
468   {
469     Licq::gLog.error("Failed to get bus %s: %s", e.name, e.message);
470     dbus_error_free(&e);
471     return false;
472   }
473 
474   // Don't let libdbus terminate Licq if we're disconnected
475   dbus_connection_set_exit_on_disconnect(d->myConn, false);
476 
477   // Register callback functions
478   dbus_connection_set_watch_functions(d->myConn, Private::addWatchCb,
479       Private::removeWatchCb, Private::toggleWatchCb, d, NULL);
480   dbus_connection_set_timeout_functions(d->myConn, Private::addTimeoutCb,
481       Private::removeTimeoutCb, Private::toggleTimeoutCb, d, NULL);
482   dbus_connection_add_filter(d->myConn, Private::handleMessage, d, NULL);
483 
484   if (d->myCallback != NULL)
485     d->myCallback->dbusConnected();
486 
487   return true;
488 }
489 
disconnect()490 void DbusInterface::disconnect()
491 {
492   LICQ_D();
493 
494   if (d->myConn != NULL)
495   {
496     dbus_connection_close(d->myConn);
497     dbus_connection_unref(d->myConn);
498     d->myConn = NULL;
499   }
500   d->myWatches.clear();
501   d->myTimeouts.clear();
502 
503   if (d->myCallback != NULL)
504     d->myCallback->dbusDisconnected();
505 }
506 
flush()507 void DbusInterface::flush()
508 {
509   LICQ_D();
510 
511   if (d->myConn == NULL)
512     return;
513 
514   dbus_connection_flush(d->myConn);
515 }
516 
requestName(const std::string & name)517 bool DbusInterface::requestName(const std::string& name)
518 {
519   LICQ_D();
520 
521   if (d->myConn == NULL)
522     return false;
523 
524   int ret = dbus_bus_request_name(d->myConn, name.c_str(), 0, NULL);
525   return (ret == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER || ret == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER);
526 }
527 
addMsgArgs(DBusMessageIter * args,const char * fmt,va_list ap)528 bool DbusInterface::Private::addMsgArgs(DBusMessageIter* args, const char* fmt, va_list ap)
529 {
530   while (true)
531   {
532     switch (*fmt)
533     {
534       case '\0':
535         return true;
536 
537       case 'i':
538       {
539         int val = va_arg(ap, int);
540         if (!dbus_message_iter_append_basic(args, DBUS_TYPE_INT32, &val))
541           return false;
542         break;
543       }
544       case 'u':
545       {
546         unsigned int val = va_arg(ap, unsigned int);
547         if (!dbus_message_iter_append_basic(args, DBUS_TYPE_UINT32, &val))
548           return false;
549         break;
550       }
551       case 's':
552       {
553         const char* val = va_arg(ap, const char*);
554         if (!dbus_message_iter_append_basic(args, DBUS_TYPE_STRING, &val))
555           return false;
556         break;
557       }
558       case 'a':
559       {
560         // Array, next character determines element type
561         switch (*(++fmt))
562         {
563           case 's':
564           case 'o':
565           {
566             DBusMessageIter subargs;
567             if (!dbus_message_iter_open_container(args, DBUS_TYPE_ARRAY,
568                 (*fmt == 'o' ? DBUS_TYPE_OBJECT_PATH_AS_STRING : DBUS_TYPE_STRING_AS_STRING),
569                 &subargs))
570               return false;
571 
572             const std::list<std::string>* vals = va_arg(ap, const std::list<std::string>*);
573             std::list<std::string>::const_iterator i;
574             for (i = vals->begin(); i != vals->end(); ++i)
575             {
576               const char* val = (*i).c_str();
577               if (!dbus_message_iter_append_basic(&subargs,
578                   (*fmt == 'o' ? DBUS_TYPE_OBJECT_PATH : DBUS_TYPE_STRING), &val))
579                 return false;
580             }
581 
582             if (!dbus_message_iter_close_container(args, &subargs))
583               return false;
584             break;
585           }
586           default:
587             // Unknown array type
588             return false;
589         }
590         break;
591       }
592       default:
593         // Unknown format
594         return false;
595     }
596     fmt++;
597   }
598 }
599 
sendSignal(const std::string & path,const std::string & iface,const std::string & name,const char * fmt,...)600 void DbusInterface::sendSignal(const std::string& path, const std::string& iface,
601     const std::string& name, const char* fmt, ...)
602 {
603   LICQ_D();
604 
605   if (d->myConn == NULL)
606     return;
607 
608   DBusMessage* msg = dbus_message_new_signal(path.c_str(), iface.c_str(), name.c_str());
609   if (msg == NULL)
610   {
611     Licq::gLog.error("Failed to allocate signal");
612     return;
613   }
614 
615   if (fmt != NULL)
616   {
617     // Append arguments to signal
618     DBusMessageIter args;
619     dbus_message_iter_init_append(msg, &args);
620     va_list ap;
621     va_start(ap, fmt);
622     if (!Private::addMsgArgs(&args, fmt, ap))
623     {
624       Licq::gLog.error("Failed to add signal parameters");
625       va_end(ap);
626       dbus_message_unref(msg);
627       return;
628     }
629     va_end(ap);
630   }
631 
632   // Queue signal to be sent (will be handled by main loop)
633   dbus_connection_send(d->myConn, msg, NULL);
634 
635   // Free message (lib will hold a reference until send is complete)
636   dbus_message_unref(msg);
637 }
638 
sendReply(DBusMessage * msgref,const char * fmt,...)639 void DbusInterface::sendReply(DBusMessage* msgref, const char* fmt, ...)
640 {
641   LICQ_D();
642 
643   if (d->myConn == NULL)
644     return;
645 
646   DBusMessage* msg = dbus_message_new_method_return(msgref);
647   if (msg == NULL)
648   {
649     Licq::gLog.error("Failed to allocate reply message");
650     return;
651   }
652 
653   if (fmt != NULL)
654   {
655     // Append arguments to signal
656     DBusMessageIter args;
657     dbus_message_iter_init_append(msg, &args);
658     va_list ap;
659     va_start(ap, fmt);
660     if (!Private::addMsgArgs(&args, fmt, ap))
661     {
662       Licq::gLog.error("Failed to add reply parameters");
663       va_end(ap);
664       dbus_message_unref(msg);
665       return;
666     }
667     va_end(ap);
668   }
669 
670   // Queue signal to be sent (will be handled by main loop)
671   dbus_connection_send(d->myConn, msg, NULL);
672 
673   // Free message (lib will hold a reference until send is complete)
674   dbus_message_unref(msg);
675 }
676 
sendError(DBusMessage * msgref,int type,const char * message)677 void DbusInterface::sendError(DBusMessage* msgref, int type, const char* message)
678 {
679   LICQ_D();
680 
681   if (d->myConn == NULL)
682     return;
683 
684   const char* errname;
685   switch (type)
686   {
687     default:
688     case ErrorFailed:           errname = DBUS_ERROR_FAILED; break;
689     case ErrorInvalidArgs:      errname = DBUS_ERROR_INVALID_ARGS; break;
690     case ErrorInvalidSignature: errname = DBUS_ERROR_INVALID_SIGNATURE; break;
691     case ErrorNotSupported:     errname = DBUS_ERROR_NOT_SUPPORTED; break;
692     case ErrorUnknownInterface: errname = DBUS_ERROR_UNKNOWN_INTERFACE; break;
693     case ErrorUnknownMethod:    errname = DBUS_ERROR_UNKNOWN_METHOD; break;
694     case ErrorUnknownObject:    errname = DBUS_ERROR_UNKNOWN_OBJECT; break;
695   }
696   DBusMessage* reply = dbus_message_new_error(msgref, errname, message);
697   if (reply == NULL)
698   {
699     Licq::gLog.error("Failed to allocate error message");
700     return;
701   }
702 
703   // Queue signal to be sent (will be handled by main loop)
704   dbus_connection_send(d->myConn, reply, NULL);
705 
706   // Free message (lib will hold a reference until send is complete)
707   dbus_message_unref(reply);
708 }
709 
getNextMessageParamValue(DBusMessageIter * msgref,void * ret)710 void DbusInterface::getNextMessageParamValue(DBusMessageIter* msgref, void* ret)
711 {
712   dbus_message_iter_get_basic(msgref, ret);
713 }
714