Remoto - VFS: VFS_websocket_server.cpp Source File
Remoto - VFS
VFS_websocket_server.cpp
Go to the documentation of this file.
1 
2 #include "VFS_websocket_server.h"
3 #include "VFS.h"
4 #include "VFS_auth/VFS_auth.h"
5 
6 #include <QFile>
7 #include <QRegExp>
8 #include <QtNetwork/QSslCertificate>
9 #include <QtNetwork/QSslKey>
10 
33 VFS_websocket_server::VFS_websocket_server(quint16 port, QString authPaths, QString sessions, QHostAddress addr, QWebSocketServer::SslMode ssl, QString sslCertPath, QString sslKeyPath)
34 : VFS_node()
35 , _listenAddress(addr)
36 , _port(port)
37 , _ssl(ssl)
38 , _sslCertPath(sslCertPath)
39 , _sslKeyPath(sslKeyPath)
40 , _socket("REMOTO_WS",ssl,this)
41 , _authPaths(authPaths.split(","))
42 , _sessionsPath(sessions)
43 {
44  //_listenTimer.setSingleShot(true);
45  //_listenTimer.setInterval(100);
46 
47  _authPaths.replaceInStrings(QRegExp("\\s+(.*)"),"\\1"); //trim the auth paths
48  _authPaths.replaceInStrings(QRegExp("(.*)\\s+"),"\\1"); //trim the auth paths
49 
50  //connect(this,SIGNAL(mounted()),this,SLOT(listen())); //bad! We don't want a connection while the VFS is being built.
51  connect(VFS::root(),SIGNAL(initialized()),this,SLOT(listen()));
52 
53  //connect(VFS::root(),SIGNAL(initialized()),&_listenTimer,SLOT(start()));
54  //connect(&_listenTimer, SIGNAL(timeout()),this,SLOT(listen())); //better! We hope the VFS is fully initialzed after 100ms
55 }
56 
64 {
65  _socket.close();
66 }
67 
74 {
75  return true;
76 }
77 
93 {
94  if (_socket.isListening())
95  { VFS::WARN( QString("Websocket was already listening! Close it first?"), 0, className() );
96  return false;
97  }
98 
99  _socket.setMaxPendingConnections(200);
100 
101  connect(&_socket, SIGNAL(newConnection()), this, SLOT(newConnection()));
102  connect(&_socket, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(clientError(QAbstractSocket::SocketError)));
103  connect(&_socket, SIGNAL(serverError(QWebSocketProtocol::CloseCode)), this, SLOT(serverError(QWebSocketProtocol::CloseCode)));
104 
105  if (_ssl==QWebSocketServer::SecureMode)
106  {
107  /*
108  qDebug() << "Support SSL: " << QSslSocket::supportsSsl()
109  << "\nLib Version Number: " << QSslSocket::sslLibraryVersionNumber()
110  << "\nLib Version String: " << QSslSocket::sslLibraryVersionString()
111  << "\nLib Build Version Number: " << QSslSocket::sslLibraryBuildVersionNumber()
112  << "\nLib Build Version String: " << QSslSocket::sslLibraryBuildVersionString();
113  */
114 
115  connect(&_socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(sslErrors(const QList<QSslError> &)));
116 
117  bool c=false,k=false;
118 
119  QSslConfiguration sslConfiguration;
120 
121  QFile certFile(_sslCertPath);
122  QFile keyFile(_sslKeyPath);
123 
124  if (!(c = certFile.open(QIODevice::ReadOnly)))
125  VFS::ERROR( QString("Unable to open cert file: '%1'.").arg(_sslCertPath), 0, className() );
126  else
127  VFS::LOG( QString("Reading cert file: '%1'").arg(_sslCertPath), 7, className() );
128 
129  if (!(k = keyFile.open(QIODevice::ReadOnly)))
130  VFS::ERROR( QString("Unable to open key file: '%1'.").arg(_sslKeyPath), 0, className() );
131  else
132  VFS::LOG( QString("Reading key file: '%1'").arg(_sslKeyPath), 7, className() );
133 
134  if (!k || !c)
135  { VFS::ERROR( QString("Will not open port %1").arg(_port) );
136  return false;
137  }
138 
139  QSslCertificate certificate(&certFile, QSsl::Pem);
140  QSslKey sslKey(&keyFile, QSsl::Rsa, QSsl::Pem);
141  certFile.close();
142  keyFile.close();
143 
144  sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); //Is this correct? We don't want Auto here?
145  sslConfiguration.setLocalCertificate(certificate);
146  sslConfiguration.setPrivateKey(sslKey);
147  //sslConfiguration.setProtocol(QSsl::TlsV1SslV3);
148  sslConfiguration.setProtocol(QSsl::SecureProtocols);
149 
150  _socket.setSslConfiguration(sslConfiguration);
151  }
152 
153  bool l = _socket.listen( _listenAddress, _port);
154 
155  if (!l)
156  VFS::ERROR( QString("Could not listen on port %1").arg(_port), 0, className() );
157  else
158  VFS::LOG( QString("%1 listening on port %2 (%3)").arg(className()).arg(_port).arg((_ssl==QWebSocketServer::SecureMode?"secure":"insecure")), 2 );
159 
160  return l;
161 }
162 
173 {
174  while(_socket.hasPendingConnections())
175  {
176  QWebSocket *s = _socket.nextPendingConnection();
178  //VFS_websocket_client *client = new VFS_websocket_client( s );
179 
180  connect( client, SIGNAL( disconnected() ),
181  this, SLOT( closeConnection() ) );
182 
183  connect( client, SIGNAL( readyBinaryMessage(QByteArray)),
184  this, SLOT( clientBinaryMessage(QByteArray)) );
185 
186  connect( client, SIGNAL( readyTextMessage(QString)),
187  this, SLOT( clientTextMessage(QString)) );
188 
189  connect( client, SIGNAL( error(QAbstractSocket::SocketError)),
190  this, SLOT( clientError(QAbstractSocket::SocketError)) );
191 
192  //now moved to the VFS_websocket_client::authorized method, which is more stable for HA
193  //QHostAddress a = s->peerAddress();
194  //QString name = uniqueChildName(a.toString());
195  //append(name,client);
196  }
197 }
198 
208 {
209  return new VFS_websocket_client( socket, this );
210 }
211 
230 bool VFS_websocket_server::authenticate(VFS_websocket_client *client, QJsonObject credentials)
231 {
232  credentials.remove("expire"); //prevent a malicious person from setting a long expiration
233 
234  QString token = credentials["authtoken"].toString("");
235  QString tokenonce = credentials["authtokenonce"].toString("");
236  QString user = credentials["username"].toString();
237  //QString pass = credentials["password"].toString();
238 
239  //check authtoken first
240  if (!token.isEmpty() && !_sessionsPath.isEmpty())
241  {
242  VFS::LOG( QString("Authenticating token '%1' on '%2'...").arg(token).arg(_sessionsPath), 7 );
243  //VFS::LOG( QString("Authenticating token on '%1'...").arg(_sessionsPath), 7 );
244 
245  VFS_request *r = client->createRequest(VFS_request::read,_sessionsPath+"/tokenauth","server",QJsonDocument(credentials));
246  issueRequest(r);
247  }
248  else if (user.isEmpty() && tokenonce.isEmpty())
249  {
250  return false;
251  }
252  else
253  {
254  int authindex;
255  if (!credentials.contains("authindex"))
256  authindex = 0;
257  else
258  authindex = credentials["authindex"].toInt()+1;
259 
260  if (authindex >= _authPaths.size())
261  {
262  VFS::ERROR( QString("Exhausted available authpaths, can't authenticate '%1'").arg(user) );
263  //client->close();
264  return false;
265  }
266 
267  QString authpath = _authPaths.at(authindex);
268 
269  if (!user.isEmpty())
270  VFS::LOG( QString("Authenticating user '%1' on '%2'...").arg(user).arg(authpath), 7 );
271  else
272  VFS::LOG( QString("Authenticating on '%1'...").arg(authpath), 7 );
273 
274  credentials["authindex"] = authindex;
275  credentials["authpath"] = authpath;
276  credentials["authtoken"] = ""; //reset the authtoken, because we're going to have to create one
277 
278  VFS_request *r = client->createRequest(VFS_request::read,authpath,"server",QJsonDocument(credentials));
279  issueRequest(r);
280  }
281 
282  return true;
283 }
284 
285 
298 void VFS_websocket_server::authorize(VFS_websocket_client *client, QJsonObject authdata)
299 {
300  QString user = authdata["username"].toString();
301 
302  VFS::LOG( QString("Authorizing user '%1' on '%2'...").arg(user).arg(_sessionsPath), 7 );
303 
304  VFS_request *r = client->createRequest(VFS_request::create,_sessionsPath,user,QJsonDocument(authdata));
305  issueRequest(r);
306 }
307 
308 
317 {
318  VFS_websocket_client *s = dynamic_cast< VFS_websocket_client * >(sender());
319 
320  if (s)
321  {
322  s->close();
323  if (!_children.key(s,"").isEmpty()) //if it was already removed, don't try again.
324  {
325  QString n;
326  remove( s,&n );
327  VFS::LOG( QString("Closing %1 connection '%2'").arg(s->className()).arg(n), 2 );
328  }
329 
330  //delete s;
331  s->deleteLater();
332  }
333  else
334  {
335  VFS::ERROR( "Client was not a VFS_websocket_client... not sure what to do." );
336  }
337 }
338 
357 {
358  VFS::WARN( QString("%1 clientBinaryMessage() should be overridden in a subclass").arg(className()) );
359 
360  VFS_websocket_client *s = dynamic_cast< VFS_websocket_client * >(sender());
361 
362  if (s)
363  {
364  VFS::LOG( QString("%1=>%2 read:\n%3").arg(className()).arg(s->className()).arg( QString(message) ) );
365 
366  //s->close();
367  }
368  else
369  VFS::WARN( QString("%1 clientBinaryMessage() called without client!").arg(className()) );
370 
371 }
372 
389 {
390  VFS::WARN( QString("%1 clientTextMessage() should be overridden in a subclass").arg(className()) );
391 
392  VFS_websocket_client *s = dynamic_cast< VFS_websocket_client * >(sender());
393 
394  if (s)
395  {
396  VFS::LOG( QString("%1=>%2 read:\n%3").arg(className()).arg(s->className()).arg( message ) );
397 
398  //s->close();
399  }
400  else
401  VFS::WARN( QString("%1 clientTextMessage() called without client!").arg(className()) );
402 
403 }
404 
410 void VFS_websocket_server::clientError(QAbstractSocket::SocketError error)
411 {
412  if ( error != QAbstractSocket::RemoteHostClosedError && //remote connection closed
413  error != QAbstractSocket::NetworkError ) //remote connection error (like when a mobile device sleeps or a network cable is yanked)
414  {
415  VFS::ERROR( QString("%1: (%2) %3").arg(className()).arg(error).arg(_socket.errorString()) );
416  VFS::WARN( QString("(%1) Seems like something should happen here.").arg(className()) );
417  }
418 }
419 
425 void VFS_websocket_server::serverError(QWebSocketProtocol::CloseCode closeCode)
426 {
427  VFS::ERROR( QString("%1: (%2) %3").arg(className()).arg(closeCode).arg(_socket.errorString()) );
428 }
429 
435 void VFS_websocket_server::sslErrors(const QList<QSslError> &errors)
436 {
437  for (int i=0;i<errors.size();i++)
438  VFS::ERROR( errors.at(i).errorString(), 0, className() );
439 }
440 
441 
VFS_node is the base class from which all other VFS_node classes derive.
Definition: VFS_node.h:143
virtual VFS_request * createRequest(VFS_request::requestType type, QString path, QString user="unknown", QJsonDocument data=QJsonDocument(), QJsonObject metadata=QJsonObject(), bool dontDelete=false)
Create a new VFS_request with this object as _origin.
Definition: VFS_node.cpp:1913
VFS_children _children
This node's children.
Definition: VFS_node.h:179
virtual void issueRequest(VFS_request *t)
A convenience function.
Definition: VFS_node.cpp:1933
QString className()
Return the class name of a node.
Definition: VFS_node.cpp:2039
void remove(bool andDelete)
Remove a child node.
Definition: VFS_node.cpp:1629
The base class for all requests between nodes.
Definition: VFS_node.h:54
@ read
read full contents (4)
Definition: VFS_node.h:68
@ create
create a new file/path (2)
Definition: VFS_node.h:66
A VFS_node that manages a QWebSocket connection.
virtual void close()
Close the socket if it is connected.
virtual ~VFS_websocket_server()
VFS_websocket_server destructor.
virtual bool authenticate(VFS_websocket_client *client, QJsonObject credentials)
Authenticate a client based on received credentials.
QHostAddress _listenAddress
Address to listen on, or 0.0.0.0 for all.
QStringList _authPaths
The list of auth paths to query when a user request authentication.
quint16 _port
Port to listen on.
virtual void sslErrors(const QList< QSslError > &errors)
One or more ssl errors have occurred.
QString _sslCertPath
ssl cert file path
QString _sessionsPath
The path to the sessionManager node.
QWebSocketServer _socket
The QWebSocket server object that will be listening.
virtual void clientTextMessage(QString message)
A client has received a text websocket message.
virtual void serverError(QWebSocketProtocol::CloseCode closeCode)
A server socket error has occurred.
virtual void clientBinaryMessage(QByteArray message)
The client has received a binary websocket message.
virtual void newConnection()
A new connection has been requested, and is pending.
virtual void clientError(QAbstractSocket::SocketError error)
A socket error has occurred.
friend class VFS_websocket_client
VFS_websocket_server(quint16 port, QString authPaths, QString sessions, QHostAddress addr=QHostAddress::Any, QWebSocketServer::SslMode ssl=QWebSocketServer::NonSecureMode, QString sslCertPath="", QString sslKeyPath="")
VFS_websocket_server constructor.
QString _sslKeyPath
ssl key file path
virtual void authorize(VFS_websocket_client *client, QJsonObject authdata)
Authorize a client and create a session based on type and username.
virtual bool isContainer()
A VFS_websocket_server is always a container; it's children will be the connected clients.
QWebSocketServer::SslMode _ssl
Use ssl?
virtual bool listen()
Start listening on the _socket for incoming connections.
virtual void closeConnection()
Close a child client connection, and remove() it from this node's child list.
virtual VFS_websocket_client * createWebsocketClient(QWebSocket *s)
Create a VFS_websocket_client to host a new connection.
static VFS * root()
Return the root node of the VFS filesystem.
Definition: VFS.cpp:399
static void LOG(QString message, int level=0, QString user="server")
Send a message to the VFS::_messages VFS_stream.
Definition: VFS.cpp:209
static void ERROR(QString message, int level=0, QString user="server")
Send a message to the VFS::_errors VFS_stream.
Definition: VFS.cpp:307
static void WARN(QString message, int level=0, QString user="server")
Send a message to the VFS::_warnings VFS_stream.
Definition: VFS.cpp:258
message(m)
Change the message of an existing arrowMessage.
setter error
Set the error value of this widget.