meteorddp.cpp 11.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/********************************************************************************************
 *                                                                                          *
 * Copyright (C) 2017 Armin Felder, Dennis Beier                                            *
 * This file is part of RocketChatMobileEngine <https://git.fairkom.net/chat/fairchat>.     *
 *                                                                                          *
 * RocketChatMobileEngine is free software: you can redistribute it and/or modify           *
 * it under the terms of the GNU General Public License as published by                     *
 * the Free Software Foundation, either version 3 of the License, or                        *
 * (at your option) any later version.                                                      *
 *                                                                                          *
 * RocketChatMobileEngine is distributed in the hope that it will be useful,                *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of                           *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                            *
 * GNU General Public License for more details.                                             *
 *                                                                                          *
 * You should have received a copy of the GNU General Public License                        *
 * along with RocketChatMobileEngine. If not, see <http://www.gnu.org/licenses/>.           *
 *                                                                                          *
 ********************************************************************************************/


armin's avatar
armin committed
22
23
24
25
26
27
28
#include "meteorddp.h"
#include "meteorddp.h"
#include <QString>
#include <QWebSocket>
#include <QTime>
#include <QCryptographicHash>
#include <functional>
Armin Felder's avatar
Armin Felder committed
29
#include <QAbstractSocket>
armin's avatar
armin committed
30
31
#include "ddpRequests/ddploginrequest.h"

armin's avatar
as    
armin committed
32
MeteorDDP::MeteorDDP( QObject *parent, const QString &pUri, bool pUnsecure ): QObject( parent ), unsecure( pUnsecure )
armin's avatar
armin committed
33
34
{
    mError = new QJsonParseError;
armin's avatar
as    
armin committed
35
    init( pUri );
armin's avatar
armin committed
36
37
38
}


armin's avatar
as    
armin committed
39
void MeteorDDP::init( const QString &pUri )
armin's avatar
armin committed
40
{
armin's avatar
armin committed
41
    QString protocol = QStringLiteral( "wss://" );
armin's avatar
armin committed
42

armin's avatar
as    
armin committed
43
    if ( unsecure ) {
armin's avatar
armin committed
44
45
        protocol = QStringLiteral( "ws://" );
    }
armin's avatar
armin committed
46

armin's avatar
armin committed
47
    QUrl wsUri = QUrl( protocol + pUri + QStringLiteral( "/websocket" ) );
armin's avatar
armin committed
48
    qDebug() << wsUri;
armin's avatar
as    
armin committed
49
    qDebug() << "meteor init" ;
armin's avatar
armin committed
50
    mWebsocketUri = pUri;
51
    mResponseBinding.reserve( 100 );
Armin Felder's avatar
Armin Felder committed
52
53
54
    connect( &mWebsocket, &QWebSocket::textMessageReceived, this, &MeteorDDP::onTextMessageReceived, Qt::UniqueConnection );
    connect( &mWebsocket, &QWebSocket::connected, this, &MeteorDDP::onConnected, Qt::UniqueConnection );
    connect( &mWebsocket, &QWebSocket::disconnected, this, &MeteorDDP::ddpDisconnected );
armin's avatar
armin committed
55
    connect( this, &MeteorDDP::sendMessageSignal, this, &MeteorDDP::sendJson, Qt::UniqueConnection );
56
57
58
59
    connect( &mWebsocket, QOverload<QAbstractSocket::SocketError>::of( &QWebSocket::error ),
    [ = ]( QAbstractSocket::SocketError error ) {
        qDebug() << mWebsocket.errorString();
    } );
Armin Felder's avatar
Armin Felder committed
60
61

    mWebsocket.open( wsUri );
armin's avatar
armin committed
62
63
64
65
66
    QDateTime now = QDateTime::currentDateTime();
    uint currentTime = now.toTime_t();
    mLastPing = currentTime;
}

armin's avatar
armin committed
67
void MeteorDDP::registerMessageHandler( MessageListener &listener )
68
{
armin's avatar
armin committed
69
    connect( this, &MeteorDDP::messageReceived, &listener, &MessageListener::handlesMessage );
70
71
}

armin's avatar
armin committed
72
void MeteorDDP::registerMessageHandler( MessageListener *listener )
73
{
armin's avatar
armin committed
74
    connect( this, &MeteorDDP::messageReceived, listener, &MessageListener::handlesMessage );
75
76
}

armin's avatar
armin committed
77
78
79
80
81
82
83
void MeteorDDP::connectWithServer()
{
    QJsonObject msgObj;
    QJsonArray support;

    support.append( "1" );

84
85
86
    msgObj[QStringLiteral( "msg" )] = QStringLiteral( "connect" );
    msgObj[QStringLiteral( "version" )] = "1";
    msgObj[QStringLiteral( "support" )] = support;
armin's avatar
armin committed
87
88
89
    sendJson( msgObj );
}

