1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 
43 #include <QDebug>
44 #include <QtCore>
45 #include <poll.h>
46 #include <dbus/dbus.h>
47 #include <dbus/dbus-glib-lowlevel.h>
48 #include <glib.h>
49 #include "dbusdispatcher.h"
50 
51 namespace Maemo {
52 
53 /*!
54     \class Maemo::DBusDispatcher
55 
56     \brief DBusDispatcher is a class that can send DBUS method call
57     messages and receive unicast signals from DBUS objects.
58 */
59 
60 class DBusDispatcherPrivate
61 {
62 public:
DBusDispatcherPrivate(const QString & service,const QString & path,const QString & interface,const QString & signalPath)63     DBusDispatcherPrivate(const QString& service,
64                           const QString& path,
65                           const QString& interface,
66                           const QString& signalPath)
67         : service(service), path(path), interface(interface),
68           signalPath(signalPath), connection(0)
69     {
70         memset(&signal_vtable, 0, sizeof(signal_vtable));
71     }
72 
~DBusDispatcherPrivate()73     ~DBusDispatcherPrivate()
74     {
75         foreach(DBusPendingCall *call, pending_calls) {
76             dbus_pending_call_cancel(call);
77             dbus_pending_call_unref(call);
78         }
79     }
80 
81     QString service;
82     QString path;
83     QString interface;
84     QString signalPath;
85     struct DBusConnection *connection;
86     QList<DBusPendingCall *> pending_calls;
87     struct DBusObjectPathVTable signal_vtable;
88 };
89 
constantVariantList(const QVariantList & variantList)90 static bool constantVariantList(const QVariantList& variantList) {
91     // Special case, empty list == empty struct
92     if (variantList.isEmpty()) {
93         return false;
94     } else {
95         QVariant::Type type = variantList[0].type();
96         // Iterate items in the list and check if they are same type
97         foreach(QVariant variant, variantList) {
98             if (variant.type() != type) {
99                 return false;
100             }
101         }
102     }
103     return true;
104 }
105 
variantToSignature(const QVariant & argument,bool constantList=true)106 static QString variantToSignature(const QVariant& argument,
107                                   bool constantList = true) {
108     switch (argument.type()) {
109         case QVariant::Bool:
110             return "b";
111         case QVariant::ByteArray:
112             return "ay";
113         case QVariant::Char:
114             return "y";
115         case QVariant::Int:
116             return "i";
117         case QVariant::UInt:
118             return "u";
119         case QVariant::StringList:
120             return "as";
121         case QVariant::String:
122             return "s";
123         case QVariant::LongLong:
124             return "x";
125         case QVariant::ULongLong:
126             return "t";
127         case QVariant::List:
128             {
129             QString signature;
130             QVariantList variantList = argument.toList();
131             if (!constantList) {
132                 signature += DBUS_STRUCT_BEGIN_CHAR_AS_STRING;
133                 foreach(QVariant listItem, variantList) {
134                     signature += variantToSignature(listItem);
135                 }
136                 signature += DBUS_STRUCT_END_CHAR_AS_STRING;
137             } else {
138                 if (variantList.isEmpty())
139                     return "";
140                 signature = "a" + variantToSignature(variantList[0]);
141             }
142 
143             return signature;
144             }
145         default:
146             qDebug() << "Unsupported variant type: " << argument.type();
147             break;
148     }
149 
150     return "";
151 }
152 
appendVariantToDBusMessage(const QVariant & argument,DBusMessageIter * dbus_iter)153 static bool appendVariantToDBusMessage(const QVariant& argument,
154                                        DBusMessageIter *dbus_iter) {
155     int idx = 0;
156     DBusMessageIter array_iter;
157     QStringList str_list;
158     dbus_bool_t bool_data;
159     dbus_int32_t int32_data;
160     dbus_uint32_t uint32_data;
161     dbus_int64_t int64_data;
162     dbus_uint64_t uint64_data;
163     char *str_data;
164     char char_data;
165 
166     switch (argument.type()) {
167 
168         case QVariant::Bool:
169             bool_data = argument.toBool();
170             dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_BOOLEAN,
171                                            &bool_data);
172             break;
173 
174         case QVariant::ByteArray:
175             str_data = argument.toByteArray().data();
176             dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY,
177                                              DBUS_TYPE_BYTE_AS_STRING, &array_iter);
178             dbus_message_iter_append_fixed_array(&array_iter,
179                                                  DBUS_TYPE_BYTE,
180                                                  &str_data,
181                                                  argument.toByteArray().size());
182             dbus_message_iter_close_container(dbus_iter, &array_iter);
183             break;
184 
185         case QVariant::Char:
186             char_data = argument.toChar().toAscii();
187             dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_BYTE,
188                                            &char_data);
189             break;
190 
191         case QVariant::Int:
192             int32_data = argument.toInt();
193             dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_INT32,
194                                            &int32_data);
195             break;
196 
197         case QVariant::String: {
198             QByteArray data = argument.toString().toUtf8();
199             str_data = data.data();
200             dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_STRING,
201                                            &str_data);
202             break;
203         }
204 
205         case QVariant::StringList:
206             str_list = argument.toStringList();
207             dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY,
208                                              "s", &array_iter);
209             for (idx = 0; idx < str_list.size(); idx++) {
210                 QByteArray data = str_list.at(idx).toLatin1();
211                 str_data = data.data();
212                 dbus_message_iter_append_basic(&array_iter,
213                                                DBUS_TYPE_STRING,
214                                                &str_data);
215             }
216             dbus_message_iter_close_container(dbus_iter, &array_iter);
217             break;
218 
219         case QVariant::UInt:
220             uint32_data = argument.toUInt();
221             dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_UINT32,
222                                            &uint32_data);
223             break;
224 
225         case QVariant::ULongLong:
226             uint64_data = argument.toULongLong();
227             dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_UINT64,
228                                            &uint64_data);
229             break;
230 
231         case QVariant::LongLong:
232             int64_data = argument.toLongLong();
233             dbus_message_iter_append_basic(dbus_iter, DBUS_TYPE_INT64,
234                                            &int64_data);
235             break;
236 
237         case QVariant::List:
238             {
239             QVariantList variantList = argument.toList();
240             bool constantList = constantVariantList(variantList);
241             DBusMessageIter array_iter;
242 
243             // List is mapped either as an DBUS array (all items same type)
244             // DBUS struct (variable types) depending on constantList
245             if (constantList) {
246                 // Resolve the signature for the first item
247                 QString signature = "";
248                 if (!variantList.isEmpty()) {
249                     signature = variantToSignature(
250                                 variantList[0],
251                                 constantVariantList(variantList[0].toList()));
252                 }
253 
254                 // Mapped as DBUS array
255                 dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_ARRAY,
256                                                  signature.toAscii(),
257                                                  &array_iter);
258 
259                 foreach(QVariant listItem, variantList) {
260                     appendVariantToDBusMessage(listItem, &array_iter);
261                 }
262 
263                 dbus_message_iter_close_container(dbus_iter, &array_iter);
264             } else {
265                 // Mapped as DBUS struct
266                 dbus_message_iter_open_container(dbus_iter, DBUS_TYPE_STRUCT,
267                                                  NULL,
268                                                  &array_iter);
269 
270                 foreach(QVariant listItem, variantList) {
271                     appendVariantToDBusMessage(listItem, &array_iter);
272                 }
273 
274                 dbus_message_iter_close_container(dbus_iter, &array_iter);
275             }
276 
277             break;
278             }
279         default:
280             qDebug() << "Unsupported variant type: " << argument.type();
281             break;
282     }
283 
284     return true;
285 }
286 
getVariantFromDBusMessage(DBusMessageIter * iter)287 static QVariant getVariantFromDBusMessage(DBusMessageIter *iter) {
288     dbus_bool_t bool_data;
289     dbus_int32_t int32_data;
290     dbus_uint32_t uint32_data;
291     dbus_int64_t int64_data;
292     dbus_uint64_t uint64_data;
293     char *str_data;
294     char char_data;
295     int argtype = dbus_message_iter_get_arg_type(iter);
296 
297     switch (argtype) {
298 
299         case DBUS_TYPE_BOOLEAN:
300         {
301             dbus_message_iter_get_basic(iter, &bool_data);
302             QVariant variant((bool)bool_data);
303             return variant;
304         }
305 
306         case DBUS_TYPE_ARRAY:
307         {
308             // Handle all arrays here
309             int elem_type = dbus_message_iter_get_element_type(iter);
310             DBusMessageIter array_iter;
311 
312             dbus_message_iter_recurse(iter, &array_iter);
313 
314             if (elem_type == DBUS_TYPE_BYTE) {
315                 QByteArray byte_array;
316                 do {
317                     dbus_message_iter_get_basic(&array_iter, &char_data);
318                     byte_array.append(char_data);
319                 } while (dbus_message_iter_next(&array_iter));
320                 QVariant variant(byte_array);
321                 return variant;
322             } else if (elem_type == DBUS_TYPE_STRING) {
323                 QStringList str_list;
324                 do {
325                     dbus_message_iter_get_basic(&array_iter, &str_data);
326                     str_list.append(str_data);
327                 } while (dbus_message_iter_next(&array_iter));
328                 QVariant variant(str_list);
329                 return variant;
330             } else {
331                 QVariantList variantList;
332                 do {
333                     variantList << getVariantFromDBusMessage(&array_iter);
334                 } while (dbus_message_iter_next(&array_iter));
335                 QVariant variant(variantList);
336                 return variant;
337             }
338             break;
339         }
340 
341         case DBUS_TYPE_BYTE:
342         {
343             dbus_message_iter_get_basic(iter, &char_data);
344             QChar ch(char_data);
345             QVariant variant(ch);
346             return variant;
347         }
348 
349         case DBUS_TYPE_INT32:
350         {
351             dbus_message_iter_get_basic(iter, &int32_data);
352             QVariant variant((int)int32_data);
353             return variant;
354         }
355 
356         case DBUS_TYPE_UINT32:
357         {
358             dbus_message_iter_get_basic(iter, &uint32_data);
359             QVariant variant((uint)uint32_data);
360             return variant;
361         }
362 
363         case DBUS_TYPE_STRING:
364         {
365             dbus_message_iter_get_basic(iter, &str_data);
366             QString str(QString::fromUtf8(str_data));
367             QVariant variant(str);
368             return variant;
369         }
370 
371         case DBUS_TYPE_INT64:
372         {
373             dbus_message_iter_get_basic(iter, &int64_data);
374             QVariant variant((qlonglong)int64_data);
375             return variant;
376         }
377 
378         case DBUS_TYPE_UINT64:
379         {
380             dbus_message_iter_get_basic(iter, &uint64_data);
381             QVariant variant((qulonglong)uint64_data);
382             return variant;
383         }
384 
385         case DBUS_TYPE_STRUCT:
386         {
387             // Handle all structs here
388             DBusMessageIter struct_iter;
389             dbus_message_iter_recurse(iter, &struct_iter);
390 
391 	    QVariantList variantList;
392 	    do {
393 	      variantList << getVariantFromDBusMessage(&struct_iter);
394 	    } while (dbus_message_iter_next(&struct_iter));
395 	    QVariant variant(variantList);
396 	    return variant;
397         }
398 
399         default:
400             qDebug() << "Unsupported DBUS type: " << argtype;
401     }
402 
403     return QVariant();
404 }
405 
signalHandler(DBusConnection * connection,DBusMessage * message,void * object_ref)406 static DBusHandlerResult signalHandler (DBusConnection *connection,
407                                         DBusMessage *message,
408                                         void *object_ref) {
409     (void)connection;
410     QString interface;
411     QString signal;
412     DBusDispatcher *dispatcher = (DBusDispatcher *)object_ref;
413 
414     if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) {
415         interface = dbus_message_get_interface(message);
416         signal = dbus_message_get_member(message);
417 
418         QList<QVariant> arglist;
419         DBusMessageIter dbus_iter;
420 
421         if (dbus_message_iter_init(message, &dbus_iter)) {
422             // Read return arguments
423             while (dbus_message_iter_get_arg_type (&dbus_iter) != DBUS_TYPE_INVALID) {
424                 arglist << getVariantFromDBusMessage(&dbus_iter);
425                 dbus_message_iter_next(&dbus_iter);
426             }
427         }
428 
429         dispatcher->emitSignalReceived(interface, signal, arglist);
430         return DBUS_HANDLER_RESULT_HANDLED;
431     }
432     (void)message;
433     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
434 }
435 
DBusDispatcher(const QString & service,const QString & path,const QString & interface,QObject * parent)436 DBusDispatcher::DBusDispatcher(const QString& service,
437                                const QString& path,
438                                const QString& interface,
439                                QObject *parent)
440  : QObject(parent),
441    d_ptr(new DBusDispatcherPrivate(service, path, interface, path)) {
442     setupDBus();
443 }
444 
DBusDispatcher(const QString & service,const QString & path,const QString & interface,const QString & signalPath,QObject * parent)445 DBusDispatcher::DBusDispatcher(const QString& service,
446                                const QString& path,
447                                const QString& interface,
448                                const QString& signalPath,
449                                QObject *parent)
450  : QObject(parent),
451    d_ptr(new DBusDispatcherPrivate(service, path, interface, signalPath)) {
452     setupDBus();
453 }
454 
~DBusDispatcher()455 DBusDispatcher::~DBusDispatcher()
456 {
457     if (d_ptr->connection) {
458         dbus_connection_close(d_ptr->connection);
459         dbus_connection_unref(d_ptr->connection);
460     }
461     delete d_ptr;
462 }
463 
setupDBus()464 void DBusDispatcher::setupDBus()
465 {
466     d_ptr->connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, NULL);
467 
468     if (d_ptr->connection == NULL)
469         qDebug() << "Unable to get DBUS connection!";
470     else {
471         d_ptr->signal_vtable.message_function = signalHandler;
472 
473 	dbus_connection_set_exit_on_disconnect(d_ptr->connection, FALSE);
474         dbus_connection_setup_with_g_main(d_ptr->connection, g_main_context_get_thread_default());
475         dbus_connection_register_object_path(d_ptr->connection,
476                                              d_ptr->signalPath.toLatin1(),
477                                              &d_ptr->signal_vtable,
478                                              this);
479     }
480 }
481 
prepareDBusCall(const QString & service,const QString & path,const QString & interface,const QString & method,const QVariant & arg1=QVariant (),const QVariant & arg2=QVariant (),const QVariant & arg3=QVariant (),const QVariant & arg4=QVariant (),const QVariant & arg5=QVariant (),const QVariant & arg6=QVariant (),const QVariant & arg7=QVariant (),const QVariant & arg8=QVariant ())482 static DBusMessage *prepareDBusCall(const QString& service,
483                                     const QString& path,
484                                     const QString& interface,
485                                     const QString& method,
486                                     const QVariant& arg1 = QVariant(),
487                                     const QVariant& arg2 = QVariant(),
488                                     const QVariant& arg3 = QVariant(),
489                                     const QVariant& arg4 = QVariant(),
490                                     const QVariant& arg5 = QVariant(),
491                                     const QVariant& arg6 = QVariant(),
492                                     const QVariant& arg7 = QVariant(),
493                                     const QVariant& arg8 = QVariant())
494 {
495     DBusMessage *message = dbus_message_new_method_call(service.toLatin1(),
496                                                         path.toLatin1(),
497                                                         interface.toLatin1(),
498                                                         method.toLatin1());
499     DBusMessageIter dbus_iter;
500 
501     // Append variants to DBUS message
502     QList<QVariant> arglist;
503     if (arg1.isValid()) arglist << arg1;
504     if (arg2.isValid()) arglist << arg2;
505     if (arg3.isValid()) arglist << arg3;
506     if (arg4.isValid()) arglist << arg4;
507     if (arg5.isValid()) arglist << arg5;
508     if (arg6.isValid()) arglist << arg6;
509     if (arg7.isValid()) arglist << arg7;
510     if (arg8.isValid()) arglist << arg8;
511 
512     dbus_message_iter_init_append (message, &dbus_iter);
513 
514     while (!arglist.isEmpty()) {
515         QVariant argument = arglist.takeFirst();
516         appendVariantToDBusMessage(argument, &dbus_iter);
517     }
518 
519     return message;
520 }
521 
call(const QString & method,const QVariant & arg1,const QVariant & arg2,const QVariant & arg3,const QVariant & arg4,const QVariant & arg5,const QVariant & arg6,const QVariant & arg7,const QVariant & arg8)522 QList<QVariant> DBusDispatcher::call(const QString& method,
523                                      const QVariant& arg1,
524                                      const QVariant& arg2,
525                                      const QVariant& arg3,
526                                      const QVariant& arg4,
527                                      const QVariant& arg5,
528                                      const QVariant& arg6,
529                                      const QVariant& arg7,
530                                      const QVariant& arg8) {
531     DBusMessageIter dbus_iter;
532     DBusMessage *message = prepareDBusCall(d_ptr->service, d_ptr->path,
533                                            d_ptr->interface, method,
534                                            arg1, arg2, arg3, arg4, arg5,
535                                            arg6, arg7, arg8);
536     DBusMessage *reply = dbus_connection_send_with_reply_and_block(
537                                                     d_ptr->connection,
538                                                     message, -1, NULL);
539     dbus_message_unref(message);
540 
541     QList<QVariant> replylist;
542     if (reply != NULL &&  dbus_message_iter_init(reply, &dbus_iter)) {
543         // Read return arguments
544         while (dbus_message_iter_get_arg_type (&dbus_iter) != DBUS_TYPE_INVALID) {
545             replylist << getVariantFromDBusMessage(&dbus_iter);
546             dbus_message_iter_next(&dbus_iter);
547         }
548     }
549     if (reply != NULL) dbus_message_unref(reply);
550     return replylist;
551 }
552 
553 class PendingCallInfo {
554 public:
555     QString method;
556     DBusDispatcher *dispatcher;
557     DBusDispatcherPrivate *priv;
558 };
559 
freePendingCallInfo(void * memory)560 static void freePendingCallInfo(void *memory) {
561     PendingCallInfo *info = (PendingCallInfo *)memory;
562     delete info;
563 }
564 
pendingCallFunction(DBusPendingCall * pending,void * memory)565 static void pendingCallFunction (DBusPendingCall *pending,
566                                  void *memory) {
567     PendingCallInfo *info = (PendingCallInfo *)memory;
568     QString errorStr;
569     QList<QVariant> replyList;
570     DBusMessage *reply = dbus_pending_call_steal_reply (pending);
571 
572     Q_ASSERT(reply != NULL);
573 
574     if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
575         errorStr = dbus_message_get_error_name (reply);
576     } else {
577         DBusMessageIter dbus_iter;
578         dbus_message_iter_init(reply, &dbus_iter);
579         // Read return arguments
580         while (dbus_message_iter_get_arg_type (&dbus_iter) != DBUS_TYPE_INVALID) {
581             replyList << getVariantFromDBusMessage(&dbus_iter);
582             dbus_message_iter_next(&dbus_iter);
583         }
584     }
585 
586     info->priv->pending_calls.removeOne(pending);
587     info->dispatcher->emitCallReply(info->method, replyList, errorStr);
588     dbus_message_unref(reply);
589     dbus_pending_call_unref(pending);
590 }
591 
callAsynchronous(const QString & method,const QVariant & arg1,const QVariant & arg2,const QVariant & arg3,const QVariant & arg4,const QVariant & arg5,const QVariant & arg6,const QVariant & arg7,const QVariant & arg8)592 bool DBusDispatcher::callAsynchronous(const QString& method,
593                                       const QVariant& arg1,
594                                       const QVariant& arg2,
595                                       const QVariant& arg3,
596                                       const QVariant& arg4,
597                                       const QVariant& arg5,
598                                       const QVariant& arg6,
599                                       const QVariant& arg7,
600                                       const QVariant& arg8) {
601     DBusMessage *message = prepareDBusCall(d_ptr->service, d_ptr->path,
602                                            d_ptr->interface, method,
603                                            arg1, arg2, arg3, arg4, arg5,
604                                            arg6, arg7, arg8);
605     DBusPendingCall *call = NULL;
606     dbus_bool_t ret = dbus_connection_send_with_reply(d_ptr->connection,
607                                                       message, &call, -1);
608     PendingCallInfo *info = new PendingCallInfo;
609     info->method = method;
610     info->dispatcher = this;
611     info->priv = d_ptr;
612 
613     dbus_pending_call_set_notify(call, pendingCallFunction, info, freePendingCallInfo);
614     d_ptr->pending_calls.append(call);
615     return (bool)ret;
616 }
617 
emitSignalReceived(const QString & interface,const QString & signal,const QList<QVariant> & args)618 void DBusDispatcher::emitSignalReceived(const QString& interface,
619                                         const QString& signal,
620                                         const QList<QVariant>& args) {
621     emit signalReceived(interface, signal, args); }
622 
emitCallReply(const QString & method,const QList<QVariant> & args,const QString & error)623 void DBusDispatcher::emitCallReply(const QString& method,
624                                    const QList<QVariant>& args,
625                                    const QString& error) {
626     emit callReply(method, args, error); }
627 
synchronousDispatch(int timeout_ms)628 void DBusDispatcher::synchronousDispatch(int timeout_ms)
629 {
630     dbus_connection_read_write_dispatch(d_ptr->connection, timeout_ms);
631 }
632 
633 } // Maemo namespace
634 
635