diff --git a/CMakeLists.txt b/CMakeLists.txt index f20142d698..3e796e3ca2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/animation-server/src/AnimationServer.cpp b/animation-server/src/AnimationServer.cpp index 3151445794..a2ca454741 100644 --- a/animation-server/src/AnimationServer.cpp +++ b/animation-server/src/AnimationServer.cpp @@ -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"); diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 015c3916ea..cdaa825ad8 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -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); diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 272bd45a30..a7a66b933c 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -6,9 +6,11 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // +#include #include #include +#include #include #include #include @@ -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(); } diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index f19c8015b3..29187fa3d6 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -21,6 +21,7 @@ private slots: void sendAssignmentRequest(); void readPendingDatagrams(); void assignmentCompleted(); + void handleAuthenticationRequest(); private: Assignment _requestAssignment; ThreadedAssignment* _currentAssignment; diff --git a/data-server/CMakeLists.txt b/data-server/CMakeLists.txt deleted file mode 100755 index 65e3e4a489..0000000000 --- a/data-server/CMakeLists.txt +++ /dev/null @@ -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) \ No newline at end of file diff --git a/data-server/external/hiredis/fmacros.h b/data-server/external/hiredis/fmacros.h deleted file mode 100755 index 799c12c1c0..0000000000 --- a/data-server/external/hiredis/fmacros.h +++ /dev/null @@ -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 diff --git a/data-server/external/hiredis/hiredis.c b/data-server/external/hiredis/hiredis.c deleted file mode 100755 index 9b74b5b775..0000000000 --- a/data-server/external/hiredis/hiredis.c +++ /dev/null @@ -1,1322 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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 -#include -#include -#include -#include -#include - -#include "hiredis.h" -#include "net.h" -#include "sds.h" - -static redisReply *createReplyObject(int type); -static void *createStringObject(const redisReadTask *task, char *str, size_t len); -static void *createArrayObject(const redisReadTask *task, int elements); -static void *createIntegerObject(const redisReadTask *task, long long value); -static void *createNilObject(const redisReadTask *task); - -/* Default set of functions to build the reply. Keep in mind that such a - * function returning NULL is interpreted as OOM. */ -static redisReplyObjectFunctions defaultFunctions = { - createStringObject, - createArrayObject, - createIntegerObject, - createNilObject, - freeReplyObject -}; - -/* Create a reply object */ -static redisReply *createReplyObject(int type) { - redisReply *r = calloc(1,sizeof(*r)); - - if (r == NULL) - return NULL; - - r->type = type; - return r; -} - -/* Free a reply object */ -void freeReplyObject(void *reply) { - redisReply *r = reply; - size_t j; - - switch(r->type) { - case REDIS_REPLY_INTEGER: - break; /* Nothing to free */ - case REDIS_REPLY_ARRAY: - if (r->element != NULL) { - for (j = 0; j < r->elements; j++) - if (r->element[j] != NULL) - freeReplyObject(r->element[j]); - free(r->element); - } - break; - case REDIS_REPLY_ERROR: - case REDIS_REPLY_STATUS: - case REDIS_REPLY_STRING: - if (r->str != NULL) - free(r->str); - break; - } - free(r); -} - -static void *createStringObject(const redisReadTask *task, char *str, size_t len) { - redisReply *r, *parent; - char *buf; - - r = createReplyObject(task->type); - if (r == NULL) - return NULL; - - buf = malloc(len+1); - if (buf == NULL) { - freeReplyObject(r); - return NULL; - } - - assert(task->type == REDIS_REPLY_ERROR || - task->type == REDIS_REPLY_STATUS || - task->type == REDIS_REPLY_STRING); - - /* Copy string value */ - memcpy(buf,str,len); - buf[len] = '\0'; - r->str = buf; - r->len = len; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); - parent->element[task->idx] = r; - } - return r; -} - -static void *createArrayObject(const redisReadTask *task, int elements) { - redisReply *r, *parent; - - r = createReplyObject(REDIS_REPLY_ARRAY); - if (r == NULL) - return NULL; - - if (elements > 0) { - r->element = calloc(elements,sizeof(redisReply*)); - if (r->element == NULL) { - freeReplyObject(r); - return NULL; - } - } - - r->elements = elements; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); - parent->element[task->idx] = r; - } - return r; -} - -static void *createIntegerObject(const redisReadTask *task, long long value) { - redisReply *r, *parent; - - r = createReplyObject(REDIS_REPLY_INTEGER); - if (r == NULL) - return NULL; - - r->integer = value; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); - parent->element[task->idx] = r; - } - return r; -} - -static void *createNilObject(const redisReadTask *task) { - redisReply *r, *parent; - - r = createReplyObject(REDIS_REPLY_NIL); - if (r == NULL) - return NULL; - - if (task->parent) { - parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); - parent->element[task->idx] = r; - } - return r; -} - -static void __redisReaderSetError(redisReader *r, int type, const char *str) { - size_t len; - - if (r->reply != NULL && r->fn && r->fn->freeObject) { - r->fn->freeObject(r->reply); - r->reply = NULL; - } - - /* Clear input buffer on errors. */ - if (r->buf != NULL) { - sdsfree(r->buf); - r->buf = NULL; - r->pos = r->len = 0; - } - - /* Reset task stack. */ - r->ridx = -1; - - /* Set error. */ - r->err = type; - len = strlen(str); - len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1); - memcpy(r->errstr,str,len); - r->errstr[len] = '\0'; -} - -static size_t chrtos(char *buf, size_t size, char byte) { - size_t len = 0; - - switch(byte) { - case '\\': - case '"': - len = snprintf(buf,size,"\"\\%c\"",byte); - break; - case '\n': len = snprintf(buf,size,"\"\\n\""); break; - case '\r': len = snprintf(buf,size,"\"\\r\""); break; - case '\t': len = snprintf(buf,size,"\"\\t\""); break; - case '\a': len = snprintf(buf,size,"\"\\a\""); break; - case '\b': len = snprintf(buf,size,"\"\\b\""); break; - default: - if (isprint(byte)) - len = snprintf(buf,size,"\"%c\"",byte); - else - len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte); - break; - } - - return len; -} - -static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) { - char cbuf[8], sbuf[128]; - - chrtos(cbuf,sizeof(cbuf),byte); - snprintf(sbuf,sizeof(sbuf), - "Protocol error, got %s as reply type byte", cbuf); - __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf); -} - -static void __redisReaderSetErrorOOM(redisReader *r) { - __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory"); -} - -static char *readBytes(redisReader *r, unsigned int bytes) { - char *p; - if (r->len-r->pos >= bytes) { - p = r->buf+r->pos; - r->pos += bytes; - return p; - } - return NULL; -} - -/* Find pointer to \r\n. */ -static char *seekNewline(char *s, size_t len) { - int pos = 0; - int _len = len-1; - - /* Position should be < len-1 because the character at "pos" should be - * followed by a \n. Note that strchr cannot be used because it doesn't - * allow to search a limited length and the buffer that is being searched - * might not have a trailing NULL character. */ - while (pos < _len) { - while(pos < _len && s[pos] != '\r') pos++; - if (s[pos] != '\r') { - /* Not found. */ - return NULL; - } else { - if (s[pos+1] == '\n') { - /* Found. */ - return s+pos; - } else { - /* Continue searching. */ - pos++; - } - } - } - return NULL; -} - -/* Read a long long value starting at *s, under the assumption that it will be - * terminated by \r\n. Ambiguously returns -1 for unexpected input. */ -static long long readLongLong(char *s) { - long long v = 0; - int dec, mult = 1; - char c; - - if (*s == '-') { - mult = -1; - s++; - } else if (*s == '+') { - mult = 1; - s++; - } - - while ((c = *(s++)) != '\r') { - dec = c - '0'; - if (dec >= 0 && dec < 10) { - v *= 10; - v += dec; - } else { - /* Should not happen... */ - return -1; - } - } - - return mult*v; -} - -static char *readLine(redisReader *r, int *_len) { - char *p, *s; - int len; - - p = r->buf+r->pos; - s = seekNewline(p,(r->len-r->pos)); - if (s != NULL) { - len = s-(r->buf+r->pos); - r->pos += len+2; /* skip \r\n */ - if (_len) *_len = len; - return p; - } - return NULL; -} - -static void moveToNextTask(redisReader *r) { - redisReadTask *cur, *prv; - while (r->ridx >= 0) { - /* Return a.s.a.p. when the stack is now empty. */ - if (r->ridx == 0) { - r->ridx--; - return; - } - - cur = &(r->rstack[r->ridx]); - prv = &(r->rstack[r->ridx-1]); - assert(prv->type == REDIS_REPLY_ARRAY); - if (cur->idx == prv->elements-1) { - r->ridx--; - } else { - /* Reset the type because the next item can be anything */ - assert(cur->idx < prv->elements); - cur->type = -1; - cur->elements = -1; - cur->idx++; - return; - } - } -} - -static int processLineItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); - void *obj; - char *p; - int len; - - if ((p = readLine(r,&len)) != NULL) { - if (cur->type == REDIS_REPLY_INTEGER) { - if (r->fn && r->fn->createInteger) - obj = r->fn->createInteger(cur,readLongLong(p)); - else - obj = (void*)REDIS_REPLY_INTEGER; - } else { - /* Type will be error or status. */ - if (r->fn && r->fn->createString) - obj = r->fn->createString(cur,p,len); - else - obj = (void*)(size_t)(cur->type); - } - - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - /* Set reply if this is the root object. */ - if (r->ridx == 0) r->reply = obj; - moveToNextTask(r); - return REDIS_OK; - } - - return REDIS_ERR; -} - -static int processBulkItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); - void *obj = NULL; - char *p, *s; - long len; - unsigned long bytelen; - int success = 0; - - p = r->buf+r->pos; - s = seekNewline(p,r->len-r->pos); - if (s != NULL) { - p = r->buf+r->pos; - bytelen = s-(r->buf+r->pos)+2; /* include \r\n */ - len = readLongLong(p); - - if (len < 0) { - /* The nil object can always be created. */ - if (r->fn && r->fn->createNil) - obj = r->fn->createNil(cur); - else - obj = (void*)REDIS_REPLY_NIL; - success = 1; - } else { - /* Only continue when the buffer contains the entire bulk item. */ - bytelen += len+2; /* include \r\n */ - if (r->pos+bytelen <= r->len) { - if (r->fn && r->fn->createString) - obj = r->fn->createString(cur,s+2,len); - else - obj = (void*)REDIS_REPLY_STRING; - success = 1; - } - } - - /* Proceed when obj was created. */ - if (success) { - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - r->pos += bytelen; - - /* Set reply if this is the root object. */ - if (r->ridx == 0) r->reply = obj; - moveToNextTask(r); - return REDIS_OK; - } - } - - return REDIS_ERR; -} - -static int processMultiBulkItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); - void *obj; - char *p; - long elements; - int root = 0; - - /* Set error for nested multi bulks with depth > 7 */ - if (r->ridx == 8) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "No support for nested multi bulk replies with depth > 7"); - return REDIS_ERR; - } - - if ((p = readLine(r,NULL)) != NULL) { - elements = readLongLong(p); - root = (r->ridx == 0); - - if (elements == -1) { - if (r->fn && r->fn->createNil) - obj = r->fn->createNil(cur); - else - obj = (void*)REDIS_REPLY_NIL; - - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - moveToNextTask(r); - } else { - if (r->fn && r->fn->createArray) - obj = r->fn->createArray(cur,elements); - else - obj = (void*)REDIS_REPLY_ARRAY; - - if (obj == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - /* Modify task stack when there are more than 0 elements. */ - if (elements > 0) { - cur->elements = elements; - cur->obj = obj; - r->ridx++; - r->rstack[r->ridx].type = -1; - r->rstack[r->ridx].elements = -1; - r->rstack[r->ridx].idx = 0; - r->rstack[r->ridx].obj = NULL; - r->rstack[r->ridx].parent = cur; - r->rstack[r->ridx].privdata = r->privdata; - } else { - moveToNextTask(r); - } - } - - /* Set reply if this is the root object. */ - if (root) r->reply = obj; - return REDIS_OK; - } - - return REDIS_ERR; -} - -static int processItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); - char *p; - - /* check if we need to read type */ - if (cur->type < 0) { - if ((p = readBytes(r,1)) != NULL) { - switch (p[0]) { - case '-': - cur->type = REDIS_REPLY_ERROR; - break; - case '+': - cur->type = REDIS_REPLY_STATUS; - break; - case ':': - cur->type = REDIS_REPLY_INTEGER; - break; - case '$': - cur->type = REDIS_REPLY_STRING; - break; - case '*': - cur->type = REDIS_REPLY_ARRAY; - break; - default: - __redisReaderSetErrorProtocolByte(r,*p); - return REDIS_ERR; - } - } else { - /* could not consume 1 byte */ - return REDIS_ERR; - } - } - - /* process typed item */ - switch(cur->type) { - case REDIS_REPLY_ERROR: - case REDIS_REPLY_STATUS: - case REDIS_REPLY_INTEGER: - return processLineItem(r); - case REDIS_REPLY_STRING: - return processBulkItem(r); - case REDIS_REPLY_ARRAY: - return processMultiBulkItem(r); - default: - assert(NULL); - return REDIS_ERR; /* Avoid warning. */ - } -} - -redisReader *redisReaderCreate(void) { - redisReader *r; - - r = calloc(sizeof(redisReader),1); - if (r == NULL) - return NULL; - - r->err = 0; - r->errstr[0] = '\0'; - r->fn = &defaultFunctions; - r->buf = sdsempty(); - r->maxbuf = REDIS_READER_MAX_BUF; - if (r->buf == NULL) { - free(r); - return NULL; - } - - r->ridx = -1; - return r; -} - -void redisReaderFree(redisReader *r) { - if (r->reply != NULL && r->fn && r->fn->freeObject) - r->fn->freeObject(r->reply); - if (r->buf != NULL) - sdsfree(r->buf); - free(r); -} - -int redisReaderFeed(redisReader *r, const char *buf, size_t len) { - sds newbuf; - - /* Return early when this reader is in an erroneous state. */ - if (r->err) - return REDIS_ERR; - - /* Copy the provided buffer. */ - if (buf != NULL && len >= 1) { - /* Destroy internal buffer when it is empty and is quite large. */ - if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) { - sdsfree(r->buf); - r->buf = sdsempty(); - r->pos = 0; - - /* r->buf should not be NULL since we just free'd a larger one. */ - assert(r->buf != NULL); - } - - newbuf = sdscatlen(r->buf,buf,len); - if (newbuf == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } - - r->buf = newbuf; - r->len = sdslen(r->buf); - } - - return REDIS_OK; -} - -int redisReaderGetReply(redisReader *r, void **reply) { - /* Default target pointer to NULL. */ - if (reply != NULL) - *reply = NULL; - - /* Return early when this reader is in an erroneous state. */ - if (r->err) - return REDIS_ERR; - - /* When the buffer is empty, there will never be a reply. */ - if (r->len == 0) - return REDIS_OK; - - /* Set first item to process when the stack is empty. */ - if (r->ridx == -1) { - r->rstack[0].type = -1; - r->rstack[0].elements = -1; - r->rstack[0].idx = -1; - r->rstack[0].obj = NULL; - r->rstack[0].parent = NULL; - r->rstack[0].privdata = r->privdata; - r->ridx = 0; - } - - /* Process items in reply. */ - while (r->ridx >= 0) - if (processItem(r) != REDIS_OK) - break; - - /* Return ASAP when an error occurred. */ - if (r->err) - return REDIS_ERR; - - /* Discard part of the buffer when we've consumed at least 1k, to avoid - * doing unnecessary calls to memmove() in sds.c. */ - if (r->pos >= 1024) { - r->buf = sdsrange(r->buf,r->pos,-1); - r->pos = 0; - r->len = sdslen(r->buf); - } - - /* Emit a reply when there is one. */ - if (r->ridx == -1) { - if (reply != NULL) - *reply = r->reply; - r->reply = NULL; - } - return REDIS_OK; -} - -/* Calculate the number of bytes needed to represent an integer as string. */ -static int intlen(int i) { - int len = 0; - if (i < 0) { - len++; - i = -i; - } - do { - len++; - i /= 10; - } while(i); - return len; -} - -/* Helper that calculates the bulk length given a certain string length. */ -static size_t bulklen(size_t len) { - return 1+intlen(len)+2+len+2; -} - -int redisvFormatCommand(char **target, const char *format, va_list ap) { - const char *c = format; - char *cmd = NULL; /* final command */ - int pos; /* position in final command */ - sds curarg, newarg; /* current argument */ - int touched = 0; /* was the current argument touched? */ - char **curargv = NULL, **newargv = NULL; - int argc = 0; - int totlen = 0; - int j; - - /* Abort if there is not target to set */ - if (target == NULL) - return -1; - - /* Build the command string accordingly to protocol */ - curarg = sdsempty(); - if (curarg == NULL) - return -1; - - while(*c != '\0') { - if (*c != '%' || c[1] == '\0') { - if (*c == ' ') { - if (touched) { - newargv = realloc(curargv,sizeof(char*)*(argc+1)); - if (newargv == NULL) goto err; - curargv = newargv; - curargv[argc++] = curarg; - totlen += bulklen(sdslen(curarg)); - - /* curarg is put in argv so it can be overwritten. */ - curarg = sdsempty(); - if (curarg == NULL) goto err; - touched = 0; - } - } else { - newarg = sdscatlen(curarg,c,1); - if (newarg == NULL) goto err; - curarg = newarg; - touched = 1; - } - } else { - char *arg; - size_t size; - - /* Set newarg so it can be checked even if it is not touched. */ - newarg = curarg; - - switch(c[1]) { - case 's': - arg = va_arg(ap,char*); - size = strlen(arg); - if (size > 0) - newarg = sdscatlen(curarg,arg,size); - break; - case 'b': - arg = va_arg(ap,char*); - size = va_arg(ap,size_t); - if (size > 0) - newarg = sdscatlen(curarg,arg,size); - break; - case '%': - newarg = sdscat(curarg,"%"); - break; - default: - /* Try to detect printf format */ - { - static const char intfmts[] = "diouxX"; - char _format[16]; - const char *_p = c+1; - size_t _l = 0; - va_list _cpy; - - /* Flags */ - if (*_p != '\0' && *_p == '#') _p++; - if (*_p != '\0' && *_p == '0') _p++; - if (*_p != '\0' && *_p == '-') _p++; - if (*_p != '\0' && *_p == ' ') _p++; - if (*_p != '\0' && *_p == '+') _p++; - - /* Field width */ - while (*_p != '\0' && isdigit(*_p)) _p++; - - /* Precision */ - if (*_p == '.') { - _p++; - while (*_p != '\0' && isdigit(*_p)) _p++; - } - - /* Copy va_list before consuming with va_arg */ - va_copy(_cpy,ap); - - /* Integer conversion (without modifiers) */ - if (strchr(intfmts,*_p) != NULL) { - va_arg(ap,int); - goto fmt_valid; - } - - /* Double conversion (without modifiers) */ - if (strchr("eEfFgGaA",*_p) != NULL) { - va_arg(ap,double); - goto fmt_valid; - } - - /* Size: char */ - if (_p[0] == 'h' && _p[1] == 'h') { - _p += 2; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,int); /* char gets promoted to int */ - goto fmt_valid; - } - goto fmt_invalid; - } - - /* Size: short */ - if (_p[0] == 'h') { - _p += 1; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,int); /* short gets promoted to int */ - goto fmt_valid; - } - goto fmt_invalid; - } - - /* Size: long long */ - if (_p[0] == 'l' && _p[1] == 'l') { - _p += 2; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,long long); - goto fmt_valid; - } - goto fmt_invalid; - } - - /* Size: long */ - if (_p[0] == 'l') { - _p += 1; - if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { - va_arg(ap,long); - goto fmt_valid; - } - goto fmt_invalid; - } - - fmt_invalid: - va_end(_cpy); - goto err; - - fmt_valid: - _l = (_p+1)-c; - if (_l < sizeof(_format)-2) { - memcpy(_format,c,_l); - _format[_l] = '\0'; - newarg = sdscatvprintf(curarg,_format,_cpy); - - /* Update current position (note: outer blocks - * increment c twice so compensate here) */ - c = _p-1; - } - - va_end(_cpy); - break; - } - } - - if (newarg == NULL) goto err; - curarg = newarg; - - touched = 1; - c++; - } - c++; - } - - /* Add the last argument if needed */ - if (touched) { - newargv = realloc(curargv,sizeof(char*)*(argc+1)); - if (newargv == NULL) goto err; - curargv = newargv; - curargv[argc++] = curarg; - totlen += bulklen(sdslen(curarg)); - } else { - sdsfree(curarg); - } - - /* Clear curarg because it was put in curargv or was free'd. */ - curarg = NULL; - - /* Add bytes needed to hold multi bulk count */ - totlen += 1+intlen(argc)+2; - - /* Build the command at protocol level */ - cmd = malloc(totlen+1); - if (cmd == NULL) goto err; - - pos = sprintf(cmd,"*%d\r\n",argc); - for (j = 0; j < argc; j++) { - pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j])); - memcpy(cmd+pos,curargv[j],sdslen(curargv[j])); - pos += sdslen(curargv[j]); - sdsfree(curargv[j]); - cmd[pos++] = '\r'; - cmd[pos++] = '\n'; - } - assert(pos == totlen); - cmd[pos] = '\0'; - - free(curargv); - *target = cmd; - return totlen; - -err: - while(argc--) - sdsfree(curargv[argc]); - free(curargv); - - if (curarg != NULL) - sdsfree(curarg); - - /* No need to check cmd since it is the last statement that can fail, - * but do it anyway to be as defensive as possible. */ - if (cmd != NULL) - free(cmd); - - return -1; -} - -/* Format a command according to the Redis protocol. This function - * takes a format similar to printf: - * - * %s represents a C null terminated string you want to interpolate - * %b represents a binary safe string - * - * When using %b you need to provide both the pointer to the string - * and the length in bytes as a size_t. Examples: - * - * len = redisFormatCommand(target, "GET %s", mykey); - * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen); - */ -int redisFormatCommand(char **target, const char *format, ...) { - va_list ap; - int len; - va_start(ap,format); - len = redisvFormatCommand(target,format,ap); - va_end(ap); - return len; -} - -/* Format a command according to the Redis protocol. This function takes the - * number of arguments, an array with arguments and an array with their - * lengths. If the latter is set to NULL, strlen will be used to compute the - * argument lengths. - */ -int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) { - char *cmd = NULL; /* final command */ - int pos; /* position in final command */ - size_t len; - int totlen, j; - - /* Calculate number of bytes needed for the command */ - totlen = 1+intlen(argc)+2; - for (j = 0; j < argc; j++) { - len = argvlen ? argvlen[j] : strlen(argv[j]); - totlen += bulklen(len); - } - - /* Build the command at protocol level */ - cmd = malloc(totlen+1); - if (cmd == NULL) - return -1; - - pos = sprintf(cmd,"*%d\r\n",argc); - for (j = 0; j < argc; j++) { - len = argvlen ? argvlen[j] : strlen(argv[j]); - pos += sprintf(cmd+pos,"$%zu\r\n",len); - memcpy(cmd+pos,argv[j],len); - pos += len; - cmd[pos++] = '\r'; - cmd[pos++] = '\n'; - } - assert(pos == totlen); - cmd[pos] = '\0'; - - *target = cmd; - return totlen; -} - -void __redisSetError(redisContext *c, int type, const char *str) { - size_t len; - - c->err = type; - if (str != NULL) { - len = strlen(str); - len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1); - memcpy(c->errstr,str,len); - c->errstr[len] = '\0'; - } else { - /* Only REDIS_ERR_IO may lack a description! */ - assert(type == REDIS_ERR_IO); - strerror_r(errno,c->errstr,sizeof(c->errstr)); - } -} - -static redisContext *redisContextInit(void) { - redisContext *c; - - c = calloc(1,sizeof(redisContext)); - if (c == NULL) - return NULL; - - c->err = 0; - c->errstr[0] = '\0'; - c->obuf = sdsempty(); - c->reader = redisReaderCreate(); - return c; -} - -void redisFree(redisContext *c) { - if (c->fd > 0) - close(c->fd); - if (c->obuf != NULL) - sdsfree(c->obuf); - if (c->reader != NULL) - redisReaderFree(c->reader); - free(c); -} - -/* Connect to a Redis instance. On error the field error in the returned - * context will be set to the return value of the error function. - * When no set of reply functions is given, the default set will be used. */ -redisContext *redisConnect(const char *ip, int port) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,NULL); - return c; -} - -redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,&tv); - return c; -} - -redisContext *redisConnectNonBlock(const char *ip, int port) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags &= ~REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,NULL); - return c; -} - -redisContext *redisConnectUnix(const char *path) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectUnix(c,path,NULL); - return c; -} - -redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectUnix(c,path,&tv); - return c; -} - -redisContext *redisConnectUnixNonBlock(const char *path) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags &= ~REDIS_BLOCK; - redisContextConnectUnix(c,path,NULL); - return c; -} - -/* Set read/write timeout on a blocking socket. */ -int redisSetTimeout(redisContext *c, const struct timeval tv) { - if (c->flags & REDIS_BLOCK) - return redisContextSetTimeout(c,tv); - return REDIS_ERR; -} - -/* Enable connection KeepAlive. */ -int redisEnableKeepAlive(redisContext *c) { - if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK) - return REDIS_ERR; - return REDIS_OK; -} - -/* Use this function to handle a read event on the descriptor. It will try - * and read some bytes from the socket and feed them to the reply parser. - * - * After this function is called, you may use redisContextReadReply to - * see if there is a reply available. */ -int redisBufferRead(redisContext *c) { - char buf[1024*16]; - int nread; - - /* Return early when the context has seen an error. */ - if (c->err) - return REDIS_ERR; - - nread = read(c->fd,buf,sizeof(buf)); - if (nread == -1) { - if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { - /* Try again later */ - } else { - __redisSetError(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } - } else if (nread == 0) { - __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection"); - return REDIS_ERR; - } else { - if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) { - __redisSetError(c,c->reader->err,c->reader->errstr); - return REDIS_ERR; - } - } - return REDIS_OK; -} - -/* Write the output buffer to the socket. - * - * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was - * succesfully written to the socket. When the buffer is empty after the - * write operation, "done" is set to 1 (if given). - * - * Returns REDIS_ERR if an error occured trying to write and sets - * c->errstr to hold the appropriate error string. - */ -int redisBufferWrite(redisContext *c, int *done) { - int nwritten; - - /* Return early when the context has seen an error. */ - if (c->err) - return REDIS_ERR; - - if (sdslen(c->obuf) > 0) { - nwritten = write(c->fd,c->obuf,sdslen(c->obuf)); - if (nwritten == -1) { - if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { - /* Try again later */ - } else { - __redisSetError(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } - } else if (nwritten > 0) { - if (nwritten == (signed)sdslen(c->obuf)) { - sdsfree(c->obuf); - c->obuf = sdsempty(); - } else { - c->obuf = sdsrange(c->obuf,nwritten,-1); - } - } - } - if (done != NULL) *done = (sdslen(c->obuf) == 0); - return REDIS_OK; -} - -/* Internal helper function to try and get a reply from the reader, - * or set an error in the context otherwise. */ -int redisGetReplyFromReader(redisContext *c, void **reply) { - if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { - __redisSetError(c,c->reader->err,c->reader->errstr); - return REDIS_ERR; - } - return REDIS_OK; -} - -int redisGetReply(redisContext *c, void **reply) { - int wdone = 0; - void *aux = NULL; - - /* Try to read pending replies */ - if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) - return REDIS_ERR; - - /* For the blocking context, flush output buffer and read reply */ - if (aux == NULL && c->flags & REDIS_BLOCK) { - /* Write until done */ - do { - if (redisBufferWrite(c,&wdone) == REDIS_ERR) - return REDIS_ERR; - } while (!wdone); - - /* Read until there is a reply */ - do { - if (redisBufferRead(c) == REDIS_ERR) - return REDIS_ERR; - if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) - return REDIS_ERR; - } while (aux == NULL); - } - - /* Set reply object */ - if (reply != NULL) *reply = aux; - return REDIS_OK; -} - - -/* Helper function for the redisAppendCommand* family of functions. - * - * Write a formatted command to the output buffer. When this family - * is used, you need to call redisGetReply yourself to retrieve - * the reply (or replies in pub/sub). - */ -int __redisAppendCommand(redisContext *c, char *cmd, size_t len) { - sds newbuf; - - newbuf = sdscatlen(c->obuf,cmd,len); - if (newbuf == NULL) { - __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); - return REDIS_ERR; - } - - c->obuf = newbuf; - return REDIS_OK; -} - -int redisvAppendCommand(redisContext *c, const char *format, va_list ap) { - char *cmd; - int len; - - len = redisvFormatCommand(&cmd,format,ap); - if (len == -1) { - __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); - return REDIS_ERR; - } - - if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { - free(cmd); - return REDIS_ERR; - } - - free(cmd); - return REDIS_OK; -} - -int redisAppendCommand(redisContext *c, const char *format, ...) { - va_list ap; - int ret; - - va_start(ap,format); - ret = redisvAppendCommand(c,format,ap); - va_end(ap); - return ret; -} - -int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { - char *cmd; - int len; - - len = redisFormatCommandArgv(&cmd,argc,argv,argvlen); - if (len == -1) { - __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); - return REDIS_ERR; - } - - if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { - free(cmd); - return REDIS_ERR; - } - - free(cmd); - return REDIS_OK; -} - -/* Helper function for the redisCommand* family of functions. - * - * Write a formatted command to the output buffer. If the given context is - * blocking, immediately read the reply into the "reply" pointer. When the - * context is non-blocking, the "reply" pointer will not be used and the - * command is simply appended to the write buffer. - * - * Returns the reply when a reply was succesfully retrieved. Returns NULL - * otherwise. When NULL is returned in a blocking context, the error field - * in the context will be set. - */ -static void *__redisBlockForReply(redisContext *c) { - void *reply; - - if (c->flags & REDIS_BLOCK) { - if (redisGetReply(c,&reply) != REDIS_OK) - return NULL; - return reply; - } - return NULL; -} - -void *redisvCommand(redisContext *c, const char *format, va_list ap) { - if (redisvAppendCommand(c,format,ap) != REDIS_OK) - return NULL; - return __redisBlockForReply(c); -} - -void *redisCommand(redisContext *c, const char *format, ...) { - va_list ap; - void *reply = NULL; - va_start(ap,format); - reply = redisvCommand(c,format,ap); - va_end(ap); - return reply; -} - -void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { - if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK) - return NULL; - return __redisBlockForReply(c); -} diff --git a/data-server/external/hiredis/hiredis.h b/data-server/external/hiredis/hiredis.h deleted file mode 100755 index c65098b740..0000000000 --- a/data-server/external/hiredis/hiredis.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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 /* for size_t */ -#include /* for va_list */ -#include /* 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 diff --git a/data-server/external/hiredis/net.c b/data-server/external/hiredis/net.c deleted file mode 100755 index 603699c936..0000000000 --- a/data-server/external/hiredis/net.c +++ /dev/null @@ -1,339 +0,0 @@ -/* Extracted from anet.c to work properly with Hiredis error reporting. - * - * Copyright (c) 2006-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/data-server/external/hiredis/net.h b/data-server/external/hiredis/net.h deleted file mode 100755 index 94b76f5ca7..0000000000 --- a/data-server/external/hiredis/net.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Extracted from anet.c to work properly with Hiredis error reporting. - * - * Copyright (c) 2006-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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 diff --git a/data-server/external/hiredis/sds.c b/data-server/external/hiredis/sds.c deleted file mode 100755 index 9226799fcf..0000000000 --- a/data-server/external/hiredis/sds.c +++ /dev/null @@ -1,606 +0,0 @@ -/* SDSLib, A C dynamic strings library - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * 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 -#include -#include -#include -#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 - -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 diff --git a/data-server/external/hiredis/sds.h b/data-server/external/hiredis/sds.h deleted file mode 100755 index 94f5871f54..0000000000 --- a/data-server/external/hiredis/sds.h +++ /dev/null @@ -1,88 +0,0 @@ -/* SDSLib, A C dynamic strings library - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * 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 -#include - -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 diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp deleted file mode 100644 index 76386d7b5a..0000000000 --- a/data-server/src/DataServer.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// -// DataServer.cpp -// hifi -// -// Created by Stephen Birarda on 1/20/2014. -// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. -// - -#include -#include -#include - -#include -#include -#include - -#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()); - } - } - } - } -} diff --git a/data-server/src/DataServer.h b/data-server/src/DataServer.h deleted file mode 100644 index 392387f4d8..0000000000 --- a/data-server/src/DataServer.h +++ /dev/null @@ -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 -#include -#include - -#include - -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__) */ diff --git a/data-server/src/main.cpp b/data-server/src/main.cpp deleted file mode 100755 index dc99de70b8..0000000000 --- a/data-server/src/main.cpp +++ /dev/null @@ -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 - -#include - -#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(); -} \ No newline at end of file diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 392bdb31e8..2ee343f268 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -12,10 +12,11 @@ #include #include #include +#include #include -#include #include +#include #include #include #include @@ -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 parsedTypes(QSet() << 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& excludedTypes) { @@ -212,24 +299,177 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSetgetNodeSocket().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(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(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(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(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(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(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(node->getLinkedData()); - foreach (const QUuid& otherNodeSessionUUID, nodeData->getSessionSecretHash().keys()) { - SharedNodePointer otherNode = NodeList::getInstance()->nodeWithUUID(otherNodeSessionUUID); - if (otherNode) { - reinterpret_cast(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(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::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::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 diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 0646653d02..2d253cc41c 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -11,8 +11,11 @@ #include #include +#include #include #include +#include +#include #include #include @@ -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& excludedTypes); void readConfigFile(const QString& path, QSet& 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 _staticAssignmentHash; QQueue _assignmentQueue; - bool _hasCompletedRestartHold; + QUrl _nodeAuthenticationURL; + + QStringList _argumentList; + + QHash _redeemedTokenResponses; private slots: + void requestCreationFromDataServer(); + void processCreateResponseFromDataServer(const QJsonObject& jsonObject); + void processTokenRedeemResponse(const QJsonObject& jsonObject); + void readAvailableDatagrams(); - void addStaticAssignmentsBackToQueueAfterRestart(); }; #endif /* defined(__hifi__DomainServer__) */ diff --git a/domain-server/src/DomainServerNodeData.cpp b/domain-server/src/DomainServerNodeData.cpp new file mode 100644 index 0000000000..2e32903712 --- /dev/null +++ b/domain-server/src/DomainServerNodeData.cpp @@ -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() +{ + +} \ No newline at end of file diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index ea56e31f1c..6686b9120f 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -10,17 +10,22 @@ #define __hifi__DomainServerNodeData__ #include +#include #include 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& getSessionSecretHash() { return _sessionSecretHash; } private: QHash _sessionSecretHash; + QUuid _staticAssignmentUUID; }; #endif /* defined(__hifi__DomainServerNodeData__) */ diff --git a/interface/resources/info/ApplicationInfo.ini b/interface/resources/info/ApplicationInfo.ini index 35a6c71eb8..1a7142cb76 100644 --- a/interface/resources/info/ApplicationInfo.ini +++ b/interface/resources/info/ApplicationInfo.ini @@ -1,4 +1,4 @@ [INFO] name=Interface organizationName=High Fidelity -organizationDomain=highfidelity.io +organizationDomain=highfidelity.io \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 70b785b172..a8fad55b6e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -51,11 +51,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include @@ -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); } - diff --git a/interface/src/Application.h b/interface/src/Application.h index c342f1f7e8..e35c4371f8 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -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; diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 90999c26cc..50e84db7b5 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -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(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; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0606737b80..dc8116c09f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -24,11 +24,10 @@ #include #include +#include #include #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(), diff --git a/interface/src/Menu.h b/interface/src/Menu.h index a3829d5bde..c5c6ba7e7a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -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"; diff --git a/interface/src/PairingHandler.cpp b/interface/src/PairingHandler.cpp deleted file mode 100644 index a710ebce7c..0000000000 --- a/interface/src/PairingHandler.cpp +++ /dev/null @@ -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 // not available on windows -#endif - -#include -#include - -#include - -#include -#include - -#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()); -} diff --git a/interface/src/PairingHandler.h b/interface/src/PairingHandler.h deleted file mode 100644 index 68d9f7de5a..0000000000 --- a/interface/src/PairingHandler.h +++ /dev/null @@ -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 - -class PairingHandler : public QObject { - Q_OBJECT -public: - static PairingHandler* getInstance(); -public slots: - void sendPairRequest(); -}; - -#endif /* defined(__hifi__PairingHandler__) */ diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e2e364c616..a946a19bd9 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -19,7 +19,6 @@ #include "Application.h" #include "Avatar.h" -#include "DataServerClient.h" #include "Hand.h" #include "Head.h" #include "Menu.h" diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 24921f45dd..db24d5bf4e 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -14,7 +14,6 @@ #include #include -#include #include "Avatar.h" diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e6676de154..2e4309ac18 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -13,13 +13,15 @@ #include +#include + +#include #include #include #include #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); + } + +} diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0d282c180a..c1c217136d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -11,8 +11,6 @@ #include -#include - #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 _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 diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp deleted file mode 100644 index a2a412199e..0000000000 --- a/interface/src/avatar/Profile.cpp +++ /dev/null @@ -1,167 +0,0 @@ -// -// Profile.cpp -// hifi -// -// Created by Stephen Birarda on 10/8/13. -// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. -// - -#include - -#include -#include - -#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; -} diff --git a/interface/src/avatar/Profile.h b/interface/src/avatar/Profile.h deleted file mode 100644 index c1f3f3c4de..0000000000 --- a/interface/src/avatar/Profile.h +++ /dev/null @@ -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 - -#include -#include -#include - -#include -#include - -#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__) */ diff --git a/interface/src/devices/Transmitter.cpp b/interface/src/devices/Transmitter.cpp deleted file mode 100644 index 5fca045cfd..0000000000 --- a/interface/src/devices/Transmitter.cpp +++ /dev/null @@ -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 -#endif - -#include - -#include - -#include - -#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(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(); -} - diff --git a/interface/src/devices/Transmitter.h b/interface/src/devices/Transmitter.h deleted file mode 100644 index 12f2b302f7..0000000000 --- a/interface/src/devices/Transmitter.h +++ /dev/null @@ -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 -#include -#include -#include -#include "world.h" -#include - -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__) */ diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 71e9a3fc2f..2d30fc06bc 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -38,6 +38,7 @@ int main(int argc, const char * argv[]) { int exitCode; { + QSettings::setDefaultFormat(QSettings::IniFormat); Application app(argc, const_cast(argv), startup_time); qDebug( "Created QT Application."); diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 6ffaf23564..a29a96f009 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -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 #include #include +#include +#include + +#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_]"), "-"); diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 13c6945349..1b78e8328e 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -11,12 +11,11 @@ #include "InterfaceConfig.h" -#include #include #include +#include #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); }; diff --git a/libraries/shared/src/AccountManager.cpp b/libraries/shared/src/AccountManager.cpp new file mode 100644 index 0000000000..a4f14f3fc8 --- /dev/null +++ b/libraries/shared/src/AccountManager.cpp @@ -0,0 +1,291 @@ +// +// AccountManager.cpp +// hifi +// +// Created by Stephen Birarda on 2/18/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include +#include +#include +#include +#include +#include +#include + +#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"); + qRegisterMetaTypeStreamOperators("OAuthAccessToken"); + + qRegisterMetaType("DataServerAccountInfo"); + qRegisterMetaTypeStreamOperators("DataServerAccountInfo"); + + qRegisterMetaType("QNetworkAccessManager::Operation"); + qRegisterMetaType("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()); + 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(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(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(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; +} \ No newline at end of file diff --git a/libraries/shared/src/AccountManager.h b/libraries/shared/src/AccountManager.h new file mode 100644 index 0000000000..0018a6bba0 --- /dev/null +++ b/libraries/shared/src/AccountManager.h @@ -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 +#include +#include +#include +#include + +#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 _pendingCallbackMap; + + QMap _accounts; +}; + +#endif /* defined(__hifi__AccountManager__) */ diff --git a/libraries/shared/src/DataServerAccountInfo.cpp b/libraries/shared/src/DataServerAccountInfo.cpp new file mode 100644 index 0000000000..7225653998 --- /dev/null +++ b/libraries/shared/src/DataServerAccountInfo.cpp @@ -0,0 +1,61 @@ +// +// DataServerAccountInfo.cpp +// hifi +// +// Created by Stephen Birarda on 2/18/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include + +#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; +} \ No newline at end of file diff --git a/libraries/shared/src/DataServerAccountInfo.h b/libraries/shared/src/DataServerAccountInfo.h new file mode 100644 index 0000000000..da7bdbe42e --- /dev/null +++ b/libraries/shared/src/DataServerAccountInfo.h @@ -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 + +#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__) */ diff --git a/libraries/shared/src/DataServerClient.cpp b/libraries/shared/src/DataServerClient.cpp deleted file mode 100644 index 8903aa0524..0000000000 --- a/libraries/shared/src/DataServerClient.cpp +++ /dev/null @@ -1,148 +0,0 @@ -// -// DataServerClient.cpp -// hifi -// -// Created by Stephen Birarda on 10/7/13. -// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. -// - -#include -#include - -#include "NodeList.h" -#include "PacketHeaders.h" -#include "UUID.h" - -#include "DataServerClient.h" - -QMap DataServerClient::_unmatchedPackets; -QMap 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()); - } - } -} diff --git a/libraries/shared/src/DataServerClient.h b/libraries/shared/src/DataServerClient.h deleted file mode 100644 index aac3150679..0000000000 --- a/libraries/shared/src/DataServerClient.h +++ /dev/null @@ -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 - -#include -#include -#include -#include - -#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 _unmatchedPackets; - static QMap _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__) */ diff --git a/libraries/shared/src/DomainInfo.cpp b/libraries/shared/src/DomainInfo.cpp new file mode 100644 index 0000000000..bed638bf59 --- /dev/null +++ b/libraries/shared/src/DomainInfo.cpp @@ -0,0 +1,126 @@ +// +// DomainInfo.cpp +// hifi +// +// Created by Stephen Birarda on 2/18/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include + +#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; + } +} diff --git a/libraries/shared/src/DomainInfo.h b/libraries/shared/src/DomainInfo.h new file mode 100644 index 0000000000..35564e7cd8 --- /dev/null +++ b/libraries/shared/src/DomainInfo.h @@ -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 +#include +#include +#include + +#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__) */ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 67e15b1f7c..89b04e00cf 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -12,8 +12,11 @@ #include #include +#include +#include #include +#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 NON_VERIFIED_PACKETS = QSet() << PacketTypeDomainList - << PacketTypeDomainListRequest << PacketTypeStunResponse << PacketTypeDataServerConfirm + const QSet NON_VERIFIED_PACKETS = QSet() + << 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); diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index ca4e8ab7b4..a66bcf3535 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -28,6 +28,7 @@ #include #include +#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__) */ diff --git a/libraries/shared/src/OAuthAccessToken.cpp b/libraries/shared/src/OAuthAccessToken.cpp new file mode 100644 index 0000000000..522a3700cd --- /dev/null +++ b/libraries/shared/src/OAuthAccessToken.cpp @@ -0,0 +1,61 @@ +// +// OAuthAccessToken.cpp +// hifi +// +// Created by Stephen Birarda on 2/18/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include + +#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; +} diff --git a/libraries/shared/src/OAuthAccessToken.h b/libraries/shared/src/OAuthAccessToken.h new file mode 100644 index 0000000000..e2a5eb4ce2 --- /dev/null +++ b/libraries/shared/src/OAuthAccessToken.h @@ -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 +#include +#include + +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__) */ diff --git a/libraries/shared/src/PacketHeaders.h b/libraries/shared/src/PacketHeaders.h index f6d745cb44..0122e36a61 100644 --- a/libraries/shared/src/PacketHeaders.h +++ b/libraries/shared/src/PacketHeaders.h @@ -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; diff --git a/pairing-server/CMakeLists.txt b/pairing-server/CMakeLists.txt deleted file mode 100644 index b1fbdf82dc..0000000000 --- a/pairing-server/CMakeLists.txt +++ /dev/null @@ -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}) \ No newline at end of file diff --git a/pairing-server/src/main.cpp b/pairing-server/src/main.cpp deleted file mode 100644 index 2fc371b42c..0000000000 --- a/pairing-server/src/main.cpp +++ /dev/null @@ -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 -#include -#include -#include - -#ifndef _WIN32 -#include // not available on windows -#endif - -const int INET_ADDR_STRLEN = 16; - -#include - -#include - -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(); - } - } - } - } - } -}