armin's avatar
armin committed
90
void MeteorDDP::onTextMessageReceived( const QString &pMsg )
armin's avatar
armin committed
91
92
{
#if defined(Q_OS_LINUX)|| defined(Q_OS_IOS)
93
    qDebug() << pMsg << "\n";
armin's avatar
armin committed
94
95
#endif

96
    if ( Q_LIKELY( pMsg.length() ) ) {
armin's avatar
armin committed
97
98
99
        QJsonDocument jsonResponse = QJsonDocument::fromJson( pMsg.toUtf8(), mError );
        QJsonObject objectResponse = jsonResponse.object();

100
        if ( Q_LIKELY( mError->error ==  QJsonParseError::NoError && objectResponse.contains( QStringLiteral( "msg" ) ) ) ) {
armin's avatar
armin committed
101
102
            QDateTime now = QDateTime::currentDateTime();
            mLastPing = now.toTime_t();
103
            QString messagePart = objectResponse[QStringLiteral( "msg" )].toString();
armin's avatar
armin committed
104

105
            if ( messagePart == QStringLiteral( "ping" ) || messagePart == QStringLiteral( "pong" ) ) {
armin's avatar
armin committed
106

armin's avatar
armin committed
107
108
109
                emit messageReceived( objectResponse );
            }

110
            if ( messagePart == QStringLiteral( "ping" ) ) {
armin's avatar
armin committed
111
112
                qDebug() << QString::number( mLastPing );
                pong();
113
114
115
            } else if ( messagePart == QStringLiteral( "ready" ) ) {
                if ( Q_LIKELY( objectResponse.contains( QStringLiteral( "subs" ) ) ) ) {
                    QJsonArray subsArray = objectResponse[QStringLiteral( "subs" )].toArray();
armin's avatar
armin committed
116
117
118
119

                    if ( Q_LIKELY( subsArray.size() ) ) {
                        QString id = subsArray[0].toString();

armin's avatar
armin committed
120
                        if ( Q_LIKELY( mResponseBinding.contains( id ) && !mResponseBinding[id].isNull() ) ) {
armin's avatar
ias    
armin committed
121
                            DdpCallback succesFuncton = mResponseBinding[id]->getSuccess();
armin's avatar
armin committed
122
123
124
125
126
127
128
129
130

                            if ( Q_LIKELY( succesFuncton ) ) {
                                succesFuncton( objectResponse, this );
                            }
                        }
                    }
                }

                //emit ddpLoginConfigured();
131
            } else if ( messagePart == QStringLiteral( "connected" ) ) {
armin's avatar
armin committed
132
133
                qDebug() << "connection confirmed";
                emit ddpConnected();
134
135
136
            } else if ( messagePart == QStringLiteral( "added" ) || messagePart == QStringLiteral( "changed" ) || messagePart == QStringLiteral( "removed" ) ) {
                if ( objectResponse.contains( QStringLiteral( "fields" ) ) ) {
                    QJsonObject fields = objectResponse[QStringLiteral( "fields" )].toObject();
armin's avatar
armin committed
137

138
139
                    if ( Q_LIKELY( fields.contains( QStringLiteral( "args" ) ) ) ) {
                        QJsonArray args = fields[QStringLiteral( "args" )].toArray();
armin's avatar
armin committed
140
141
142
143

                        for ( auto currentArg : args ) {
                            QJsonObject argObj = currentArg.toObject();

144
                            if ( Q_LIKELY( argObj.contains( QStringLiteral( "_id" ) ) ) ) {
armin's avatar
armin committed
145
146
147
148
                                //qDebug()<<"message received";
                                emit messageReceived( objectResponse );
                            }
                        }
149
                    } else {
armin's avatar
armin committed
150
151
152
153
154
                        emit messageReceived( objectResponse );
                    }
                }
            }

155
            if ( objectResponse.contains( QStringLiteral( "id" ) ) ) {
armin's avatar
armin committed
156

157
                QString id = objectResponse[QStringLiteral( "id" )].toString();
armin's avatar
armin committed
158
159
160
161
162

                if ( Q_LIKELY( !mReceivedEvents.contains( id ) ) ) {
                    mReceivedEvents.insert( id );

                    if ( Q_LIKELY( mResponseBinding.contains( id ) ) ) {
armin's avatar
ias    
armin committed
163
                        DdpCallback succesFunction = mResponseBinding[id]->getSuccess();
armin's avatar
armin committed
164
                        DdpCallback errorFunction =  mResponseBinding[id]->getError();
165
                        bool error = objectResponse.contains( QStringLiteral( "error" ) );
armin's avatar
armin committed
166
167
168
169
170
171
172
173
174
175
176
177
178

                        if ( Q_UNLIKELY( error && errorFunction ) ) {
                            errorFunction( objectResponse, this );
                        } else if ( Q_LIKELY( succesFunction ) ) {
                            succesFunction( objectResponse, this );
                        }
                    }
                }
            }
        }
    }
}

armin's avatar
armin committed
179
void MeteorDDP::sendJson( const QJsonObject &pMsgObj )
armin's avatar
armin committed
180
181
182
183
{

    QJsonDocument msgJson = QJsonDocument( pMsgObj );
    QString msgString = msgJson.toJson( QJsonDocument::Compact );
184
    qDebug() << "message String" << msgString;
Armin Felder's avatar
Armin Felder committed
185
    mWebsocket.sendTextMessage( msgString );
armin's avatar
armin committed
186
187
}

