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 QtGui module 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 #include "qcopchannel_qws.h"
43 
44 #ifndef QT_NO_COP
45 
46 #include "qwsdisplay_qws.h"
47 #include "qwscommand_qws_p.h"
48 #include "qwindowsystem_qws.h"
49 #include "qwindowsystem_p.h"
50 #include "qlist.h"
51 #include "qmap.h"
52 #include "qdatastream.h"
53 #include "qpointer.h"
54 #include "qmutex.h"
55 
56 #include "qdebug.h"
57 
58 QT_BEGIN_NAMESPACE
59 
60 typedef QMap<QString, QList<QWSClient*> > QCopServerMap;
61 static QCopServerMap *qcopServerMap = 0;
62 
63 class QCopServerRegexp
64 {
65 public:
66     QCopServerRegexp( const QString& channel, QWSClient *client );
67     QCopServerRegexp( const QCopServerRegexp& other );
68 
69     QString channel;
70     QWSClient *client;
71     QRegExp regexp;
72 };
73 
QCopServerRegexp(const QString & channel,QWSClient * client)74 QCopServerRegexp::QCopServerRegexp( const QString& channel, QWSClient *client )
75 {
76     this->channel = channel;
77     this->client = client;
78     this->regexp = QRegExp( channel, Qt::CaseSensitive, QRegExp::Wildcard );
79 }
80 
QCopServerRegexp(const QCopServerRegexp & other)81 QCopServerRegexp::QCopServerRegexp( const QCopServerRegexp& other )
82 {
83     channel = other.channel;
84     client = other.client;
85     regexp = other.regexp;
86 }
87 
88 typedef QList<QCopServerRegexp> QCopServerRegexpList;
89 static QCopServerRegexpList *qcopServerRegexpList = 0;
90 
91 typedef QMap<QString, QList< QPointer<QCopChannel> > > QCopClientMap;
92 static QCopClientMap *qcopClientMap = 0;
93 
Q_GLOBAL_STATIC(QMutex,qcopClientMapMutex)94 Q_GLOBAL_STATIC(QMutex, qcopClientMapMutex)
95 
96 // Determine if a channel name contains wildcard characters.
97 static bool containsWildcards( const QString& channel )
98 {
99     return channel.contains(QLatin1Char('*'));
100 }
101 
102 class QCopChannelPrivate
103 {
104 public:
105     QString channel;
106 };
107 
108 /*!
109     \class QCopChannel
110     \ingroup qws
111 
112     \brief The QCopChannel class provides communication capabilities
113     between clients in \l{Qt for Embedded Linux}.
114 
115     Note that this class is only available in \l{Qt for Embedded Linux}.
116 
117     The Qt COmmunication Protocol (QCOP) is a many-to-many protocol
118     for transferring messages across registered channels. A channel is
119     registered by name, and anyone who wants to can listen to the
120     channel as well as send messages through it. The QCOP protocol
121     allows clients to communicate both within the same address space
122     and between different processes.
123 
124     To send messages to a given channel, QCopChannel provides the
125     static send() function. Using this function alone, the messages
126     are queued until Qt re-enters the event loop. To immediately flush
127     all queued messages to the registered listeners, call the static
128     flush() function.
129 
130     To listen to the traffic on a given channel, you typically
131     instantiate a QCopChannel object for the given channel and connect
132     to its received() signal that is emitted whenever there is
133     incoming data.  Use the static isRegistered() function to query
134     the server for the existence of a given channel. QCopChannel
135     provides the channel() function returning the name of this
136     QCopChannel object's channel.
137 
138     In additon, QCopChannel provides the virtual receive() function
139     that can be reimplemented to filter the incoming messages and
140     data. The default implementation simply emits the received()
141     signal.
142 
143     \sa QWSServer, QWSClient, {Qt for Embedded Linux Architecture}
144 */
145 
146 /*!
147     Constructs a QCopChannel object for the specified \a channel, with
148     the given \a parent. Once created, the channel is registered by
149     the server.
150 
151     \sa isRegistered(), channel()
152 */
153 
QCopChannel(const QString & channel,QObject * parent)154 QCopChannel::QCopChannel(const QString& channel, QObject *parent) :
155     QObject(parent)
156 {
157     init(channel);
158 }
159 
160 #ifdef QT3_SUPPORT
161 /*!
162     Use the two argument overload instead, and call the
163     QObject::setObjectName() function to \a name the instance.
164 */
QCopChannel(const QString & channel,QObject * parent,const char * name)165 QCopChannel::QCopChannel(const QString& channel, QObject *parent, const char *name) :
166     QObject(parent)
167 {
168     setObjectName(QString::fromAscii(name));
169     init(channel);
170 }
171 #endif
172 
init(const QString & channel)173 void QCopChannel::init(const QString& channel)
174 {
175     d = new QCopChannelPrivate;
176     d->channel = channel;
177 
178     if (!qt_fbdpy) {
179         qFatal("QCopChannel: Must construct a QApplication "
180                 "before QCopChannel");
181         return;
182     }
183 
184     {
185 	QMutexLocker locker(qcopClientMapMutex());
186 
187 	if (!qcopClientMap)
188 	    qcopClientMap = new QCopClientMap;
189 
190 	// do we need a new channel list ?
191 	QCopClientMap::Iterator it = qcopClientMap->find(channel);
192 	if (it != qcopClientMap->end()) {
193 	    it.value().append(this);
194 	    return;
195 	}
196 
197 	it = qcopClientMap->insert(channel, QList< QPointer<QCopChannel> >());
198 	it.value().append(QPointer<QCopChannel>(this));
199     }
200 
201     // inform server about this channel
202     qt_fbdpy->registerChannel(channel);
203 }
204 
205 /*!
206   \internal
207 
208   Resend all channel registrations
209   */
reregisterAll()210 void QCopChannel::reregisterAll()
211 {
212     if(qcopClientMap)
213         for(QCopClientMap::Iterator iter = qcopClientMap->begin();
214             iter != qcopClientMap->end();
215             ++iter)
216             qt_fbdpy->registerChannel(iter.key());
217 }
218 
219 /*!
220     Destroys this QCopChannel object.
221 
222     The server is notified that this particular listener has closed
223     its connection. The server will keep the channel open until the
224     last registered listener detaches.
225 
226     \sa isRegistered(), channel()
227 */
228 
~QCopChannel()229 QCopChannel::~QCopChannel()
230 {
231     QMutexLocker locker(qcopClientMapMutex());
232     QCopClientMap::Iterator it = qcopClientMap->find(d->channel);
233     Q_ASSERT(it != qcopClientMap->end());
234     it.value().removeAll(this);
235     // still any clients connected locally ?
236     if (it.value().isEmpty()) {
237         QByteArray data;
238         QDataStream s(&data, QIODevice::WriteOnly);
239         s << d->channel;
240         if (qt_fbdpy)
241             send(QLatin1String(""), QLatin1String("detach()"), data);
242         qcopClientMap->remove(d->channel);
243     }
244 
245     delete d;
246 }
247 
248 /*!
249     Returns the name of this object's channel.
250 
251     \sa isRegistered()
252 */
253 
channel() const254 QString QCopChannel::channel() const
255 {
256     return d->channel;
257 }
258 
259 /*!
260     \fn void QCopChannel::receive(const QString& message, const QByteArray &data)
261 
262     Processes the incoming \a message and \a data.
263 
264     This function is called by the server when this object's channel
265     receives new messages. Note that the default implementation simply
266     emits the received() signal; reimplement this function to process
267     the incoming \a message and \a data.
268 
269     Note that the format of the given \a data has to be well defined
270     in order to extract the information it contains. In addition, it
271     is recommended to use the DCOP convention. This is not a
272     requirement, but you must ensure that the sender and receiver
273     agree on the argument types. For example:
274 
275     \snippet doc/src/snippets/code/src_gui_embedded_qcopchannel_qws.cpp 0
276 
277     The above code assumes that the \c message is a DCOP-style
278     function signature and the \c data contains the function's
279     arguments.
280 
281     \sa send(), channel(), received()
282  */
receive(const QString & msg,const QByteArray & data)283 void QCopChannel::receive(const QString& msg, const QByteArray &data)
284 {
285     emit received(msg, data);
286 }
287 
288 /*!
289     \fn void QCopChannel::received(const QString& message, const QByteArray &data)
290 
291     This signal is emitted whenever this object's channel receives new
292     messages (i.e., it is emitted by the receive() function), passing
293     the incoming \a message and \a data as parameters.
294 
295     \sa receive(), channel()
296 */
297 
298 /*!
299     Queries the server for the existence of the given \a channel. Returns true
300     if the channel is registered; otherwise returns false.
301 
302     \sa channel(), send()
303 */
304 
isRegistered(const QString & channel)305 bool QCopChannel::isRegistered(const QString&  channel)
306 {
307     QByteArray data;
308     QDataStream s(&data, QIODevice::WriteOnly);
309     s << channel;
310     if (!send(QLatin1String(""), QLatin1String("isRegistered()"), data))
311         return false;
312 
313     QWSQCopMessageEvent *e = qt_fbdpy->waitForQCopResponse();
314     bool known = e->message == "known";
315     delete e;
316     return known;
317 }
318 
319 /*!
320     \fn bool QCopChannel::send(const QString& channel, const QString& message)
321     \overload
322 */
323 
send(const QString & channel,const QString & msg)324 bool QCopChannel::send(const QString& channel, const QString& msg)
325 {
326     QByteArray data;
327     return send(channel, msg, data);
328 }
329 
330 /*!
331     \fn bool QCopChannel::send(const QString& channel, const QString& message,
332                        const QByteArray &data)
333 
334     Sends the given \a message on the specified \a channel with the
335     given \a data. The message will be distributed to all clients
336     subscribed to the channel. Returns true if the message is sent
337     successfully; otherwise returns false.
338 
339     It is recommended to use the DCOP convention. This is not a
340     requirement, but you must ensure that the sender and receiver
341     agree on the argument types.
342 
343     Note that QDataStream provides a convenient way to fill the byte
344     array with auxiliary data. For example:
345 
346     \snippet doc/src/snippets/code/src_gui_embedded_qcopchannel_qws.cpp 1
347 
348     In the code above the channel is \c "System/Shell". The \c message
349     is an arbitrary string, but in the example we've used the DCOP
350     convention of passing a function signature. Such a signature is
351     formatted as \c "functionname(types)" where \c types is a list of
352     zero or more comma-separated type names, with no whitespace, no
353     consts and no pointer or reference marks, i.e. no "*" or "&".
354 
355     \sa receive(), isRegistered()
356 */
357 
send(const QString & channel,const QString & msg,const QByteArray & data)358 bool QCopChannel::send(const QString& channel, const QString& msg,
359                        const QByteArray &data)
360 {
361     if (!qt_fbdpy) {
362         qFatal("QCopChannel::send: Must construct a QApplication "
363                 "before using QCopChannel");
364         return false;
365     }
366 
367     qt_fbdpy->sendMessage(channel, msg, data);
368 
369     return true;
370 }
371 
372 /*!
373     \since 4.2
374 
375     Flushes all queued messages to the registered listeners.
376 
377     Note that this function returns false if no QApplication has been
378     constructed, otherwise it returns true.
379 
380     \sa send()
381 
382 */
flush()383 bool QCopChannel::flush()
384 {
385     if (!qt_fbdpy) {
386         qFatal("QCopChannel::flush: Must construct a QApplication "
387                 "before using QCopChannel");
388         return false;
389     }
390 
391     qt_fbdpy->flushCommands();
392 
393     return true;
394 }
395 
396 class QWSServerSignalBridge : public QObject {
397   Q_OBJECT
398 
399 public:
400   void emitNewChannel(const QString& channel);
401   void emitRemovedChannel(const QString& channel);
402 
403   signals:
404   void newChannel(const QString& channel);
405   void removedChannel(const QString& channel);
406 };
407 
emitNewChannel(const QString & channel)408 void QWSServerSignalBridge::emitNewChannel(const QString& channel){
409   emit newChannel(channel);
410 }
411 
emitRemovedChannel(const QString & channel)412 void QWSServerSignalBridge::emitRemovedChannel(const QString& channel) {
413   emit removedChannel(channel);
414 }
415 
416 /*!
417     \internal
418     Server side: subscribe client \a cl on channel \a ch.
419 */
420 
registerChannel(const QString & ch,QWSClient * cl)421 void QCopChannel::registerChannel(const QString& ch, QWSClient *cl)
422 {
423     if (!qcopServerMap)
424         qcopServerMap = new QCopServerMap;
425 
426     // do we need a new channel list ?
427     QCopServerMap::Iterator it = qcopServerMap->find(ch);
428     if (it == qcopServerMap->end())
429       it = qcopServerMap->insert(ch, QList<QWSClient*>());
430 
431     // If the channel name contains wildcard characters, then we also
432     // register it on the server regexp matching list.
433     if (containsWildcards( ch )) {
434 	QCopServerRegexp item(ch, cl);
435 	if (!qcopServerRegexpList)
436 	    qcopServerRegexpList = new QCopServerRegexpList;
437 	qcopServerRegexpList->append( item );
438     }
439 
440     // If this is the first client in the channel, announce the channel as being created.
441     if (it.value().count() == 0) {
442       QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge();
443       connect(qwsBridge, SIGNAL(newChannel(QString)), qwsServer, SIGNAL(newChannel(QString)));
444       qwsBridge->emitNewChannel(ch);
445       delete qwsBridge;
446     }
447 
448     it.value().append(cl);
449 }
450 
451 /*!
452     \internal
453     Server side: unsubscribe \a cl from all channels.
454 */
455 
detach(QWSClient * cl)456 void QCopChannel::detach(QWSClient *cl)
457 {
458     if (!qcopServerMap)
459         return;
460 
461     QCopServerMap::Iterator it = qcopServerMap->begin();
462     for (; it != qcopServerMap->end(); ++it) {
463       if (it.value().contains(cl)) {
464         it.value().removeAll(cl);
465         // If this was the last client in the channel, announce the channel as dead.
466         if (it.value().count() == 0) {
467           QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge();
468           connect(qwsBridge, SIGNAL(removedChannel(QString)), qwsServer, SIGNAL(removedChannel(QString)));
469           qwsBridge->emitRemovedChannel(it.key());
470           delete qwsBridge;
471         }
472       }
473     }
474 
475     if (!qcopServerRegexpList)
476 	return;
477 
478     QCopServerRegexpList::Iterator it2 = qcopServerRegexpList->begin();
479     while(it2 != qcopServerRegexpList->end()) {
480 	if ((*it2).client == cl)
481 	    it2 = qcopServerRegexpList->erase(it2);
482 	else
483 	    ++it2;
484     }
485 }
486 
487 /*!
488     \internal
489     Server side: transmit the message to all clients registered to the
490     specified channel.
491 */
492 
answer(QWSClient * cl,const QString & ch,const QString & msg,const QByteArray & data)493 void QCopChannel::answer(QWSClient *cl, const QString& ch,
494                           const QString& msg, const QByteArray &data)
495 {
496     // internal commands
497     if (ch.isEmpty()) {
498         if (msg == QLatin1String("isRegistered()")) {
499             QString c;
500             QDataStream s(data);
501             s >> c;
502             bool known = qcopServerMap && qcopServerMap->contains(c)
503                         && !((*qcopServerMap)[c]).isEmpty();
504             // Yes, it's a typo, it's not user-visible, and we choose not to fix it for compatibility
505             QLatin1String ans = QLatin1String(known ? "known" : "unknown");
506             QWSServerPrivate::sendQCopEvent(cl, QLatin1String(""),
507                                             ans, data, true);
508             return;
509         } else if (msg == QLatin1String("detach()")) {
510             QString c;
511             QDataStream s(data);
512             s >> c;
513             Q_ASSERT(qcopServerMap);
514             QCopServerMap::Iterator it = qcopServerMap->find(c);
515             if (it != qcopServerMap->end()) {
516                 //Q_ASSERT(it.value().contains(cl));
517                 it.value().removeAll(cl);
518                 if (it.value().isEmpty()) {
519                   // If this was the last client in the channel, announce the channel as dead
520                   QWSServerSignalBridge* qwsBridge = new QWSServerSignalBridge();
521                   connect(qwsBridge, SIGNAL(removedChannel(QString)), qwsServer, SIGNAL(removedChannel(QString)));
522                   qwsBridge->emitRemovedChannel(it.key());
523                   delete qwsBridge;
524                   qcopServerMap->erase(it);
525                 }
526             }
527 	    if (qcopServerRegexpList && containsWildcards(c)) {
528 		// Remove references to a wildcarded channel.
529 		QCopServerRegexpList::Iterator it
530 		    = qcopServerRegexpList->begin();
531 		while(it != qcopServerRegexpList->end()) {
532 		    if ((*it).client == cl && (*it).channel == c)
533 			it = qcopServerRegexpList->erase(it);
534 		    else
535 			++it;
536 		}
537 	    }
538             return;
539         }
540         qWarning("QCopChannel: unknown internal command %s", qPrintable(msg));
541         QWSServerPrivate::sendQCopEvent(cl, QLatin1String(""),
542                                         QLatin1String("bad"), data);
543         return;
544     }
545 
546     if (qcopServerMap) {
547         QList<QWSClient*> clist = qcopServerMap->value(ch);
548         for (int i=0; i < clist.size(); ++i) {
549             QWSClient *c = clist.at(i);
550             QWSServerPrivate::sendQCopEvent(c, ch, msg, data);
551         }
552     }
553 
554     if(qcopServerRegexpList && !containsWildcards(ch)) {
555 	// Search for wildcard matches and forward the message on.
556 	QCopServerRegexpList::ConstIterator it = qcopServerRegexpList->constBegin();
557 	for (; it != qcopServerRegexpList->constEnd(); ++it) {
558 	    if ((*it).regexp.exactMatch(ch)) {
559 		QByteArray newData;
560 		{
561 		    QDataStream stream
562 			(&newData, QIODevice::WriteOnly | QIODevice::Append);
563 		    stream << ch;
564 		    stream << msg;
565 		    stream << data;
566 		    // Stream is flushed and closed at this point.
567 		}
568 		QWSServerPrivate::sendQCopEvent
569 		    ((*it).client, (*it).channel,
570 		     QLatin1String("forwardedMessage(QString,QString,QByteArray)"),
571 		     newData);
572 	    }
573 	}
574     }
575 }
576 
577 /*!
578     \internal
579     Client side: distribute received event to the QCop instance managing the
580     channel.
581 */
sendLocally(const QString & ch,const QString & msg,const QByteArray & data)582 void QCopChannel::sendLocally(const QString& ch, const QString& msg,
583                                 const QByteArray &data)
584 {
585     Q_ASSERT(qcopClientMap);
586 
587     // filter out internal events
588     if (ch.isEmpty())
589         return;
590 
591     // feed local clients with received data
592     QList< QPointer<QCopChannel> > clients;
593     {
594 	QMutexLocker locker(qcopClientMapMutex());
595 	clients = (*qcopClientMap)[ch];
596     }
597     for (int i = 0; i < clients.size(); ++i) {
598 	QCopChannel *channel = (QCopChannel *)clients.at(i);
599 	if ( channel )
600 	    channel->receive(msg, data);
601     }
602 }
603 
604 QT_END_NAMESPACE
605 
606 #include "qcopchannel_qws.moc"
607 
608 #endif
609