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