188
189
190
191
192
bool MeteorDDP::getUnsecure() const
{
    return unsecure;
}

armin's avatar
armin committed
193
194
195
196
void MeteorDDP::onConnected()
{
    qDebug() << "connected websocket";
    mWebsocketConnectionEstablished = true;
197
    // emit( websocketConnected() );
armin's avatar
armin committed
198
199
200
201
202
203
204
    this->connectWithServer();
}


void MeteorDDP::pong()
{
    QJsonObject msgObj;
205
206
    msgObj[QStringLiteral( "msg" )] = QStringLiteral( "pong" );
    msgObj[QStringLiteral( "id" )] = getFrameCount();
armin's avatar
armin committed
207
208
209
210
211
    sendJson( msgObj );
}



armin's avatar
armin committed
212
void MeteorDDP::sendRequest( const QSharedPointer<DDPRequest> &pDdpRequest )
armin's avatar
armin committed
213
{
214
    if ( !pDdpRequest.isNull() ) {
armin's avatar
armin committed
215
216
        QString frame = getFrameCount();
        QJsonObject rawRequest =  pDdpRequest->getRawRequest();
armin's avatar
armin committed
217

218
219
        if ( !rawRequest.contains( QStringLiteral( "id" ) ) ) {
            rawRequest[QStringLiteral( "id" )] = frame;
armin's avatar
armin committed
220
        }
armin's avatar
armin committed
221

armin's avatar
armin committed
222
223
224
225
226
        pDdpRequest->setFrame( frame );
        mResponseBinding[frame] = pDdpRequest;
        emit sendMessageSignal( rawRequest );
        // sendJson( rawRequest );
    }
armin's avatar
armin committed
227
228
229

}

armin's avatar
armin committed
230
//debug
armin's avatar
armin committed
231
232
233
void MeteorDDP::resume()
{
    qDebug() << "resuming ddp";
234
235
236
237
238
239
240
241
242
    qDebug() << "websocket valid: " << mWebsocket.isValid();
    QUrl wsUri = QUrl( QStringLiteral( "wss://" ) + mWebsocketUri + QStringLiteral( "/websocket" ) );

    if ( mWebsocket.state() == QAbstractSocket::UnconnectedState ) {
        mWebsocket.open( wsUri );
    } else if ( mWebsocket.state() != QAbstractSocket::ConnectingState ) {
        auto const connection = new QMetaObject::Connection;
        *connection = connect( &mWebsocket, &QWebSocket::disconnected, [ = ]() {
            qDebug() << "websocket closed";
Armin Felder's avatar
Armin Felder committed
243
            mWebsocket.open( wsUri );
244
245
246
            QDateTime now = QDateTime::currentDateTime();
            uint currentTime = now.toTime_t();
            mLastPing = currentTime;
armin's avatar
armin committed
247

248
249
250
251
            if ( connection != nullptr ) {
                QObject::disconnect( *connection );
                delete connection;
            }
252
        } );
Armin Felder's avatar
Armin Felder committed
253
        mWebsocket.close();
254
    }
255

256
    if ( mWebsocket.state() == QAbstractSocket::UnconnectedState ) {
Armin Felder's avatar
Armin Felder committed
257

258
259
        // connectWithServer();
    }
armin's avatar
armin committed
260
261
}

armin's avatar
armin committed
262
void MeteorDDP::unsetResponseBinding( const QString &pId )
armin's avatar
armin committed
263
{
264
    if ( mResponseBinding.contains( pId ) ) {
armin's avatar
armin committed
265
266
        mResponseBinding.remove( pId );
    }
armin's avatar
armin committed
267
268
269
270
}

bool MeteorDDP::websocketIsValid()
{
Armin Felder's avatar
Armin Felder committed
271
    return mWebsocket.isValid();
armin's avatar
armin committed
272
273
274
275
276
277
278
279
280
281
}

uint MeteorDDP::diffToLastMessage()
{
    QDateTime now = QDateTime::currentDateTime();
    uint currentTime = now.toTime_t();
    uint difference = currentTime - mLastPing;
    return difference;
}

armin's avatar
armin committed
282
283
void MeteorDDP::disconnectFromServer()
{
284
    qDebug() << "disconnect from server";
Armin Felder's avatar
Armin Felder committed
285
    mWebsocket.close();
armin's avatar
armin committed
286
287
}

armin's avatar
armin committed
288
void MeteorDDP::setResumeToken( const QString &pToken )
armin's avatar
armin committed
289
290
291
292
{
    this->mToken = pToken;
}

armin's avatar
armin committed
293
void MeteorDDP::setUserId( const QString &pUserId )
armin's avatar
armin committed
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
{
    this->mUid = pUserId;
}


QString MeteorDDP::getFrameCount()
{
    return QString::number( frameCount++ );
}

QString MeteorDDP::getToken() const
{
    return mToken;
}

void MeteorDDP::setToken( const QString &pValue )
{
    mToken = pValue;
}

bool MeteorDDP::getWebsocketConnectionEstablished() const
{
    return mWebsocketConnectionEstablished;
}