mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-23 00:34:26 +02:00
Merge pull request #2076 from birarda/authentication
groundwork for secure node communication via data-server
This commit is contained in:
commit
e637e48598
54 changed files with 1641 additions and 4336 deletions
|
@ -31,13 +31,11 @@ ENDIF(APPLE)
|
|||
# targets not supported on windows
|
||||
if (NOT WIN32)
|
||||
add_subdirectory(animation-server)
|
||||
add_subdirectory(data-server)
|
||||
endif (NOT WIN32)
|
||||
|
||||
# targets on all platforms
|
||||
add_subdirectory(assignment-client)
|
||||
add_subdirectory(domain-server)
|
||||
add_subdirectory(interface)
|
||||
add_subdirectory(pairing-server)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(voxel-edit)
|
||||
|
|
|
@ -736,12 +736,12 @@ AnimationServer::AnimationServer(int &argc, char **argv) :
|
|||
::wantLocalDomain = cmdOptionExists(argc, (const char**) argv,local);
|
||||
if (::wantLocalDomain) {
|
||||
printf("Local Domain MODE!\n");
|
||||
nodeList->setDomainIPToLocalhost();
|
||||
nodeList->getDomainInfo().setIPToLocalhost();
|
||||
}
|
||||
|
||||
const char* domainHostname = getCmdOption(argc, (const char**) argv, "--domain");
|
||||
if (domainHostname) {
|
||||
NodeList::getInstance()->setDomainHostname(domainHostname);
|
||||
NodeList::getInstance()->getDomainInfo().setHostname(domainHostname);
|
||||
}
|
||||
|
||||
const char* packetsPerSecondCommand = getCmdOption(argc, (const char**) argv, "--pps");
|
||||
|
|
|
@ -79,7 +79,7 @@ void Agent::run() {
|
|||
|
||||
// figure out the URL for the script for this agent assignment
|
||||
QString scriptURLString("http://%1:8080/assignment/%2");
|
||||
scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainIP().toString(),
|
||||
scriptURLString = scriptURLString.arg(NodeList::getInstance()->getDomainInfo().getIP().toString(),
|
||||
uuidStringWithoutCurlyBraces(_uuid));
|
||||
|
||||
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <Assignment.h>
|
||||
#include <Logging.h>
|
||||
#include <NodeList.h>
|
||||
|
@ -28,6 +30,11 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
|||
QCoreApplication(argc, argv),
|
||||
_currentAssignment(NULL)
|
||||
{
|
||||
setOrganizationName("High Fidelity");
|
||||
setOrganizationDomain("highfidelity.io");
|
||||
setApplicationName("assignment-client");
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
|
||||
// register meta type is required for queued invoke method on Assignment subclasses
|
||||
|
||||
// set the logging target to the the CHILD_TARGET_NAME
|
||||
|
@ -91,6 +98,10 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
|||
|
||||
// connect our readPendingDatagrams method to the readyRead() signal of the socket
|
||||
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams);
|
||||
|
||||
// connections to AccountManager for authentication
|
||||
connect(&AccountManager::getInstance(), &AccountManager::authRequired,
|
||||
this, &AssignmentClient::handleAuthenticationRequest);
|
||||
}
|
||||
|
||||
void AssignmentClient::sendAssignmentRequest() {
|
||||
|
@ -120,10 +131,10 @@ void AssignmentClient::readPendingDatagrams() {
|
|||
|
||||
// switch our nodelist domain IP and port to whoever sent us the assignment
|
||||
|
||||
nodeList->setDomainSockAddr(senderSockAddr);
|
||||
nodeList->setSessionUUID(_currentAssignment->getUUID());
|
||||
nodeList->getDomainInfo().setSockAddr(senderSockAddr);
|
||||
nodeList->getDomainInfo().setAssignmentUUID(_currentAssignment->getUUID());
|
||||
|
||||
qDebug() << "Destination IP for assignment is" << nodeList->getDomainIP().toString();
|
||||
qDebug() << "Destination IP for assignment is" << nodeList->getDomainInfo().getIP().toString();
|
||||
|
||||
// start the deployed assignment
|
||||
QThread* workerThread = new QThread(this);
|
||||
|
@ -158,6 +169,30 @@ void AssignmentClient::readPendingDatagrams() {
|
|||
}
|
||||
}
|
||||
|
||||
void AssignmentClient::handleAuthenticationRequest() {
|
||||
const QString DATA_SERVER_USERNAME_ENV = "HIFI_AC_USERNAME";
|
||||
const QString DATA_SERVER_PASSWORD_ENV = "HIFI_AC_PASSWORD";
|
||||
|
||||
// this node will be using an authentication server, let's make sure we have a username/password
|
||||
QProcessEnvironment sysEnvironment = QProcessEnvironment::systemEnvironment();
|
||||
|
||||
QString username = sysEnvironment.value(DATA_SERVER_USERNAME_ENV);
|
||||
QString password = sysEnvironment.value(DATA_SERVER_PASSWORD_ENV);
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
if (!username.isEmpty() && !password.isEmpty()) {
|
||||
// ask the account manager to log us in from the env variables
|
||||
accountManager.requestAccessToken(username, password);
|
||||
} else {
|
||||
qDebug() << "Authentication was requested against" << qPrintable(accountManager.getRootURL().toString())
|
||||
<< "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV)
|
||||
<< "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate.";
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AssignmentClient::assignmentCompleted() {
|
||||
// reset the logging target to the the CHILD_TARGET_NAME
|
||||
Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||
|
@ -175,4 +210,5 @@ void AssignmentClient::assignmentCompleted() {
|
|||
// reset our NodeList by switching back to unassigned and clearing the list
|
||||
nodeList->setOwnerType(NodeType::Unassigned);
|
||||
nodeList->reset();
|
||||
nodeList->resetNodeInterestSet();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ private slots:
|
|||
void sendAssignmentRequest();
|
||||
void readPendingDatagrams();
|
||||
void assignmentCompleted();
|
||||
void handleAuthenticationRequest();
|
||||
private:
|
||||
Assignment _requestAssignment;
|
||||
ThreadedAssignment* _currentAssignment;
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
set(ROOT_DIR ..)
|
||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
||||
|
||||
set(TARGET_NAME data-server)
|
||||
|
||||
find_package(Qt5Network REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||
setup_hifi_project(${TARGET_NAME} TRUE)
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Network)
|
||||
|
||||
# link the shared hifi library
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
||||
|
||||
# add hiredis as a library
|
||||
FILE(GLOB HIREDIS_SRCS external/hiredis/*.c)
|
||||
add_library(hiredis ${HIREDIS_SRCS})
|
||||
include_directories(external/hiredis/)
|
||||
target_link_libraries(${TARGET_NAME} hiredis)
|
20
data-server/external/hiredis/fmacros.h
vendored
20
data-server/external/hiredis/fmacros.h
vendored
|
@ -1,20 +0,0 @@
|
|||
#ifndef __HIREDIS_FMACRO_H
|
||||
#define __HIREDIS_FMACRO_H
|
||||
|
||||
#if !defined(_BSD_SOURCE)
|
||||
#define _BSD_SOURCE
|
||||
#endif
|
||||
|
||||
#if defined(__sun__)
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#elif defined(__linux__)
|
||||
#define _XOPEN_SOURCE 600
|
||||
#else
|
||||
#define _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#if __APPLE__ && __MACH__
|
||||
#define _OSX
|
||||
#endif
|
||||
|
||||
#endif
|
1322
data-server/external/hiredis/hiredis.c
vendored
1322
data-server/external/hiredis/hiredis.c
vendored
File diff suppressed because it is too large
Load diff
213
data-server/external/hiredis/hiredis.h
vendored
213
data-server/external/hiredis/hiredis.h
vendored
|
@ -1,213 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_H
|
||||
#define __HIREDIS_H
|
||||
#include <stdio.h> /* for size_t */
|
||||
#include <stdarg.h> /* for va_list */
|
||||
#include <sys/time.h> /* for struct timeval */
|
||||
|
||||
#define HIREDIS_MAJOR 0
|
||||
#define HIREDIS_MINOR 11
|
||||
#define HIREDIS_PATCH 0
|
||||
|
||||
#define REDIS_ERR -1
|
||||
#define REDIS_OK 0
|
||||
|
||||
/* When an error occurs, the err flag in a context is set to hold the type of
|
||||
* error that occured. REDIS_ERR_IO means there was an I/O error and you
|
||||
* should use the "errno" variable to find out what is wrong.
|
||||
* For other values, the "errstr" field will hold a description. */
|
||||
#define REDIS_ERR_IO 1 /* Error in read or write */
|
||||
#define REDIS_ERR_EOF 3 /* End of file */
|
||||
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
|
||||
#define REDIS_ERR_OOM 5 /* Out of memory */
|
||||
#define REDIS_ERR_OTHER 2 /* Everything else... */
|
||||
|
||||
/* Connection type can be blocking or non-blocking and is set in the
|
||||
* least significant bit of the flags field in redisContext. */
|
||||
#define REDIS_BLOCK 0x1
|
||||
|
||||
/* Connection may be disconnected before being free'd. The second bit
|
||||
* in the flags field is set when the context is connected. */
|
||||
#define REDIS_CONNECTED 0x2
|
||||
|
||||
/* The async API might try to disconnect cleanly and flush the output
|
||||
* buffer and read all subsequent replies before disconnecting.
|
||||
* This flag means no new commands can come in and the connection
|
||||
* should be terminated once all replies have been read. */
|
||||
#define REDIS_DISCONNECTING 0x4
|
||||
|
||||
/* Flag specific to the async API which means that the context should be clean
|
||||
* up as soon as possible. */
|
||||
#define REDIS_FREEING 0x8
|
||||
|
||||
/* Flag that is set when an async callback is executed. */
|
||||
#define REDIS_IN_CALLBACK 0x10
|
||||
|
||||
/* Flag that is set when the async context has one or more subscriptions. */
|
||||
#define REDIS_SUBSCRIBED 0x20
|
||||
|
||||
/* Flag that is set when monitor mode is active */
|
||||
#define REDIS_MONITORING 0x40
|
||||
|
||||
#define REDIS_REPLY_STRING 1
|
||||
#define REDIS_REPLY_ARRAY 2
|
||||
#define REDIS_REPLY_INTEGER 3
|
||||
#define REDIS_REPLY_NIL 4
|
||||
#define REDIS_REPLY_STATUS 5
|
||||
#define REDIS_REPLY_ERROR 6
|
||||
|
||||
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
|
||||
|
||||
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This is the reply object returned by redisCommand() */
|
||||
typedef struct redisReply {
|
||||
int type; /* REDIS_REPLY_* */
|
||||
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
|
||||
int len; /* Length of string */
|
||||
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
|
||||
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
|
||||
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
|
||||
} redisReply;
|
||||
|
||||
typedef struct redisReadTask {
|
||||
int type;
|
||||
int elements; /* number of elements in multibulk container */
|
||||
int idx; /* index in parent (array) object */
|
||||
void *obj; /* holds user-generated value for a read task */
|
||||
struct redisReadTask *parent; /* parent task */
|
||||
void *privdata; /* user-settable arbitrary field */
|
||||
} redisReadTask;
|
||||
|
||||
typedef struct redisReplyObjectFunctions {
|
||||
void *(*createString)(const redisReadTask*, char*, size_t);
|
||||
void *(*createArray)(const redisReadTask*, int);
|
||||
void *(*createInteger)(const redisReadTask*, long long);
|
||||
void *(*createNil)(const redisReadTask*);
|
||||
void (*freeObject)(void*);
|
||||
} redisReplyObjectFunctions;
|
||||
|
||||
/* State for the protocol parser */
|
||||
typedef struct redisReader {
|
||||
int err; /* Error flags, 0 when there is no error */
|
||||
char errstr[128]; /* String representation of error when applicable */
|
||||
|
||||
char *buf; /* Read buffer */
|
||||
size_t pos; /* Buffer cursor */
|
||||
size_t len; /* Buffer length */
|
||||
size_t maxbuf; /* Max length of unused buffer */
|
||||
|
||||
redisReadTask rstack[9];
|
||||
int ridx; /* Index of current read task */
|
||||
void *reply; /* Temporary reply pointer */
|
||||
|
||||
redisReplyObjectFunctions *fn;
|
||||
void *privdata;
|
||||
} redisReader;
|
||||
|
||||
/* Public API for the protocol parser. */
|
||||
redisReader *redisReaderCreate(void);
|
||||
void redisReaderFree(redisReader *r);
|
||||
int redisReaderFeed(redisReader *r, const char *buf, size_t len);
|
||||
int redisReaderGetReply(redisReader *r, void **reply);
|
||||
|
||||
/* Backwards compatibility, can be removed on big version bump. */
|
||||
#define redisReplyReaderCreate redisReaderCreate
|
||||
#define redisReplyReaderFree redisReaderFree
|
||||
#define redisReplyReaderFeed redisReaderFeed
|
||||
#define redisReplyReaderGetReply redisReaderGetReply
|
||||
#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
|
||||
#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)
|
||||
#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)
|
||||
|
||||
/* Function to free the reply objects hiredis returns by default. */
|
||||
void freeReplyObject(void *reply);
|
||||
|
||||
/* Functions to format a command according to the protocol. */
|
||||
int redisvFormatCommand(char **target, const char *format, va_list ap);
|
||||
int redisFormatCommand(char **target, const char *format, ...);
|
||||
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
|
||||
|
||||
/* Context for a connection to Redis */
|
||||
typedef struct redisContext {
|
||||
int err; /* Error flags, 0 when there is no error */
|
||||
char errstr[128]; /* String representation of error when applicable */
|
||||
int fd;
|
||||
int flags;
|
||||
char *obuf; /* Write buffer */
|
||||
redisReader *reader; /* Protocol reader */
|
||||
} redisContext;
|
||||
|
||||
redisContext *redisConnect(const char *ip, int port);
|
||||
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
|
||||
redisContext *redisConnectNonBlock(const char *ip, int port);
|
||||
redisContext *redisConnectUnix(const char *path);
|
||||
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
|
||||
redisContext *redisConnectUnixNonBlock(const char *path);
|
||||
int redisSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisEnableKeepAlive(redisContext *c);
|
||||
void redisFree(redisContext *c);
|
||||
int redisBufferRead(redisContext *c);
|
||||
int redisBufferWrite(redisContext *c, int *done);
|
||||
|
||||
/* In a blocking context, this function first checks if there are unconsumed
|
||||
* replies to return and returns one if so. Otherwise, it flushes the output
|
||||
* buffer to the socket and reads until it has a reply. In a non-blocking
|
||||
* context, it will return unconsumed replies until there are no more. */
|
||||
int redisGetReply(redisContext *c, void **reply);
|
||||
int redisGetReplyFromReader(redisContext *c, void **reply);
|
||||
|
||||
/* Write a command to the output buffer. Use these functions in blocking mode
|
||||
* to get a pipeline of commands. */
|
||||
int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
|
||||
int redisAppendCommand(redisContext *c, const char *format, ...);
|
||||
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
||||
|
||||
/* Issue a command to Redis. In a blocking context, it is identical to calling
|
||||
* redisAppendCommand, followed by redisGetReply. The function will return
|
||||
* NULL if there was an error in performing the request, otherwise it will
|
||||
* return the reply. In a non-blocking context, it is identical to calling
|
||||
* only redisAppendCommand and will always return NULL. */
|
||||
void *redisvCommand(redisContext *c, const char *format, va_list ap);
|
||||
void *redisCommand(redisContext *c, const char *format, ...);
|
||||
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
339
data-server/external/hiredis/net.c
vendored
339
data-server/external/hiredis/net.c
vendored
|
@ -1,339 +0,0 @@
|
|||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
||||
*
|
||||
* Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "net.h"
|
||||
#include "sds.h"
|
||||
|
||||
/* Defined in hiredis.c */
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
|
||||
char buf[128] = { 0 };
|
||||
size_t len = 0;
|
||||
|
||||
if (prefix != NULL)
|
||||
len = snprintf(buf,sizeof(buf),"%s: ",prefix);
|
||||
strerror_r(errno,buf+len,sizeof(buf)-len);
|
||||
__redisSetError(c,type,buf);
|
||||
}
|
||||
|
||||
static int redisSetReuseAddr(redisContext *c, int fd) {
|
||||
int on = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisCreateSocket(redisContext *c, int type) {
|
||||
int s;
|
||||
if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (type == AF_INET) {
|
||||
if (redisSetReuseAddr(c,s) == REDIS_ERR) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static int redisSetBlocking(redisContext *c, int fd, int blocking) {
|
||||
int flags;
|
||||
|
||||
/* Set the socket nonblocking.
|
||||
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
|
||||
* interrupted by a signal. */
|
||||
if ((flags = fcntl(fd, F_GETFL)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (blocking)
|
||||
flags &= ~O_NONBLOCK;
|
||||
else
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if (fcntl(fd, F_SETFL, flags) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisKeepAlive(redisContext *c, int interval) {
|
||||
int val = 1;
|
||||
int fd = c->fd;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
#ifdef _OSX
|
||||
val = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#else
|
||||
val = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
val = interval/3;
|
||||
if (val == 0) val = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
val = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisSetTcpNoDelay(redisContext *c, int fd) {
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
|
||||
|
||||
static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) {
|
||||
struct pollfd wfd[1];
|
||||
long msec;
|
||||
|
||||
msec = -1;
|
||||
wfd[0].fd = fd;
|
||||
wfd[0].events = POLLOUT;
|
||||
|
||||
/* Only use timeout when not NULL. */
|
||||
if (timeout != NULL) {
|
||||
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
|
||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL);
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000);
|
||||
|
||||
if (msec < 0 || msec > INT_MAX) {
|
||||
msec = INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
if (errno == EINPROGRESS) {
|
||||
int res;
|
||||
|
||||
if ((res = poll(wfd, 1, msec)) == -1) {
|
||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
} else if (res == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (redisCheckSocketError(c, fd) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisCheckSocketError(redisContext *c, int fd) {
|
||||
int err = 0;
|
||||
socklen_t errlen = sizeof(err);
|
||||
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)");
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
errno = err;
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout) {
|
||||
int s, rv;
|
||||
char _port[6]; /* strlen("65535"); */
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
|
||||
snprintf(_port, 6, "%d", port);
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
/* Try with IPv6 if no IPv4 address was found. We do it in this order since
|
||||
* in a Redis client you can't afford to test if you have IPv6 connectivity
|
||||
* as this would add latency to every connect. Otherwise a more sensible
|
||||
* route could be: Use IPv6 if both addresses are available and there is IPv6
|
||||
* connectivity. */
|
||||
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
||||
hints.ai_family = AF_INET6;
|
||||
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
||||
continue;
|
||||
|
||||
if (redisSetBlocking(c,s,0) != REDIS_OK)
|
||||
goto error;
|
||||
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
||||
if (errno == EHOSTUNREACH) {
|
||||
close(s);
|
||||
continue;
|
||||
} else if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else {
|
||||
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
|
||||
goto error;
|
||||
if (redisSetTcpNoDelay(c,s) != REDIS_OK)
|
||||
goto error;
|
||||
|
||||
c->fd = s;
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
rv = REDIS_OK;
|
||||
goto end;
|
||||
}
|
||||
if (p == NULL) {
|
||||
char buf[128];
|
||||
snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno));
|
||||
__redisSetError(c,REDIS_ERR_OTHER,buf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
rv = REDIS_ERR;
|
||||
end:
|
||||
freeaddrinfo(servinfo);
|
||||
return rv; // Need to return REDIS_OK if alright
|
||||
}
|
||||
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
|
||||
int s;
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
struct sockaddr_un sa;
|
||||
|
||||
if ((s = redisCreateSocket(c,AF_LOCAL)) < 0)
|
||||
return REDIS_ERR;
|
||||
if (redisSetBlocking(c,s,0) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
sa.sun_family = AF_LOCAL;
|
||||
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
|
||||
if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
|
||||
if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else {
|
||||
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset socket to be blocking after connect(2). */
|
||||
if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
c->fd = s;
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
return REDIS_OK;
|
||||
}
|
48
data-server/external/hiredis/net.h
vendored
48
data-server/external/hiredis/net.h
vendored
|
@ -1,48 +0,0 @@
|
|||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
||||
*
|
||||
* Copyright (c) 2006-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __NET_H
|
||||
#define __NET_H
|
||||
|
||||
#include "hiredis.h"
|
||||
|
||||
#if defined(__sun)
|
||||
#define AF_LOCAL AF_UNIX
|
||||
#endif
|
||||
|
||||
int redisCheckSocketError(redisContext *c, int fd);
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
|
||||
int redisKeepAlive(redisContext *c, int interval);
|
||||
|
||||
#endif
|
606
data-server/external/hiredis/sds.c
vendored
606
data-server/external/hiredis/sds.c
vendored
|
@ -1,606 +0,0 @@
|
|||
/* SDSLib, A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "sds.h"
|
||||
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
static void sdsOomAbort(void) {
|
||||
fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen) {
|
||||
struct sdshdr *sh;
|
||||
|
||||
sh = malloc(sizeof(struct sdshdr)+initlen+1);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (sh == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (sh == NULL) return NULL;
|
||||
#endif
|
||||
sh->len = initlen;
|
||||
sh->free = 0;
|
||||
if (initlen) {
|
||||
if (init) memcpy(sh->buf, init, initlen);
|
||||
else memset(sh->buf,0,initlen);
|
||||
}
|
||||
sh->buf[initlen] = '\0';
|
||||
return (char*)sh->buf;
|
||||
}
|
||||
|
||||
sds sdsempty(void) {
|
||||
return sdsnewlen("",0);
|
||||
}
|
||||
|
||||
sds sdsnew(const char *init) {
|
||||
size_t initlen = (init == NULL) ? 0 : strlen(init);
|
||||
return sdsnewlen(init, initlen);
|
||||
}
|
||||
|
||||
sds sdsdup(const sds s) {
|
||||
return sdsnewlen(s, sdslen(s));
|
||||
}
|
||||
|
||||
void sdsfree(sds s) {
|
||||
if (s == NULL) return;
|
||||
free(s-sizeof(struct sdshdr));
|
||||
}
|
||||
|
||||
void sdsupdatelen(sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
int reallen = strlen(s);
|
||||
sh->free += (sh->len-reallen);
|
||||
sh->len = reallen;
|
||||
}
|
||||
|
||||
static sds sdsMakeRoomFor(sds s, size_t addlen) {
|
||||
struct sdshdr *sh, *newsh;
|
||||
size_t free = sdsavail(s);
|
||||
size_t len, newlen;
|
||||
|
||||
if (free >= addlen) return s;
|
||||
len = sdslen(s);
|
||||
sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
newlen = (len+addlen)*2;
|
||||
newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (newsh == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (newsh == NULL) return NULL;
|
||||
#endif
|
||||
|
||||
newsh->free = newlen - len;
|
||||
return newsh->buf;
|
||||
}
|
||||
|
||||
/* Grow the sds to have the specified length. Bytes that were not part of
|
||||
* the original length of the sds will be set to zero. */
|
||||
sds sdsgrowzero(sds s, size_t len) {
|
||||
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
size_t totlen, curlen = sh->len;
|
||||
|
||||
if (len <= curlen) return s;
|
||||
s = sdsMakeRoomFor(s,len-curlen);
|
||||
if (s == NULL) return NULL;
|
||||
|
||||
/* Make sure added region doesn't contain garbage */
|
||||
sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
|
||||
totlen = sh->len+sh->free;
|
||||
sh->len = len;
|
||||
sh->free = totlen-sh->len;
|
||||
return s;
|
||||
}
|
||||
|
||||
sds sdscatlen(sds s, const void *t, size_t len) {
|
||||
struct sdshdr *sh;
|
||||
size_t curlen = sdslen(s);
|
||||
|
||||
s = sdsMakeRoomFor(s,len);
|
||||
if (s == NULL) return NULL;
|
||||
sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
memcpy(s+curlen, t, len);
|
||||
sh->len = curlen+len;
|
||||
sh->free = sh->free-len;
|
||||
s[curlen+len] = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
sds sdscat(sds s, const char *t) {
|
||||
return sdscatlen(s, t, strlen(t));
|
||||
}
|
||||
|
||||
sds sdscpylen(sds s, char *t, size_t len) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
size_t totlen = sh->free+sh->len;
|
||||
|
||||
if (totlen < len) {
|
||||
s = sdsMakeRoomFor(s,len-sh->len);
|
||||
if (s == NULL) return NULL;
|
||||
sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
totlen = sh->free+sh->len;
|
||||
}
|
||||
memcpy(s, t, len);
|
||||
s[len] = '\0';
|
||||
sh->len = len;
|
||||
sh->free = totlen-len;
|
||||
return s;
|
||||
}
|
||||
|
||||
sds sdscpy(sds s, char *t) {
|
||||
return sdscpylen(s, t, strlen(t));
|
||||
}
|
||||
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
||||
va_list cpy;
|
||||
char *buf, *t;
|
||||
size_t buflen = 16;
|
||||
|
||||
while(1) {
|
||||
buf = malloc(buflen);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (buf == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (buf == NULL) return NULL;
|
||||
#endif
|
||||
buf[buflen-2] = '\0';
|
||||
va_copy(cpy,ap);
|
||||
vsnprintf(buf, buflen, fmt, cpy);
|
||||
va_end(cpy);
|
||||
if (buf[buflen-2] != '\0') {
|
||||
free(buf);
|
||||
buflen *= 2;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
t = sdscat(s, buf);
|
||||
free(buf);
|
||||
return t;
|
||||
}
|
||||
|
||||
sds sdscatprintf(sds s, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
char *t;
|
||||
va_start(ap, fmt);
|
||||
t = sdscatvprintf(s,fmt,ap);
|
||||
va_end(ap);
|
||||
return t;
|
||||
}
|
||||
|
||||
sds sdstrim(sds s, const char *cset) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
char *start, *end, *sp, *ep;
|
||||
size_t len;
|
||||
|
||||
sp = start = s;
|
||||
ep = end = s+sdslen(s)-1;
|
||||
while(sp <= end && strchr(cset, *sp)) sp++;
|
||||
while(ep > start && strchr(cset, *ep)) ep--;
|
||||
len = (sp > ep) ? 0 : ((ep-sp)+1);
|
||||
if (sh->buf != sp) memmove(sh->buf, sp, len);
|
||||
sh->buf[len] = '\0';
|
||||
sh->free = sh->free+(sh->len-len);
|
||||
sh->len = len;
|
||||
return s;
|
||||
}
|
||||
|
||||
sds sdsrange(sds s, int start, int end) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
size_t newlen, len = sdslen(s);
|
||||
|
||||
if (len == 0) return s;
|
||||
if (start < 0) {
|
||||
start = len+start;
|
||||
if (start < 0) start = 0;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = len+end;
|
||||
if (end < 0) end = 0;
|
||||
}
|
||||
newlen = (start > end) ? 0 : (end-start)+1;
|
||||
if (newlen != 0) {
|
||||
if (start >= (signed)len) {
|
||||
newlen = 0;
|
||||
} else if (end >= (signed)len) {
|
||||
end = len-1;
|
||||
newlen = (start > end) ? 0 : (end-start)+1;
|
||||
}
|
||||
} else {
|
||||
start = 0;
|
||||
}
|
||||
if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
|
||||
sh->buf[newlen] = 0;
|
||||
sh->free = sh->free+(sh->len-newlen);
|
||||
sh->len = newlen;
|
||||
return s;
|
||||
}
|
||||
|
||||
void sdstolower(sds s) {
|
||||
int len = sdslen(s), j;
|
||||
|
||||
for (j = 0; j < len; j++) s[j] = tolower(s[j]);
|
||||
}
|
||||
|
||||
void sdstoupper(sds s) {
|
||||
int len = sdslen(s), j;
|
||||
|
||||
for (j = 0; j < len; j++) s[j] = toupper(s[j]);
|
||||
}
|
||||
|
||||
int sdscmp(sds s1, sds s2) {
|
||||
size_t l1, l2, minlen;
|
||||
int cmp;
|
||||
|
||||
l1 = sdslen(s1);
|
||||
l2 = sdslen(s2);
|
||||
minlen = (l1 < l2) ? l1 : l2;
|
||||
cmp = memcmp(s1,s2,minlen);
|
||||
if (cmp == 0) return l1-l2;
|
||||
return cmp;
|
||||
}
|
||||
|
||||
/* Split 's' with separator in 'sep'. An array
|
||||
* of sds strings is returned. *count will be set
|
||||
* by reference to the number of tokens returned.
|
||||
*
|
||||
* On out of memory, zero length string, zero length
|
||||
* separator, NULL is returned.
|
||||
*
|
||||
* Note that 'sep' is able to split a string using
|
||||
* a multi-character separator. For example
|
||||
* sdssplit("foo_-_bar","_-_"); will return two
|
||||
* elements "foo" and "bar".
|
||||
*
|
||||
* This version of the function is binary-safe but
|
||||
* requires length arguments. sdssplit() is just the
|
||||
* same function but for zero-terminated strings.
|
||||
*/
|
||||
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
|
||||
int elements = 0, slots = 5, start = 0, j;
|
||||
|
||||
sds *tokens = malloc(sizeof(sds)*slots);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (tokens == NULL) sdsOomAbort();
|
||||
#endif
|
||||
if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
|
||||
if (len == 0) {
|
||||
*count = 0;
|
||||
return tokens;
|
||||
}
|
||||
for (j = 0; j < (len-(seplen-1)); j++) {
|
||||
/* make sure there is room for the next element and the final one */
|
||||
if (slots < elements+2) {
|
||||
sds *newtokens;
|
||||
|
||||
slots *= 2;
|
||||
newtokens = realloc(tokens,sizeof(sds)*slots);
|
||||
if (newtokens == NULL) {
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
sdsOomAbort();
|
||||
#else
|
||||
goto cleanup;
|
||||
#endif
|
||||
}
|
||||
tokens = newtokens;
|
||||
}
|
||||
/* search the separator */
|
||||
if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
|
||||
tokens[elements] = sdsnewlen(s+start,j-start);
|
||||
if (tokens[elements] == NULL) {
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
sdsOomAbort();
|
||||
#else
|
||||
goto cleanup;
|
||||
#endif
|
||||
}
|
||||
elements++;
|
||||
start = j+seplen;
|
||||
j = j+seplen-1; /* skip the separator */
|
||||
}
|
||||
}
|
||||
/* Add the final element. We are sure there is room in the tokens array. */
|
||||
tokens[elements] = sdsnewlen(s+start,len-start);
|
||||
if (tokens[elements] == NULL) {
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
sdsOomAbort();
|
||||
#else
|
||||
goto cleanup;
|
||||
#endif
|
||||
}
|
||||
elements++;
|
||||
*count = elements;
|
||||
return tokens;
|
||||
|
||||
#ifndef SDS_ABORT_ON_OOM
|
||||
cleanup:
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < elements; i++) sdsfree(tokens[i]);
|
||||
free(tokens);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void sdsfreesplitres(sds *tokens, int count) {
|
||||
if (!tokens) return;
|
||||
while(count--)
|
||||
sdsfree(tokens[count]);
|
||||
free(tokens);
|
||||
}
|
||||
|
||||
sds sdsfromlonglong(long long value) {
|
||||
char buf[32], *p;
|
||||
unsigned long long v;
|
||||
|
||||
v = (value < 0) ? -value : value;
|
||||
p = buf+31; /* point to the last character */
|
||||
do {
|
||||
*p-- = '0'+(v%10);
|
||||
v /= 10;
|
||||
} while(v);
|
||||
if (value < 0) *p-- = '-';
|
||||
p++;
|
||||
return sdsnewlen(p,32-(p-buf));
|
||||
}
|
||||
|
||||
sds sdscatrepr(sds s, char *p, size_t len) {
|
||||
s = sdscatlen(s,"\"",1);
|
||||
if (s == NULL) return NULL;
|
||||
|
||||
while(len--) {
|
||||
switch(*p) {
|
||||
case '\\':
|
||||
case '"':
|
||||
s = sdscatprintf(s,"\\%c",*p);
|
||||
break;
|
||||
case '\n': s = sdscatlen(s,"\\n",2); break;
|
||||
case '\r': s = sdscatlen(s,"\\r",2); break;
|
||||
case '\t': s = sdscatlen(s,"\\t",2); break;
|
||||
case '\a': s = sdscatlen(s,"\\a",2); break;
|
||||
case '\b': s = sdscatlen(s,"\\b",2); break;
|
||||
default:
|
||||
if (isprint(*p))
|
||||
s = sdscatprintf(s,"%c",*p);
|
||||
else
|
||||
s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
if (s == NULL) return NULL;
|
||||
}
|
||||
return sdscatlen(s,"\"",1);
|
||||
}
|
||||
|
||||
/* Split a line into arguments, where every argument can be in the
|
||||
* following programming-language REPL-alike form:
|
||||
*
|
||||
* foo bar "newline are supported\n" and "\xff\x00otherstuff"
|
||||
*
|
||||
* The number of arguments is stored into *argc, and an array
|
||||
* of sds is returned. The caller should sdsfree() all the returned
|
||||
* strings and finally free() the array itself.
|
||||
*
|
||||
* Note that sdscatrepr() is able to convert back a string into
|
||||
* a quoted string in the same format sdssplitargs() is able to parse.
|
||||
*/
|
||||
sds *sdssplitargs(char *line, int *argc) {
|
||||
char *p = line;
|
||||
char *current = NULL;
|
||||
char **vector = NULL, **_vector = NULL;
|
||||
|
||||
*argc = 0;
|
||||
while(1) {
|
||||
/* skip blanks */
|
||||
while(*p && isspace(*p)) p++;
|
||||
if (*p) {
|
||||
/* get a token */
|
||||
int inq=0; /* set to 1 if we are in "quotes" */
|
||||
int done=0;
|
||||
|
||||
if (current == NULL) {
|
||||
current = sdsempty();
|
||||
if (current == NULL) goto err;
|
||||
}
|
||||
|
||||
while(!done) {
|
||||
if (inq) {
|
||||
if (*p == '\\' && *(p+1)) {
|
||||
char c;
|
||||
|
||||
p++;
|
||||
switch(*p) {
|
||||
case 'n': c = '\n'; break;
|
||||
case 'r': c = '\r'; break;
|
||||
case 't': c = '\t'; break;
|
||||
case 'b': c = '\b'; break;
|
||||
case 'a': c = '\a'; break;
|
||||
default: c = *p; break;
|
||||
}
|
||||
current = sdscatlen(current,&c,1);
|
||||
} else if (*p == '"') {
|
||||
/* closing quote must be followed by a space */
|
||||
if (*(p+1) && !isspace(*(p+1))) goto err;
|
||||
done=1;
|
||||
} else if (!*p) {
|
||||
/* unterminated quotes */
|
||||
goto err;
|
||||
} else {
|
||||
current = sdscatlen(current,p,1);
|
||||
}
|
||||
} else {
|
||||
switch(*p) {
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\0':
|
||||
done=1;
|
||||
break;
|
||||
case '"':
|
||||
inq=1;
|
||||
break;
|
||||
default:
|
||||
current = sdscatlen(current,p,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*p) p++;
|
||||
if (current == NULL) goto err;
|
||||
}
|
||||
/* add the token to the vector */
|
||||
_vector = realloc(vector,((*argc)+1)*sizeof(char*));
|
||||
if (_vector == NULL) goto err;
|
||||
|
||||
vector = _vector;
|
||||
vector[*argc] = current;
|
||||
(*argc)++;
|
||||
current = NULL;
|
||||
} else {
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
while((*argc)--)
|
||||
sdsfree(vector[*argc]);
|
||||
if (vector != NULL) free(vector);
|
||||
if (current != NULL) sdsfree(current);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef SDS_TEST_MAIN
|
||||
#include <stdio.h>
|
||||
|
||||
int __failed_tests = 0;
|
||||
int __test_num = 0;
|
||||
#define test_cond(descr,_c) do { \
|
||||
__test_num++; printf("%d - %s: ", __test_num, descr); \
|
||||
if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
|
||||
} while(0);
|
||||
#define test_report() do { \
|
||||
printf("%d tests, %d passed, %d failed\n", __test_num, \
|
||||
__test_num-__failed_tests, __failed_tests); \
|
||||
if (__failed_tests) { \
|
||||
printf("=== WARNING === We have failed tests here...\n"); \
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
int main(void) {
|
||||
{
|
||||
sds x = sdsnew("foo"), y;
|
||||
|
||||
test_cond("Create a string and obtain the length",
|
||||
sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
|
||||
|
||||
sdsfree(x);
|
||||
x = sdsnewlen("foo",2);
|
||||
test_cond("Create a string with specified length",
|
||||
sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
|
||||
|
||||
x = sdscat(x,"bar");
|
||||
test_cond("Strings concatenation",
|
||||
sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
|
||||
|
||||
x = sdscpy(x,"a");
|
||||
test_cond("sdscpy() against an originally longer string",
|
||||
sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
|
||||
|
||||
x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
|
||||
test_cond("sdscpy() against an originally shorter string",
|
||||
sdslen(x) == 33 &&
|
||||
memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
|
||||
|
||||
sdsfree(x);
|
||||
x = sdscatprintf(sdsempty(),"%d",123);
|
||||
test_cond("sdscatprintf() seems working in the base case",
|
||||
sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
|
||||
|
||||
sdsfree(x);
|
||||
x = sdstrim(sdsnew("xxciaoyyy"),"xy");
|
||||
test_cond("sdstrim() correctly trims characters",
|
||||
sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
|
||||
|
||||
y = sdsrange(sdsdup(x),1,1);
|
||||
test_cond("sdsrange(...,1,1)",
|
||||
sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),1,-1);
|
||||
test_cond("sdsrange(...,1,-1)",
|
||||
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),-2,-1);
|
||||
test_cond("sdsrange(...,-2,-1)",
|
||||
sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),2,1);
|
||||
test_cond("sdsrange(...,2,1)",
|
||||
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),1,100);
|
||||
test_cond("sdsrange(...,1,100)",
|
||||
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),100,100);
|
||||
test_cond("sdsrange(...,100,100)",
|
||||
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
sdsfree(x);
|
||||
x = sdsnew("foo");
|
||||
y = sdsnew("foa");
|
||||
test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
|
||||
|
||||
sdsfree(y);
|
||||
sdsfree(x);
|
||||
x = sdsnew("bar");
|
||||
y = sdsnew("bar");
|
||||
test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
sdsfree(x);
|
||||
x = sdsnew("aar");
|
||||
y = sdsnew("bar");
|
||||
test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
|
||||
}
|
||||
test_report()
|
||||
}
|
||||
#endif
|
88
data-server/external/hiredis/sds.h
vendored
88
data-server/external/hiredis/sds.h
vendored
|
@ -1,88 +0,0 @@
|
|||
/* SDSLib, A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __SDS_H
|
||||
#define __SDS_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef char *sds;
|
||||
|
||||
struct sdshdr {
|
||||
int len;
|
||||
int free;
|
||||
char buf[];
|
||||
};
|
||||
|
||||
static inline size_t sdslen(const sds s) {
|
||||
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
return sh->len;
|
||||
}
|
||||
|
||||
static inline size_t sdsavail(const sds s) {
|
||||
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
return sh->free;
|
||||
}
|
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen);
|
||||
sds sdsnew(const char *init);
|
||||
sds sdsempty(void);
|
||||
size_t sdslen(const sds s);
|
||||
sds sdsdup(const sds s);
|
||||
void sdsfree(sds s);
|
||||
size_t sdsavail(sds s);
|
||||
sds sdsgrowzero(sds s, size_t len);
|
||||
sds sdscatlen(sds s, const void *t, size_t len);
|
||||
sds sdscat(sds s, const char *t);
|
||||
sds sdscpylen(sds s, char *t, size_t len);
|
||||
sds sdscpy(sds s, char *t);
|
||||
|
||||
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
|
||||
#ifdef __GNUC__
|
||||
sds sdscatprintf(sds s, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
#else
|
||||
sds sdscatprintf(sds s, const char *fmt, ...);
|
||||
#endif
|
||||
|
||||
sds sdstrim(sds s, const char *cset);
|
||||
sds sdsrange(sds s, int start, int end);
|
||||
void sdsupdatelen(sds s);
|
||||
int sdscmp(sds s1, sds s2);
|
||||
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count);
|
||||
void sdsfreesplitres(sds *tokens, int count);
|
||||
void sdstolower(sds s);
|
||||
void sdstoupper(sds s);
|
||||
sds sdsfromlonglong(long long value);
|
||||
sds sdscatrepr(sds s, char *p, size_t len);
|
||||
sds *sdssplitargs(char *line, int *argc);
|
||||
|
||||
#endif
|
|
@ -1,191 +0,0 @@
|
|||
//
|
||||
// DataServer.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 1/20/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
#include <HifiSockAddr.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "DataServer.h"
|
||||
|
||||
const quint16 DATA_SERVER_LISTEN_PORT = 3282;
|
||||
|
||||
const char REDIS_HOSTNAME[] = "127.0.0.1";
|
||||
const unsigned short REDIS_PORT = 6379;
|
||||
|
||||
DataServer::DataServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_socket(),
|
||||
_redis(NULL),
|
||||
_uuid(QUuid::createUuid())
|
||||
{
|
||||
_socket.bind(QHostAddress::Any, DATA_SERVER_LISTEN_PORT);
|
||||
|
||||
connect(&_socket, &QUdpSocket::readyRead, this, &DataServer::readPendingDatagrams);
|
||||
|
||||
_redis = redisConnect(REDIS_HOSTNAME, REDIS_PORT);
|
||||
|
||||
if (_redis && _redis->err) {
|
||||
if (_redis) {
|
||||
qDebug() << "Redis connection error:" << _redis->errstr;
|
||||
} else {
|
||||
qDebug("Redis connection error - can't allocate redis context.");
|
||||
}
|
||||
|
||||
// couldn't connect to redis, bail out
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
DataServer::~DataServer() {
|
||||
// disconnect from redis and free the context
|
||||
if (_redis) {
|
||||
redisFree(_redis);
|
||||
}
|
||||
}
|
||||
|
||||
const int MAX_PACKET_SIZE = 1500;
|
||||
|
||||
void DataServer::readPendingDatagrams() {
|
||||
QByteArray receivedPacket;
|
||||
HifiSockAddr senderSockAddr;
|
||||
|
||||
while (_socket.hasPendingDatagrams()) {
|
||||
receivedPacket.resize(_socket.pendingDatagramSize());
|
||||
_socket.readDatagram(receivedPacket.data(), _socket.pendingDatagramSize(),
|
||||
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
||||
|
||||
PacketType requestType = packetTypeForPacket(receivedPacket);
|
||||
|
||||
if ((requestType == PacketTypeDataServerPut || requestType == PacketTypeDataServerGet) &&
|
||||
receivedPacket[numBytesArithmeticCodingFromBuffer(receivedPacket.data())] == versionForPacketType(requestType)) {
|
||||
|
||||
QDataStream packetStream(receivedPacket);
|
||||
int numReceivedHeaderBytes = numBytesForPacketHeader(receivedPacket);
|
||||
packetStream.skipRawData(numReceivedHeaderBytes);
|
||||
|
||||
// pull the sequence number used for this packet
|
||||
quint8 sequenceNumber = 0;
|
||||
|
||||
packetStream >> sequenceNumber;
|
||||
|
||||
// pull the UUID that we will need as part of the key
|
||||
QString userString;
|
||||
packetStream >> userString;
|
||||
QUuid parsedUUID(userString);
|
||||
|
||||
if (parsedUUID.isNull()) {
|
||||
// we failed to parse a UUID, this means the user has sent us a username
|
||||
|
||||
// ask redis for the UUID for this user
|
||||
redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(userString));
|
||||
|
||||
if (reply->type == REDIS_REPLY_STRING) {
|
||||
parsedUUID = QUuid(QString(reply->str));
|
||||
}
|
||||
|
||||
if (!parsedUUID.isNull()) {
|
||||
qDebug() << "Found UUID" << parsedUUID << "for username" << userString;
|
||||
} else {
|
||||
qDebug() << "Failed UUID lookup for username" << userString;
|
||||
}
|
||||
|
||||
freeReplyObject(reply);
|
||||
reply = NULL;
|
||||
}
|
||||
|
||||
if (!parsedUUID.isNull()) {
|
||||
if (requestType == PacketTypeDataServerPut) {
|
||||
|
||||
// pull the key and value that specifies the data the user is putting/getting
|
||||
QString dataKey, dataValue;
|
||||
|
||||
packetStream >> dataKey >> dataValue;
|
||||
|
||||
qDebug("Sending command to redis: SET uuid:%s:%s %s",
|
||||
qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)),
|
||||
qPrintable(dataKey), qPrintable(dataValue));
|
||||
|
||||
redisReply* reply = (redisReply*) redisCommand(_redis, "SET uuid:%s:%s %s",
|
||||
qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)),
|
||||
qPrintable(dataKey), qPrintable(dataValue));
|
||||
|
||||
if (reply->type == REDIS_REPLY_STATUS && strcmp("OK", reply->str) == 0) {
|
||||
// if redis stored the value successfully reply back with a confirm
|
||||
// which is a reply packet with the sequence number
|
||||
QByteArray replyPacket = byteArrayWithPopulatedHeader(PacketTypeDataServerConfirm, _uuid);
|
||||
|
||||
replyPacket.append(sequenceNumber);
|
||||
|
||||
_socket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
}
|
||||
|
||||
freeReplyObject(reply);
|
||||
} else {
|
||||
// setup a send packet with the returned data
|
||||
// leverage the packetData sent by overwriting and appending
|
||||
QByteArray sendPacket = byteArrayWithPopulatedHeader(PacketTypeDataServerSend, _uuid);
|
||||
QDataStream sendPacketStream(&sendPacket, QIODevice::Append);
|
||||
|
||||
sendPacketStream << sequenceNumber;
|
||||
|
||||
// pull the key list that specifies the data the user is putting/getting
|
||||
QString keyListString;
|
||||
packetStream >> keyListString;
|
||||
|
||||
if (keyListString != "uuid") {
|
||||
|
||||
// copy the parsed UUID
|
||||
sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID);
|
||||
|
||||
const char MULTI_KEY_VALUE_SEPARATOR = '|';
|
||||
|
||||
// append the keyListString back to the sendPacket
|
||||
sendPacketStream << keyListString;
|
||||
|
||||
QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR);
|
||||
QStringList valueList;
|
||||
|
||||
foreach (const QString& dataKey, keyList) {
|
||||
qDebug("Sending command to redis: GET uuid:%s:%s",
|
||||
qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)),
|
||||
qPrintable(dataKey));
|
||||
redisReply* reply = (redisReply*) redisCommand(_redis, "GET uuid:%s:%s",
|
||||
qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)),
|
||||
qPrintable(dataKey));
|
||||
|
||||
if (reply->len) {
|
||||
// copy the value that redis returned
|
||||
valueList << QString(reply->str);
|
||||
} else {
|
||||
// didn't find a value - insert a space
|
||||
valueList << QChar(' ');
|
||||
}
|
||||
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
// append the value QStringList using the right separator
|
||||
sendPacketStream << valueList.join(MULTI_KEY_VALUE_SEPARATOR);
|
||||
} else {
|
||||
// user was asking for their UUID
|
||||
sendPacketStream << userString;
|
||||
sendPacketStream << QString("uuid");
|
||||
sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID);
|
||||
}
|
||||
|
||||
// reply back with the send packet
|
||||
_socket.writeDatagram(sendPacket, senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
//
|
||||
// DataServer.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 1/20/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__DataServer__
|
||||
#define __hifi__DataServer__
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include <hiredis.h>
|
||||
|
||||
class DataServer : public QCoreApplication {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataServer(int argc, char* argv[]);
|
||||
~DataServer();
|
||||
private:
|
||||
QUdpSocket _socket;
|
||||
redisContext* _redis;
|
||||
QUuid _uuid;
|
||||
private slots:
|
||||
void readPendingDatagrams();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__DataServer__) */
|
|
@ -1,28 +0,0 @@
|
|||
//
|
||||
// main.cpp
|
||||
// data-server
|
||||
//
|
||||
// Created by Stephen Birarda on 1/1/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <Logging.h>
|
||||
|
||||
#include "DataServer.h"
|
||||
|
||||
const char DATA_SERVER_LOGGING_TARGET_NAME[] = "data-server";
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
|
||||
qInstallMessageHandler(Logging::verboseMessageHandler);
|
||||
|
||||
Logging::setTargetName(DATA_SERVER_LOGGING_TARGET_NAME);
|
||||
|
||||
DataServer dataServer(argc, argv);
|
||||
|
||||
return dataServer.exec();
|
||||
}
|
|
@ -12,10 +12,11 @@
|
|||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <QtCore/QProcess>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <HTTPConnection.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -25,12 +26,6 @@
|
|||
|
||||
#include "DomainServer.h"
|
||||
|
||||
const int RESTART_HOLD_TIME_MSECS = 5 * 1000;
|
||||
|
||||
const char* VOXEL_SERVER_CONFIG = "voxelServerConfig";
|
||||
const char* PARTICLE_SERVER_CONFIG = "particleServerConfig";
|
||||
const char* METAVOXEL_SERVER_CONFIG = "metavoxelServerConfig";
|
||||
|
||||
const quint16 DOMAIN_SERVER_HTTP_PORT = 8080;
|
||||
|
||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
|
@ -38,39 +33,131 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_HTTPManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||
_staticAssignmentHash(),
|
||||
_assignmentQueue(),
|
||||
_hasCompletedRestartHold(false)
|
||||
_nodeAuthenticationURL(),
|
||||
_redeemedTokenResponses()
|
||||
{
|
||||
const char CUSTOM_PORT_OPTION[] = "-p";
|
||||
const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION);
|
||||
unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT;
|
||||
setOrganizationName("High Fidelity");
|
||||
setOrganizationDomain("highfidelity.io");
|
||||
setApplicationName("domain-server");
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
|
||||
QStringList argumentList = arguments();
|
||||
_argumentList = arguments();
|
||||
int argumentIndex = 0;
|
||||
|
||||
// check if this domain server should use no authentication or a custom hostname for authentication
|
||||
const QString DEFAULT_AUTH_OPTION = "--defaultAuth";
|
||||
const QString CUSTOM_AUTH_OPTION = "--customAuth";
|
||||
if ((argumentIndex = _argumentList.indexOf(DEFAULT_AUTH_OPTION) != -1)) {
|
||||
_nodeAuthenticationURL = QUrl(DEFAULT_NODE_AUTH_URL);
|
||||
} else if ((argumentIndex = _argumentList.indexOf(CUSTOM_AUTH_OPTION)) != -1) {
|
||||
_nodeAuthenticationURL = QUrl(_argumentList.value(argumentIndex + 1));
|
||||
}
|
||||
|
||||
if (!_nodeAuthenticationURL.isEmpty()) {
|
||||
const QString DATA_SERVER_USERNAME_ENV = "HIFI_DS_USERNAME";
|
||||
const QString DATA_SERVER_PASSWORD_ENV = "HIFI_DS_PASSWORD";
|
||||
|
||||
// this node will be using an authentication server, let's make sure we have a username/password
|
||||
QProcessEnvironment sysEnvironment = QProcessEnvironment::systemEnvironment();
|
||||
|
||||
QString username = sysEnvironment.value(DATA_SERVER_USERNAME_ENV);
|
||||
QString password = sysEnvironment.value(DATA_SERVER_PASSWORD_ENV);
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
accountManager.setRootURL(_nodeAuthenticationURL);
|
||||
|
||||
if (!username.isEmpty() && !password.isEmpty()) {
|
||||
|
||||
connect(&accountManager, &AccountManager::loginComplete, this, &DomainServer::requestCreationFromDataServer);
|
||||
|
||||
// ask the account manager to log us in from the env variables
|
||||
accountManager.requestAccessToken(username, password);
|
||||
} else {
|
||||
qDebug() << "Authentication was requested against" << qPrintable(_nodeAuthenticationURL.toString())
|
||||
<< "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV)
|
||||
<< "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Qutting!";
|
||||
|
||||
// bail out
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
// auth is not requested for domain-server, setup NodeList and assignments now
|
||||
setupNodeListAndAssignments();
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::requestCreationFromDataServer() {
|
||||
// this slot is fired when we get a valid access token from the data-server
|
||||
// now let's ask it to set us up with a UUID
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.jsonCallbackReceiver = this;
|
||||
callbackParams.jsonCallbackMethod = "processCreateResponseFromDataServer";
|
||||
|
||||
AccountManager::getInstance().authenticatedRequest("/api/v1/domains/create",
|
||||
QNetworkAccessManager::PostOperation,
|
||||
callbackParams);
|
||||
}
|
||||
|
||||
void DomainServer::processCreateResponseFromDataServer(const QJsonObject& jsonObject) {
|
||||
if (jsonObject["status"].toString() == "success") {
|
||||
// pull out the UUID the data-server is telling us to use, and complete our setup with it
|
||||
QUuid newSessionUUID = QUuid(jsonObject["data"].toObject()["uuid"].toString());
|
||||
setupNodeListAndAssignments(newSessionUUID);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processTokenRedeemResponse(const QJsonObject& jsonObject) {
|
||||
// pull out the registration token this is associated with
|
||||
QString registrationToken = jsonObject["data"].toObject()["registration_token"].toString();
|
||||
|
||||
// if we have a registration token add it to our hash of redeemed token responses
|
||||
if (!registrationToken.isEmpty()) {
|
||||
qDebug() << "Redeemed registration token" << registrationToken;
|
||||
_redeemedTokenResponses.insert(registrationToken, jsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
||||
|
||||
int argumentIndex = 0;
|
||||
|
||||
const QString CUSTOM_PORT_OPTION = "-p";
|
||||
unsigned short domainServerPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||
|
||||
if ((argumentIndex = _argumentList.indexOf(CUSTOM_PORT_OPTION)) != -1) {
|
||||
domainServerPort = _argumentList.value(argumentIndex + 1).toUShort();
|
||||
}
|
||||
|
||||
QSet<Assignment::Type> parsedTypes(QSet<Assignment::Type>() << Assignment::AgentType);
|
||||
parseCommandLineTypeConfigs(argumentList, parsedTypes);
|
||||
parseCommandLineTypeConfigs(_argumentList, parsedTypes);
|
||||
|
||||
const QString CONFIG_FILE_OPTION = "--configFile";
|
||||
if ((argumentIndex = argumentList.indexOf(CONFIG_FILE_OPTION)) != -1) {
|
||||
QString configFilePath = argumentList.value(argumentIndex + 1);
|
||||
if ((argumentIndex = _argumentList.indexOf(CONFIG_FILE_OPTION)) != -1) {
|
||||
QString configFilePath = _argumentList.value(argumentIndex + 1);
|
||||
readConfigFile(configFilePath, parsedTypes);
|
||||
}
|
||||
|
||||
populateDefaultStaticAssignmentsExcludingTypes(parsedTypes);
|
||||
|
||||
|
||||
NodeList* nodeList = NodeList::createInstance(NodeType::DomainServer, domainServerPort);
|
||||
|
||||
// create a random UUID for this session for the domain-server
|
||||
nodeList->setSessionUUID(sessionUUID);
|
||||
|
||||
connect(nodeList, &NodeList::nodeAdded, this, &DomainServer::nodeAdded);
|
||||
connect(nodeList, &NodeList::nodeKilled, this, &DomainServer::nodeKilled);
|
||||
|
||||
|
||||
QTimer* silentNodeTimer = new QTimer(this);
|
||||
connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes()));
|
||||
silentNodeTimer->start(NODE_SILENCE_THRESHOLD_USECS / 1000);
|
||||
|
||||
|
||||
connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams()));
|
||||
|
||||
// fire a single shot timer to add static assignments back into the queue after a restart
|
||||
QTimer::singleShot(RESTART_HOLD_TIME_MSECS, this, SLOT(addStaticAssignmentsBackToQueueAfterRestart()));
|
||||
|
||||
// add whatever static assignments that have been parsed to the queue
|
||||
addStaticAssignmentsToQueue();
|
||||
}
|
||||
|
||||
void DomainServer::parseCommandLineTypeConfigs(const QStringList& argumentList, QSet<Assignment::Type>& excludedTypes) {
|
||||
|
@ -212,24 +299,177 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::requestAuthenticationFromPotentialNode(const HifiSockAddr& senderSockAddr) {
|
||||
// this is a node we do not recognize and we need authentication - ask them to do so
|
||||
// by providing them the hostname they should authenticate with
|
||||
QByteArray authenticationRequestPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerAuthRequest);
|
||||
|
||||
QDataStream authPacketStream(&authenticationRequestPacket, QIODevice::Append);
|
||||
authPacketStream << _nodeAuthenticationURL;
|
||||
|
||||
qDebug() << "Asking node at" << senderSockAddr << "to authenticate.";
|
||||
|
||||
// send the authentication request back to the node
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram(authenticationRequestPacket,
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
}
|
||||
|
||||
const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
||||
<< NodeType::AvatarMixer << NodeType::VoxelServer << NodeType::ParticleServer
|
||||
<< NodeType::MetavoxelServer;
|
||||
|
||||
|
||||
void DomainServer::addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr,
|
||||
const QJsonObject& authJsonObject) {
|
||||
|
||||
NodeType_t nodeType;
|
||||
HifiSockAddr publicSockAddr, localSockAddr;
|
||||
|
||||
int numPreInterestBytes = parseNodeDataFromByteArray(nodeType, publicSockAddr, localSockAddr, packet, senderSockAddr);
|
||||
|
||||
QUuid assignmentUUID = uuidFromPacketHeader(packet);
|
||||
SharedAssignmentPointer matchingAssignment;
|
||||
|
||||
if (!assignmentUUID.isNull() && (matchingAssignment = matchingStaticAssignmentForCheckIn(assignmentUUID, nodeType))
|
||||
&& matchingAssignment) {
|
||||
// this is an assigned node, make sure the UUID sent is for an assignment we're actually trying to give out
|
||||
|
||||
// remove the matching assignment from the assignment queue so we don't take the next check in
|
||||
// (if it exists)
|
||||
removeMatchingAssignmentFromQueue(matchingAssignment);
|
||||
} else {
|
||||
assignmentUUID = QUuid();
|
||||
}
|
||||
|
||||
// create a new session UUID for this node
|
||||
QUuid nodeUUID = QUuid::createUuid();
|
||||
|
||||
SharedNodePointer newNode = NodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType, publicSockAddr, localSockAddr);
|
||||
|
||||
// when the newNode is created the linked data is also created, if this was a static assignment set the UUID
|
||||
reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData())->setStaticAssignmentUUID(assignmentUUID);
|
||||
|
||||
if (!authJsonObject.isEmpty()) {
|
||||
// pull the connection secret from the authJsonObject and set it as the connection secret for this node
|
||||
QUuid connectionSecret(authJsonObject["data"].toObject()["connection_secret"].toString());
|
||||
newNode->setConnectionSecret(connectionSecret);
|
||||
}
|
||||
|
||||
// reply back to the user with a PacketTypeDomainList
|
||||
sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes));
|
||||
}
|
||||
|
||||
const int NUM_BYTES_DATA_SERVER_REGISTRATION_TOKEN = 16;
|
||||
|
||||
int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
||||
HifiSockAddr& localSockAddr, const QByteArray& packet,
|
||||
const HifiSockAddr& senderSockAddr) {
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
if (packetTypeForPacket(packet) == PacketTypeDomainConnectRequest) {
|
||||
// we need to skip a quint8 that indicates if there is a registration token
|
||||
// and potentially the registration token itself
|
||||
quint8 hasRegistrationToken;
|
||||
packetStream >> hasRegistrationToken;
|
||||
|
||||
if (hasRegistrationToken) {
|
||||
QByteArray registrationToken;
|
||||
packetStream >> registrationToken;
|
||||
}
|
||||
}
|
||||
|
||||
packetStream >> nodeType;
|
||||
packetStream >> publicSockAddr >> localSockAddr;
|
||||
|
||||
if (publicSockAddr.getAddress().isNull()) {
|
||||
// this node wants to use us its STUN server
|
||||
// so set the node public address to whatever we perceive the public address to be
|
||||
|
||||
// if the sender is on our box then leave its public address to 0 so that
|
||||
// other users attempt to reach it on the same address they have for the domain-server
|
||||
if (senderSockAddr.getAddress().isLoopback()) {
|
||||
publicSockAddr.setAddress(QHostAddress());
|
||||
} else {
|
||||
publicSockAddr.setAddress(senderSockAddr.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
return packetStream.device()->pos();
|
||||
}
|
||||
|
||||
NodeSet DomainServer::nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes) {
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numPreceedingBytes);
|
||||
|
||||
quint8 numInterestTypes = 0;
|
||||
packetStream >> numInterestTypes;
|
||||
|
||||
quint8 nodeType;
|
||||
NodeSet nodeInterestSet;
|
||||
|
||||
for (int i = 0; i < numInterestTypes; i++) {
|
||||
packetStream >> nodeType;
|
||||
nodeInterestSet.insert((NodeType_t) nodeType);
|
||||
}
|
||||
|
||||
return nodeInterestSet;
|
||||
}
|
||||
|
||||
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr,
|
||||
const NodeSet& nodeInterestList) {
|
||||
|
||||
QByteArray broadcastPacket = byteArrayWithPopulatedHeader(PacketTypeDomainList);
|
||||
|
||||
// always send the node their own UUID back
|
||||
QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append);
|
||||
broadcastDataStream << node->getUUID();
|
||||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
if (nodeInterestList.size() > 0) {
|
||||
// if the node has any interest types, send back those nodes as well
|
||||
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
|
||||
if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) {
|
||||
|
||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||
broadcastDataStream << *otherNode.data();
|
||||
|
||||
// pack the secret that these two nodes will use to communicate with each other
|
||||
QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID());
|
||||
if (secretUUID.isNull()) {
|
||||
// generate a new secret UUID these two nodes can use
|
||||
secretUUID = QUuid::createUuid();
|
||||
|
||||
// set that on the current Node's sessionSecretHash
|
||||
nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID);
|
||||
|
||||
// set it on the other Node's sessionSecretHash
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())
|
||||
->getSessionSecretHash().insert(node->getUUID(), secretUUID);
|
||||
|
||||
}
|
||||
|
||||
broadcastDataStream << secretUUID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodeList->writeDatagram(broadcastPacket, node, senderSockAddr);
|
||||
}
|
||||
|
||||
void DomainServer::readAvailableDatagrams() {
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
HifiSockAddr senderSockAddr, nodePublicAddress, nodeLocalAddress;
|
||||
|
||||
static QByteArray broadcastPacket = byteArrayWithPopulatedHeader(PacketTypeDomainList);
|
||||
static int numBroadcastPacketHeaderBytes = broadcastPacket.size();
|
||||
HifiSockAddr senderSockAddr;
|
||||
|
||||
static QByteArray assignmentPacket = byteArrayWithPopulatedHeader(PacketTypeCreateAssignment);
|
||||
static int numAssignmentPacketHeaderBytes = assignmentPacket.size();
|
||||
|
||||
QByteArray receivedPacket;
|
||||
NodeType_t nodeType;
|
||||
|
||||
|
||||
while (nodeList->getNodeSocket().hasPendingDatagrams()) {
|
||||
receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
|
||||
|
@ -238,111 +478,69 @@ void DomainServer::readAvailableDatagrams() {
|
|||
|
||||
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
|
||||
PacketType requestType = packetTypeForPacket(receivedPacket);
|
||||
if (requestType == PacketTypeDomainListRequest) {
|
||||
|
||||
// this is an RFD or domain list request packet, and there is a version match
|
||||
|
||||
if (requestType == PacketTypeDomainConnectRequest) {
|
||||
QDataStream packetStream(receivedPacket);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(receivedPacket));
|
||||
|
||||
QUuid nodeUUID = uuidFromPacketHeader(receivedPacket);
|
||||
quint8 hasRegistrationToken;
|
||||
packetStream >> hasRegistrationToken;
|
||||
|
||||
packetStream >> nodeType;
|
||||
packetStream >> nodePublicAddress >> nodeLocalAddress;
|
||||
|
||||
if (nodePublicAddress.getAddress().isNull()) {
|
||||
// this node wants to use us its STUN server
|
||||
// so set the node public address to whatever we perceive the public address to be
|
||||
if (requiresAuthentication() && !hasRegistrationToken) {
|
||||
// we need authentication and this node did not give us a registration token - tell it to auth
|
||||
requestAuthenticationFromPotentialNode(senderSockAddr);
|
||||
} else if (requiresAuthentication()) {
|
||||
QByteArray registrationToken;
|
||||
packetStream >> registrationToken;
|
||||
|
||||
// if the sender is on our box then leave its public address to 0 so that
|
||||
// other users attempt to reach it on the same address they have for the domain-server
|
||||
if (senderSockAddr.getAddress().isLoopback()) {
|
||||
nodePublicAddress.setAddress(QHostAddress());
|
||||
} else {
|
||||
nodePublicAddress.setAddress(senderSockAddr.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
SharedAssignmentPointer matchingStaticAssignment;
|
||||
|
||||
// check if this is a non-statically assigned node, a node that is assigned and checking in for the first time
|
||||
// or a node that has already checked in and is continuing to report for duty
|
||||
if (!STATICALLY_ASSIGNED_NODES.contains(nodeType)
|
||||
|| (matchingStaticAssignment = matchingStaticAssignmentForCheckIn(nodeUUID, nodeType))
|
||||
|| nodeList->getInstance()->nodeWithUUID(nodeUUID)) {
|
||||
QString registrationTokenString(registrationToken.toHex());
|
||||
QJsonObject jsonForRedeemedToken = _redeemedTokenResponses.value(registrationTokenString);
|
||||
|
||||
if (nodeUUID.isNull()) {
|
||||
// this is a check in from an unidentified node
|
||||
// we need to generate a session UUID for this node
|
||||
nodeUUID = QUuid::createUuid();
|
||||
}
|
||||
|
||||
SharedNodePointer checkInNode = nodeList->addOrUpdateNode(nodeUUID,
|
||||
nodeType,
|
||||
nodePublicAddress,
|
||||
nodeLocalAddress);
|
||||
|
||||
// resize our broadcast packet in preparation to set it up again
|
||||
broadcastPacket.resize(numBroadcastPacketHeaderBytes);
|
||||
|
||||
if (matchingStaticAssignment) {
|
||||
// this was a newly added node with a matching static assignment
|
||||
// check if we have redeemed this token and are ready to check the node in
|
||||
if (jsonForRedeemedToken.isEmpty()) {
|
||||
// make a request against the data-server to get information required to connect to this node
|
||||
JSONCallbackParameters tokenCallbackParams;
|
||||
tokenCallbackParams.jsonCallbackReceiver = this;
|
||||
tokenCallbackParams.jsonCallbackMethod = "processTokenRedeemResponse";
|
||||
|
||||
// remove the matching assignment from the assignment queue so we don't take the next check in
|
||||
// (if it exists)
|
||||
if (_hasCompletedRestartHold) {
|
||||
removeMatchingAssignmentFromQueue(matchingStaticAssignment);
|
||||
}
|
||||
QString redeemURLString = QString("/api/v1/nodes/redeem/%1.json").arg(registrationTokenString);
|
||||
accountManager.authenticatedRequest(redeemURLString, QNetworkAccessManager::GetOperation,
|
||||
tokenCallbackParams);
|
||||
} else if (jsonForRedeemedToken["status"].toString() != "success") {
|
||||
// we redeemed the token, but it was invalid - get the node to get another
|
||||
requestAuthenticationFromPotentialNode(senderSockAddr);
|
||||
} else {
|
||||
// we've redeemed the token for this node and are ready to start communicating with it
|
||||
// add the node to our NodeList
|
||||
addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr, jsonForRedeemedToken);
|
||||
}
|
||||
|
||||
quint8 numInterestTypes = 0;
|
||||
packetStream >> numInterestTypes;
|
||||
// if it exists, remove this response from the in-memory hash
|
||||
_redeemedTokenResponses.remove(registrationTokenString);
|
||||
|
||||
NodeType_t* nodeTypesOfInterest = reinterpret_cast<NodeType_t*>(receivedPacket.data()
|
||||
+ packetStream.device()->pos());
|
||||
|
||||
// always send the node their own UUID back
|
||||
QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append);
|
||||
broadcastDataStream << checkInNode->getUUID();
|
||||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(checkInNode->getLinkedData());
|
||||
|
||||
if (numInterestTypes > 0) {
|
||||
// if the node has any interest types, send back those nodes as well
|
||||
foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
|
||||
if (otherNode->getUUID() != nodeUUID &&
|
||||
memchr(nodeTypesOfInterest, otherNode->getType(), numInterestTypes)) {
|
||||
|
||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||
broadcastDataStream << *otherNode.data();
|
||||
|
||||
// pack the secret that these two nodes will use to communicate with each other
|
||||
QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID());
|
||||
if (secretUUID.isNull()) {
|
||||
// generate a new secret UUID these two nodes can use
|
||||
secretUUID = QUuid::createUuid();
|
||||
|
||||
// set that on the current Node's sessionSecretHash
|
||||
nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID);
|
||||
|
||||
// set it on the other Node's sessionSecretHash
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())
|
||||
->getSessionSecretHash().insert(nodeUUID, secretUUID);
|
||||
|
||||
}
|
||||
|
||||
broadcastDataStream << secretUUID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update last receive to now
|
||||
quint64 timeNow = usecTimestampNow();
|
||||
checkInNode->setLastHeardMicrostamp(timeNow);
|
||||
|
||||
// send the constructed list back to this node
|
||||
nodeList->getNodeSocket().writeDatagram(broadcastPacket,
|
||||
senderSockAddr.getAddress(), senderSockAddr.getPort());
|
||||
} else {
|
||||
// we don't require authentication - add this node to our NodeList
|
||||
// and send back session UUID right away
|
||||
addNodeToNodeListAndConfirmConnection(receivedPacket, senderSockAddr);
|
||||
}
|
||||
|
||||
} else if (requestType == PacketTypeDomainListRequest) {
|
||||
QUuid nodeUUID = uuidFromPacketHeader(receivedPacket);
|
||||
NodeType_t throwawayNodeType;
|
||||
HifiSockAddr nodePublicAddress, nodeLocalAddress;
|
||||
|
||||
int numNodeInfoBytes = parseNodeDataFromByteArray(throwawayNodeType, nodePublicAddress, nodeLocalAddress,
|
||||
receivedPacket, senderSockAddr);
|
||||
|
||||
SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID, nodePublicAddress, nodeLocalAddress);
|
||||
|
||||
// update last receive to now
|
||||
quint64 timeNow = usecTimestampNow();
|
||||
checkInNode->setLastHeardMicrostamp(timeNow);
|
||||
|
||||
|
||||
sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestListFromPacket(receivedPacket, numNodeInfoBytes));
|
||||
|
||||
} else if (requestType == PacketTypeRequestAssignment) {
|
||||
|
||||
// construct the requested assignment from the packet data
|
||||
|
@ -588,43 +786,39 @@ void DomainServer::nodeAdded(SharedNodePointer node) {
|
|||
}
|
||||
|
||||
void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||
// if this node's UUID matches a static assignment we need to throw it back in the assignment queue
|
||||
SharedAssignmentPointer matchedAssignment = _staticAssignmentHash.value(node->getUUID());
|
||||
|
||||
if (matchedAssignment) {
|
||||
refreshStaticAssignmentAndAddToQueue(matchedAssignment);
|
||||
}
|
||||
|
||||
// cleanup the connection secrets that we set up for this node (on the other nodes)
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
foreach (const QUuid& otherNodeSessionUUID, nodeData->getSessionSecretHash().keys()) {
|
||||
SharedNodePointer otherNode = NodeList::getInstance()->nodeWithUUID(otherNodeSessionUUID);
|
||||
if (otherNode) {
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())->getSessionSecretHash().remove(node->getUUID());
|
||||
if (nodeData) {
|
||||
// if this node's UUID matches a static assignment we need to throw it back in the assignment queue
|
||||
if (!nodeData->getStaticAssignmentUUID().isNull()) {
|
||||
SharedAssignmentPointer matchedAssignment = _staticAssignmentHash.value(nodeData->getStaticAssignmentUUID());
|
||||
|
||||
if (matchedAssignment) {
|
||||
refreshStaticAssignmentAndAddToQueue(matchedAssignment);
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup the connection secrets that we set up for this node (on the other nodes)
|
||||
foreach (const QUuid& otherNodeSessionUUID, nodeData->getSessionSecretHash().keys()) {
|
||||
SharedNodePointer otherNode = NodeList::getInstance()->nodeWithUUID(otherNodeSessionUUID);
|
||||
if (otherNode) {
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())->getSessionSecretHash().remove(node->getUUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SharedAssignmentPointer DomainServer::matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) {
|
||||
if (_hasCompletedRestartHold) {
|
||||
// look for a match in the assignment hash
|
||||
|
||||
QQueue<SharedAssignmentPointer>::iterator i = _assignmentQueue.begin();
|
||||
|
||||
while (i != _assignmentQueue.end()) {
|
||||
if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) {
|
||||
return _assignmentQueue.takeAt(i - _assignmentQueue.begin());
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SharedAssignmentPointer matchingStaticAssignment = _staticAssignmentHash.value(checkInUUID);
|
||||
if (matchingStaticAssignment && matchingStaticAssignment->getType() == nodeType) {
|
||||
return matchingStaticAssignment;
|
||||
QQueue<SharedAssignmentPointer>::iterator i = _assignmentQueue.begin();
|
||||
|
||||
while (i != _assignmentQueue.end()) {
|
||||
if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) {
|
||||
return _assignmentQueue.takeAt(i - _assignmentQueue.begin());
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return SharedAssignmentPointer();
|
||||
}
|
||||
|
||||
|
@ -680,8 +874,7 @@ void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPoint
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::addStaticAssignmentsBackToQueueAfterRestart() {
|
||||
_hasCompletedRestartHold = true;
|
||||
void DomainServer::addStaticAssignmentsToQueue() {
|
||||
|
||||
// if the domain-server has just restarted,
|
||||
// check if there are static assignments that we need to throw into the assignment queue
|
||||
|
|
|
@ -11,8 +11,11 @@
|
|||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QQueue>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <Assignment.h>
|
||||
#include <HTTPManager.h>
|
||||
|
@ -25,6 +28,8 @@ class DomainServer : public QCoreApplication, public HTTPRequestHandler {
|
|||
public:
|
||||
DomainServer(int argc, char* argv[]);
|
||||
|
||||
bool requiresAuthentication() const { return !_nodeAuthenticationURL.isEmpty(); }
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
|
||||
|
||||
void exit(int retCode = 0);
|
||||
|
@ -36,6 +41,17 @@ public slots:
|
|||
void nodeKilled(SharedNodePointer node);
|
||||
|
||||
private:
|
||||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||
|
||||
void requestAuthenticationFromPotentialNode(const HifiSockAddr& senderSockAddr);
|
||||
void addNodeToNodeListAndConfirmConnection(const QByteArray& packet, const HifiSockAddr& senderSockAddr,
|
||||
const QJsonObject& authJsonObject = QJsonObject());
|
||||
int parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr,
|
||||
HifiSockAddr& localSockAddr, const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||
NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes);
|
||||
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr,
|
||||
const NodeSet& nodeInterestList);
|
||||
|
||||
void parseCommandLineTypeConfigs(const QStringList& argumentList, QSet<Assignment::Type>& excludedTypes);
|
||||
void readConfigFile(const QString& path, QSet<Assignment::Type>& excludedTypes);
|
||||
QString readServerAssignmentConfig(const QJsonObject& jsonObject, const QString& nodeName);
|
||||
|
@ -47,6 +63,7 @@ private:
|
|||
SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment);
|
||||
void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment);
|
||||
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
|
||||
void addStaticAssignmentsToQueue();
|
||||
|
||||
QJsonObject jsonForSocket(const HifiSockAddr& socket);
|
||||
QJsonObject jsonObjectForNode(const SharedNodePointer& node);
|
||||
|
@ -56,10 +73,17 @@ private:
|
|||
QHash<QUuid, SharedAssignmentPointer> _staticAssignmentHash;
|
||||
QQueue<SharedAssignmentPointer> _assignmentQueue;
|
||||
|
||||
bool _hasCompletedRestartHold;
|
||||
QUrl _nodeAuthenticationURL;
|
||||
|
||||
QStringList _argumentList;
|
||||
|
||||
QHash<QString, QJsonObject> _redeemedTokenResponses;
|
||||
private slots:
|
||||
void requestCreationFromDataServer();
|
||||
void processCreateResponseFromDataServer(const QJsonObject& jsonObject);
|
||||
void processTokenRedeemResponse(const QJsonObject& jsonObject);
|
||||
|
||||
void readAvailableDatagrams();
|
||||
void addStaticAssignmentsBackToQueueAfterRestart();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__DomainServer__) */
|
||||
|
|
16
domain-server/src/DomainServerNodeData.cpp
Normal file
16
domain-server/src/DomainServerNodeData.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// DomainServerNodeData.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/6/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include "DomainServerNodeData.h"
|
||||
|
||||
DomainServerNodeData::DomainServerNodeData() :
|
||||
_sessionSecretHash(),
|
||||
_staticAssignmentUUID()
|
||||
{
|
||||
|
||||
}
|
|
@ -10,17 +10,22 @@
|
|||
#define __hifi__DomainServerNodeData__
|
||||
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <NodeData.h>
|
||||
|
||||
class DomainServerNodeData : public NodeData {
|
||||
public:
|
||||
DomainServerNodeData() : _sessionSecretHash() {};
|
||||
DomainServerNodeData();
|
||||
int parseData(const QByteArray& packet) { return 0; }
|
||||
|
||||
void setStaticAssignmentUUID(const QUuid& staticAssignmentUUID) { _staticAssignmentUUID = staticAssignmentUUID; }
|
||||
const QUuid& getStaticAssignmentUUID() const { return _staticAssignmentUUID; }
|
||||
|
||||
QHash<QUuid, QUuid>& getSessionSecretHash() { return _sessionSecretHash; }
|
||||
private:
|
||||
QHash<QUuid, QUuid> _sessionSecretHash;
|
||||
QUuid _staticAssignmentUUID;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__DomainServerNodeData__) */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[INFO]
|
||||
name=Interface
|
||||
organizationName=High Fidelity
|
||||
organizationDomain=highfidelity.io
|
||||
organizationDomain=highfidelity.io
|
|
@ -51,11 +51,11 @@
|
|||
#include <QMimeData>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <AudioInjector.h>
|
||||
#include <Logging.h>
|
||||
#include <OctalCode.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <PairingHandler.h>
|
||||
#include <ParticlesScriptingInterface.h>
|
||||
#include <PerfStat.h>
|
||||
#include <UUID.h>
|
||||
|
@ -63,7 +63,6 @@
|
|||
|
||||
#include "Application.h"
|
||||
#include "ClipboardScriptingInterface.h"
|
||||
#include "DataServerClient.h"
|
||||
#include "InterfaceVersion.h"
|
||||
#include "Menu.h"
|
||||
#include "Swatch.h"
|
||||
|
@ -129,7 +128,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_wantToKillLocalVoxels(false),
|
||||
_audioScope(256, 200, true),
|
||||
_myAvatar(),
|
||||
_profile(QString()),
|
||||
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
|
||||
_mouseX(0),
|
||||
_mouseY(0),
|
||||
|
@ -161,11 +159,27 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
_pasteMode(false),
|
||||
_logger(new FileLogger(this))
|
||||
{
|
||||
switchToResourcesParentIfRequired();
|
||||
|
||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||
QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||
|
||||
// set the associated application properties
|
||||
applicationInfo.beginGroup("INFO");
|
||||
|
||||
qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion());
|
||||
|
||||
setApplicationName(applicationInfo.value("name").toString());
|
||||
setApplicationVersion(BUILD_VERSION);
|
||||
setOrganizationName(applicationInfo.value("organizationName").toString());
|
||||
setOrganizationDomain(applicationInfo.value("organizationDomain").toString());
|
||||
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
|
||||
_myAvatar = _avatarManager.getMyAvatar();
|
||||
|
||||
_applicationStartupTime = startup_time;
|
||||
|
||||
switchToResourcesParentIfRequired();
|
||||
|
||||
QFontDatabase::addApplicationFont("resources/styles/Inconsolata.otf");
|
||||
_window->setWindowTitle("Interface");
|
||||
|
||||
|
@ -203,27 +217,21 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
connect(audioThread, SIGNAL(started()), &_audio, SLOT(start()));
|
||||
|
||||
audioThread->start();
|
||||
|
||||
connect(nodeList, SIGNAL(domainChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||
|
||||
connect(&nodeList->getDomainInfo(), SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
|
||||
connect(&nodeList->getDomainInfo(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&)));
|
||||
|
||||
connect(nodeList, &NodeList::nodeAdded, this, &Application::nodeAdded);
|
||||
connect(nodeList, &NodeList::nodeKilled, this, &Application::nodeKilled);
|
||||
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
|
||||
connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer)));
|
||||
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer)));
|
||||
connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle);
|
||||
|
||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||
QSettings applicationInfo("resources/info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||
|
||||
// set the associated application properties
|
||||
applicationInfo.beginGroup("INFO");
|
||||
|
||||
setApplicationName(applicationInfo.value("name").toString());
|
||||
setApplicationVersion(BUILD_VERSION);
|
||||
setOrganizationName(applicationInfo.value("organizationName").toString());
|
||||
setOrganizationDomain(applicationInfo.value("organizationDomain").toString());
|
||||
|
||||
qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion());
|
||||
|
||||
// connect to appropriate slots on AccountManager
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain);
|
||||
connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
|
||||
|
||||
_settings = new QSettings(this);
|
||||
|
||||
|
@ -601,13 +609,6 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum)
|
|||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
void Application::resetProfile(const QString& username) {
|
||||
// call the destructor on the old profile and construct a new one
|
||||
(&_profile)->~Profile();
|
||||
new (&_profile) Profile(username);
|
||||
updateWindowTitle();
|
||||
}
|
||||
|
||||
void Application::controlledBroadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) {
|
||||
foreach(NodeType_t type, destinationNodeTypes) {
|
||||
// Intercept data to voxel server when voxels are disabled
|
||||
|
@ -1427,12 +1428,7 @@ void Application::timer() {
|
|||
// ask the node list to check in with the domain server
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
|
||||
// send unmatched DataServerClient packets
|
||||
DataServerClient::resendUnmatchedPackets();
|
||||
|
||||
// give the MyAvatar object position, orientation to the Profile so it can propagate to the data-server
|
||||
_profile.updatePosition(_myAvatar->getPosition());
|
||||
_profile.updateOrientation(_myAvatar->getOrientation());
|
||||
|
||||
}
|
||||
|
||||
static glm::vec3 getFaceVector(BoxFace face) {
|
||||
|
@ -2942,9 +2938,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
}
|
||||
}
|
||||
|
||||
// render transmitter pick ray, if non-empty
|
||||
_myAvatar->renderTransmitterPickRay();
|
||||
|
||||
// give external parties a change to hook in
|
||||
emit renderingInWorldInterface();
|
||||
|
||||
|
@ -3010,8 +3003,6 @@ void Application::displayOverlay() {
|
|||
_myAvatar->renderHeadMouse();
|
||||
}
|
||||
|
||||
_myAvatar->renderTransmitterLevels(_glWidget->width(), _glWidget->height());
|
||||
|
||||
// Display stats and log text onscreen
|
||||
glLineWidth(1.0f);
|
||||
glPointSize(1.0f);
|
||||
|
@ -3990,17 +3981,15 @@ void Application::updateWindowTitle(){
|
|||
QString buildVersion = " (build " + applicationVersion() + ")";
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
QString title = QString() + _profile.getUsername() + " " + nodeList->getSessionUUID().toString()
|
||||
+ " @ " + nodeList->getDomainHostname() + buildVersion;
|
||||
QString username = AccountManager::getInstance().getUsername();
|
||||
QString title = QString() + (!username.isEmpty() ? username + " " : QString()) + nodeList->getSessionUUID().toString()
|
||||
+ " @ " + nodeList->getDomainInfo().getHostname() + buildVersion;
|
||||
|
||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||
_window->setWindowTitle(title);
|
||||
}
|
||||
|
||||
void Application::domainChanged(const QString& domainHostname) {
|
||||
// update the user's last domain in their Profile (which will propagate to data-server)
|
||||
_profile.updateDomain(domainHostname);
|
||||
|
||||
updateWindowTitle();
|
||||
|
||||
// reset the environment so that we don't erroneously end up with multiple
|
||||
|
@ -4015,6 +4004,19 @@ void Application::domainChanged(const QString& domainHostname) {
|
|||
_particles.clear();
|
||||
}
|
||||
|
||||
void Application::connectedToDomain(const QString& hostname) {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
if (accountManager.isLoggedIn()) {
|
||||
// update our domain-server with the data-server we're logged in with
|
||||
|
||||
QString domainPutJsonString = "{\"location\":{\"domain\":\"" + hostname + "\"}}";
|
||||
|
||||
accountManager.authenticatedRequest("/api/v1/users/location", QNetworkAccessManager::PutOperation,
|
||||
JSONCallbackParameters(), domainPutJsonString.toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
void Application::nodeAdded(SharedNodePointer node) {
|
||||
if (node->getType() == NodeType::AvatarMixer) {
|
||||
// new avatar mixer, send off our identity packet right away
|
||||
|
@ -4402,6 +4404,5 @@ void Application::takeSnapshot() {
|
|||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||
player->play();
|
||||
|
||||
Snapshot::saveSnapshot(_glWidget, &_profile, _myAvatar);
|
||||
Snapshot::saveSnapshot(_glWidget, _myAvatar);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
#include "avatar/Avatar.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "avatar/Profile.h"
|
||||
#include "devices/Faceshift.h"
|
||||
#include "devices/SixenseManager.h"
|
||||
#include "devices/Visage.h"
|
||||
|
@ -183,7 +182,6 @@ public:
|
|||
ControllerScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; }
|
||||
|
||||
AvatarManager& getAvatarManager() { return _avatarManager; }
|
||||
Profile* getProfile() { return &_profile; }
|
||||
void resetProfile(const QString& username);
|
||||
|
||||
void controlledBroadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes);
|
||||
|
@ -267,6 +265,8 @@ public slots:
|
|||
private slots:
|
||||
void timer();
|
||||
void idle();
|
||||
|
||||
void connectedToDomain(const QString& hostname);
|
||||
|
||||
void setFullscreen(bool fullscreen);
|
||||
void setEnable3DTVMode(bool enable3DTVMode);
|
||||
|
@ -407,7 +407,6 @@ private:
|
|||
|
||||
AvatarManager _avatarManager;
|
||||
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
|
||||
Profile _profile; // The data-server linked profile for this user
|
||||
|
||||
Faceshift _faceshift;
|
||||
Visage _visage;
|
||||
|
|
|
@ -43,12 +43,6 @@ void DatagramProcessor::processDatagrams() {
|
|||
if (nodeList->packetVersionAndHashMatch(incomingPacket)) {
|
||||
// only process this packet if we have a match on the packet version
|
||||
switch (packetTypeForPacket(incomingPacket)) {
|
||||
case PacketTypeTransmitterData:
|
||||
// V2 = IOS transmitter app
|
||||
application->getAvatar()->getTransmitter().processIncomingData(reinterpret_cast<unsigned char*>(incomingPacket.data()),
|
||||
incomingPacket.size());
|
||||
|
||||
break;
|
||||
case PacketTypeMixedAudio:
|
||||
QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection,
|
||||
Q_ARG(QByteArray, incomingPacket));
|
||||
|
@ -115,12 +109,6 @@ void DatagramProcessor::processDatagrams() {
|
|||
application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size());
|
||||
break;
|
||||
}
|
||||
case PacketTypeDataServerGet:
|
||||
case PacketTypeDataServerPut:
|
||||
case PacketTypeDataServerSend:
|
||||
case PacketTypeDataServerConfirm:
|
||||
DataServerClient::processMessageFromDataServer(incomingPacket);
|
||||
break;
|
||||
default:
|
||||
nodeList->processNodeData(senderSockAddr, incomingPacket);
|
||||
break;
|
||||
|
|
|
@ -24,11 +24,10 @@
|
|||
#include <QUuid>
|
||||
#include <QWindow>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "DataServerClient.h"
|
||||
#include "PairingHandler.h"
|
||||
#include "Menu.h"
|
||||
#include "Util.h"
|
||||
#include "InfoView.h"
|
||||
|
@ -71,7 +70,8 @@ Menu::Menu() :
|
|||
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
||||
_boundaryLevelAdjust(0),
|
||||
_maxVoxelPacketsPerSecond(DEFAULT_MAX_VOXEL_PPS),
|
||||
_lastAdjust(usecTimestampNow())
|
||||
_lastAdjust(usecTimestampNow()),
|
||||
_loginAction(NULL)
|
||||
{
|
||||
Application *appInstance = Application::getInstance();
|
||||
|
||||
|
@ -86,11 +86,17 @@ Menu::Menu() :
|
|||
QAction::AboutRole);
|
||||
#endif
|
||||
|
||||
(addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Login,
|
||||
0,
|
||||
this,
|
||||
SLOT(login())));
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
_loginAction = addActionToQMenuAndActionHash(fileMenu, MenuOption::Logout);
|
||||
|
||||
// call our toggle login function now so the menu option is setup properly
|
||||
toggleLoginMenuItem();
|
||||
|
||||
// connect to the appropriate slots of the AccountManager so that we can change the Login/Logout menu item
|
||||
connect(&accountManager, &AccountManager::loginComplete, this, &Menu::toggleLoginMenuItem);
|
||||
connect(&accountManager, &AccountManager::logoutComplete, this, &Menu::toggleLoginMenuItem);
|
||||
connect(&accountManager, &AccountManager::authEndpointChanged, this, &Menu::toggleLoginMenuItem);
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Scripts");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, appInstance, SLOT(loadDialog()));
|
||||
|
@ -129,10 +135,6 @@ Menu::Menu() :
|
|||
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings()));
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Devices");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Pair, 0, PairingHandler::getInstance(), SLOT(sendPairRequest()));
|
||||
addCheckableActionToQMenuAndActionHash(fileMenu, MenuOption::TransmitterDrive, 0, true);
|
||||
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Quit,
|
||||
Qt::CTRL | Qt::Key_Q,
|
||||
|
@ -528,7 +530,6 @@ void Menu::loadSettings(QSettings* settings) {
|
|||
scanMenuBar(&loadAction, settings);
|
||||
Application::getInstance()->getAvatar()->loadData(settings);
|
||||
Application::getInstance()->getSwatch()->loadData(settings);
|
||||
Application::getInstance()->getProfile()->loadData(settings);
|
||||
Application::getInstance()->updateWindowTitle();
|
||||
NodeList::getInstance()->loadData(settings);
|
||||
|
||||
|
@ -561,7 +562,6 @@ void Menu::saveSettings(QSettings* settings) {
|
|||
scanMenuBar(&saveAction, settings);
|
||||
Application::getInstance()->getAvatar()->saveData(settings);
|
||||
Application::getInstance()->getSwatch()->saveData(settings);
|
||||
Application::getInstance()->getProfile()->saveData(settings);
|
||||
NodeList::getInstance()->saveData(settings);
|
||||
}
|
||||
|
||||
|
@ -757,24 +757,38 @@ void sendFakeEnterEvent() {
|
|||
const int QLINE_MINIMUM_WIDTH = 400;
|
||||
const float DIALOG_RATIO_OF_WINDOW = 0.30f;
|
||||
|
||||
void Menu::login() {
|
||||
QInputDialog loginDialog(Application::getInstance()->getWindow());
|
||||
void Menu::loginForCurrentDomain() {
|
||||
QDialog loginDialog(Application::getInstance()->getWindow());
|
||||
loginDialog.setWindowTitle("Login");
|
||||
loginDialog.setLabelText("Username:");
|
||||
QString username = Application::getInstance()->getProfile()->getUsername();
|
||||
loginDialog.setTextValue(username);
|
||||
|
||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
loginDialog.setLayout(layout);
|
||||
loginDialog.setWindowFlags(Qt::Sheet);
|
||||
loginDialog.resize(loginDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, loginDialog.size().height());
|
||||
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
QLineEdit* loginLineEdit = new QLineEdit();
|
||||
loginLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
form->addRow("Login:", loginLineEdit);
|
||||
|
||||
QLineEdit* passwordLineEdit = new QLineEdit();
|
||||
passwordLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
|
||||
passwordLineEdit->setEchoMode(QLineEdit::Password);
|
||||
form->addRow("Password:", passwordLineEdit);
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
loginDialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||
loginDialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
|
||||
layout->addWidget(buttons);
|
||||
|
||||
int dialogReturn = loginDialog.exec();
|
||||
|
||||
if (dialogReturn == QDialog::Accepted && !loginDialog.textValue().isEmpty() && loginDialog.textValue() != username) {
|
||||
// there has been a username change
|
||||
// ask for a profile reset with the new username
|
||||
Application::getInstance()->resetProfile(loginDialog.textValue());
|
||||
|
||||
|
||||
if (dialogReturn == QDialog::Accepted && !loginLineEdit->text().isEmpty() && !passwordLineEdit->text().isEmpty()) {
|
||||
// attempt to get an access token given this username and password
|
||||
AccountManager::getInstance().requestAccessToken(loginLineEdit->text(), passwordLineEdit->text());
|
||||
}
|
||||
|
||||
|
||||
sendFakeEnterEvent();
|
||||
}
|
||||
|
||||
|
@ -783,9 +797,10 @@ void Menu::editPreferences() {
|
|||
|
||||
QDialog dialog(applicationInstance->getWindow());
|
||||
dialog.setWindowTitle("Interface Preferences");
|
||||
|
||||
QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||
dialog.setLayout(layout);
|
||||
|
||||
|
||||
QFormLayout* form = new QFormLayout();
|
||||
layout->addLayout(form, 1);
|
||||
|
||||
|
@ -916,23 +931,23 @@ void Menu::editPreferences() {
|
|||
}
|
||||
|
||||
void Menu::goToDomain(const QString newDomain) {
|
||||
if (NodeList::getInstance()->getDomainHostname() != newDomain) {
|
||||
if (NodeList::getInstance()->getDomainInfo().getHostname() != newDomain) {
|
||||
|
||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||
Application::getInstance()->getAvatar()->sendKillAvatar();
|
||||
|
||||
// give our nodeList the new domain-server hostname
|
||||
NodeList::getInstance()->setDomainHostname(newDomain);
|
||||
NodeList::getInstance()->getDomainInfo().setHostname(newDomain);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::goToDomainDialog() {
|
||||
|
||||
QString currentDomainHostname = NodeList::getInstance()->getDomainHostname();
|
||||
QString currentDomainHostname = NodeList::getInstance()->getDomainInfo().getHostname();
|
||||
|
||||
if (NodeList::getInstance()->getDomainPort() != DEFAULT_DOMAIN_SERVER_PORT) {
|
||||
if (NodeList::getInstance()->getDomainInfo().getPort() != DEFAULT_DOMAIN_SERVER_PORT) {
|
||||
// add the port to the currentDomainHostname string if it is custom
|
||||
currentDomainHostname.append(QString(":%1").arg(NodeList::getInstance()->getDomainPort()));
|
||||
currentDomainHostname.append(QString(":%1").arg(NodeList::getInstance()->getDomainInfo().getPort()));
|
||||
}
|
||||
|
||||
QInputDialog domainDialog(Application::getInstance()->getWindow());
|
||||
|
@ -1026,7 +1041,7 @@ void Menu::goTo() {
|
|||
QInputDialog gotoDialog(Application::getInstance()->getWindow());
|
||||
gotoDialog.setWindowTitle("Go to");
|
||||
gotoDialog.setLabelText("Destination:");
|
||||
QString destination = Application::getInstance()->getProfile()->getUsername();
|
||||
QString destination = QString();
|
||||
gotoDialog.setTextValue(destination);
|
||||
gotoDialog.setWindowFlags(Qt::Sheet);
|
||||
gotoDialog.resize(gotoDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, gotoDialog.size().height());
|
||||
|
@ -1038,14 +1053,14 @@ void Menu::goTo() {
|
|||
|
||||
// go to coordinate destination or to Username
|
||||
if (!goToDestination(destination)) {
|
||||
// there's a username entered by the user, make a request to the data-server
|
||||
DataServerClient::getValuesForKeysAndUserString(
|
||||
QStringList()
|
||||
<< DataServerKey::Domain
|
||||
<< DataServerKey::Position
|
||||
<< DataServerKey::Orientation,
|
||||
destination, Application::getInstance()->getProfile());
|
||||
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.jsonCallbackReceiver = Application::getInstance()->getAvatar();
|
||||
callbackParams.jsonCallbackMethod = "goToLocationFromResponse";
|
||||
|
||||
// there's a username entered by the user, make a request to the data-server for the associated location
|
||||
AccountManager::getInstance().authenticatedRequest("/api/v1/users/" + gotoDialog.textValue() + "/location",
|
||||
QNetworkAccessManager::GetOperation,
|
||||
callbackParams);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1101,6 +1116,32 @@ void Menu::pasteToVoxel() {
|
|||
sendFakeEnterEvent();
|
||||
}
|
||||
|
||||
void Menu::toggleLoginMenuItem() {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
disconnect(_loginAction, 0, 0, 0);
|
||||
|
||||
if (accountManager.isLoggedIn()) {
|
||||
// change the menu item to logout
|
||||
_loginAction->setText("Logout " + accountManager.getUsername());
|
||||
connect(_loginAction, &QAction::triggered, &accountManager, &AccountManager::logout);
|
||||
|
||||
_loginAction->setEnabled(true);
|
||||
} else {
|
||||
// change the menu item to login
|
||||
_loginAction->setText("Login");
|
||||
|
||||
// if we don't have a rootURL in the AccountManager we're in a domain that doesn't use auth
|
||||
// so setup the menu item according to the presence of that root URL
|
||||
if (accountManager.hasAuthEndpoint()) {
|
||||
connect(_loginAction, &QAction::triggered, this, &Menu::loginForCurrentDomain);
|
||||
_loginAction->setEnabled(true);
|
||||
} else {
|
||||
_loginAction->setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::bandwidthDetails() {
|
||||
if (! _bandwidthDialog) {
|
||||
_bandwidthDialog = new BandwidthDialog(Application::getInstance()->getGLWidget(),
|
||||
|
|
|
@ -76,7 +76,6 @@ public:
|
|||
int getMaxVoxels() const { return _maxVoxels; }
|
||||
QAction* getUseVoxelShader() const { return _useVoxelShader; }
|
||||
|
||||
|
||||
void handleViewFrustumOffsetKeyModifier(int key);
|
||||
|
||||
// User Tweakable LOD Items
|
||||
|
@ -102,6 +101,7 @@ public:
|
|||
void goToDomain(const QString newDomain);
|
||||
|
||||
public slots:
|
||||
void loginForCurrentDomain();
|
||||
void bandwidthDetails();
|
||||
void voxelStatsDetails();
|
||||
void lodTools();
|
||||
|
@ -111,10 +111,11 @@ public slots:
|
|||
void exportSettings();
|
||||
void goTo();
|
||||
void pasteToVoxel();
|
||||
|
||||
void toggleLoginMenuItem();
|
||||
|
||||
private slots:
|
||||
void aboutApp();
|
||||
void login();
|
||||
void editPreferences();
|
||||
void goToDomainDialog();
|
||||
void goToLocation();
|
||||
|
@ -173,6 +174,7 @@ private:
|
|||
QMenu* _activeScriptsMenu;
|
||||
QString replaceLastOccurrence(QChar search, QChar replace, QString string);
|
||||
quint64 _lastAdjust;
|
||||
QAction* _loginAction;
|
||||
};
|
||||
|
||||
namespace MenuOption {
|
||||
|
@ -247,6 +249,7 @@ namespace MenuOption {
|
|||
const QString LodTools = "LOD Tools";
|
||||
const QString Log = "Log";
|
||||
const QString Login = "Login";
|
||||
const QString Logout = "Logout";
|
||||
const QString LookAtVectors = "Look-at Vectors";
|
||||
const QString MetavoxelEditor = "Metavoxel Editor...";
|
||||
const QString Metavoxels = "Metavoxels";
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
//
|
||||
// PairingHandler.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 5/13/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h> // not available on windows
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include <HifiSockAddr.h>
|
||||
#include <NodeList.h>
|
||||
|
||||
#include "PairingHandler.h"
|
||||
|
||||
const char PAIRING_SERVER_HOSTNAME[] = "pairing.highfidelity.io";
|
||||
const int PAIRING_SERVER_PORT = 7247;
|
||||
|
||||
PairingHandler* PairingHandler::getInstance() {
|
||||
static PairingHandler* instance = NULL;
|
||||
|
||||
if (!instance) {
|
||||
instance = new PairingHandler();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void PairingHandler::sendPairRequest() {
|
||||
|
||||
// prepare the pairing request packet
|
||||
|
||||
NodeList* nodeList = NodeList::getInstance();
|
||||
|
||||
// use the getLocalAddress helper to get this client's listening address
|
||||
quint32 localAddress = htonl(getHostOrderLocalAddress());
|
||||
|
||||
char pairPacket[24] = {};
|
||||
sprintf(pairPacket, "Find %d.%d.%d.%d:%hu",
|
||||
localAddress & 0xFF,
|
||||
(localAddress >> 8) & 0xFF,
|
||||
(localAddress >> 16) & 0xFF,
|
||||
(localAddress >> 24) & 0xFF,
|
||||
NodeList::getInstance()->getNodeSocket().localPort());
|
||||
qDebug("Sending pair packet: %s", pairPacket);
|
||||
|
||||
HifiSockAddr pairingServerSocket(PAIRING_SERVER_HOSTNAME, PAIRING_SERVER_PORT);
|
||||
|
||||
// send the pair request to the pairing server
|
||||
nodeList->getNodeSocket().writeDatagram((char*) pairPacket, strlen(pairPacket),
|
||||
pairingServerSocket.getAddress(), pairingServerSocket.getPort());
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
//
|
||||
// PairingHandler.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 5/13/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__PairingHandler__
|
||||
#define __hifi__PairingHandler__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
class PairingHandler : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static PairingHandler* getInstance();
|
||||
public slots:
|
||||
void sendPairRequest();
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__PairingHandler__) */
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
#include "DataServerClient.h"
|
||||
#include "Hand.h"
|
||||
#include "Head.h"
|
||||
#include "Menu.h"
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <QtCore/QSharedPointer>
|
||||
|
||||
#include <AvatarHashMap.h>
|
||||
#include <DataServerClient.h>
|
||||
|
||||
#include "Avatar.h"
|
||||
|
||||
|
|
|
@ -13,13 +13,15 @@
|
|||
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <NodeList.h>
|
||||
#include <PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Audio.h"
|
||||
#include "DataServerClient.h"
|
||||
#include "Environment.h"
|
||||
#include "Menu.h"
|
||||
#include "MyAvatar.h"
|
||||
|
@ -42,6 +44,8 @@ const bool USING_HEAD_LEAN = false;
|
|||
const float SKIN_COLOR[] = {1.0f, 0.84f, 0.66f};
|
||||
const float DARK_SKIN_COLOR[] = {0.9f, 0.78f, 0.63f};
|
||||
|
||||
const float DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
|
||||
|
||||
MyAvatar::MyAvatar() :
|
||||
Avatar(),
|
||||
_mousePressed(false),
|
||||
|
@ -66,6 +70,11 @@ MyAvatar::MyAvatar() :
|
|||
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
||||
_driveKeys[i] = 0.0f;
|
||||
}
|
||||
|
||||
// update our location every 5 seconds in the data-server, assuming that we are authenticated with one
|
||||
QTimer* locationUpdateTimer = new QTimer(this);
|
||||
connect(locationUpdateTimer, &QTimer::timeout, this, &MyAvatar::updateLocationInDataServer);
|
||||
locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);
|
||||
}
|
||||
|
||||
MyAvatar::~MyAvatar() {
|
||||
|
@ -81,7 +90,6 @@ void MyAvatar::reset() {
|
|||
|
||||
setVelocity(glm::vec3(0,0,0));
|
||||
setThrust(glm::vec3(0,0,0));
|
||||
_transmitter.resetLevels();
|
||||
}
|
||||
|
||||
void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) {
|
||||
|
@ -89,32 +97,7 @@ void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) {
|
|||
_moveTargetStepCounter = 0;
|
||||
}
|
||||
|
||||
void MyAvatar::updateTransmitter(float deltaTime) {
|
||||
// no transmitter drive implies transmitter pick
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _transmitter.isConnected()) {
|
||||
_transmitterPickStart = getChestPosition();
|
||||
glm::vec3 direction = getOrientation() * glm::quat(glm::radians(_transmitter.getEstimatedRotation())) * IDENTITY_FRONT;
|
||||
|
||||
// check against voxels, avatars
|
||||
const float MAX_PICK_DISTANCE = 100.0f;
|
||||
float minDistance = MAX_PICK_DISTANCE;
|
||||
VoxelDetail detail;
|
||||
float distance;
|
||||
BoxFace face;
|
||||
VoxelSystem* voxels = Application::getInstance()->getVoxels();
|
||||
if (voxels->findRayIntersection(_transmitterPickStart, direction, detail, distance, face)) {
|
||||
minDistance = min(minDistance, distance);
|
||||
}
|
||||
_transmitterPickEnd = _transmitterPickStart + direction * minDistance;
|
||||
|
||||
} else {
|
||||
_transmitterPickStart = _transmitterPickEnd = glm::vec3();
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::update(float deltaTime) {
|
||||
updateTransmitter(deltaTime);
|
||||
|
||||
updateFromGyros(deltaTime);
|
||||
|
||||
// Update head mouse from faceshift if active
|
||||
|
@ -570,35 +553,6 @@ void MyAvatar::renderHeadMouse() const {
|
|||
*/
|
||||
}
|
||||
|
||||
void MyAvatar::renderTransmitterPickRay() const {
|
||||
if (_transmitterPickStart != _transmitterPickEnd) {
|
||||
Glower glower;
|
||||
const float TRANSMITTER_PICK_COLOR[] = { 1.0f, 1.0f, 0.0f };
|
||||
glColor3fv(TRANSMITTER_PICK_COLOR);
|
||||
glLineWidth(3.0f);
|
||||
glBegin(GL_LINES);
|
||||
glVertex3f(_transmitterPickStart.x, _transmitterPickStart.y, _transmitterPickStart.z);
|
||||
glVertex3f(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z);
|
||||
glEnd();
|
||||
glLineWidth(1.0f);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(_transmitterPickEnd.x, _transmitterPickEnd.y, _transmitterPickEnd.z);
|
||||
|
||||
const float PICK_END_RADIUS = 0.025f;
|
||||
glutSolidSphere(PICK_END_RADIUS, 8, 8);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::renderTransmitterLevels(int width, int height) const {
|
||||
// Show hand transmitter data if detected
|
||||
if (_transmitter.isConnected()) {
|
||||
_transmitter.renderLevels(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::saveData(QSettings* settings) {
|
||||
settings->beginGroup("Avatar");
|
||||
|
||||
|
@ -780,36 +734,6 @@ void MyAvatar::updateThrust(float deltaTime) {
|
|||
_shouldJump = false;
|
||||
}
|
||||
|
||||
// Add thrusts from Transmitter
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _transmitter.isConnected()) {
|
||||
_transmitter.checkForLostTransmitter();
|
||||
glm::vec3 rotation = _transmitter.getEstimatedRotation();
|
||||
const float TRANSMITTER_MIN_RATE = 1.f;
|
||||
const float TRANSMITTER_MIN_YAW_RATE = 4.f;
|
||||
const float TRANSMITTER_LATERAL_FORCE_SCALE = 5.f;
|
||||
const float TRANSMITTER_FWD_FORCE_SCALE = 25.f;
|
||||
const float TRANSMITTER_UP_FORCE_SCALE = 100.f;
|
||||
const float TRANSMITTER_YAW_SCALE = 10.0f;
|
||||
const float TRANSMITTER_LIFT_SCALE = 3.f;
|
||||
const float TOUCH_POSITION_RANGE_HALF = 32767.f;
|
||||
if (fabs(rotation.z) > TRANSMITTER_MIN_RATE) {
|
||||
_thrust += rotation.z * TRANSMITTER_LATERAL_FORCE_SCALE * deltaTime * right;
|
||||
}
|
||||
if (fabs(rotation.x) > TRANSMITTER_MIN_RATE) {
|
||||
_thrust += -rotation.x * TRANSMITTER_FWD_FORCE_SCALE * deltaTime * front;
|
||||
}
|
||||
if (fabs(rotation.y) > TRANSMITTER_MIN_YAW_RATE) {
|
||||
_bodyYawDelta += rotation.y * TRANSMITTER_YAW_SCALE * deltaTime;
|
||||
}
|
||||
if (_transmitter.getTouchState()->state == 'D') {
|
||||
_thrust += TRANSMITTER_UP_FORCE_SCALE *
|
||||
(float)(_transmitter.getTouchState()->y - TOUCH_POSITION_RANGE_HALF) / TOUCH_POSITION_RANGE_HALF *
|
||||
TRANSMITTER_LIFT_SCALE *
|
||||
deltaTime *
|
||||
up;
|
||||
}
|
||||
}
|
||||
|
||||
// Update speed brake status
|
||||
const float MIN_SPEED_BRAKE_VELOCITY = _scale * 0.4f;
|
||||
if ((glm::length(_thrust) == 0.0f) && _isThrustOn && (glm::length(_velocity) > MIN_SPEED_BRAKE_VELOCITY)) {
|
||||
|
@ -1197,3 +1121,62 @@ void MyAvatar::resetSize() {
|
|||
qDebug("Reseted scale to %f", _targetScale);
|
||||
}
|
||||
|
||||
static QByteArray createByteArray(const glm::vec3& vector) {
|
||||
return QByteArray::number(vector.x) + ',' + QByteArray::number(vector.y) + ',' + QByteArray::number(vector.z);
|
||||
}
|
||||
|
||||
void MyAvatar::updateLocationInDataServer() {
|
||||
// TODO: don't re-send this when it hasn't change or doesn't change by some threshold
|
||||
// This will required storing the last sent values and clearing them when the AccountManager rootURL changes
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
if (accountManager.isLoggedIn()) {
|
||||
QString positionString(createByteArray(_position));
|
||||
QString orientationString(createByteArray(safeEulerAngles(getOrientation())));
|
||||
|
||||
// construct the json to put the user's location
|
||||
QString locationPutJson = QString() + "{\"location\":{\"position\":\""
|
||||
+ positionString + "\", \"orientation\":\"" + orientationString + "\"}}";
|
||||
|
||||
accountManager.authenticatedRequest("/api/v1/users/location", QNetworkAccessManager::PutOperation,
|
||||
JSONCallbackParameters(), locationPutJson.toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) {
|
||||
|
||||
if (jsonObject["status"].toString() == "success") {
|
||||
|
||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||
sendKillAvatar();
|
||||
|
||||
QJsonObject locationObject = jsonObject["data"].toObject()["location"].toObject();
|
||||
QString positionString = locationObject["position"].toString();
|
||||
QString orientationString = locationObject["orientation"].toString();
|
||||
QString domainHostnameString = locationObject["domain"].toString();
|
||||
|
||||
qDebug() << "Changing domain to" << domainHostnameString <<
|
||||
", position to" << positionString <<
|
||||
", and orientation to" << orientationString;
|
||||
|
||||
QStringList coordinateItems = positionString.split(',');
|
||||
QStringList orientationItems = orientationString.split(',');
|
||||
|
||||
NodeList::getInstance()->getDomainInfo().setHostname(domainHostnameString);
|
||||
|
||||
// orient the user to face the target
|
||||
glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(),
|
||||
orientationItems[1].toFloat(),
|
||||
orientationItems[2].toFloat())))
|
||||
* glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f);
|
||||
setOrientation(newOrientation);
|
||||
|
||||
// move the user a couple units away
|
||||
const float DISTANCE_TO_USER = 2.0f;
|
||||
glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(),
|
||||
coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
|
||||
setPosition(newPosition);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include <QSettings>
|
||||
|
||||
#include <devices/Transmitter.h>
|
||||
|
||||
#include "Avatar.h"
|
||||
|
||||
enum AvatarHandState
|
||||
|
@ -36,13 +34,10 @@ public:
|
|||
void update(float deltaTime);
|
||||
void simulate(float deltaTime);
|
||||
void updateFromGyros(float deltaTime);
|
||||
void updateTransmitter(float deltaTime);
|
||||
|
||||
void render(bool forceRenderHead);
|
||||
void renderDebugBodyPoints();
|
||||
void renderHeadMouse() const;
|
||||
void renderTransmitterPickRay() const;
|
||||
void renderTransmitterLevels(int width, int height) const;
|
||||
|
||||
// setters
|
||||
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
|
||||
|
@ -63,7 +58,6 @@ public:
|
|||
float getAbsoluteHeadYaw() const;
|
||||
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
|
||||
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
|
||||
Transmitter& getTransmitter() { return _transmitter; }
|
||||
glm::vec3 getGravity() const { return _gravity; }
|
||||
glm::vec3 getUprightHeadPosition() const;
|
||||
bool getShouldRenderLocally() const { return _shouldRender; }
|
||||
|
@ -81,21 +75,22 @@ public:
|
|||
|
||||
static void sendKillAvatar();
|
||||
|
||||
|
||||
void orbit(const glm::vec3& position, int deltaX, int deltaY);
|
||||
|
||||
AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); }
|
||||
void updateLookAtTargetAvatar();
|
||||
void clearLookAtTargetAvatar();
|
||||
|
||||
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL);
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
|
||||
public slots:
|
||||
void goHome();
|
||||
void increaseSize();
|
||||
void decreaseSize();
|
||||
void resetSize();
|
||||
|
||||
void updateLocationInDataServer();
|
||||
void goToLocationFromResponse(const QJsonObject& jsonObject);
|
||||
|
||||
// Set/Get update the thrust that will move the avatar around
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
|
@ -122,10 +117,6 @@ private:
|
|||
QWeakPointer<AvatarData> _lookAtTargetAvatar;
|
||||
bool _shouldRender;
|
||||
|
||||
Transmitter _transmitter; // Gets UDP data from transmitter app used to animate the avatar
|
||||
glm::vec3 _transmitterPickStart;
|
||||
glm::vec3 _transmitterPickEnd;
|
||||
|
||||
bool _billboardValid;
|
||||
|
||||
// private methods
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
//
|
||||
// Profile.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 10/8/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QSettings>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Profile.h"
|
||||
#include "Util.h"
|
||||
|
||||
Profile::Profile(const QString &username) :
|
||||
_username(),
|
||||
_uuid(),
|
||||
_lastDomain(),
|
||||
_lastPosition(0.0, 0.0, 0.0),
|
||||
_lastOrientationSend(0)
|
||||
{
|
||||
if (!username.isEmpty()) {
|
||||
setUsername(username);
|
||||
|
||||
// we've been given a new username, ask the data-server for profile
|
||||
DataServerClient::getValueForKeyAndUserString(DataServerKey::UUID, getUserString(), this);
|
||||
|
||||
// send our current domain server to the data-server
|
||||
updateDomain(NodeList::getInstance()->getDomainHostname());
|
||||
}
|
||||
}
|
||||
|
||||
QString Profile::getUserString() const {
|
||||
if (_uuid.isNull()) {
|
||||
return _username;
|
||||
} else {
|
||||
return uuidStringWithoutCurlyBraces(_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
void Profile::updateDomain(const QString& domain) {
|
||||
if (_lastDomain != domain) {
|
||||
_lastDomain = domain;
|
||||
|
||||
// send the changed domain to the data-server
|
||||
DataServerClient::putValueForKeyAndUserString(DataServerKey::Domain, domain, getUserString());
|
||||
}
|
||||
}
|
||||
|
||||
static QByteArray createByteArray(const glm::vec3& vector) {
|
||||
return QByteArray::number(vector.x) + ',' + QByteArray::number(vector.y) + ',' + QByteArray::number(vector.z);
|
||||
}
|
||||
|
||||
void Profile::updatePosition(const glm::vec3 position) {
|
||||
if (_lastPosition != position) {
|
||||
|
||||
static timeval lastPositionSend = {};
|
||||
const quint64 DATA_SERVER_POSITION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000;
|
||||
const float DATA_SERVER_POSITION_CHANGE_THRESHOLD_METERS = 1;
|
||||
|
||||
if (usecTimestampNow() - usecTimestamp(&lastPositionSend) >= DATA_SERVER_POSITION_UPDATE_INTERVAL_USECS &&
|
||||
(fabsf(_lastPosition.x - position.x) >= DATA_SERVER_POSITION_CHANGE_THRESHOLD_METERS ||
|
||||
fabsf(_lastPosition.y - position.y) >= DATA_SERVER_POSITION_CHANGE_THRESHOLD_METERS ||
|
||||
fabsf(_lastPosition.z - position.z) >= DATA_SERVER_POSITION_CHANGE_THRESHOLD_METERS)) {
|
||||
|
||||
// if it has been 5 seconds since the last position change and the user has moved >= the threshold
|
||||
// in at least one of the axis then send the position update to the data-server
|
||||
|
||||
_lastPosition = position;
|
||||
|
||||
// update the lastPositionSend to now
|
||||
gettimeofday(&lastPositionSend, NULL);
|
||||
|
||||
// send the changed position to the data-server
|
||||
DataServerClient::putValueForKeyAndUserString(DataServerKey::Position,
|
||||
QString(createByteArray(position)), getUserString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Profile::updateOrientation(const glm::quat& orientation) {
|
||||
glm::vec3 eulerAngles = safeEulerAngles(orientation);
|
||||
if (_lastOrientation == eulerAngles) {
|
||||
return;
|
||||
}
|
||||
const quint64 DATA_SERVER_ORIENTATION_UPDATE_INTERVAL_USECS = 5 * 1000 * 1000;
|
||||
const float DATA_SERVER_ORIENTATION_CHANGE_THRESHOLD_DEGREES = 5.0f;
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
if (now - _lastOrientationSend >= DATA_SERVER_ORIENTATION_UPDATE_INTERVAL_USECS &&
|
||||
glm::distance(_lastOrientation, eulerAngles) >= DATA_SERVER_ORIENTATION_CHANGE_THRESHOLD_DEGREES) {
|
||||
DataServerClient::putValueForKeyAndUserString(DataServerKey::Orientation, QString(createByteArray(eulerAngles)),
|
||||
getUserString());
|
||||
|
||||
_lastOrientation = eulerAngles;
|
||||
_lastOrientationSend = now;
|
||||
}
|
||||
}
|
||||
|
||||
void Profile::saveData(QSettings* settings) {
|
||||
settings->beginGroup("Profile");
|
||||
|
||||
settings->setValue("username", _username);
|
||||
settings->setValue("UUID", _uuid);
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void Profile::loadData(QSettings* settings) {
|
||||
settings->beginGroup("Profile");
|
||||
|
||||
setUsername(settings->value("username").toString());
|
||||
this->setUUID(settings->value("UUID").toUuid());
|
||||
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void Profile::processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList) {
|
||||
for (int i = 0; i < keyList.size(); i++) {
|
||||
if (valueList[i] != " ") {
|
||||
if (keyList[i] == DataServerKey::Domain && keyList[i + 1] == DataServerKey::Position &&
|
||||
keyList[i + 2] == DataServerKey::Orientation && valueList[i] != " " &&
|
||||
valueList[i + 1] != " " && valueList[i + 2] != " ") {
|
||||
|
||||
QStringList coordinateItems = valueList[i + 1].split(',');
|
||||
QStringList orientationItems = valueList[i + 2].split(',');
|
||||
|
||||
if (coordinateItems.size() == 3 && orientationItems.size() == 3) {
|
||||
|
||||
// send a node kill request, indicating to other clients that they should play the "disappeared" effect
|
||||
MyAvatar::sendKillAvatar();
|
||||
|
||||
qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() <<
|
||||
", position to" << valueList[i + 1].toLocal8Bit().constData() <<
|
||||
", and orientation to" << valueList[i + 2].toLocal8Bit().constData() <<
|
||||
"to go to" << userString;
|
||||
|
||||
NodeList::getInstance()->setDomainHostname(valueList[i]);
|
||||
// orient the user to face the target
|
||||
glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(),
|
||||
orientationItems[1].toFloat(),
|
||||
orientationItems[2].toFloat()))) *
|
||||
glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f);
|
||||
Application::getInstance()->getAvatar()->setOrientation(newOrientation);
|
||||
|
||||
// move the user a couple units away
|
||||
const float DISTANCE_TO_USER = 2.0f;
|
||||
glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(),
|
||||
coordinateItems[2].toFloat()
|
||||
) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER;
|
||||
Application::getInstance()->getAvatar()->setPosition(newPosition);
|
||||
}
|
||||
|
||||
} else if (keyList[i] == DataServerKey::UUID) {
|
||||
// this is the user's UUID - set it on the profile
|
||||
setUUID(QUuid(valueList[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Profile::setUsername(const QString& username) {
|
||||
_username = username;
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
//
|
||||
// Profile.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 10/8/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__Profile__
|
||||
#define __hifi__Profile__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include "DataServerClient.h"
|
||||
|
||||
class Profile : public DataServerCallbackObject {
|
||||
public:
|
||||
Profile(const QString& username);
|
||||
|
||||
QString getUserString() const;
|
||||
|
||||
const QString& getUsername() const { return _username; }
|
||||
|
||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||
const QUuid& getUUID() { return _uuid; }
|
||||
|
||||
void updateDomain(const QString& domain);
|
||||
void updatePosition(const glm::vec3 position);
|
||||
void updateOrientation(const glm::quat& orientation);
|
||||
|
||||
QString getLastDomain() const { return _lastDomain; }
|
||||
const glm::vec3& getLastPosition() const { return _lastPosition; }
|
||||
|
||||
void saveData(QSettings* settings);
|
||||
void loadData(QSettings* settings);
|
||||
|
||||
void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList);
|
||||
private:
|
||||
|
||||
void setUsername(const QString& username);
|
||||
|
||||
void setDisplayName(const QString& displaName);
|
||||
|
||||
QString _username;
|
||||
QUuid _uuid;
|
||||
QString _lastDomain;
|
||||
glm::vec3 _lastPosition;
|
||||
glm::vec3 _lastOrientation;
|
||||
quint64 _lastOrientationSend;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Profile__) */
|
|
@ -1,167 +0,0 @@
|
|||
//
|
||||
// Transmitter.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Philip Rosedale on 5/20/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifdef WIN32
|
||||
#define WANT_TIMEVAL
|
||||
#include <Systime.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <PacketHeaders.h>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
#include "Transmitter.h"
|
||||
#include "Util.h"
|
||||
|
||||
const float DELTA_TIME = 1.f / 60.f;
|
||||
const float DECAY_RATE = 0.15f;
|
||||
|
||||
Transmitter::Transmitter() :
|
||||
_isConnected(false),
|
||||
_lastRotationRate(0,0,0),
|
||||
_lastAcceleration(0,0,0),
|
||||
_estimatedRotation(0,0,0),
|
||||
_lastReceivedPacket(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Transmitter::~Transmitter() {
|
||||
if (_lastReceivedPacket) {
|
||||
delete _lastReceivedPacket;
|
||||
}
|
||||
}
|
||||
|
||||
void Transmitter::checkForLostTransmitter() {
|
||||
// If we are in motion, check for loss of transmitter packets
|
||||
if (glm::length(_estimatedRotation) > 0.f) {
|
||||
timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
const int TIME_TO_ASSUME_LOST_MSECS = 2000;
|
||||
int msecsSinceLast = diffclock(_lastReceivedPacket, &now);
|
||||
if (msecsSinceLast > TIME_TO_ASSUME_LOST_MSECS) {
|
||||
resetLevels();
|
||||
qDebug("Transmitter signal lost.");
|
||||
}
|
||||
}
|
||||
}
|
||||
void Transmitter::resetLevels() {
|
||||
_lastRotationRate *= 0.f;
|
||||
_estimatedRotation *= 0.f;
|
||||
}
|
||||
|
||||
void Transmitter::processIncomingData(unsigned char* packetData, int numBytes) {
|
||||
// Packet's first byte is 'T'
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<const char*>(packetData));
|
||||
|
||||
const int ROTATION_MARKER_SIZE = 1; // 'R' = Rotation (clockwise about x,y,z)
|
||||
const int ACCELERATION_MARKER_SIZE = 1; // 'A' = Acceleration (x,y,z)
|
||||
if (!_lastReceivedPacket) {
|
||||
_lastReceivedPacket = new timeval;
|
||||
}
|
||||
gettimeofday(_lastReceivedPacket, NULL);
|
||||
|
||||
if (numBytes == numBytesPacketHeader + ROTATION_MARKER_SIZE + ACCELERATION_MARKER_SIZE
|
||||
+ sizeof(_lastRotationRate) + sizeof(_lastAcceleration)
|
||||
+ sizeof(_touchState.x) + sizeof(_touchState.y) + sizeof(_touchState.state)) {
|
||||
unsigned char* packetDataPosition = packetData + numBytesPacketHeader + ROTATION_MARKER_SIZE;
|
||||
memcpy(&_lastRotationRate, packetDataPosition, sizeof(_lastRotationRate));
|
||||
packetDataPosition += sizeof(_lastRotationRate) + ACCELERATION_MARKER_SIZE;
|
||||
memcpy(&_lastAcceleration, packetDataPosition, sizeof(_lastAcceleration));
|
||||
packetDataPosition += sizeof(_lastAcceleration);
|
||||
memcpy(&_touchState.state, packetDataPosition, sizeof(_touchState.state));
|
||||
packetDataPosition += sizeof(_touchState.state);
|
||||
memcpy(&_touchState.x, packetDataPosition, sizeof(_touchState.x));
|
||||
packetDataPosition += sizeof(_touchState.x);
|
||||
memcpy(&_touchState.y, packetDataPosition, sizeof(_touchState.y));
|
||||
packetDataPosition += sizeof(_touchState.y);
|
||||
|
||||
// Update estimated absolute position from rotation rates
|
||||
_estimatedRotation += _lastRotationRate * DELTA_TIME;
|
||||
|
||||
// Sensor Fusion! Slowly adjust estimated rotation to be relative to gravity (average acceleration)
|
||||
const float GRAVITY_FOLLOW_RATE = 1.f;
|
||||
float rollAngle = angleBetween(glm::vec3(_lastAcceleration.x, _lastAcceleration.y, 0.f), glm::vec3(0,-1,0)) *
|
||||
((_lastAcceleration.x < 0.f) ? -1.f : 1.f);
|
||||
float pitchAngle = angleBetween(glm::vec3(0.f, _lastAcceleration.y, _lastAcceleration.z), glm::vec3(0,-1,0)) *
|
||||
((_lastAcceleration.z < 0.f) ? 1.f : -1.f);
|
||||
|
||||
_estimatedRotation.x = (1.f - GRAVITY_FOLLOW_RATE * DELTA_TIME) * _estimatedRotation.x +
|
||||
GRAVITY_FOLLOW_RATE * DELTA_TIME * pitchAngle;
|
||||
_estimatedRotation.z = (1.f - GRAVITY_FOLLOW_RATE * DELTA_TIME) * _estimatedRotation.z +
|
||||
GRAVITY_FOLLOW_RATE * DELTA_TIME * rollAngle;
|
||||
|
||||
// Can't apply gravity fusion to Yaw, so decay estimated yaw to zero,
|
||||
// presuming that the average yaw direction is toward screen
|
||||
_estimatedRotation.y *= (1.f - DECAY_RATE * DELTA_TIME);
|
||||
|
||||
if (!_isConnected) {
|
||||
qDebug("Transmitter Connected.");
|
||||
_isConnected = true;
|
||||
_estimatedRotation *= 0.0;
|
||||
}
|
||||
} else {
|
||||
qDebug("Transmitter packet read error, %d bytes.", numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void Transmitter::renderLevels(int width, int height) const {
|
||||
char val[50];
|
||||
const int LEVEL_CORNER_X = 10;
|
||||
const int LEVEL_CORNER_Y = 400;
|
||||
|
||||
// Draw the numeric degree/sec values from the gyros
|
||||
sprintf(val, "Pitch Rate %4.1f", _lastRotationRate.x);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y, 0.10f, 0, 1.0f, 1, val, 0, 1, 0);
|
||||
sprintf(val, "Yaw Rate %4.1f", _lastRotationRate.y);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 15, 0.10f, 0, 1.0f, 1, val, 0, 1, 0);
|
||||
sprintf(val, "Roll Rate %4.1f", _lastRotationRate.z);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 30, 0.10f, 0, 1.0f, 1, val, 0, 1, 0);
|
||||
sprintf(val, "Pitch %4.3f", _estimatedRotation.x);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 45, 0.10f, 0, 1.0f, 1, val, 0, 1, 0);
|
||||
sprintf(val, "Yaw %4.3f", _estimatedRotation.y);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 60, 0.10f, 0, 1.0f, 1, val, 0, 1, 0);
|
||||
sprintf(val, "Roll %4.3f", _estimatedRotation.z);
|
||||
drawtext(LEVEL_CORNER_X, LEVEL_CORNER_Y + 75, 0.10f, 0, 1.0f, 1, val, 0, 1, 0);
|
||||
|
||||
// Draw the levels as horizontal lines
|
||||
const int LEVEL_CENTER = 150;
|
||||
const float ACCEL_VIEW_SCALING = 1.f;
|
||||
glLineWidth(2.0);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
glBegin(GL_LINES);
|
||||
// Gyro rates
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y - 3);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + _lastRotationRate.x, LEVEL_CORNER_Y - 3);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 12);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + _lastRotationRate.y, LEVEL_CORNER_Y + 12);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 27);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + _lastRotationRate.z, LEVEL_CORNER_Y + 27);
|
||||
// Acceleration
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 42);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)(_estimatedRotation.x * ACCEL_VIEW_SCALING),
|
||||
LEVEL_CORNER_Y + 42);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 57);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)(_estimatedRotation.y * ACCEL_VIEW_SCALING),
|
||||
LEVEL_CORNER_Y + 57);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 72);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER + (int)(_estimatedRotation.z * ACCEL_VIEW_SCALING),
|
||||
LEVEL_CORNER_Y + 72);
|
||||
|
||||
glEnd();
|
||||
// Draw green vertical centerline
|
||||
glColor4f(0, 1, 0, 0.5);
|
||||
glBegin(GL_LINES);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y - 6);
|
||||
glVertex2f(LEVEL_CORNER_X + LEVEL_CENTER, LEVEL_CORNER_Y + 30);
|
||||
glEnd();
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
//
|
||||
// Transmitter.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Philip Rosedale on 5/20/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__Transmitter__
|
||||
#define __hifi__Transmitter__
|
||||
|
||||
#include <iostream>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <cstring>
|
||||
#include "world.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct TouchState {
|
||||
uint16_t x, y;
|
||||
char state;
|
||||
};
|
||||
|
||||
class Transmitter
|
||||
{
|
||||
public:
|
||||
Transmitter();
|
||||
~Transmitter();
|
||||
void render();
|
||||
void checkForLostTransmitter();
|
||||
void resetLevels();
|
||||
void renderLevels(int width, int height) const;
|
||||
bool isConnected() const { return _isConnected; };
|
||||
const glm::vec3 getLastRotationRate() const { return _lastRotationRate; };
|
||||
const glm::vec3 getLastAcceleration() const { return _lastRotationRate; };
|
||||
const glm::vec3 getEstimatedRotation() const { return _estimatedRotation; };
|
||||
const TouchState* getTouchState() const { return &_touchState; };
|
||||
void processIncomingData(unsigned char* packetData, int numBytes);
|
||||
|
||||
private:
|
||||
bool _isConnected;
|
||||
glm::vec3 _lastRotationRate;
|
||||
glm::vec3 _lastAcceleration;
|
||||
glm::vec3 _estimatedRotation;
|
||||
TouchState _touchState;
|
||||
timeval* _lastReceivedPacket;
|
||||
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Transmitter__) */
|
|
@ -38,6 +38,7 @@ int main(int argc, const char * argv[]) {
|
|||
|
||||
int exitCode;
|
||||
{
|
||||
QSettings::setDefaultFormat(QSettings::IniFormat);
|
||||
Application app(argc, const_cast<char**>(argv), startup_time);
|
||||
|
||||
qDebug( "Created QT Application.");
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
// hifi
|
||||
//
|
||||
// Created by Stojce Slavkovski on 1/26/14.
|
||||
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
//
|
||||
|
||||
#include "Snapshot.h"
|
||||
|
||||
#include <FileUtils.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <FileUtils.h>
|
||||
|
||||
#include "Snapshot.h"
|
||||
|
||||
// filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg
|
||||
// %1 <= username, %2 <= date and time, %3 <= current location
|
||||
const QString FILENAME_PATH_FORMAT = "hifi-snap-by-%1-on-%2@%3.jpg";
|
||||
|
@ -31,7 +32,6 @@ const QString ORIENTATION_W = "orientation-w";
|
|||
|
||||
const QString DOMAIN_KEY = "domain";
|
||||
|
||||
|
||||
SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
|
||||
|
||||
if (!QFile(snapshotPath).exists()) {
|
||||
|
@ -60,7 +60,7 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
|
|||
return data;
|
||||
}
|
||||
|
||||
void Snapshot::saveSnapshot(QGLWidget* widget, Profile* profile, Avatar* avatar) {
|
||||
void Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
|
||||
QImage shot = widget->grabFrameBuffer();
|
||||
|
||||
glm::vec3 location = avatar->getPosition();
|
||||
|
@ -76,13 +76,13 @@ void Snapshot::saveSnapshot(QGLWidget* widget, Profile* profile, Avatar* avatar)
|
|||
shot.setText(ORIENTATION_Z, QString::number(orientation.z));
|
||||
shot.setText(ORIENTATION_W, QString::number(orientation.w));
|
||||
|
||||
shot.setText(DOMAIN_KEY, profile->getLastDomain());
|
||||
shot.setText(DOMAIN_KEY, NodeList::getInstance()->getDomainInfo().getHostname());
|
||||
|
||||
QString formattedLocation = QString("%1_%2_%3").arg(location.x).arg(location.y).arg(location.z);
|
||||
// replace decimal . with '-'
|
||||
formattedLocation.replace('.', '-');
|
||||
|
||||
QString username = profile->getUsername();
|
||||
QString username = AccountManager::getInstance().getUsername();
|
||||
// normalize username, replace all non alphanumeric with '-'
|
||||
username.replace(QRegExp("[^A-Za-z0-9_]"), "-");
|
||||
|
||||
|
|
|
@ -11,12 +11,11 @@
|
|||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QImage>
|
||||
#include <QGLWidget>
|
||||
#include <QString>
|
||||
|
||||
#include "avatar/Avatar.h"
|
||||
#include "avatar/Profile.h"
|
||||
|
||||
class SnapshotMetaData {
|
||||
public:
|
||||
|
@ -39,7 +38,7 @@ private:
|
|||
class Snapshot {
|
||||
|
||||
public:
|
||||
static void saveSnapshot(QGLWidget* widget, Profile* profile, Avatar* avatar);
|
||||
static void saveSnapshot(QGLWidget* widget, Avatar* avatar);
|
||||
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
||||
};
|
||||
|
||||
|
|
291
libraries/shared/src/AccountManager.cpp
Normal file
291
libraries/shared/src/AccountManager.cpp
Normal file
|
@ -0,0 +1,291 @@
|
|||
//
|
||||
// AccountManager.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QUrlQuery>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include "NodeList.h"
|
||||
#include "PacketHeaders.h"
|
||||
|
||||
#include "AccountManager.h"
|
||||
|
||||
const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false;
|
||||
|
||||
AccountManager& AccountManager::getInstance() {
|
||||
static AccountManager sharedInstance;
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(OAuthAccessToken)
|
||||
Q_DECLARE_METATYPE(DataServerAccountInfo)
|
||||
Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
|
||||
Q_DECLARE_METATYPE(JSONCallbackParameters)
|
||||
|
||||
const QString ACCOUNTS_GROUP = "accounts";
|
||||
|
||||
AccountManager::AccountManager() :
|
||||
_rootURL(),
|
||||
_networkAccessManager(),
|
||||
_pendingCallbackMap(),
|
||||
_accounts()
|
||||
{
|
||||
qRegisterMetaType<OAuthAccessToken>("OAuthAccessToken");
|
||||
qRegisterMetaTypeStreamOperators<OAuthAccessToken>("OAuthAccessToken");
|
||||
|
||||
qRegisterMetaType<DataServerAccountInfo>("DataServerAccountInfo");
|
||||
qRegisterMetaTypeStreamOperators<DataServerAccountInfo>("DataServerAccountInfo");
|
||||
|
||||
qRegisterMetaType<QNetworkAccessManager::Operation>("QNetworkAccessManager::Operation");
|
||||
qRegisterMetaType<JSONCallbackParameters>("JSONCallbackParameters");
|
||||
|
||||
// check if there are existing access tokens to load from settings
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
foreach(const QString& key, settings.allKeys()) {
|
||||
// take a key copy to perform the double slash replacement
|
||||
QString keyCopy(key);
|
||||
QUrl keyURL(keyCopy.replace("slashslash", "//"));
|
||||
|
||||
// pull out the stored access token and put it in our in memory array
|
||||
_accounts.insert(keyURL, settings.value(key).value<DataServerAccountInfo>());
|
||||
qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString());
|
||||
}
|
||||
}
|
||||
|
||||
const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";
|
||||
|
||||
void AccountManager::logout() {
|
||||
// a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file
|
||||
_accounts.remove(_rootURL);
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
QString keyURLString(_rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE));
|
||||
settings.remove(keyURLString);
|
||||
|
||||
qDebug() << "Removed account info for" << _rootURL << "from in-memory accounts and .ini file";
|
||||
|
||||
emit logoutComplete();
|
||||
// the username has changed to blank
|
||||
emit usernameChanged(QString());
|
||||
|
||||
}
|
||||
|
||||
void AccountManager::setRootURL(const QUrl& rootURL) {
|
||||
if (_rootURL != rootURL) {
|
||||
_rootURL = rootURL;
|
||||
|
||||
qDebug() << "URL for node authentication has been changed to" << qPrintable(_rootURL.toString());
|
||||
qDebug() << "Re-setting authentication flow.";
|
||||
|
||||
// tell listeners that the auth endpoint has changed
|
||||
emit authEndpointChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
||||
const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray) {
|
||||
QMetaObject::invokeMethod(this, "invokedRequest",
|
||||
Q_ARG(const QString&, path),
|
||||
Q_ARG(QNetworkAccessManager::Operation, operation),
|
||||
Q_ARG(const JSONCallbackParameters&, callbackParams),
|
||||
Q_ARG(const QByteArray&, dataByteArray));
|
||||
}
|
||||
|
||||
void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
||||
const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray) {
|
||||
if (hasValidAccessToken()) {
|
||||
QNetworkRequest authenticatedRequest;
|
||||
|
||||
QUrl requestURL = _rootURL;
|
||||
requestURL.setPath(path);
|
||||
requestURL.setQuery("access_token=" + _accounts.value(_rootURL).getAccessToken().token);
|
||||
|
||||
authenticatedRequest.setUrl(requestURL);
|
||||
|
||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||
qDebug() << "Making an authenticated request to" << qPrintable(requestURL.toString());
|
||||
|
||||
if (!dataByteArray.isEmpty()) {
|
||||
qDebug() << "The POST/PUT body -" << QString(dataByteArray);
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkReply* networkReply = NULL;
|
||||
|
||||
switch (operation) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
networkReply = _networkAccessManager.get(authenticatedRequest);
|
||||
break;
|
||||
case QNetworkAccessManager::PostOperation:
|
||||
case QNetworkAccessManager::PutOperation:
|
||||
authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
if (operation == QNetworkAccessManager::PostOperation) {
|
||||
networkReply = _networkAccessManager.post(authenticatedRequest, dataByteArray);
|
||||
} else {
|
||||
networkReply = _networkAccessManager.put(authenticatedRequest, dataByteArray);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
// other methods not yet handled
|
||||
break;
|
||||
}
|
||||
|
||||
if (networkReply) {
|
||||
if (!callbackParams.isEmpty()) {
|
||||
// if we have information for a callback, insert the callbackParams into our local map
|
||||
_pendingCallbackMap.insert(networkReply, callbackParams);
|
||||
}
|
||||
|
||||
// if we ended up firing of a request, hook up to it now
|
||||
connect(networkReply, SIGNAL(finished()), this, SLOT(passSuccessToCallback()));
|
||||
connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
this, SLOT(passErrorToCallback(QNetworkReply::NetworkError)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AccountManager::passSuccessToCallback() {
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||
|
||||
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
|
||||
|
||||
if (callbackParams.jsonCallbackReceiver) {
|
||||
// invoke the right method on the callback receiver
|
||||
QMetaObject::invokeMethod(callbackParams.jsonCallbackReceiver, qPrintable(callbackParams.jsonCallbackMethod),
|
||||
Q_ARG(const QJsonObject&, jsonResponse.object()));
|
||||
|
||||
// remove the related reply-callback group from the map
|
||||
_pendingCallbackMap.remove(requestReply);
|
||||
|
||||
} else {
|
||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||
qDebug() << "Received JSON response from data-server that has no matching callback.";
|
||||
qDebug() << jsonResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AccountManager::passErrorToCallback(QNetworkReply::NetworkError errorCode) {
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply);
|
||||
|
||||
if (callbackParams.errorCallbackReceiver) {
|
||||
// invoke the right method on the callback receiver
|
||||
QMetaObject::invokeMethod(callbackParams.errorCallbackReceiver, qPrintable(callbackParams.errorCallbackMethod),
|
||||
Q_ARG(QNetworkReply::NetworkError, errorCode),
|
||||
Q_ARG(const QString&, requestReply->errorString()));
|
||||
|
||||
// remove the related reply-callback group from the map
|
||||
_pendingCallbackMap.remove(requestReply);
|
||||
} else {
|
||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||
qDebug() << "Received error response from data-server that has no matching callback.";
|
||||
qDebug() << "Error" << errorCode << "-" << requestReply->errorString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AccountManager::hasValidAccessToken() {
|
||||
DataServerAccountInfo accountInfo = _accounts.value(_rootURL);
|
||||
|
||||
if (accountInfo.getAccessToken().token.isEmpty() || accountInfo.getAccessToken().isExpired()) {
|
||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||
qDebug() << "An access token is required for requests to" << qPrintable(_rootURL.toString());
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AccountManager::checkAndSignalForAccessToken() {
|
||||
bool hasToken = hasValidAccessToken();
|
||||
|
||||
if (!hasToken) {
|
||||
// emit a signal so somebody can call back to us and request an access token given a username and password
|
||||
emit authRequired();
|
||||
}
|
||||
|
||||
return hasToken;
|
||||
}
|
||||
|
||||
void AccountManager::requestAccessToken(const QString& login, const QString& password) {
|
||||
QNetworkRequest request;
|
||||
|
||||
QUrl grantURL = _rootURL;
|
||||
grantURL.setPath("/oauth/token");
|
||||
|
||||
QByteArray postData;
|
||||
postData.append("grant_type=password&");
|
||||
postData.append("username=" + login + "&");
|
||||
postData.append("password=" + password);
|
||||
|
||||
request.setUrl(grantURL);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
QNetworkReply* requestReply = _networkAccessManager.post(request, postData);
|
||||
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestFinished);
|
||||
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
|
||||
|
||||
void AccountManager::requestFinished() {
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||
const QJsonObject& rootObject = jsonResponse.object();
|
||||
|
||||
if (!rootObject.contains("error")) {
|
||||
// construct an OAuthAccessToken from the json object
|
||||
|
||||
if (!rootObject.contains("access_token") || !rootObject.contains("expires_in")
|
||||
|| !rootObject.contains("token_type")) {
|
||||
// TODO: error handling - malformed token response
|
||||
qDebug() << "Received a response for password grant that is missing one or more expected values.";
|
||||
} else {
|
||||
// clear the path from the response URL so we have the right root URL for this access token
|
||||
QUrl rootURL = requestReply->url();
|
||||
rootURL.setPath("");
|
||||
|
||||
qDebug() << "Storing an account with access-token for" << qPrintable(rootURL.toString());
|
||||
|
||||
DataServerAccountInfo freshAccountInfo(rootObject);
|
||||
_accounts.insert(rootURL, freshAccountInfo);
|
||||
|
||||
emit loginComplete(rootURL);
|
||||
// the username has changed to whatever came back
|
||||
emit usernameChanged(freshAccountInfo.getUsername());
|
||||
|
||||
// store this access token into the local settings
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(freshAccountInfo));
|
||||
}
|
||||
} else {
|
||||
// TODO: error handling
|
||||
qDebug() << "Error in response for password grant -" << rootObject["error_description"].toString();
|
||||
}
|
||||
}
|
||||
|
||||
void AccountManager::requestError(QNetworkReply::NetworkError error) {
|
||||
// TODO: error handling
|
||||
qDebug() << "AccountManager requestError - " << error;
|
||||
}
|
84
libraries/shared/src/AccountManager.h
Normal file
84
libraries/shared/src/AccountManager.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// AccountManager.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__AccountManager__
|
||||
#define __hifi__AccountManager__
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include "DataServerAccountInfo.h"
|
||||
|
||||
class JSONCallbackParameters {
|
||||
public:
|
||||
JSONCallbackParameters() :
|
||||
jsonCallbackReceiver(NULL), jsonCallbackMethod(),
|
||||
errorCallbackReceiver(NULL), errorCallbackMethod() {};
|
||||
|
||||
bool isEmpty() const { return jsonCallbackReceiver == NULL && errorCallbackReceiver == NULL; }
|
||||
|
||||
QObject* jsonCallbackReceiver;
|
||||
QString jsonCallbackMethod;
|
||||
QObject* errorCallbackReceiver;
|
||||
QString errorCallbackMethod;
|
||||
};
|
||||
|
||||
class AccountManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static AccountManager& getInstance();
|
||||
|
||||
void authenticatedRequest(const QString& path,
|
||||
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
||||
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
|
||||
const QByteArray& dataByteArray = QByteArray());
|
||||
|
||||
const QUrl& getRootURL() const { return _rootURL; }
|
||||
void setRootURL(const QUrl& rootURL);
|
||||
bool hasAuthEndpoint() { return !_rootURL.isEmpty(); }
|
||||
|
||||
bool isLoggedIn() { return !_rootURL.isEmpty() && hasValidAccessToken(); }
|
||||
bool hasValidAccessToken();
|
||||
bool checkAndSignalForAccessToken();
|
||||
|
||||
void requestAccessToken(const QString& login, const QString& password);
|
||||
|
||||
QString getUsername() const { return _accounts[_rootURL].getUsername(); }
|
||||
|
||||
public slots:
|
||||
void requestFinished();
|
||||
void requestError(QNetworkReply::NetworkError error);
|
||||
void logout();
|
||||
signals:
|
||||
void authRequired();
|
||||
void authEndpointChanged();
|
||||
void usernameChanged(const QString& username);
|
||||
void loginComplete(const QUrl& rootURL);
|
||||
void logoutComplete();
|
||||
private slots:
|
||||
void passSuccessToCallback();
|
||||
void passErrorToCallback(QNetworkReply::NetworkError errorCode);
|
||||
private:
|
||||
AccountManager();
|
||||
AccountManager(AccountManager const& other); // not implemented
|
||||
void operator=(AccountManager const& other); // not implemented
|
||||
|
||||
Q_INVOKABLE void invokedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
||||
const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray);
|
||||
|
||||
QUrl _rootURL;
|
||||
QNetworkAccessManager _networkAccessManager;
|
||||
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
|
||||
|
||||
QMap<QUrl, DataServerAccountInfo> _accounts;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__AccountManager__) */
|
61
libraries/shared/src/DataServerAccountInfo.cpp
Normal file
61
libraries/shared/src/DataServerAccountInfo.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// DataServerAccountInfo.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "DataServerAccountInfo.h"
|
||||
|
||||
DataServerAccountInfo::DataServerAccountInfo() :
|
||||
_accessToken(),
|
||||
_username()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) :
|
||||
_accessToken(jsonObject),
|
||||
_username()
|
||||
{
|
||||
setUsername(jsonObject["user"].toObject()["username"].toString());
|
||||
}
|
||||
|
||||
DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) {
|
||||
_accessToken = otherInfo._accessToken;
|
||||
_username = otherInfo._username;
|
||||
}
|
||||
|
||||
DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) {
|
||||
DataServerAccountInfo temp(otherInfo);
|
||||
swap(temp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
|
||||
using std::swap;
|
||||
|
||||
swap(_accessToken, otherInfo._accessToken);
|
||||
swap(_username, otherInfo._username);
|
||||
}
|
||||
|
||||
void DataServerAccountInfo::setUsername(const QString& username) {
|
||||
if (_username != username) {
|
||||
_username = username;
|
||||
|
||||
qDebug() << "Username changed to" << username;
|
||||
}
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
|
||||
out << info._accessToken << info._username;
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) {
|
||||
in >> info._accessToken >> info._username;
|
||||
return in;
|
||||
}
|
38
libraries/shared/src/DataServerAccountInfo.h
Normal file
38
libraries/shared/src/DataServerAccountInfo.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// DataServerAccountInfo.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/21/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__DataServerAccountInfo__
|
||||
#define __hifi__DataServerAccountInfo__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include "OAuthAccessToken.h"
|
||||
|
||||
class DataServerAccountInfo : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataServerAccountInfo();
|
||||
DataServerAccountInfo(const QJsonObject& jsonObject);
|
||||
DataServerAccountInfo(const DataServerAccountInfo& otherInfo);
|
||||
DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo);
|
||||
|
||||
const OAuthAccessToken& getAccessToken() const { return _accessToken; }
|
||||
|
||||
const QString& getUsername() const { return _username; }
|
||||
void setUsername(const QString& username);
|
||||
|
||||
friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info);
|
||||
friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info);
|
||||
private:
|
||||
void swap(DataServerAccountInfo& otherInfo);
|
||||
|
||||
OAuthAccessToken _accessToken;
|
||||
QString _username;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__DataServerAccountInfo__) */
|
|
@ -1,148 +0,0 @@
|
|||
//
|
||||
// DataServerClient.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 10/7/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include "NodeList.h"
|
||||
#include "PacketHeaders.h"
|
||||
#include "UUID.h"
|
||||
|
||||
#include "DataServerClient.h"
|
||||
|
||||
QMap<quint8, QByteArray> DataServerClient::_unmatchedPackets;
|
||||
QMap<quint8, DataServerCallbackObject*> DataServerClient::_callbackObjects;
|
||||
quint8 DataServerClient::_sequenceNumber = 0;
|
||||
|
||||
const char MULTI_KEY_VALUE_SEPARATOR = '|';
|
||||
|
||||
const char DATA_SERVER_HOSTNAME[] = "data.highfidelity.io";
|
||||
const unsigned short DATA_SERVER_PORT = 3282;
|
||||
|
||||
const HifiSockAddr& DataServerClient::dataServerSockAddr() {
|
||||
static HifiSockAddr dsSockAddr = HifiSockAddr(DATA_SERVER_HOSTNAME, DATA_SERVER_PORT);
|
||||
return dsSockAddr;
|
||||
}
|
||||
|
||||
void DataServerClient::putValueForKeyAndUserString(const QString& key, const QString& value, const QString& userString) {
|
||||
// setup the header for this packet and push packetStream to desired spot
|
||||
QByteArray putPacket = byteArrayWithPopulatedHeader(PacketTypeDataServerPut);
|
||||
QDataStream packetStream(&putPacket, QIODevice::Append);
|
||||
|
||||
// pack our data for the put packet
|
||||
packetStream << _sequenceNumber << userString << key << value;
|
||||
|
||||
// add the putPacket to our vector of unconfirmed packets, will be deleted once put is confirmed
|
||||
_unmatchedPackets.insert(_sequenceNumber, putPacket);
|
||||
|
||||
// send this put request to the data server
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram(putPacket, dataServerSockAddr().getAddress(),
|
||||
dataServerSockAddr().getPort());
|
||||
|
||||
// push the sequence number forwards
|
||||
_sequenceNumber++;
|
||||
}
|
||||
|
||||
void DataServerClient::putValueForKeyAndUUID(const QString& key, const QString& value, const QUuid& uuid) {
|
||||
putValueForKeyAndUserString(key, value, uuidStringWithoutCurlyBraces(uuid));
|
||||
}
|
||||
|
||||
void DataServerClient::getValueForKeyAndUUID(const QString& key, const QUuid &uuid, DataServerCallbackObject* callbackObject) {
|
||||
getValuesForKeysAndUUID(QStringList(key), uuid, callbackObject);
|
||||
}
|
||||
|
||||
void DataServerClient::getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid,
|
||||
DataServerCallbackObject* callbackObject) {
|
||||
if (!uuid.isNull()) {
|
||||
getValuesForKeysAndUserString(keys, uuidStringWithoutCurlyBraces(uuid), callbackObject);
|
||||
}
|
||||
}
|
||||
|
||||
void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, const QString& userString,
|
||||
DataServerCallbackObject* callbackObject) {
|
||||
if (!userString.isEmpty() && keys.size() <= UCHAR_MAX) {
|
||||
QByteArray getPacket = byteArrayWithPopulatedHeader(PacketTypeDataServerGet);
|
||||
QDataStream packetStream(&getPacket, QIODevice::Append);
|
||||
|
||||
// pack our data for the getPacket
|
||||
packetStream << _sequenceNumber << userString << keys.join(MULTI_KEY_VALUE_SEPARATOR);
|
||||
|
||||
// add the getPacket to our map of unconfirmed packets, will be deleted once we get a response from the nameserver
|
||||
_unmatchedPackets.insert(_sequenceNumber, getPacket);
|
||||
_callbackObjects.insert(_sequenceNumber, callbackObject);
|
||||
|
||||
// send the get to the data server
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram(getPacket, dataServerSockAddr().getAddress(),
|
||||
dataServerSockAddr().getPort());
|
||||
_sequenceNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
void DataServerClient::getValueForKeyAndUserString(const QString& key, const QString& userString,
|
||||
DataServerCallbackObject* callbackObject) {
|
||||
getValuesForKeysAndUserString(QStringList(key), userString, callbackObject);
|
||||
}
|
||||
|
||||
void DataServerClient::processConfirmFromDataServer(const QByteArray& packet) {
|
||||
removeMatchedPacketFromMap(packet);
|
||||
}
|
||||
|
||||
void DataServerClient::processSendFromDataServer(const QByteArray& packet) {
|
||||
// pull the user string from the packet so we know who to associate this with
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
quint8 sequenceNumber = 0;
|
||||
packetStream >> sequenceNumber;
|
||||
|
||||
if (_callbackObjects.find(sequenceNumber) != _callbackObjects.end()) {
|
||||
// remove the packet from our two maps, it's matched
|
||||
DataServerCallbackObject* callbackObject = _callbackObjects.take(sequenceNumber);
|
||||
_unmatchedPackets.remove(sequenceNumber);
|
||||
|
||||
QString userString, keyListString, valueListString;
|
||||
|
||||
packetStream >> userString >> keyListString >> valueListString;
|
||||
|
||||
callbackObject->processDataServerResponse(userString, keyListString.split(MULTI_KEY_VALUE_SEPARATOR),
|
||||
valueListString.split(MULTI_KEY_VALUE_SEPARATOR));
|
||||
}
|
||||
}
|
||||
|
||||
void DataServerClient::processMessageFromDataServer(const QByteArray& packet) {
|
||||
switch (packetTypeForPacket(packet)) {
|
||||
case PacketTypeDataServerSend:
|
||||
processSendFromDataServer(packet);
|
||||
break;
|
||||
case PacketTypeDataServerConfirm:
|
||||
processConfirmFromDataServer(packet);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DataServerClient::removeMatchedPacketFromMap(const QByteArray& packet) {
|
||||
quint8 sequenceNumber = packet[numBytesForPacketHeader(packet)];
|
||||
|
||||
// attempt to remove a packet with this sequence number from the QMap of unmatched packets
|
||||
_unmatchedPackets.remove(sequenceNumber);
|
||||
}
|
||||
|
||||
void DataServerClient::resendUnmatchedPackets() {
|
||||
if (_unmatchedPackets.size() > 0) {
|
||||
qDebug() << "Resending" << _unmatchedPackets.size() << "packets to the data server.";
|
||||
|
||||
foreach (const QByteArray& packet, _unmatchedPackets) {
|
||||
// send the unmatched packet to the data server
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram(packet.data(), packet.size(),
|
||||
dataServerSockAddr().getAddress(),
|
||||
dataServerSockAddr().getPort());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
//
|
||||
// DataServerClient.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 10/7/13.
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__DataServerClient__
|
||||
#define __hifi__DataServerClient__
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
class DataServerCallbackObject {
|
||||
public:
|
||||
virtual void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList) = 0;
|
||||
};
|
||||
|
||||
class DataServerClient {
|
||||
public:
|
||||
static const HifiSockAddr& dataServerSockAddr();
|
||||
|
||||
static void putValueForKeyAndUserString(const QString& key, const QString& value, const QString& userString);
|
||||
static void putValueForKeyAndUUID(const QString& key, const QString& value, const QUuid& uuid);
|
||||
|
||||
static void getValueForKeyAndUserString(const QString& key, const QString& userString,
|
||||
DataServerCallbackObject* callbackObject);
|
||||
static void getValueForKeyAndUUID(const QString& key, const QUuid& uuid, DataServerCallbackObject* callbackObject);
|
||||
static void getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, DataServerCallbackObject* callbackObject);
|
||||
static void getValuesForKeysAndUserString(const QStringList& keys, const QString& userString,
|
||||
DataServerCallbackObject* callbackObject);
|
||||
|
||||
static void processMessageFromDataServer(const QByteArray& packet);
|
||||
|
||||
static void resendUnmatchedPackets();
|
||||
private:
|
||||
static void processConfirmFromDataServer(const QByteArray& packet);
|
||||
static void processSendFromDataServer(const QByteArray& packet);
|
||||
static void removeMatchedPacketFromMap(const QByteArray& packet);
|
||||
|
||||
static QMap<quint8, QByteArray> _unmatchedPackets;
|
||||
static QMap<quint8, DataServerCallbackObject*> _callbackObjects;
|
||||
static quint8 _sequenceNumber;
|
||||
};
|
||||
|
||||
namespace DataServerKey {
|
||||
const QString Domain = "domain";
|
||||
const QString Position = "position";
|
||||
const QString Orientation = "orientation";
|
||||
const QString UUID = "uuid";
|
||||
}
|
||||
|
||||
#endif /* defined(__hifi__DataServerClient__) */
|
126
libraries/shared/src/DomainInfo.cpp
Normal file
126
libraries/shared/src/DomainInfo.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// DomainInfo.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QJsonObject>
|
||||
|
||||
#include "AccountManager.h"
|
||||
|
||||
#include "DomainInfo.h"
|
||||
|
||||
DomainInfo::DomainInfo() :
|
||||
_uuid(),
|
||||
_sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
|
||||
_assignmentUUID(),
|
||||
_connectionSecret(),
|
||||
_registrationToken(),
|
||||
_rootAuthenticationURL(),
|
||||
_publicKey(),
|
||||
_isConnected(false)
|
||||
{
|
||||
// clear appropriate variables after a domain-server logout
|
||||
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete, this, &DomainInfo::logout);
|
||||
}
|
||||
|
||||
void DomainInfo::clearConnectionInfo() {
|
||||
_uuid = QUuid();
|
||||
_connectionSecret = QUuid();
|
||||
_registrationToken = QByteArray();
|
||||
_rootAuthenticationURL = QUrl();
|
||||
_publicKey = QString();
|
||||
_isConnected = false;
|
||||
}
|
||||
|
||||
void DomainInfo::reset() {
|
||||
clearConnectionInfo();
|
||||
_hostname = QString();
|
||||
_sockAddr.setAddress(QHostAddress::Null);
|
||||
}
|
||||
|
||||
void DomainInfo::parseAuthInformationFromJsonObject(const QJsonObject& jsonObject) {
|
||||
QJsonObject dataObject = jsonObject["data"].toObject();
|
||||
_connectionSecret = QUuid(dataObject["connection_secret"].toString());
|
||||
_registrationToken = QByteArray::fromHex(dataObject["registration_token"].toString().toUtf8());
|
||||
_publicKey = dataObject["public_key"].toString();
|
||||
}
|
||||
|
||||
void DomainInfo::setSockAddr(const HifiSockAddr& sockAddr) {
|
||||
if (_sockAddr != sockAddr) {
|
||||
// we should reset on a sockAddr change
|
||||
reset();
|
||||
// change the sockAddr
|
||||
_sockAddr = sockAddr;
|
||||
}
|
||||
}
|
||||
|
||||
void DomainInfo::setHostname(const QString& hostname) {
|
||||
|
||||
if (hostname != _hostname) {
|
||||
// re-set the domain info so that auth information is reloaded
|
||||
reset();
|
||||
|
||||
int colonIndex = hostname.indexOf(':');
|
||||
|
||||
if (colonIndex > 0) {
|
||||
// the user has included a custom DS port with the hostname
|
||||
|
||||
// the new hostname is everything up to the colon
|
||||
_hostname = hostname.left(colonIndex);
|
||||
|
||||
// grab the port by reading the string after the colon
|
||||
_sockAddr.setPort(atoi(hostname.mid(colonIndex + 1, hostname.size()).toLocal8Bit().constData()));
|
||||
|
||||
qDebug() << "Updated hostname to" << _hostname << "and port to" << _sockAddr.getPort();
|
||||
|
||||
} else {
|
||||
// no port included with the hostname, simply set the member variable and reset the domain server port to default
|
||||
_hostname = hostname;
|
||||
_sockAddr.setPort(DEFAULT_DOMAIN_SERVER_PORT);
|
||||
}
|
||||
|
||||
// re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname
|
||||
qDebug("Looking up DS hostname %s.", _hostname.toLocal8Bit().constData());
|
||||
QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&)));
|
||||
|
||||
emit hostnameChanged(_hostname);
|
||||
}
|
||||
}
|
||||
|
||||
void DomainInfo::completedHostnameLookup(const QHostInfo& hostInfo) {
|
||||
for (int i = 0; i < hostInfo.addresses().size(); i++) {
|
||||
if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
_sockAddr.setAddress(hostInfo.addresses()[i]);
|
||||
qDebug("DS at %s is at %s", _hostname.toLocal8Bit().constData(),
|
||||
_sockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we got here then we failed to lookup the address
|
||||
qDebug("Failed domain server lookup");
|
||||
}
|
||||
|
||||
void DomainInfo::setIsConnected(bool isConnected) {
|
||||
if (_isConnected != isConnected) {
|
||||
_isConnected = isConnected;
|
||||
|
||||
if (_isConnected) {
|
||||
emit connectedToDomain(_hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainInfo::logout() {
|
||||
// clear any information related to auth for this domain, assuming it had requested auth
|
||||
if (!_rootAuthenticationURL.isEmpty()) {
|
||||
_rootAuthenticationURL = QUrl();
|
||||
_connectionSecret = QUuid();
|
||||
_registrationToken = QByteArray();
|
||||
_publicKey = QString();
|
||||
_isConnected = false;
|
||||
}
|
||||
}
|
80
libraries/shared/src/DomainInfo.h
Normal file
80
libraries/shared/src/DomainInfo.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// DomainInfo.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__DomainInfo__
|
||||
#define __hifi__DomainInfo__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
const QString DEFAULT_DOMAIN_HOSTNAME = "root.highfidelity.io";
|
||||
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
|
||||
|
||||
class DomainInfo : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DomainInfo();
|
||||
|
||||
void clearConnectionInfo();
|
||||
|
||||
void parseAuthInformationFromJsonObject(const QJsonObject& jsonObject);
|
||||
|
||||
const QUuid& getUUID() const { return _uuid; }
|
||||
void setUUID(const QUuid& uuid) { _uuid = uuid; }
|
||||
|
||||
const QString& getHostname() const { return _hostname; }
|
||||
void setHostname(const QString& hostname);
|
||||
|
||||
const QHostAddress& getIP() const { return _sockAddr.getAddress(); }
|
||||
void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); }
|
||||
|
||||
const HifiSockAddr& getSockAddr() { return _sockAddr; }
|
||||
void setSockAddr(const HifiSockAddr& sockAddr);
|
||||
|
||||
unsigned short getPort() const { return _sockAddr.getPort(); }
|
||||
|
||||
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
|
||||
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
|
||||
|
||||
const QUuid& getConnectionSecret() const { return _connectionSecret; }
|
||||
void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
|
||||
|
||||
const QByteArray& getRegistrationToken() const { return _registrationToken; }
|
||||
|
||||
const QUrl& getRootAuthenticationURL() const { return _rootAuthenticationURL; }
|
||||
void setRootAuthenticationURL(const QUrl& rootAuthenticationURL) { _rootAuthenticationURL = rootAuthenticationURL; }
|
||||
|
||||
bool isConnected() const { return _isConnected; }
|
||||
void setIsConnected(bool isConnected);
|
||||
|
||||
private slots:
|
||||
void completedHostnameLookup(const QHostInfo& hostInfo);
|
||||
|
||||
void logout();
|
||||
signals:
|
||||
void hostnameChanged(const QString& hostname);
|
||||
void connectedToDomain(const QString& hostname);
|
||||
private:
|
||||
void reset();
|
||||
|
||||
QUuid _uuid;
|
||||
QString _hostname;
|
||||
HifiSockAddr _sockAddr;
|
||||
QUuid _assignmentUUID;
|
||||
QUuid _connectionSecret;
|
||||
QByteArray _registrationToken;
|
||||
QUrl _rootAuthenticationURL;
|
||||
QString _publicKey;
|
||||
bool _isConnected;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__DomainInfo__) */
|
|
@ -12,8 +12,11 @@
|
|||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "Assignment.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "Logging.h"
|
||||
|
@ -27,8 +30,7 @@ const char SOLO_NODE_TYPES[2] = {
|
|||
NodeType::AudioMixer
|
||||
};
|
||||
|
||||
const QString DEFAULT_DOMAIN_HOSTNAME = "root.highfidelity.io";
|
||||
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
|
||||
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data-web.highfidelity.io");
|
||||
|
||||
NodeList* NodeList::_sharedInstance = NULL;
|
||||
|
||||
|
@ -59,8 +61,6 @@ NodeList* NodeList::getInstance() {
|
|||
NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
|
||||
_nodeHash(),
|
||||
_nodeHashMutex(QMutex::Recursive),
|
||||
_domainHostname(),
|
||||
_domainSockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
|
||||
_nodeSocket(this),
|
||||
_ownerType(newOwnerType),
|
||||
_nodeTypesOfInterest(),
|
||||
|
@ -73,16 +73,18 @@ NodeList::NodeList(char newOwnerType, unsigned short int newSocketListenPort) :
|
|||
{
|
||||
_nodeSocket.bind(QHostAddress::AnyIPv4, newSocketListenPort);
|
||||
qDebug() << "NodeList socket is listening on" << _nodeSocket.localPort();
|
||||
}
|
||||
|
||||
|
||||
NodeList::~NodeList() {
|
||||
clear();
|
||||
|
||||
// clear our NodeList when the domain changes
|
||||
connect(&_domainInfo, &DomainInfo::hostnameChanged, this, &NodeList::reset);
|
||||
|
||||
// clear our NodeList when logout is requested
|
||||
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
|
||||
}
|
||||
|
||||
bool NodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
||||
if (packet[1] != versionForPacketType(packetTypeForPacket(packet))
|
||||
&& packetTypeForPacket(packet) != PacketTypeStunResponse) {
|
||||
PacketType checkType = packetTypeForPacket(packet);
|
||||
if (packet[1] != versionForPacketType(checkType)
|
||||
&& checkType != PacketTypeStunResponse) {
|
||||
PacketType mismatchType = packetTypeForPacket(packet);
|
||||
int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data());
|
||||
|
||||
|
@ -91,12 +93,13 @@ bool NodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
|||
<< qPrintable(QString::number(versionForPacketType(mismatchType))) << "expected.";
|
||||
}
|
||||
|
||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>() << PacketTypeDomainList
|
||||
<< PacketTypeDomainListRequest << PacketTypeStunResponse << PacketTypeDataServerConfirm
|
||||
const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||
<< PacketTypeDomainServerAuthRequest << PacketTypeDomainConnectRequest
|
||||
<< PacketTypeStunResponse << PacketTypeDataServerConfirm
|
||||
<< PacketTypeDataServerGet << PacketTypeDataServerPut << PacketTypeDataServerSend
|
||||
<< PacketTypeCreateAssignment << PacketTypeRequestAssignment;
|
||||
|
||||
if (!NON_VERIFIED_PACKETS.contains(packetTypeForPacket(packet))) {
|
||||
if (!NON_VERIFIED_PACKETS.contains(checkType)) {
|
||||
// figure out which node this is from
|
||||
SharedNodePointer sendingNode = sendingNodeForPacket(packet);
|
||||
if (sendingNode) {
|
||||
|
@ -104,11 +107,36 @@ bool NodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
|||
if (hashFromPacketHeader(packet) == hashForPacketAndConnectionUUID(packet, sendingNode->getConnectionSecret())) {
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "Packet hash mismatch on" << packetTypeForPacket(packet) << "- Sender"
|
||||
qDebug() << "Packet hash mismatch on" << checkType << "- Sender"
|
||||
<< uuidFromPacketHeader(packet);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Packet of type" << packetTypeForPacket(packet) << "received from unknown node with UUID"
|
||||
if (checkType == PacketTypeDomainList) {
|
||||
|
||||
if (_domainInfo.getRootAuthenticationURL().isEmpty() && _domainInfo.getUUID().isNull()) {
|
||||
// if this is a domain-server that doesn't require auth,
|
||||
// pull the UUID from this packet and set it as our domain-server UUID
|
||||
_domainInfo.setUUID(uuidFromPacketHeader(packet));
|
||||
|
||||
// we also know this domain-server requires no authentication
|
||||
// so set the account manager root URL empty
|
||||
AccountManager::getInstance().setRootURL(QUrl());
|
||||
}
|
||||
|
||||
if (_domainInfo.getUUID() == uuidFromPacketHeader(packet)) {
|
||||
if (hashForPacketAndConnectionUUID(packet, _domainInfo.getConnectionSecret()) == hashFromPacketHeader(packet)) {
|
||||
// this is a packet from the domain-server (PacketTypeDomainServerListRequest)
|
||||
// and the sender UUID matches the UUID we expect for the domain
|
||||
return true;
|
||||
} else {
|
||||
// this is a packet from the domain-server but there is a hash mismatch
|
||||
qDebug() << "Packet hash mismatch on" << checkType << "from domain-server at" << _domainInfo.getHostname();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Packet of type" << checkType << "received from unknown node with UUID"
|
||||
<< uuidFromPacketHeader(packet);
|
||||
}
|
||||
} else {
|
||||
|
@ -118,6 +146,17 @@ bool NodeList::packetVersionAndHashMatch(const QByteArray& packet) {
|
|||
return false;
|
||||
}
|
||||
|
||||
qint64 NodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr,
|
||||
const QUuid& connectionSecret) {
|
||||
QByteArray datagramCopy = datagram;
|
||||
|
||||
// setup the MD5 hash for source verification in the header
|
||||
replaceHashInPacketGivenConnectionUUID(datagramCopy, connectionSecret);
|
||||
|
||||
return _nodeSocket.writeDatagram(datagramCopy, destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||
|
||||
}
|
||||
|
||||
qint64 NodeList::writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
|
||||
const HifiSockAddr& overridenSockAddr) {
|
||||
if (destinationNode) {
|
||||
|
@ -133,11 +172,7 @@ qint64 NodeList::writeDatagram(const QByteArray& datagram, const SharedNodePoint
|
|||
}
|
||||
}
|
||||
|
||||
QByteArray datagramCopy = datagram;
|
||||
// setup the MD5 hash for source verification in the header
|
||||
replaceHashInPacketGivenConnectionUUID(datagramCopy, destinationNode->getConnectionSecret());
|
||||
|
||||
return _nodeSocket.writeDatagram(datagramCopy, destinationSockAddr->getAddress(), destinationSockAddr->getPort());
|
||||
writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret());
|
||||
}
|
||||
|
||||
// didn't have a destinationNode to send to, return 0
|
||||
|
@ -149,37 +184,6 @@ qint64 NodeList::writeDatagram(const char* data, qint64 size, const SharedNodePo
|
|||
return writeDatagram(QByteArray(data, size), destinationNode, overridenSockAddr);
|
||||
}
|
||||
|
||||
void NodeList::setDomainHostname(const QString& domainHostname) {
|
||||
|
||||
if (domainHostname != _domainHostname) {
|
||||
int colonIndex = domainHostname.indexOf(':');
|
||||
|
||||
if (colonIndex > 0) {
|
||||
// the user has included a custom DS port with the hostname
|
||||
|
||||
// the new hostname is everything up to the colon
|
||||
_domainHostname = domainHostname.left(colonIndex);
|
||||
|
||||
// grab the port by reading the string after the colon
|
||||
_domainSockAddr.setPort(atoi(domainHostname.mid(colonIndex + 1, domainHostname.size()).toLocal8Bit().constData()));
|
||||
|
||||
qDebug() << "Updated hostname to" << _domainHostname << "and port to" << _domainSockAddr.getPort();
|
||||
|
||||
} else {
|
||||
// no port included with the hostname, simply set the member variable and reset the domain server port to default
|
||||
_domainHostname = domainHostname;
|
||||
_domainSockAddr.setPort(DEFAULT_DOMAIN_SERVER_PORT);
|
||||
}
|
||||
|
||||
// clear the NodeList so nodes from this domain are killed
|
||||
clear();
|
||||
|
||||
// reset our _domainIP to the null address so that a lookup happens on next check in
|
||||
_domainSockAddr.setAddress(QHostAddress::Null);
|
||||
emit domainChanged(_domainHostname);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode) {
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
@ -218,11 +222,13 @@ void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer&
|
|||
void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) {
|
||||
switch (packetTypeForPacket(packet)) {
|
||||
case PacketTypeDomainList: {
|
||||
// only process the DS if this is our current domain server
|
||||
if (_domainSockAddr == senderSockAddr) {
|
||||
processDomainServerList(packet);
|
||||
}
|
||||
|
||||
processDomainServerList(packet);
|
||||
break;
|
||||
}
|
||||
case PacketTypeDomainServerAuthRequest: {
|
||||
// the domain-server has asked us to auth via a data-server
|
||||
processDomainServerAuthRequest(packet);
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketTypePing: {
|
||||
|
@ -320,10 +326,11 @@ void NodeList::reset() {
|
|||
clear();
|
||||
_numNoReplyDomainCheckIns = 0;
|
||||
|
||||
_nodeTypesOfInterest.clear();
|
||||
|
||||
// refresh the owner UUID
|
||||
_sessionUUID = QUuid::createUuid();
|
||||
// refresh the owner UUID to the NULL UUID
|
||||
setSessionUUID(QUuid());
|
||||
|
||||
// clear the domain connection information
|
||||
_domainInfo.clearConnectionInfo();
|
||||
}
|
||||
|
||||
void NodeList::addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd) {
|
||||
|
@ -495,66 +502,60 @@ void NodeList::processKillNode(const QByteArray& dataByteArray) {
|
|||
}
|
||||
|
||||
void NodeList::sendDomainServerCheckIn() {
|
||||
static bool printedDomainServerIP = false;
|
||||
|
||||
// Lookup the IP address of the domain server if we need to
|
||||
if (_domainSockAddr.getAddress().isNull() && !_domainHostname.isEmpty()) {
|
||||
qDebug("Looking up DS hostname %s.", _domainHostname.toLocal8Bit().constData());
|
||||
QHostInfo domainServerHostInfo = QHostInfo::fromName(_domainHostname);
|
||||
|
||||
for (int i = 0; i < domainServerHostInfo.addresses().size(); i++) {
|
||||
if (domainServerHostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
_domainSockAddr.setAddress(domainServerHostInfo.addresses()[i]);
|
||||
qDebug("DS at %s is at %s", _domainHostname.toLocal8Bit().constData(),
|
||||
_domainSockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
|
||||
printedDomainServerIP = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// if we got here without a break out of the for loop then we failed to lookup the address
|
||||
if (i == domainServerHostInfo.addresses().size() - 1) {
|
||||
qDebug("Failed domain server lookup");
|
||||
}
|
||||
}
|
||||
} else if (!printedDomainServerIP) {
|
||||
qDebug("Domain Server IP: %s", _domainSockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||
printedDomainServerIP = true;
|
||||
}
|
||||
|
||||
if (_publicSockAddr.isNull() && !_hasCompletedInitialSTUNFailure) {
|
||||
// we don't know our public socket and we need to send it to the domain server
|
||||
// send a STUN request to figure it out
|
||||
sendSTUNRequest();
|
||||
} else if (!_domainSockAddr.getAddress().isNull()) {
|
||||
// construct the DS check in packet if we can
|
||||
|
||||
// check in packet has header, optional UUID, node type, port, IP, node types of interest, null termination
|
||||
QByteArray domainServerPacket = byteArrayWithPopulatedHeader(PacketTypeDomainListRequest);
|
||||
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
||||
|
||||
// pack our data to send to the domain-server
|
||||
packetStream << _ownerType << _publicSockAddr
|
||||
<< HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), _nodeSocket.localPort())
|
||||
<< (quint8) _nodeTypesOfInterest.size();
|
||||
|
||||
// copy over the bytes for node types of interest, if required
|
||||
foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) {
|
||||
packetStream << nodeTypeOfInterest;
|
||||
} else if (!_domainInfo.getIP().isNull()) {
|
||||
if (_domainInfo.getRootAuthenticationURL().isEmpty()
|
||||
|| !_sessionUUID.isNull()
|
||||
|| !_domainInfo.getRegistrationToken().isEmpty() ) {
|
||||
// construct the DS check in packet
|
||||
|
||||
PacketType domainPacketType = _sessionUUID.isNull() ? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
|
||||
|
||||
QUuid packetUUID = (domainPacketType == PacketTypeDomainListRequest)
|
||||
? _sessionUUID : _domainInfo.getAssignmentUUID();
|
||||
|
||||
QByteArray domainServerPacket = byteArrayWithPopulatedHeader(domainPacketType, packetUUID);
|
||||
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
||||
|
||||
if (domainPacketType == PacketTypeDomainConnectRequest) {
|
||||
// we may need a registration token to present to the domain-server
|
||||
packetStream << (quint8) !_domainInfo.getRegistrationToken().isEmpty();
|
||||
|
||||
if (!_domainInfo.getRegistrationToken().isEmpty()) {
|
||||
// if we have a registration token send that along in the request
|
||||
packetStream << _domainInfo.getRegistrationToken();
|
||||
}
|
||||
}
|
||||
|
||||
// pack our data to send to the domain-server
|
||||
packetStream << _ownerType << _publicSockAddr
|
||||
<< HifiSockAddr(QHostAddress(getHostOrderLocalAddress()), _nodeSocket.localPort())
|
||||
<< (quint8) _nodeTypesOfInterest.size();
|
||||
|
||||
// copy over the bytes for node types of interest, if required
|
||||
foreach (NodeType_t nodeTypeOfInterest, _nodeTypesOfInterest) {
|
||||
packetStream << nodeTypeOfInterest;
|
||||
}
|
||||
|
||||
writeDatagram(domainServerPacket, _domainInfo.getSockAddr(), _domainInfo.getConnectionSecret());
|
||||
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
||||
static unsigned int numDomainCheckins = 0;
|
||||
|
||||
// send a STUN request every Nth domain server check in so we update our public socket, if required
|
||||
if (numDomainCheckins++ % NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST == 0) {
|
||||
sendSTUNRequest();
|
||||
}
|
||||
|
||||
// increment the count of un-replied check-ins
|
||||
_numNoReplyDomainCheckIns++;
|
||||
} else if (AccountManager::getInstance().hasValidAccessToken()) {
|
||||
// we have an access token we can use for the authentication server the domain-server requested
|
||||
// so ask that server to provide us with information to connect to the domain-server
|
||||
requestAuthForDomainServer();
|
||||
}
|
||||
|
||||
_nodeSocket.writeDatagram(domainServerPacket, _domainSockAddr.getAddress(), _domainSockAddr.getPort());
|
||||
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
||||
static unsigned int numDomainCheckins = 0;
|
||||
|
||||
// send a STUN request every Nth domain server check in so we update our public socket, if required
|
||||
if (numDomainCheckins++ % NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST == 0) {
|
||||
sendSTUNRequest();
|
||||
}
|
||||
|
||||
// increment the count of un-replied check-ins
|
||||
_numNoReplyDomainCheckIns++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -563,7 +564,8 @@ void NodeList::setSessionUUID(const QUuid& sessionUUID) {
|
|||
_sessionUUID = sessionUUID;
|
||||
|
||||
if (sessionUUID != oldUUID) {
|
||||
qDebug() << "NodeList UUID changed from" << oldUUID << "to" << _sessionUUID;
|
||||
qDebug() << "NodeList UUID changed from" << uuidStringWithoutCurlyBraces(oldUUID)
|
||||
<< "to" << uuidStringWithoutCurlyBraces(_sessionUUID);
|
||||
emit uuidChanged(sessionUUID);
|
||||
}
|
||||
}
|
||||
|
@ -571,6 +573,9 @@ void NodeList::setSessionUUID(const QUuid& sessionUUID) {
|
|||
int NodeList::processDomainServerList(const QByteArray& packet) {
|
||||
// this is a packet from the domain server, reset the count of un-replied check-ins
|
||||
_numNoReplyDomainCheckIns = 0;
|
||||
|
||||
// if this was the first domain-server list from this domain, we've now connected
|
||||
_domainInfo.setIsConnected(true);
|
||||
|
||||
int readNodes = 0;
|
||||
|
||||
|
@ -597,7 +602,7 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
|
|||
// if the public socket address is 0 then it's reachable at the same IP
|
||||
// as the domain server
|
||||
if (nodePublicSocket.getAddress().isNull()) {
|
||||
nodePublicSocket.setAddress(_domainSockAddr.getAddress());
|
||||
nodePublicSocket.setAddress(_domainInfo.getIP());
|
||||
}
|
||||
|
||||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket);
|
||||
|
@ -613,6 +618,41 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
|
|||
return readNodes;
|
||||
}
|
||||
|
||||
void NodeList::domainServerAuthReply(const QJsonObject& jsonObject) {
|
||||
_domainInfo.parseAuthInformationFromJsonObject(jsonObject);
|
||||
}
|
||||
|
||||
void NodeList::requestAuthForDomainServer() {
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.jsonCallbackReceiver = this;
|
||||
callbackParams.jsonCallbackMethod = "domainServerAuthReply";
|
||||
|
||||
AccountManager::getInstance().authenticatedRequest("/api/v1/domains/"
|
||||
+ uuidStringWithoutCurlyBraces(_domainInfo.getUUID()) + "/auth.json",
|
||||
QNetworkAccessManager::GetOperation,
|
||||
callbackParams);
|
||||
}
|
||||
|
||||
void NodeList::processDomainServerAuthRequest(const QByteArray& packet) {
|
||||
QDataStream authPacketStream(packet);
|
||||
authPacketStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
_domainInfo.setUUID(uuidFromPacketHeader(packet));
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
// grab the hostname this domain-server wants us to authenticate with
|
||||
QUrl authenticationRootURL;
|
||||
authPacketStream >> authenticationRootURL;
|
||||
|
||||
accountManager.setRootURL(authenticationRootURL);
|
||||
_domainInfo.setRootAuthenticationURL(authenticationRootURL);
|
||||
|
||||
if (AccountManager::getInstance().checkAndSignalForAccessToken()) {
|
||||
// request a domain-server auth
|
||||
requestAuthForDomainServer();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeList::sendAssignment(Assignment& assignment) {
|
||||
|
||||
PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand
|
||||
|
@ -676,9 +716,8 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
|
|||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) {
|
||||
_nodeHashMutex.lock();
|
||||
|
||||
SharedNodePointer matchingNode = _nodeHash.value(uuid);
|
||||
|
||||
if (!matchingNode) {
|
||||
if (!_nodeHash.contains(uuid)) {
|
||||
|
||||
// we didn't have this node, so add them
|
||||
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket);
|
||||
SharedNodePointer newNodeSharedPointer(newNode, &QObject::deleteLater);
|
||||
|
@ -695,29 +734,32 @@ SharedNodePointer NodeList::addOrUpdateNode(const QUuid& uuid, char nodeType,
|
|||
} else {
|
||||
_nodeHashMutex.unlock();
|
||||
|
||||
return updateSocketsForNode(uuid, publicSocket, localSocket);
|
||||
}
|
||||
}
|
||||
|
||||
SharedNodePointer NodeList::updateSocketsForNode(const QUuid& uuid,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) {
|
||||
|
||||
SharedNodePointer matchingNode = nodeWithUUID(uuid);
|
||||
|
||||
if (matchingNode) {
|
||||
// perform appropriate updates to this node
|
||||
QMutexLocker locker(&matchingNode->getMutex());
|
||||
|
||||
if (matchingNode->getType() == NodeType::AudioMixer ||
|
||||
matchingNode->getType() == NodeType::VoxelServer ||
|
||||
matchingNode->getType() == NodeType::MetavoxelServer) {
|
||||
// until the Audio class also uses our nodeList, we need to update
|
||||
// the lastRecvTimeUsecs for the audio mixer so it doesn't get killed and re-added continously
|
||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||
}
|
||||
|
||||
|
||||
// check if we need to change this node's public or local sockets
|
||||
if (publicSocket != matchingNode->getPublicSocket()) {
|
||||
matchingNode->setPublicSocket(publicSocket);
|
||||
qDebug() << "Public socket change for node" << *matchingNode;
|
||||
}
|
||||
|
||||
|
||||
if (localSocket != matchingNode->getLocalSocket()) {
|
||||
matchingNode->setLocalSocket(localSocket);
|
||||
qDebug() << "Local socket change for node" << *matchingNode;
|
||||
}
|
||||
// we had this node already, do nothing for now
|
||||
return matchingNode;
|
||||
}
|
||||
|
||||
return matchingNode;
|
||||
}
|
||||
|
||||
unsigned NodeList::broadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) {
|
||||
|
@ -806,21 +848,20 @@ void NodeList::loadData(QSettings *settings) {
|
|||
QString domainServerHostname = settings->value(DOMAIN_SERVER_SETTING_KEY).toString();
|
||||
|
||||
if (domainServerHostname.size() > 0) {
|
||||
_domainHostname = domainServerHostname;
|
||||
_domainInfo.setHostname(domainServerHostname);
|
||||
} else {
|
||||
_domainHostname = DEFAULT_DOMAIN_HOSTNAME;
|
||||
_domainInfo.setHostname(DEFAULT_DOMAIN_HOSTNAME);
|
||||
}
|
||||
|
||||
emit domainChanged(_domainHostname);
|
||||
settings->endGroup();
|
||||
}
|
||||
|
||||
void NodeList::saveData(QSettings* settings) {
|
||||
settings->beginGroup(DOMAIN_SERVER_SETTING_KEY);
|
||||
|
||||
if (_domainHostname != DEFAULT_DOMAIN_HOSTNAME) {
|
||||
if (_domainInfo.getHostname() != DEFAULT_DOMAIN_HOSTNAME) {
|
||||
// the user is using a different hostname, store it
|
||||
settings->setValue(DOMAIN_SERVER_SETTING_KEY, QVariant(_domainHostname));
|
||||
settings->setValue(DOMAIN_SERVER_SETTING_KEY, QVariant(_domainInfo.getHostname()));
|
||||
} else {
|
||||
// the user has switched back to default, remove the current setting
|
||||
settings->remove(DOMAIN_SERVER_SETTING_KEY);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <QtNetwork/QHostAddress>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include "DomainInfo.h"
|
||||
#include "Node.h"
|
||||
|
||||
const quint64 NODE_SILENCE_THRESHOLD_USECS = 2 * 1000 * 1000;
|
||||
|
@ -36,8 +37,7 @@ const quint64 PING_INACTIVE_NODE_INTERVAL_USECS = 1 * 1000 * 1000;
|
|||
|
||||
extern const char SOLO_NODE_TYPES[2];
|
||||
|
||||
extern const QString DEFAULT_DOMAIN_HOSTNAME;
|
||||
extern const unsigned short DEFAULT_DOMAIN_SERVER_PORT;
|
||||
extern const QUrl DEFAULT_NODE_AUTH_URL;
|
||||
|
||||
const char DEFAULT_ASSIGNMENT_SERVER_HOSTNAME[] = "localhost";
|
||||
|
||||
|
@ -67,16 +67,6 @@ public:
|
|||
NodeType_t getOwnerType() const { return _ownerType; }
|
||||
void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; }
|
||||
|
||||
const QString& getDomainHostname() const { return _domainHostname; }
|
||||
void setDomainHostname(const QString& domainHostname);
|
||||
|
||||
const QHostAddress& getDomainIP() const { return _domainSockAddr.getAddress(); }
|
||||
void setDomainIPToLocalhost() { _domainSockAddr.setAddress(QHostAddress(INADDR_LOOPBACK)); }
|
||||
|
||||
void setDomainSockAddr(const HifiSockAddr& domainSockAddr) { _domainSockAddr = domainSockAddr; }
|
||||
|
||||
unsigned short getDomainPort() const { return _domainSockAddr.getPort(); }
|
||||
|
||||
const QUuid& getSessionUUID() const { return _sessionUUID; }
|
||||
void setSessionUUID(const QUuid& sessionUUID);
|
||||
|
||||
|
@ -95,12 +85,12 @@ public:
|
|||
int size() const { return _nodeHash.size(); }
|
||||
|
||||
int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; }
|
||||
|
||||
void reset();
|
||||
DomainInfo& getDomainInfo() { return _domainInfo; }
|
||||
|
||||
const NodeSet& getNodeInterestSet() const { return _nodeTypesOfInterest; }
|
||||
void addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd);
|
||||
void addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes);
|
||||
void resetNodeInterestSet() { _nodeTypesOfInterest.clear(); }
|
||||
|
||||
int processDomainServerList(const QByteArray& packet);
|
||||
|
||||
|
@ -116,6 +106,8 @@ public:
|
|||
|
||||
SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
|
||||
SharedNodePointer updateSocketsForNode(const QUuid& uuid,
|
||||
const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket);
|
||||
|
||||
void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
|
||||
void processKillNode(const QByteArray& datagram);
|
||||
|
@ -129,35 +121,44 @@ public:
|
|||
void loadData(QSettings* settings);
|
||||
void saveData(QSettings* settings);
|
||||
public slots:
|
||||
void reset();
|
||||
|
||||
void sendDomainServerCheckIn();
|
||||
void pingInactiveNodes();
|
||||
void removeSilentNodes();
|
||||
|
||||
void killNodeWithUUID(const QUuid& nodeUUID);
|
||||
signals:
|
||||
void domainChanged(const QString& domainHostname);
|
||||
void uuidChanged(const QUuid& ownerUUID);
|
||||
void nodeAdded(SharedNodePointer);
|
||||
void nodeKilled(SharedNodePointer);
|
||||
private slots:
|
||||
void domainServerAuthReply(const QJsonObject& jsonObject);
|
||||
private:
|
||||
static NodeList* _sharedInstance;
|
||||
|
||||
NodeList(char ownerType, unsigned short int socketListenPort);
|
||||
~NodeList();
|
||||
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||
void sendSTUNRequest();
|
||||
void processSTUNResponse(const QByteArray& packet);
|
||||
|
||||
qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr,
|
||||
const QUuid& connectionSecret);
|
||||
|
||||
NodeHash::iterator killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill);
|
||||
|
||||
void clear();
|
||||
|
||||
void processDomainServerAuthRequest(const QByteArray& packet);
|
||||
void requestAuthForDomainServer();
|
||||
|
||||
NodeHash _nodeHash;
|
||||
QMutex _nodeHashMutex;
|
||||
QString _domainHostname;
|
||||
HifiSockAddr _domainSockAddr;
|
||||
QUdpSocket _nodeSocket;
|
||||
NodeType_t _ownerType;
|
||||
NodeSet _nodeTypesOfInterest;
|
||||
DomainInfo _domainInfo;
|
||||
QUuid _sessionUUID;
|
||||
int _numNoReplyDomainCheckIns;
|
||||
HifiSockAddr _assignmentServerSocket;
|
||||
|
@ -166,10 +167,7 @@ private:
|
|||
unsigned int _stunRequestsSinceSuccess;
|
||||
|
||||
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||
void timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||
void resetDomainData(char domainField[], const char* domainData);
|
||||
void domainLookup();
|
||||
void clear();
|
||||
void timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__NodeList__) */
|
||||
|
|
61
libraries/shared/src/OAuthAccessToken.cpp
Normal file
61
libraries/shared/src/OAuthAccessToken.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// OAuthAccessToken.cpp
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
|
||||
#include "OAuthAccessToken.h"
|
||||
|
||||
OAuthAccessToken::OAuthAccessToken() :
|
||||
token(),
|
||||
refreshToken(),
|
||||
expiryTimestamp(),
|
||||
tokenType()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OAuthAccessToken::OAuthAccessToken(const QJsonObject& jsonObject) :
|
||||
token(jsonObject["access_token"].toString()),
|
||||
refreshToken(jsonObject["refresh_token"].toString()),
|
||||
expiryTimestamp(QDateTime::currentMSecsSinceEpoch() + jsonObject["expires_in"].toDouble()),
|
||||
tokenType(jsonObject["token_type"].toString())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OAuthAccessToken::OAuthAccessToken(const OAuthAccessToken& otherToken) {
|
||||
token = otherToken.token;
|
||||
refreshToken = otherToken.refreshToken;
|
||||
expiryTimestamp = otherToken.expiryTimestamp;
|
||||
tokenType = otherToken.tokenType;
|
||||
}
|
||||
|
||||
OAuthAccessToken& OAuthAccessToken::operator=(const OAuthAccessToken& otherToken) {
|
||||
OAuthAccessToken temp(otherToken);
|
||||
swap(temp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OAuthAccessToken::swap(OAuthAccessToken& otherToken) {
|
||||
using std::swap;
|
||||
|
||||
swap(token, otherToken.token);
|
||||
swap(refreshToken, otherToken.refreshToken);
|
||||
swap(expiryTimestamp, otherToken.expiryTimestamp);
|
||||
swap(tokenType, otherToken.tokenType);
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream &out, const OAuthAccessToken& token) {
|
||||
out << token.token << token.expiryTimestamp << token.tokenType << token.refreshToken;
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream &in, OAuthAccessToken& token) {
|
||||
in >> token.token >> token.expiryTimestamp >> token.tokenType >> token.refreshToken;
|
||||
return in;
|
||||
}
|
37
libraries/shared/src/OAuthAccessToken.h
Normal file
37
libraries/shared/src/OAuthAccessToken.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// OAuthAccessToken.h
|
||||
// hifi
|
||||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __hifi__OAuthAccessToken__
|
||||
#define __hifi__OAuthAccessToken__
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QJsonObject>
|
||||
|
||||
class OAuthAccessToken : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OAuthAccessToken();
|
||||
OAuthAccessToken(const QJsonObject& jsonObject);
|
||||
OAuthAccessToken(const OAuthAccessToken& otherToken);
|
||||
OAuthAccessToken& operator=(const OAuthAccessToken& otherToken);
|
||||
|
||||
bool isExpired() const { return expiryTimestamp <= QDateTime::currentMSecsSinceEpoch(); }
|
||||
|
||||
QString token;
|
||||
QString refreshToken;
|
||||
qlonglong expiryTimestamp;
|
||||
QString tokenType;
|
||||
|
||||
friend QDataStream& operator<<(QDataStream &out, const OAuthAccessToken& token);
|
||||
friend QDataStream& operator>>(QDataStream &in, OAuthAccessToken& token);
|
||||
private:
|
||||
void swap(OAuthAccessToken& otherToken);
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__OAuthAccessToken__) */
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include "UUID.h"
|
||||
|
||||
// NOTE: if adding a new packet type, you can replace one marked usable or add at the end
|
||||
|
||||
enum PacketType {
|
||||
PacketTypeUnknown,
|
||||
PacketTypeStunResponse,
|
||||
|
@ -30,7 +32,7 @@ enum PacketType {
|
|||
PacketTypeMicrophoneAudioNoEcho,
|
||||
PacketTypeMicrophoneAudioWithEcho,
|
||||
PacketTypeBulkAvatarData,
|
||||
PacketTypeTransmitterData,
|
||||
PacketTypeTransmitterData, // usable
|
||||
PacketTypeEnvironmentData,
|
||||
PacketTypeDomainListRequest,
|
||||
PacketTypeRequestAssignment,
|
||||
|
@ -54,7 +56,9 @@ enum PacketType {
|
|||
PacketTypeParticleAddResponse,
|
||||
PacketTypeMetavoxelData,
|
||||
PacketTypeAvatarIdentity,
|
||||
PacketTypeAvatarBillboard
|
||||
PacketTypeAvatarBillboard,
|
||||
PacketTypeDomainConnectRequest,
|
||||
PacketTypeDomainServerAuthRequest
|
||||
};
|
||||
|
||||
typedef char PacketVersion;
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
set(ROOT_DIR ..)
|
||||
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
|
||||
|
||||
set(TARGET_NAME pairing-server)
|
||||
|
||||
find_package(Qt5Network REQUIRED)
|
||||
|
||||
include(${MACRO_DIR}/SetupHifiProject.cmake)
|
||||
setup_hifi_project(${TARGET_NAME} TRUE)
|
||||
|
||||
qt5_use_modules(${TARGET_NAME} Network)
|
||||
|
||||
# link the shared hifi library
|
||||
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
|
||||
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
|
|
@ -1,135 +0,0 @@
|
|||
//
|
||||
// main.cpp
|
||||
// pairing-server
|
||||
//
|
||||
// Created by Stephen Birarda on 5/1/13.
|
||||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h> // not available on windows
|
||||
#endif
|
||||
|
||||
const int INET_ADDR_STRLEN = 16;
|
||||
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
#include <HifiSockAddr.h>
|
||||
|
||||
const int PAIRING_SERVER_LISTEN_PORT = 7247;
|
||||
const int MAX_PACKET_SIZE_BYTES = 1400;
|
||||
|
||||
struct PairableDevice {
|
||||
char identifier[64];
|
||||
char name[64];
|
||||
HifiSockAddr sendingSocket;
|
||||
HifiSockAddr localSocket;
|
||||
};
|
||||
|
||||
struct RequestingClient {
|
||||
char address[INET_ADDR_STRLEN];
|
||||
int port;
|
||||
};
|
||||
|
||||
QUdpSocket serverSocket;
|
||||
PairableDevice* lastDevice = NULL;
|
||||
RequestingClient* lastClient = NULL;
|
||||
|
||||
int indexOfFirstOccurenceOfCharacter(char* haystack, char needle) {
|
||||
int currentIndex = 0;
|
||||
|
||||
while (haystack[currentIndex] != '\0' && haystack[currentIndex] != needle) {
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
return currentIndex;
|
||||
}
|
||||
|
||||
void sendLastClientToLastDevice() {
|
||||
char pairData[INET_ADDR_STRLEN + 6] = {};
|
||||
int bytesWritten = sprintf(pairData, "%s:%d", ::lastClient->address, ::lastClient->port);
|
||||
|
||||
::serverSocket.writeDatagram(pairData, bytesWritten,
|
||||
::lastDevice->sendingSocket.getAddress(), ::lastDevice->sendingSocket.getPort());
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
|
||||
serverSocket.bind(QHostAddress::LocalHost, PAIRING_SERVER_LISTEN_PORT);
|
||||
|
||||
HifiSockAddr senderSockAddr;
|
||||
char senderData[MAX_PACKET_SIZE_BYTES] = {};
|
||||
|
||||
while (true) {
|
||||
if (::serverSocket.hasPendingDatagrams()
|
||||
&& ::serverSocket.readDatagram(senderData, MAX_PACKET_SIZE_BYTES,
|
||||
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer())) {
|
||||
if (senderData[0] == 'A') {
|
||||
// this is a device reporting itself as available
|
||||
|
||||
PairableDevice tempDevice = {};
|
||||
|
||||
char deviceAddress[INET_ADDR_STRLEN] = {};
|
||||
int socketPort = 0;
|
||||
|
||||
int numMatches = sscanf(senderData, "Available %s %[^:]:%d %s",
|
||||
tempDevice.identifier,
|
||||
deviceAddress,
|
||||
&socketPort,
|
||||
tempDevice.name);
|
||||
|
||||
if (numMatches >= 3) {
|
||||
// if we have fewer than 3 matches the packet wasn't properly formatted
|
||||
|
||||
// setup the localSocket for the pairing device
|
||||
tempDevice.localSocket.setAddress(QHostAddress(deviceAddress));
|
||||
tempDevice.localSocket.setPort(socketPort);
|
||||
|
||||
// store this device's sending socket so we can talk back to it
|
||||
tempDevice.sendingSocket = senderSockAddr;
|
||||
|
||||
// push this new device into the vector
|
||||
printf("New last device is %s (%s) at %s:%d\n",
|
||||
tempDevice.identifier,
|
||||
tempDevice.name,
|
||||
deviceAddress,
|
||||
socketPort);
|
||||
|
||||
// copy the tempDevice to the persisting lastDevice
|
||||
::lastDevice = new PairableDevice(tempDevice);
|
||||
|
||||
if (::lastClient) {
|
||||
sendLastClientToLastDevice();
|
||||
}
|
||||
}
|
||||
} else if (senderData[0] == 'F') {
|
||||
// this is a client looking to pair with a device
|
||||
// send the most recent device this address so it can attempt to pair
|
||||
|
||||
RequestingClient tempClient = {};
|
||||
|
||||
int requestorMatches = sscanf(senderData, "Find %[^:]:%d",
|
||||
tempClient.address,
|
||||
&tempClient.port);
|
||||
|
||||
if (requestorMatches == 2) {
|
||||
// good data, copy the tempClient to the persisting lastInterfaceClient
|
||||
::lastClient = new RequestingClient(tempClient);
|
||||
|
||||
printf("New last client is at %s:%d\n",
|
||||
::lastClient->address,
|
||||
::lastClient->port);
|
||||
|
||||
if (::lastDevice) {
|
||||
sendLastClientToLastDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue