1 #include <tests/lib/test.h>
2 
3 #include <tests/lib/glib-helpers/test-conn-helper.h>
4 
5 #include <tests/lib/glib/simple-conn.h>
6 #include <tests/lib/glib/stream-tube-chan.h>
7 
8 #include <TelepathyQt/Connection>
9 #include <TelepathyQt/IncomingStreamTubeChannel>
10 #include <TelepathyQt/OutgoingStreamTubeChannel>
11 #include <TelepathyQt/PendingReady>
12 #include <TelepathyQt/PendingStreamTubeConnection>
13 #include <TelepathyQt/ReferencedHandles>
14 #include <TelepathyQt/StreamTubeChannel>
15 
16 #include <telepathy-glib/telepathy-glib.h>
17 
18 #include <QHostAddress>
19 #include <QLocalServer>
20 #include <QLocalSocket>
21 #include <QTcpServer>
22 #include <QTcpSocket>
23 
24 #include <stdio.h>
25 
26 using namespace Tp;
27 
28 namespace
29 {
30 
31 struct TestContext
32 {
33     bool withContact;
34     TpSocketAddressType addressType;
35     TpSocketAccessControl accessControl;
36 };
37 
38 // FIXME: Enable IPv6 and Port access control tests
39 TestContext contexts[] = {
40   { FALSE, TP_SOCKET_ADDRESS_TYPE_UNIX, TP_SOCKET_ACCESS_CONTROL_LOCALHOST },
41   { FALSE, TP_SOCKET_ADDRESS_TYPE_IPV4, TP_SOCKET_ACCESS_CONTROL_LOCALHOST },
42   //{ FALSE, TP_SOCKET_ADDRESS_TYPE_IPV6, TP_SOCKET_ACCESS_CONTROL_LOCALHOST },
43   { FALSE, TP_SOCKET_ADDRESS_TYPE_UNIX, TP_SOCKET_ACCESS_CONTROL_CREDENTIALS },
44   { FALSE, TP_SOCKET_ADDRESS_TYPE_IPV4, TP_SOCKET_ACCESS_CONTROL_PORT },
45 
46   { TRUE, TP_SOCKET_ADDRESS_TYPE_UNIX, TP_SOCKET_ACCESS_CONTROL_LOCALHOST },
47   { TRUE, TP_SOCKET_ADDRESS_TYPE_IPV4, TP_SOCKET_ACCESS_CONTROL_LOCALHOST },
48   //{ TRUE, TP_SOCKET_ADDRESS_TYPE_IPV6, TP_SOCKET_ACCESS_CONTROL_LOCALHOST },
49   { TRUE, TP_SOCKET_ADDRESS_TYPE_UNIX, TP_SOCKET_ACCESS_CONTROL_CREDENTIALS },
50   { TRUE, TP_SOCKET_ADDRESS_TYPE_IPV4, TP_SOCKET_ACCESS_CONTROL_PORT },
51 
52   { FALSE, (TpSocketAddressType) NUM_TP_SOCKET_ADDRESS_TYPES, (TpSocketAccessControl) NUM_TP_SOCKET_ACCESS_CONTROLS }
53 };
54 
destroySocketControlList(gpointer data)55 void destroySocketControlList(gpointer data)
56 {
57     g_array_free((GArray *) data, TRUE);
58 }
59 
createSupportedSocketTypesHash(TpSocketAddressType addressType,TpSocketAccessControl accessControl)60 GHashTable *createSupportedSocketTypesHash(TpSocketAddressType addressType,
61         TpSocketAccessControl accessControl)
62 {
63     GHashTable *ret;
64     GArray *tab;
65 
66     ret = g_hash_table_new_full(NULL, NULL, NULL, destroySocketControlList);
67 
68     tab = g_array_sized_new(FALSE, FALSE, sizeof(TpSocketAccessControl), 1);
69     g_array_append_val(tab, accessControl);
70 
71     g_hash_table_insert(ret, GUINT_TO_POINTER(addressType), tab);
72 
73     return ret;
74 }
75 
createTcpClientGSocket(TpSocketAddressType socketType)76 GSocket *createTcpClientGSocket(TpSocketAddressType socketType)
77 {
78     Q_ASSERT(socketType != TP_SOCKET_ADDRESS_TYPE_UNIX);
79 
80     GSocketFamily family = (GSocketFamily) 0;
81     switch (socketType) {
82         case TP_SOCKET_ADDRESS_TYPE_UNIX:
83             family = G_SOCKET_FAMILY_UNIX;
84             break;
85 
86         case TP_SOCKET_ADDRESS_TYPE_IPV4:
87             family = G_SOCKET_FAMILY_IPV4;
88             break;
89 
90         case TP_SOCKET_ADDRESS_TYPE_IPV6:
91             family = G_SOCKET_FAMILY_IPV6;
92             break;
93 
94         default:
95             Q_ASSERT(false);
96     }
97 
98     /* Create socket to connect to the CM */
99     GError *error = NULL;
100     GSocket *clientSocket = g_socket_new(family, G_SOCKET_TYPE_STREAM,
101             G_SOCKET_PROTOCOL_DEFAULT, &error);
102     Q_ASSERT(clientSocket != NULL);
103 
104     if (socketType == TP_SOCKET_ADDRESS_TYPE_IPV4 ||
105         socketType == TP_SOCKET_ADDRESS_TYPE_IPV6) {
106         /* Bind local address */
107         GSocketAddress *localAddress;
108         GInetAddress *tmp;
109         gboolean success;
110 
111         tmp = g_inet_address_new_loopback(family);
112         localAddress = g_inet_socket_address_new(tmp, 0);
113 
114         success = g_socket_bind(clientSocket, localAddress,
115                 TRUE, &error);
116 
117         g_object_unref(tmp);
118         g_object_unref(localAddress);
119 
120         Q_ASSERT(success);
121     }
122 
123     return clientSocket;
124 }
125 
126 }
127 
128 class TestStreamTubeChan : public Test
129 {
130     Q_OBJECT
131 
132 public:
TestStreamTubeChan(QObject * parent=0)133     TestStreamTubeChan(QObject *parent = 0)
134         : Test(parent),
135           mConn(0), mChanService(0), mLocalConnectionId(-1), mRemoteConnectionId(-1),
136           mGotLocalConnection(false), mGotRemoteConnection(false),
137           mGotSocketConnection(false), mGotConnectionClosed(false),
138           mOfferFinished(false), mRequiresCredentials(false), mCredentialByte(0)
139     { }
140 
141 protected Q_SLOTS:
142     void onNewLocalConnection(uint connectionId);
143     void onNewRemoteConnection(uint connectionId);
144     void onNewSocketConnection();
145     void onConnectionClosed(uint connectionId, const QString &errorName,
146             const QString &errorMesssage);
147     void onOfferFinished(Tp::PendingOperation *op);
148     void expectPendingTubeConnectionFinished(Tp::PendingOperation *op);
149 
150 private Q_SLOTS:
151     void initTestCase();
152     void init();
153 
154     void testCreation();
155     void testAcceptTwice();
156     void testAcceptSuccess();
157     void testAcceptFail();
158     void testOfferSuccess();
159     void testOutgoingConnectionMonitoring();
160 
161     void cleanup();
162     void cleanupTestCase();
163 
164 private:
165     void testCheckRemoteConnectionsCommon();
166 
167     void createTubeChannel(bool requested, TpSocketAddressType addressType,
168             TpSocketAccessControl accessControl, bool withContact);
169 
170     TestConnHelper *mConn;
171     TpTestsStreamTubeChannel *mChanService;
172     StreamTubeChannelPtr mChan;
173 
174     uint mCurrentContext;
175 
176     uint mLocalConnectionId;
177     uint mRemoteConnectionId;
178     bool mGotLocalConnection;
179     bool mGotRemoteConnection;
180     bool mGotSocketConnection;
181     bool mGotConnectionClosed;
182     bool mOfferFinished;
183     bool mRequiresCredentials;
184     uchar mCredentialByte;
185 
186     QHostAddress mExpectedAddress;
187     uint mExpectedPort;
188     uint mExpectedHandle;
189     QString mExpectedId;
190 };
191 
onNewLocalConnection(uint connectionId)192 void TestStreamTubeChan::onNewLocalConnection(uint connectionId)
193 {
194     qDebug() << "Got local connection with id:" << connectionId;
195     mLocalConnectionId = connectionId;
196     mGotLocalConnection = true;
197     QVERIFY(mChan->connections().contains(connectionId));
198 
199     mLoop->exit(0);
200 }
201 
onNewRemoteConnection(uint connectionId)202 void TestStreamTubeChan::onNewRemoteConnection(uint connectionId)
203 {
204     qDebug() << "Got remote connection with id:" << connectionId;
205     mRemoteConnectionId = connectionId;
206     mGotRemoteConnection = true;
207     QVERIFY(mChan->connections().contains(connectionId));
208 
209     testCheckRemoteConnectionsCommon();
210 }
211 
onNewSocketConnection()212 void TestStreamTubeChan::onNewSocketConnection()
213 {
214     qDebug() << "Got new socket connection";
215     mGotSocketConnection = true;
216     mLoop->exit(0);
217 }
218 
onConnectionClosed(uint connectionId,const QString & errorName,const QString & errorMesssage)219 void TestStreamTubeChan::onConnectionClosed(uint connectionId,
220         const QString &errorName, const QString &errorMesssage)
221 {
222     qDebug() << "Got connetion closed for connection" << connectionId;
223     mGotConnectionClosed = true;
224     QVERIFY(!mChan->connections().contains(connectionId));
225 
226     if (mChan->isRequested()) {
227         testCheckRemoteConnectionsCommon();
228     }
229 
230     mLoop->exit(0);
231 }
232 
onOfferFinished(Tp::PendingOperation * op)233 void TestStreamTubeChan::onOfferFinished(Tp::PendingOperation *op)
234 {
235     TEST_VERIFY_OP(op);
236 
237     mOfferFinished = true;
238     mLoop->exit(0);
239 }
240 
expectPendingTubeConnectionFinished(PendingOperation * op)241 void TestStreamTubeChan::expectPendingTubeConnectionFinished(PendingOperation *op)
242 {
243     TEST_VERIFY_OP(op);
244 
245     PendingStreamTubeConnection *pstc = qobject_cast<PendingStreamTubeConnection*>(op);
246     mRequiresCredentials = pstc->requiresCredentials();
247     mCredentialByte = pstc->credentialByte();
248     mLoop->exit(0);
249 }
250 
createTubeChannel(bool requested,TpSocketAddressType addressType,TpSocketAccessControl accessControl,bool withContact)251 void TestStreamTubeChan::createTubeChannel(bool requested,
252         TpSocketAddressType addressType,
253         TpSocketAccessControl accessControl,
254         bool withContact)
255 {
256     mChan.reset();
257     mLoop->processEvents();
258     tp_clear_object(&mChanService);
259 
260     /* Create service-side tube channel object */
261     QString chanPath = QString(QLatin1String("%1/Channel")).arg(mConn->objectPath());
262 
263     TpHandleRepoIface *contactRepo = tp_base_connection_get_handles(
264             TP_BASE_CONNECTION(mConn->service()), TP_HANDLE_TYPE_CONTACT);
265     TpHandleRepoIface *roomRepo = tp_base_connection_get_handles(
266             TP_BASE_CONNECTION(mConn->service()), TP_HANDLE_TYPE_ROOM);
267     TpHandle handle;
268     GType type;
269     if (withContact) {
270         handle = tp_handle_ensure(contactRepo, "bob", NULL, NULL);
271         type = TP_TESTS_TYPE_CONTACT_STREAM_TUBE_CHANNEL;
272     } else {
273         handle = tp_handle_ensure(roomRepo, "#test", NULL, NULL);
274         type = TP_TESTS_TYPE_ROOM_STREAM_TUBE_CHANNEL;
275     }
276 
277     TpHandle alfHandle = tp_handle_ensure(contactRepo, "alf", NULL, NULL);
278 
279     GHashTable *sockets = createSupportedSocketTypesHash(addressType, accessControl);
280 
281     mChanService = TP_TESTS_STREAM_TUBE_CHANNEL(g_object_new(
282             type,
283             "connection", mConn->service(),
284             "handle", handle,
285             "requested", requested,
286             "object-path", chanPath.toLatin1().constData(),
287             "supported-socket-types", sockets,
288             "initiator-handle", alfHandle,
289             NULL));
290 
291     /* Create client-side tube channel object */
292     GHashTable *props;
293     g_object_get(mChanService, "channel-properties", &props, NULL);
294 
295     if (requested) {
296         mChan = OutgoingStreamTubeChannel::create(mConn->client(), chanPath, QVariantMap());
297     } else {
298         mChan = IncomingStreamTubeChannel::create(mConn->client(), chanPath, QVariantMap());
299     }
300 
301     g_hash_table_unref(props);
302     g_hash_table_unref(sockets);
303 }
304 
initTestCase()305 void TestStreamTubeChan::initTestCase()
306 {
307     initTestCaseImpl();
308 
309     g_type_init();
310     g_set_prgname("stream-tube-chan");
311     tp_debug_set_flags("all");
312     dbus_g_bus_get(DBUS_BUS_STARTER, 0);
313 
314     mConn = new TestConnHelper(this,
315             TP_TESTS_TYPE_SIMPLE_CONNECTION,
316             "account", "me@example.com",
317             "protocol", "example",
318             NULL);
319     QCOMPARE(mConn->connect(), true);
320 }
321 
init()322 void TestStreamTubeChan::init()
323 {
324     initImpl();
325 
326     mCurrentContext = -1;
327 
328     mLocalConnectionId = -1;
329     mRemoteConnectionId = -1;
330     mGotLocalConnection = false;
331     mGotRemoteConnection = false;
332     mGotConnectionClosed = false;
333     mGotSocketConnection = false;
334     mOfferFinished = false;
335     mRequiresCredentials = false;
336     mCredentialByte = 0;
337 
338     mExpectedAddress = QHostAddress();
339     mExpectedPort = -1;
340     mExpectedHandle = -1;
341     mExpectedId = QString();
342 }
343 
testCheckRemoteConnectionsCommon()344 void TestStreamTubeChan::testCheckRemoteConnectionsCommon()
345 {
346     OutgoingStreamTubeChannelPtr chan = OutgoingStreamTubeChannelPtr::qObjectCast(mChan);
347     QVERIFY(chan);
348 
349     QCOMPARE(chan->contactsForConnections().isEmpty(), false);
350     QCOMPARE(chan->contactsForConnections().contains(mRemoteConnectionId), true);
351     QCOMPARE(chan->contactsForConnections().value(mRemoteConnectionId)->handle()[0],
352             mExpectedHandle);
353     QCOMPARE(chan->contactsForConnections().value(mRemoteConnectionId)->id(),
354             mExpectedId);
355 
356     if (contexts[mCurrentContext].accessControl == TP_SOCKET_ACCESS_CONTROL_PORT) {
357         // qDebug() << "+++ conn for source addresses" << chan->connectionsForSourceAddresses();
358         QCOMPARE(chan->connectionsForSourceAddresses().isEmpty(), false);
359         QCOMPARE(chan->connectionsForCredentials().isEmpty(), true);
360         QPair<QHostAddress, quint16> srcAddr(mExpectedAddress, mExpectedPort);
361         QCOMPARE(chan->connectionsForSourceAddresses().contains(srcAddr), true);
362         QCOMPARE(chan->connectionsForSourceAddresses().value(srcAddr), mRemoteConnectionId);
363     } else if (contexts[mCurrentContext].accessControl == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS) {
364         // qDebug() << "+++ conn for credentials" << chan->connectionsForCredentials();
365         QCOMPARE(chan->connectionsForCredentials().isEmpty(), false);
366         QCOMPARE(chan->connectionsForSourceAddresses().isEmpty(), true);
367         QCOMPARE(chan->connectionsForCredentials().contains(mCredentialByte), true);
368         QCOMPARE(chan->connectionsForCredentials().value(mCredentialByte), mRemoteConnectionId);
369     }
370 
371     mLoop->exit(0);
372 }
373 
testCreation()374 void TestStreamTubeChan::testCreation()
375 {
376     /* Outgoing tube */
377     createTubeChannel(true, TP_SOCKET_ADDRESS_TYPE_UNIX,
378             TP_SOCKET_ACCESS_CONTROL_LOCALHOST, true);
379     QVERIFY(connect(mChan->becomeReady(OutgoingStreamTubeChannel::FeatureCore),
380                 SIGNAL(finished(Tp::PendingOperation *)),
381                 SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
382     QCOMPARE(mLoop->exec(), 0);
383     QCOMPARE(mChan->isReady(OutgoingStreamTubeChannel::FeatureCore), true);
384     QCOMPARE(mChan->isReady(StreamTubeChannel::FeatureConnectionMonitoring), false);
385     QCOMPARE(mChan->state(), TubeChannelStateNotOffered);
386     QCOMPARE(mChan->parameters().isEmpty(), true);
387     QCOMPARE(mChan->service(), QLatin1String("test-service"));
388     QCOMPARE(mChan->supportsIPv4SocketsOnLocalhost(), false);
389     QCOMPARE(mChan->supportsIPv4SocketsWithSpecifiedAddress(), false);
390     QCOMPARE(mChan->supportsIPv6SocketsOnLocalhost(), false);
391     QCOMPARE(mChan->supportsIPv6SocketsWithSpecifiedAddress(), false);
392     QCOMPARE(mChan->supportsUnixSocketsOnLocalhost(), true);
393     QCOMPARE(mChan->supportsUnixSocketsWithCredentials(), false);
394     QCOMPARE(mChan->supportsAbstractUnixSocketsOnLocalhost(), false);
395     QCOMPARE(mChan->supportsAbstractUnixSocketsWithCredentials(), false);
396     QCOMPARE(mChan->connections().isEmpty(), true);
397     QCOMPARE(mChan->addressType(), SocketAddressTypeUnix);
398     QCOMPARE(mChan->ipAddress().first.isNull(), true);
399     QCOMPARE(mChan->localAddress(), QString());
400 
401     /* incoming tube */
402     createTubeChannel(false, TP_SOCKET_ADDRESS_TYPE_UNIX,
403             TP_SOCKET_ACCESS_CONTROL_LOCALHOST, false);
404     QVERIFY(connect(mChan->becomeReady(IncomingStreamTubeChannel::FeatureCore),
405                 SIGNAL(finished(Tp::PendingOperation *)),
406                 SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
407     QCOMPARE(mLoop->exec(), 0);
408     QCOMPARE(mChan->isReady(IncomingStreamTubeChannel::FeatureCore), true);
409     QCOMPARE(mChan->isReady(StreamTubeChannel::FeatureConnectionMonitoring), false);
410     QCOMPARE(mChan->state(), TubeChannelStateLocalPending);
411     QCOMPARE(mChan->parameters().isEmpty(), false);
412     QCOMPARE(mChan->parameters().size(), 1);
413     QCOMPARE(mChan->parameters().contains(QLatin1String("badger")), true);
414     QCOMPARE(mChan->parameters().value(QLatin1String("badger")), QVariant(42));
415     QCOMPARE(mChan->service(), QLatin1String("test-service"));
416     QCOMPARE(mChan->supportsIPv4SocketsOnLocalhost(), false);
417     QCOMPARE(mChan->supportsIPv4SocketsWithSpecifiedAddress(), false);
418     QCOMPARE(mChan->supportsIPv6SocketsOnLocalhost(), false);
419     QCOMPARE(mChan->supportsIPv6SocketsWithSpecifiedAddress(), false);
420     QCOMPARE(mChan->supportsUnixSocketsOnLocalhost(), true);
421     QCOMPARE(mChan->supportsUnixSocketsWithCredentials(), false);
422     QCOMPARE(mChan->supportsAbstractUnixSocketsOnLocalhost(), false);
423     QCOMPARE(mChan->supportsAbstractUnixSocketsWithCredentials(), false);
424     QCOMPARE(mChan->connections().isEmpty(), true);
425     QCOMPARE(mChan->addressType(), SocketAddressTypeUnix);
426     QCOMPARE(mChan->ipAddress().first.isNull(), true);
427     QCOMPARE(mChan->localAddress(), QString());
428 }
429 
testAcceptTwice()430 void TestStreamTubeChan::testAcceptTwice()
431 {
432     /* incoming tube */
433     createTubeChannel(false, TP_SOCKET_ADDRESS_TYPE_UNIX,
434             TP_SOCKET_ACCESS_CONTROL_LOCALHOST, false);
435     QVERIFY(connect(mChan->becomeReady(IncomingStreamTubeChannel::FeatureCore |
436                         StreamTubeChannel::FeatureConnectionMonitoring),
437                 SIGNAL(finished(Tp::PendingOperation *)),
438                 SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
439     QCOMPARE(mLoop->exec(), 0);
440     QCOMPARE(mChan->isReady(IncomingStreamTubeChannel::FeatureCore), true);
441     QCOMPARE(mChan->isReady(StreamTubeChannel::FeatureConnectionMonitoring), true);
442     QCOMPARE(mChan->state(), TubeChannelStateLocalPending);
443 
444     IncomingStreamTubeChannelPtr chan = IncomingStreamTubeChannelPtr::qObjectCast(mChan);
445     QVERIFY(connect(chan->acceptTubeAsUnixSocket(),
446                 SIGNAL(finished(Tp::PendingOperation *)),
447                 SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
448     QCOMPARE(mLoop->exec(), 0);
449     QCOMPARE(mChan->state(), TubeChannelStateOpen);
450 
451     /* try to re-accept the tube */
452     QVERIFY(connect(chan->acceptTubeAsUnixSocket(),
453                 SIGNAL(finished(Tp::PendingOperation *)),
454                 SLOT(expectFailure(Tp::PendingOperation *))));
455     QCOMPARE(mLoop->exec(), 0);
456     QCOMPARE(mChan->state(), TubeChannelStateOpen);
457 }
458 
testAcceptSuccess()459 void TestStreamTubeChan::testAcceptSuccess()
460 {
461     /* incoming tube */
462     for (int i = 0; contexts[i].addressType != NUM_TP_SOCKET_ADDRESS_TYPES; i++) {
463         /* as we run several tests here, let's init/cleanup properly */
464         init();
465 
466         qDebug() << "Testing context:" << i;
467         mCurrentContext = i;
468 
469         createTubeChannel(false, contexts[i].addressType,
470                 contexts[i].accessControl, contexts[i].withContact);
471         QVERIFY(connect(mChan->becomeReady(IncomingStreamTubeChannel::FeatureCore |
472                             StreamTubeChannel::FeatureConnectionMonitoring),
473                     SIGNAL(finished(Tp::PendingOperation *)),
474                     SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
475         QCOMPARE(mLoop->exec(), 0);
476         QCOMPARE(mChan->isReady(IncomingStreamTubeChannel::FeatureCore), true);
477         QCOMPARE(mChan->isReady(StreamTubeChannel::FeatureConnectionMonitoring), true);
478         QCOMPARE(mChan->state(), TubeChannelStateLocalPending);
479 
480         mLocalConnectionId = -1;
481         mGotLocalConnection = false;
482         QVERIFY(connect(mChan.data(),
483                     SIGNAL(newConnection(uint)),
484                     SLOT(onNewLocalConnection(uint))));
485 
486         bool requiresCredentials = ((contexts[i].accessControl == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS) ?
487             true : false);
488 
489         GSocket *gSocket = 0;
490         QHostAddress addr;
491         quint16 port = 0;
492         IncomingStreamTubeChannelPtr chan = IncomingStreamTubeChannelPtr::qObjectCast(mChan);
493         if (contexts[i].addressType == TP_SOCKET_ADDRESS_TYPE_UNIX) {
494             QVERIFY(connect(chan->acceptTubeAsUnixSocket(requiresCredentials),
495                         SIGNAL(finished(Tp::PendingOperation *)),
496                         SLOT(expectPendingTubeConnectionFinished(Tp::PendingOperation *))));
497         } else {
498             if (contexts[i].accessControl == TP_SOCKET_ACCESS_CONTROL_PORT) {
499                 gSocket = createTcpClientGSocket(contexts[i].addressType);
500 
501                 // There is no way to bind a QTcpSocket and using
502                 // QAbstractSocket::setSocketDescriptor does not work either, so using glib sockets
503                 // for this test. See http://bugreports.qt.nokia.com/browse/QTBUG-121
504                 GSocketAddress *localAddr;
505 
506                 localAddr = g_socket_get_local_address(gSocket, NULL);
507                 QVERIFY(localAddr != NULL);
508                 addr = QHostAddress(QLatin1String(g_inet_address_to_string(
509                         g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(localAddr)))));
510                 port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(localAddr));
511                 g_object_unref(localAddr);
512             } else {
513                 addr = QHostAddress::Any;
514                 port = 0;
515             }
516 
517             QVERIFY(connect(chan->acceptTubeAsTcpSocket(addr, port),
518                         SIGNAL(finished(Tp::PendingOperation *)),
519                         SLOT(expectPendingTubeConnectionFinished(Tp::PendingOperation *))));
520         }
521         QCOMPARE(mLoop->exec(), 0);
522         QCOMPARE(mChan->state(), TubeChannelStateOpen);
523         QCOMPARE(mRequiresCredentials, requiresCredentials);
524 
525         if (contexts[i].addressType == TP_SOCKET_ADDRESS_TYPE_UNIX) {
526             qDebug() << "Connecting to host" << mChan->localAddress();
527 
528             QLocalSocket *socket = new QLocalSocket(this);
529             socket->connectToServer(mChan->localAddress());
530 
531             if (requiresCredentials) {
532                 qDebug() << "Sending credential byte" << mCredentialByte;
533                 socket->write(reinterpret_cast<const char*>(&mCredentialByte), 1);
534             }
535 
536             QCOMPARE(mLoop->exec(), 0);
537             QCOMPARE(mGotLocalConnection, true);
538             qDebug() << "Connected to host";
539 
540             delete socket;
541         } else {
542             qDebug().nospace() << "Connecting to host " << mChan->ipAddress().first << ":" <<
543                 mChan->ipAddress().second;
544 
545             QTcpSocket *socket = 0;
546 
547             if (contexts[i].accessControl == TP_SOCKET_ACCESS_CONTROL_PORT) {
548                 GSocketAddress *remoteAddr = (GSocketAddress*) g_inet_socket_address_new(
549                         g_inet_address_new_from_string(
550                             mChan->ipAddress().first.toString().toLatin1().constData()),
551                         mChan->ipAddress().second);
552                 g_socket_connect(gSocket, remoteAddr, NULL, NULL);
553             } else {
554                 socket = new QTcpSocket();
555                 socket->connectToHost(mChan->ipAddress().first, mChan->ipAddress().second);
556             }
557 
558             QCOMPARE(mLoop->exec(), 0);
559             QCOMPARE(mGotLocalConnection, true);
560             qDebug() << "Connected to host";
561 
562             if (gSocket) {
563                 tp_clear_object(&gSocket);
564             }
565 
566             delete socket;
567         }
568 
569         mGotConnectionClosed = false;
570         QVERIFY(connect(mChan.data(),
571                     SIGNAL(connectionClosed(uint,QString,QString)),
572                     SLOT(onConnectionClosed(uint,QString,QString))));
573         tp_tests_stream_tube_channel_last_connection_disconnected(mChanService,
574                 TP_ERROR_STR_DISCONNECTED);
575         QCOMPARE(mLoop->exec(), 0);
576         QCOMPARE(mGotConnectionClosed, true);
577 
578         /* as we run several tests here, let's init/cleanup properly */
579         cleanup();
580     }
581 }
582 
testAcceptFail()583 void TestStreamTubeChan::testAcceptFail()
584 {
585     /* incoming tube */
586     createTubeChannel(false, TP_SOCKET_ADDRESS_TYPE_UNIX,
587             TP_SOCKET_ACCESS_CONTROL_LOCALHOST, false);
588     QVERIFY(connect(mChan->becomeReady(IncomingStreamTubeChannel::FeatureCore |
589                         StreamTubeChannel::FeatureConnectionMonitoring),
590                 SIGNAL(finished(Tp::PendingOperation *)),
591                 SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
592     QCOMPARE(mLoop->exec(), 0);
593     QCOMPARE(mChan->isReady(IncomingStreamTubeChannel::FeatureCore), true);
594     QCOMPARE(mChan->isReady(StreamTubeChannel::FeatureConnectionMonitoring), true);
595     QCOMPARE(mChan->state(), TubeChannelStateLocalPending);
596 
597     /* when accept is called the channel will be closed service side */
598     tp_tests_stream_tube_channel_set_close_on_accept (mChanService, TRUE);
599 
600     /* calling accept should fail */
601     IncomingStreamTubeChannelPtr chan = IncomingStreamTubeChannelPtr::qObjectCast(mChan);
602     QVERIFY(connect(chan->acceptTubeAsUnixSocket(),
603                 SIGNAL(finished(Tp::PendingOperation *)),
604                 SLOT(expectFailure(Tp::PendingOperation *))));
605 
606     QCOMPARE(mLoop->exec(), 0);
607 
608     QCOMPARE(mChan->isValid(), false);
609 
610     /* trying to accept again should fail immediately */
611     QVERIFY(connect(chan->acceptTubeAsUnixSocket(),
612                 SIGNAL(finished(Tp::PendingOperation *)),
613                 SLOT(expectFailure(Tp::PendingOperation *))));
614     QCOMPARE(mLoop->exec(), 0);
615 }
616 
testOfferSuccess()617 void TestStreamTubeChan::testOfferSuccess()
618 {
619     /* incoming tube */
620     for (int i = 0; contexts[i].addressType != NUM_TP_SOCKET_ADDRESS_TYPES; i++) {
621         /* as we run several tests here, let's init/cleanup properly */
622         init();
623 
624         qDebug() << "Testing context:" << i;
625         mCurrentContext = i;
626 
627         createTubeChannel(true, contexts[i].addressType,
628                 contexts[i].accessControl, contexts[i].withContact);
629         QVERIFY(connect(mChan->becomeReady(OutgoingStreamTubeChannel::FeatureCore |
630                             StreamTubeChannel::FeatureConnectionMonitoring),
631                     SIGNAL(finished(Tp::PendingOperation *)),
632                     SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
633         QCOMPARE(mLoop->exec(), 0);
634         QCOMPARE(mChan->isReady(OutgoingStreamTubeChannel::FeatureCore), true);
635         QCOMPARE(mChan->isReady(StreamTubeChannel::FeatureConnectionMonitoring), true);
636         QCOMPARE(mChan->state(), TubeChannelStateNotOffered);
637         QCOMPARE(mChan->parameters().isEmpty(), true);
638 
639         mRemoteConnectionId = -1;
640         mGotRemoteConnection = false;
641         QVERIFY(connect(mChan.data(),
642                     SIGNAL(newConnection(uint)),
643                     SLOT(onNewRemoteConnection(uint))));
644 
645         bool requiresCredentials = ((contexts[i].accessControl == TP_SOCKET_ACCESS_CONTROL_CREDENTIALS) ?
646             true : false);
647 
648         mExpectedAddress = QHostAddress();
649         mExpectedPort = -1;
650         mExpectedHandle = -1;
651         mExpectedId = QString();
652 
653         mOfferFinished = false;
654         mGotSocketConnection = false;
655         QLocalServer *localServer = 0;
656         QTcpServer *tcpServer = 0;
657         OutgoingStreamTubeChannelPtr chan = OutgoingStreamTubeChannelPtr::qObjectCast(mChan);
658         QVariantMap offerParameters;
659         offerParameters.insert(QLatin1String("mushroom"), 44);
660         if (contexts[i].addressType == TP_SOCKET_ADDRESS_TYPE_UNIX) {
661             localServer = new QLocalServer(this);
662             localServer->listen(QLatin1String(tmpnam(NULL)));
663             connect(localServer, SIGNAL(newConnection()), SLOT(onNewSocketConnection()));
664 
665             QVERIFY(connect(chan->offerUnixSocket(localServer, offerParameters, requiresCredentials),
666                         SIGNAL(finished(Tp::PendingOperation *)),
667                         SLOT(onOfferFinished(Tp::PendingOperation *))));
668         } else {
669             tcpServer = new QTcpServer(this);
670             tcpServer->listen(QHostAddress::Any, 0);
671             connect(tcpServer, SIGNAL(newConnection()), SLOT(onNewSocketConnection()));
672 
673             QVERIFY(connect(chan->offerTcpSocket(tcpServer, offerParameters),
674                         SIGNAL(finished(Tp::PendingOperation *)),
675                         SLOT(onOfferFinished(Tp::PendingOperation *))));
676         }
677 
678         while (mChan->state() != TubeChannelStateRemotePending) {
679             mLoop->processEvents();
680         }
681 
682         QCOMPARE(mGotSocketConnection, false);
683 
684         // A client now connects to the tube
685         QLocalSocket *localSocket = 0;
686         QTcpSocket *tcpSocket = 0;
687         if (contexts[i].addressType == TP_SOCKET_ADDRESS_TYPE_UNIX) {
688             qDebug() << "Connecting to host" << localServer->fullServerName();
689             localSocket = new QLocalSocket(this);
690             localSocket->connectToServer(localServer->fullServerName());
691         } else {
692             qDebug().nospace() << "Connecting to host" << tcpServer->serverAddress() <<
693                 ":" << tcpServer->serverPort();
694             tcpSocket = new QTcpSocket(this);
695             tcpSocket->connectToHost(tcpServer->serverAddress(), tcpServer->serverPort());
696         }
697 
698         QCOMPARE(mGotSocketConnection, false);
699         QCOMPARE(mLoop->exec(), 0);
700         QCOMPARE(mGotSocketConnection, true);
701 
702         if (tcpSocket) {
703             mExpectedAddress = tcpSocket->localAddress();
704             mExpectedPort = tcpSocket->localPort();
705         }
706 
707         /* simulate CM when peer connects */
708         GValue *connParam = 0;
709         mCredentialByte = 0;
710         switch (contexts[i].accessControl) {
711             case TP_SOCKET_ACCESS_CONTROL_LOCALHOST:
712                 connParam = tp_g_value_slice_new_static_string("");
713                 break;
714 
715             case TP_SOCKET_ACCESS_CONTROL_CREDENTIALS:
716                 {
717                     mCredentialByte = g_random_int_range(0, G_MAXUINT8);
718 
719                     localSocket->write(reinterpret_cast<const char*>(&mCredentialByte), 1);
720                     connParam = tp_g_value_slice_new_byte(mCredentialByte);
721                 }
722                 break;
723 
724             case TP_SOCKET_ACCESS_CONTROL_PORT:
725                 {
726                     connParam = tp_g_value_slice_new_take_boxed(
727                             TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4,
728                             dbus_g_type_specialized_construct(TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4));
729                     dbus_g_type_struct_set(connParam,
730                             0, tcpSocket->localAddress().toString().toLatin1().constData(),
731                             1, tcpSocket->localPort(),
732                             G_MAXUINT);
733                 }
734                 break;
735 
736             default:
737                 Q_ASSERT(false);
738         }
739 
740         TpHandleRepoIface *contactRepo = tp_base_connection_get_handles(
741             TP_BASE_CONNECTION(mConn->service()), TP_HANDLE_TYPE_CONTACT);
742         TpHandle bobHandle = tp_handle_ensure(contactRepo, "bob", NULL, NULL);
743         tp_tests_stream_tube_channel_peer_connected_no_stream(mChanService,
744                 connParam, bobHandle);
745 
746         tp_g_value_slice_free(connParam);
747 
748         mExpectedHandle = bobHandle;
749         mExpectedId = QLatin1String("bob");
750 
751         QCOMPARE(mChan->state(), TubeChannelStateRemotePending);
752 
753         while (!mOfferFinished) {
754             QCOMPARE(mLoop->exec(), 0);
755         }
756 
757         QCOMPARE(mChan->state(), TubeChannelStateOpen);
758         QCOMPARE(mChan->parameters().isEmpty(), false);
759         QCOMPARE(mChan->parameters().size(), 1);
760         QCOMPARE(mChan->parameters().contains(QLatin1String("mushroom")), true);
761         QCOMPARE(mChan->parameters().value(QLatin1String("mushroom")), QVariant(44));
762 
763         if (!mGotRemoteConnection) {
764             QCOMPARE(mLoop->exec(), 0);
765         }
766 
767         QCOMPARE(mGotRemoteConnection, true);
768 
769         qDebug() << "Connected to host";
770 
771         mGotConnectionClosed = false;
772         QVERIFY(connect(mChan.data(),
773                     SIGNAL(connectionClosed(uint,QString,QString)),
774                     SLOT(onConnectionClosed(uint,QString,QString))));
775         tp_tests_stream_tube_channel_last_connection_disconnected(mChanService,
776                 TP_ERROR_STR_DISCONNECTED);
777         QCOMPARE(mLoop->exec(), 0);
778         QCOMPARE(mGotConnectionClosed, true);
779 
780         /* let the internal OutgoingStreamTubeChannel::onConnectionClosed slot be called before
781          * checking the data for that connection */
782         mLoop->processEvents();
783 
784         QCOMPARE(chan->contactsForConnections().isEmpty(), true);
785         QCOMPARE(chan->connectionsForSourceAddresses().isEmpty(), true);
786         QCOMPARE(chan->connectionsForCredentials().isEmpty(), true);
787 
788         delete localServer;
789         delete localSocket;
790         delete tcpServer;
791         delete tcpSocket;
792 
793         /* as we run several tests here, let's init/cleanup properly */
794         cleanup();
795     }
796 }
797 
testOutgoingConnectionMonitoring()798 void TestStreamTubeChan::testOutgoingConnectionMonitoring()
799 {
800     mCurrentContext = 3; // should point to the room, IPv4, AC port one
801     createTubeChannel(true, TP_SOCKET_ADDRESS_TYPE_IPV4, TP_SOCKET_ACCESS_CONTROL_PORT, false);
802     QVERIFY(connect(mChan->becomeReady(OutgoingStreamTubeChannel::FeatureCore |
803                     StreamTubeChannel::FeatureConnectionMonitoring),
804                 SIGNAL(finished(Tp::PendingOperation *)),
805                 SLOT(expectSuccessfulCall(Tp::PendingOperation *))));
806     QCOMPARE(mLoop->exec(), 0);
807 
808     QVERIFY(connect(mChan.data(),
809                 SIGNAL(newConnection(uint)),
810                 SLOT(onNewRemoteConnection(uint))));
811     QVERIFY(connect(mChan.data(),
812                 SIGNAL(connectionClosed(uint,QString,QString)),
813                 SLOT(onConnectionClosed(uint,QString,QString))));
814 
815     OutgoingStreamTubeChannelPtr chan = OutgoingStreamTubeChannelPtr::qObjectCast(mChan);
816     QVERIFY(connect(chan->offerTcpSocket(QHostAddress(QHostAddress::LocalHost), 9), // DISCARD
817                 SIGNAL(finished(Tp::PendingOperation *)),
818                 SLOT(onOfferFinished(Tp::PendingOperation *))));
819 
820     while (mChan->state() != TubeChannelStateRemotePending) {
821         mLoop->processEvents();
822     }
823 
824     /* simulate CM when peer connects */
825     GValue *connParam = tp_g_value_slice_new_take_boxed(
826             TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4,
827             dbus_g_type_specialized_construct(TP_STRUCT_TYPE_SOCKET_ADDRESS_IPV4));
828 
829     mExpectedAddress.setAddress(QLatin1String("127.0.0.1"));
830     mExpectedPort = 12345;
831 
832     dbus_g_type_struct_set(connParam,
833             0, mExpectedAddress.toString().toLatin1().constData(),
834             1, static_cast<quint16>(mExpectedPort),
835             G_MAXUINT);
836 
837     // Simulate a peer connection from someone we don't have a prebuilt contact for yet, and
838     // immediately drop it
839     TpHandleRepoIface *contactRepo = tp_base_connection_get_handles(
840             TP_BASE_CONNECTION(mConn->service()), TP_HANDLE_TYPE_CONTACT);
841     TpHandle handle = tp_handle_ensure(contactRepo, "YouHaventSeenMeYet", NULL, NULL);
842 
843     mExpectedHandle = handle;
844     mExpectedId = QLatin1String("youhaventseenmeyet");
845 
846     tp_tests_stream_tube_channel_peer_connected_no_stream(mChanService,
847             connParam, handle);
848     tp_tests_stream_tube_channel_last_connection_disconnected(mChanService,
849             TP_ERROR_STR_DISCONNECTED);
850     tp_g_value_slice_free(connParam);
851 
852     // Test that we get newConnection first and only then connectionClosed, unlike how the code has
853     // been for a long time, queueing newConnection events and emitting connectionClosed directly
854     while (!mOfferFinished || !mGotRemoteConnection) {
855         QVERIFY(!mGotConnectionClosed || !mOfferFinished);
856         QCOMPARE(mLoop->exec(), 0);
857     }
858 
859     QCOMPARE(mChan->connections().size(), 1);
860 
861     // The connectionClosed emission should finally exit the main loop
862     QCOMPARE(mLoop->exec(), 0);
863     QVERIFY(mGotConnectionClosed);
864 
865     QCOMPARE(mChan->connections().size(), 0);
866 }
867 
cleanup()868 void TestStreamTubeChan::cleanup()
869 {
870     cleanupImpl();
871 
872     if (mChan && mChan->isValid()) {
873         qDebug() << "waiting for the channel to become invalidated";
874 
875         QVERIFY(connect(mChan.data(),
876                 SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
877                 mLoop,
878                 SLOT(quit())));
879         tp_base_channel_close(TP_BASE_CHANNEL(mChanService));
880         QCOMPARE(mLoop->exec(), 0);
881     }
882 
883     mChan.reset();
884 
885     if (mChanService != 0) {
886         g_object_unref(mChanService);
887         mChanService = 0;
888     }
889 
890     mLoop->processEvents();
891 }
892 
cleanupTestCase()893 void TestStreamTubeChan::cleanupTestCase()
894 {
895     QCOMPARE(mConn->disconnect(), true);
896     delete mConn;
897 
898     cleanupTestCaseImpl();
899 }
900 
901 QTEST_MAIN(TestStreamTubeChan)
902 #include "_gen/stream-tube-chan.cpp.moc.hpp"
903