From 649a12472bdcdf1c8e2ed73380362948f042d9ba Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 21 Jul 2015 10:39:29 -0700 Subject: Report controllerInstanceId in all objects so that controller resets can be easily detected by whatever is using the service. --- controller/SqliteNetworkController.cpp | 33 ++++++++++++++++++--------------- controller/SqliteNetworkController.hpp | 1 + 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 19e3d920..0986f7b5 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -205,7 +205,7 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) : /* Config */ ||(sqlite3_prepare_v2(_db,"SELECT \"v\" FROM \"Config\" WHERE \"k\" = ?",-1,&_sGetConfig,(const char **)0) != SQLITE_OK) - ||(sqlite3_prepare_v2(_db,"INSERT INTO \"Config\" (\"k\",\"v\") VALUES (?,?)",-1,&_sSetConfig,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"INSERT OR REPLACE INTO \"Config\" (\"k\",\"v\") VALUES (?,?)",-1,&_sSetConfig,(const char **)0) != SQLITE_OK) ) { //printf("!!! %s\n",sqlite3_errmsg(_db)); @@ -221,16 +221,19 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) : if (sqlite3_step(_sGetConfig) != SQLITE_ROW) { unsigned char sr[32]; Utils::getSecureRandom(sr,32); - char instanceId[32]; for(unsigned int i=0;i<32;++i) - instanceId[i] = "0123456789abcdef"[(unsigned int)sr[i] & 0xf]; + _instanceId.push_back("0123456789abcdef"[(unsigned int)sr[i] & 0xf]); + sqlite3_reset(_sSetConfig); sqlite3_bind_text(_sSetConfig,1,"instanceId",10,SQLITE_STATIC); - sqlite3_bind_text(_sSetConfig,2,instanceId,32,SQLITE_STATIC); - if (sqlite3_step(_sSetConfig) != SQLITE_DONE) { - sqlite3_close(_db); + sqlite3_bind_text(_sSetConfig,2,_instanceId.c_str(),-1,SQLITE_STATIC); + if (sqlite3_step(_sSetConfig) != SQLITE_DONE) throw std::runtime_error("SqliteNetworkController unable to read or initialize instanceId"); - } + } else { + const char *iid = reinterpret_cast(sqlite3_column_text(_sGetConfig,0)); + if (!iid) + throw std::runtime_error("SqliteNetworkController unable to read instanceId (it's NULL)"); + _instanceId = iid; } } @@ -1286,6 +1289,7 @@ unsigned int SqliteNetworkController::_doCPGet( "{\n" "\t\"nwid\": \"%s\",\n" "\t\"address\": \"%s\",\n" + "\t\"controllerInstanceId\": \"%s\",\n" "\t\"authorized\": %s,\n" "\t\"activeBridge\": %s,\n" "\t\"memberRevision\": %llu,\n" @@ -1293,6 +1297,7 @@ unsigned int SqliteNetworkController::_doCPGet( "\t\"ipAssignments\": [", nwids, addrs, + _instanceId.c_str(), (sqlite3_column_int(_sGetMember2,0) > 0) ? "true" : "false", (sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false", (unsigned long long)sqlite3_column_int64(_sGetMember2,2), @@ -1384,6 +1389,7 @@ unsigned int SqliteNetworkController::_doCPGet( Utils::snprintf(json,sizeof(json), "{\n" "\t\"nwid\": \"%s\",\n" + "\t\"controllerInstanceId\": \"%s\",\n" "\t\"name\": \"%s\",\n" "\t\"private\": %s,\n" "\t\"enableBroadcast\": %s,\n" @@ -1396,6 +1402,7 @@ unsigned int SqliteNetworkController::_doCPGet( "\t\"memberRevisionCounter\": %llu,\n" "\t\"members\": [", nwids, + _instanceId.c_str(), _jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,0)).c_str(), (sqlite3_column_int(_sGetNetworkById,1) > 0) ? "true" : "false", (sqlite3_column_int(_sGetNetworkById,2) > 0) ? "true" : "false", @@ -1657,14 +1664,10 @@ unsigned int SqliteNetworkController::_doCPGet( } else { // GET /controller returns status and API version if controller is supported - sqlite3_reset(_sGetConfig); - sqlite3_bind_text(_sGetConfig,1,"instanceId",10,SQLITE_STATIC); - if (sqlite3_step(_sGetConfig) == SQLITE_ROW) { - Utils::snprintf(json,sizeof(json),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu,\n\t\"instanceId\": \"%s\"\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now(),(const char *)sqlite3_column_text(_sGetConfig,0)); - responseBody = json; - responseContentType = "applicaiton/json"; - return 200; - } else return 500; + Utils::snprintf(json,sizeof(json),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu,\n\t\"instanceId\": \"%s\"\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now(),_instanceId.c_str()); + responseBody = json; + responseContentType = "applicaiton/json"; + return 200; } return 404; diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index 95905bd1..8b39f7d9 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -96,6 +96,7 @@ private: std::string &responseContentType); std::string _dbPath; + std::string _instanceId; sqlite3 *_db; sqlite3_stmt *_sGetNetworkById; -- cgit v1.2.3 From b343eac10d2c7cc86f1e6d98edaf8034095e1829 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 21 Jul 2015 12:42:43 -0700 Subject: Fix IP auto-assign bug due to missing subnet routes. --- controller/SqliteNetworkController.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 0986f7b5..a39982a6 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -175,7 +175,7 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) : ||(sqlite3_prepare_v2(_db,"DELETE FROM IpAssignmentPool WHERE networkId = ?",-1,&_sDeleteIpAssignmentPoolsForNetwork,(const char **)0) != SQLITE_OK) /* IpAssignment */ - ||(sqlite3_prepare_v2(_db,"SELECT \"type\",ip,ipNetmaskBits FROM IpAssignment WHERE networkId = ? AND nodeId = ? AND ipVersion = ?",-1,&_sGetIpAssignmentsForNode,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"SELECT \"type\",ip,ipNetmaskBits FROM IpAssignment WHERE networkId = ? AND (nodeId = ? OR nodeId IS NULL) AND ipVersion = ?",-1,&_sGetIpAssignmentsForNode,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"SELECT ip,ipNetmaskBits,ipVersion FROM IpAssignment WHERE networkId = ? AND nodeId = ? AND \"type\" = ? ORDER BY ip ASC",-1,&_sGetIpAssignmentsForNode2,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"SELECT ip,ipNetmaskBits,ipVersion FROM IpAssignment WHERE networkId = ? AND nodeId IS NULL AND \"type\" = ?",-1,&_sGetLocalRoutes,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"SELECT 1 FROM IpAssignment WHERE networkId = ? AND ip = ? AND ipVersion = ? AND \"type\" = ?",-1,&_sCheckIfIpIsAllocated,(const char **)0) != SQLITE_OK) @@ -329,7 +329,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co sqlite3_bind_text(_sCreateNode,1,member.nodeId,10,SQLITE_STATIC); sqlite3_bind_text(_sCreateNode,2,idstr.c_str(),-1,SQLITE_STATIC); if (sqlite3_step(_sCreateNode) != SQLITE_DONE) { - netconf["error"] = "unable to create new node record"; + netconf["error"] = "unable to create new Node record"; return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; } } @@ -349,7 +349,9 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co network.creationTime = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7); network.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,8); network.memberRevisionCounter = (uint64_t)sqlite3_column_int64(_sGetNetworkById,9); - } else return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND; + } else { + return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND; + } // Fetch Member record -- cgit v1.2.3 From a061aa3d872c25a845cf698d2d6e95863297834e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 21 Jul 2015 12:57:01 -0700 Subject: Remove "members" from Network record and instead enumerate members via specific query to /network/nwid/member sub-path. More RESTful, scalable, and compatible with how OnePoint code works. --- controller/SqliteNetworkController.cpp | 233 +++++++++++++++++---------------- service/README.md | 1 - 2 files changed, 122 insertions(+), 112 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index a39982a6..1d121ed3 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -1278,111 +1278,136 @@ unsigned int SqliteNetworkController::_doCPGet( Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); if (path.size() >= 3) { - if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { - uint64_t address = Utils::hexStrToU64(path[3].c_str()); - char addrs[24]; - Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address); - - sqlite3_reset(_sGetMember2); - sqlite3_bind_text(_sGetMember2,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sGetMember2,2,addrs,10,SQLITE_STATIC); - if (sqlite3_step(_sGetMember2) == SQLITE_ROW) { - Utils::snprintf(json,sizeof(json), - "{\n" - "\t\"nwid\": \"%s\",\n" - "\t\"address\": \"%s\",\n" - "\t\"controllerInstanceId\": \"%s\",\n" - "\t\"authorized\": %s,\n" - "\t\"activeBridge\": %s,\n" - "\t\"memberRevision\": %llu,\n" - "\t\"identity\": \"%s\",\n" - "\t\"ipAssignments\": [", - nwids, - addrs, - _instanceId.c_str(), - (sqlite3_column_int(_sGetMember2,0) > 0) ? "true" : "false", - (sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false", - (unsigned long long)sqlite3_column_int64(_sGetMember2,2), - _jsonEscape((const char *)sqlite3_column_text(_sGetMember2,3)).c_str()); - responseBody = json; - - sqlite3_reset(_sGetIpAssignmentsForNode2); - sqlite3_bind_text(_sGetIpAssignmentsForNode2,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sGetIpAssignmentsForNode2,2,addrs,10,SQLITE_STATIC); - sqlite3_bind_int(_sGetIpAssignmentsForNode2,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); - bool firstIp = true; - while (sqlite3_step(_sGetIpAssignmentsForNode2) == SQLITE_ROW) { - int ipversion = sqlite3_column_int(_sGetIpAssignmentsForNode2,2); - char ipBlob[16]; - memcpy(ipBlob,(const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode2,0),16); - InetAddress ip( - (const void *)(ipversion == 6 ? ipBlob : &ipBlob[12]), - (ipversion == 6 ? 16 : 4), - (unsigned int)sqlite3_column_int(_sGetIpAssignmentsForNode2,1) - ); - responseBody.append(firstIp ? "\"" : ",\""); - firstIp = false; - responseBody.append(_jsonEscape(ip.toString())); - responseBody.push_back('"'); - } + // /network//... + + if (path[2] == "member") { + + if (path.size() >= 4) { + // Get specific member info + + uint64_t address = Utils::hexStrToU64(path[3].c_str()); + char addrs[24]; + Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address); + + sqlite3_reset(_sGetMember2); + sqlite3_bind_text(_sGetMember2,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sGetMember2,2,addrs,10,SQLITE_STATIC); + if (sqlite3_step(_sGetMember2) == SQLITE_ROW) { + Utils::snprintf(json,sizeof(json), + "{\n" + "\t\"nwid\": \"%s\",\n" + "\t\"address\": \"%s\",\n" + "\t\"controllerInstanceId\": \"%s\",\n" + "\t\"authorized\": %s,\n" + "\t\"activeBridge\": %s,\n" + "\t\"memberRevision\": %llu,\n" + "\t\"identity\": \"%s\",\n" + "\t\"ipAssignments\": [", + nwids, + addrs, + _instanceId.c_str(), + (sqlite3_column_int(_sGetMember2,0) > 0) ? "true" : "false", + (sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false", + (unsigned long long)sqlite3_column_int64(_sGetMember2,2), + _jsonEscape((const char *)sqlite3_column_text(_sGetMember2,3)).c_str()); + responseBody = json; + + sqlite3_reset(_sGetIpAssignmentsForNode2); + sqlite3_bind_text(_sGetIpAssignmentsForNode2,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sGetIpAssignmentsForNode2,2,addrs,10,SQLITE_STATIC); + sqlite3_bind_int(_sGetIpAssignmentsForNode2,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); + bool firstIp = true; + while (sqlite3_step(_sGetIpAssignmentsForNode2) == SQLITE_ROW) { + int ipversion = sqlite3_column_int(_sGetIpAssignmentsForNode2,2); + char ipBlob[16]; + memcpy(ipBlob,(const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode2,0),16); + InetAddress ip( + (const void *)(ipversion == 6 ? ipBlob : &ipBlob[12]), + (ipversion == 6 ? 16 : 4), + (unsigned int)sqlite3_column_int(_sGetIpAssignmentsForNode2,1) + ); + responseBody.append(firstIp ? "\"" : ",\""); + firstIp = false; + responseBody.append(_jsonEscape(ip.toString())); + responseBody.push_back('"'); + } - responseBody.append("]"); - - /* It's possible to get the actual netconf dictionary by including these - * three URL arguments. The member identity must be the string - * serialized identity of this member, and the signing identity must be - * the full secret identity of this network controller. The have revision - * is optional but would designate the revision our hypothetical client - * already has. - * - * This is primarily for testing and is not used in production. It makes - * it easy to test the entire network controller via its JSON API. - * - * If these arguments are included, three more object fields are returned: - * 'netconf', 'netconfResult', and 'netconfResultMessage'. These are all - * string fields and contain the actual netconf dictionary, the query - * result code, and any verbose message e.g. an error description. */ - std::map::const_iterator memids(urlArgs.find("memberIdentity")); - std::map::const_iterator sigids(urlArgs.find("signingIdentity")); - std::map::const_iterator hrs(urlArgs.find("haveRevision")); - if ((memids != urlArgs.end())&&(sigids != urlArgs.end())) { - Dictionary netconf; - Identity memid,sigid; - try { - if (memid.fromString(memids->second)&&sigid.fromString(sigids->second)&&sigid.hasPrivate()) { - uint64_t hr = 0; - if (hrs != urlArgs.end()) - hr = Utils::strToU64(hrs->second.c_str()); - const char *result = ""; - switch(this->doNetworkConfigRequest(InetAddress(),sigid,memid,nwid,Dictionary(),hr,netconf)) { - case NetworkController::NETCONF_QUERY_OK: result = "OK"; break; - case NetworkController::NETCONF_QUERY_OK_BUT_NOT_NEWER: result = "OK_BUT_NOT_NEWER"; break; - case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: result = "OBJECT_NOT_FOUND"; break; - case NetworkController::NETCONF_QUERY_ACCESS_DENIED: result = "ACCESS_DENIED"; break; - case NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR: result = "INTERNAL_SERVER_ERROR"; break; - default: result = "(unrecognized result code)"; break; + responseBody.append("]"); + + /* It's possible to get the actual netconf dictionary by including these + * three URL arguments. The member identity must be the string + * serialized identity of this member, and the signing identity must be + * the full secret identity of this network controller. The have revision + * is optional but would designate the revision our hypothetical client + * already has. + * + * This is primarily for testing and is not used in production. It makes + * it easy to test the entire network controller via its JSON API. + * + * If these arguments are included, three more object fields are returned: + * 'netconf', 'netconfResult', and 'netconfResultMessage'. These are all + * string fields and contain the actual netconf dictionary, the query + * result code, and any verbose message e.g. an error description. */ + std::map::const_iterator memids(urlArgs.find("memberIdentity")); + std::map::const_iterator sigids(urlArgs.find("signingIdentity")); + std::map::const_iterator hrs(urlArgs.find("haveRevision")); + if ((memids != urlArgs.end())&&(sigids != urlArgs.end())) { + Dictionary netconf; + Identity memid,sigid; + try { + if (memid.fromString(memids->second)&&sigid.fromString(sigids->second)&&sigid.hasPrivate()) { + uint64_t hr = 0; + if (hrs != urlArgs.end()) + hr = Utils::strToU64(hrs->second.c_str()); + const char *result = ""; + switch(this->doNetworkConfigRequest(InetAddress(),sigid,memid,nwid,Dictionary(),hr,netconf)) { + case NetworkController::NETCONF_QUERY_OK: result = "OK"; break; + case NetworkController::NETCONF_QUERY_OK_BUT_NOT_NEWER: result = "OK_BUT_NOT_NEWER"; break; + case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: result = "OBJECT_NOT_FOUND"; break; + case NetworkController::NETCONF_QUERY_ACCESS_DENIED: result = "ACCESS_DENIED"; break; + case NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR: result = "INTERNAL_SERVER_ERROR"; break; + default: result = "(unrecognized result code)"; break; + } + responseBody.append(",\n\t\"netconf\": \""); + responseBody.append(_jsonEscape(netconf.toString().c_str())); + responseBody.append("\",\n\t\"netconfResult\": \""); + responseBody.append(result); + responseBody.append("\",\n\t\"netconfResultMessage\": \""); + responseBody.append(_jsonEscape(netconf["error"].c_str())); + responseBody.append("\""); + } else { + responseBody.append(",\n\t\"netconf\": \"\",\n\t\"netconfResult\": \"INTERNAL_SERVER_ERROR\",\n\t\"netconfResultMessage\": \"invalid member or signing identity\""); } - responseBody.append(",\n\t\"netconf\": \""); - responseBody.append(_jsonEscape(netconf.toString().c_str())); - responseBody.append("\",\n\t\"netconfResult\": \""); - responseBody.append(result); - responseBody.append("\",\n\t\"netconfResultMessage\": \""); - responseBody.append(_jsonEscape(netconf["error"].c_str())); - responseBody.append("\""); - } else { - responseBody.append(",\n\t\"netconf\": \"\",\n\t\"netconfResult\": \"INTERNAL_SERVER_ERROR\",\n\t\"netconfResultMessage\": \"invalid member or signing identity\""); + } catch ( ... ) { + responseBody.append(",\n\t\"netconf\": \"\",\n\t\"netconfResult\": \"INTERNAL_SERVER_ERROR\",\n\t\"netconfResultMessage\": \"unexpected exception\""); } - } catch ( ... ) { - responseBody.append(",\n\t\"netconf\": \"\",\n\t\"netconfResult\": \"INTERNAL_SERVER_ERROR\",\n\t\"netconfResultMessage\": \"unexpected exception\""); } - } - responseBody.append("\n}\n"); + responseBody.append("\n}\n"); + + responseContentType = "application/json"; + return 200; + } // else 404 + } else { + // List members + + sqlite3_reset(_sListNetworkMembers); + sqlite3_bind_text(_sListNetworkMembers,1,nwids,16,SQLITE_STATIC); + while (sqlite3_step(_sListNetworkMembers) == SQLITE_ROW) { + responseBody.push_back((responseBody.length()) ? ',' : '['); + responseBody.push_back('"'); + responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,0)); + responseBody.push_back('"'); + } + responseBody.push_back(']'); responseContentType = "application/json"; return 200; - } // else 404 + + } + } // else 404 + } else { // get network info sqlite3_reset(_sGetNetworkById); @@ -1402,7 +1427,7 @@ unsigned int SqliteNetworkController::_doCPGet( "\t\"creationTime\": %llu,\n" "\t\"revision\": %llu,\n" "\t\"memberRevisionCounter\": %llu,\n" - "\t\"members\": [", + "\t\"relays\": [", nwids, _instanceId.c_str(), _jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,0)).c_str(), @@ -1417,20 +1442,6 @@ unsigned int SqliteNetworkController::_doCPGet( (unsigned long long)sqlite3_column_int64(_sGetNetworkById,9)); responseBody = json; - sqlite3_reset(_sListNetworkMembers); - sqlite3_bind_text(_sListNetworkMembers,1,nwids,16,SQLITE_STATIC); - bool firstMember = true; - while (sqlite3_step(_sListNetworkMembers) == SQLITE_ROW) { - if (!firstMember) - responseBody.push_back(','); - responseBody.push_back('"'); - responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,0)); - responseBody.push_back('"'); - firstMember = false; - } - - responseBody.append("],\n\t\"relays\": ["); - sqlite3_reset(_sGetRelays); sqlite3_bind_text(_sGetRelays,1,nwids,16,SQLITE_STATIC); bool firstRelay = true; diff --git a/service/README.md b/service/README.md index d2dc6e91..668aab05 100644 --- a/service/README.md +++ b/service/README.md @@ -173,7 +173,6 @@ To create a new network with a random last six digits safely and atomically, you creationTimeintegerTime network was created in ms since epochno revisionintegerNetwork config revision numberno memberRevisionCounterintegerCurrent value of network revision counter (incremented after every member add or revision)no -members[string]Array of ZeroTier addresses of network membersno relays[object]Array of network-specific relay nodes (see below)yes ipLocalRoutes[string]Array of IP network/netmask entries corresponding to networks routed directly via this interface (e.g. 10.0.0.0/8 to route 10.0.0.0 via this interface) ipAssignmentPools[object]Array of IP auto-assignment pools for 'zt' assignment modeyes -- cgit v1.2.3 From 3f8a5b8b7690e8572d310bcca532602c503ff2c4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 21 Jul 2015 13:38:59 -0700 Subject: List members in the form of a hash of member ID and member revision so code can quickly detect which members have changed. --- controller/SqliteNetworkController.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 1d121ed3..c663d1de 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -193,7 +193,7 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) : ||(sqlite3_prepare_v2(_db,"SELECT m.authorized,m.activeBridge,m.memberRevision,n.identity FROM Member AS m JOIN Node AS n ON n.id = m.nodeId WHERE m.networkId = ? AND m.nodeId = ?",-1,&_sGetMember2,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"INSERT INTO Member (networkId,nodeId,authorized,activeBridge,memberRevision) VALUES (?,?,?,0,(SELECT memberRevisionCounter FROM Network WHERE id = ?))",-1,&_sCreateMember,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"SELECT nodeId FROM Member WHERE networkId = ? AND activeBridge > 0 AND authorized > 0",-1,&_sGetActiveBridges,(const char **)0) != SQLITE_OK) - ||(sqlite3_prepare_v2(_db,"SELECT m.nodeId FROM Member AS m WHERE m.networkId = ? ORDER BY m.nodeId ASC",-1,&_sListNetworkMembers,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"SELECT m.nodeId,m.memberRevision FROM Member AS m WHERE m.networkId = ? ORDER BY m.nodeId ASC",-1,&_sListNetworkMembers,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"UPDATE Member SET authorized = ?,memberRevision = (SELECT memberRevisionCounter FROM Network WHERE id = ?) WHERE rowid = ?",-1,&_sUpdateMemberAuthorized,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"UPDATE Member SET activeBridge = ?,memberRevision = (SELECT memberRevisionCounter FROM Network WHERE id = ?) WHERE rowid = ?",-1,&_sUpdateMemberActiveBridge,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"DELETE FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sDeleteMember,(const char **)0) != SQLITE_OK) @@ -1395,10 +1395,11 @@ unsigned int SqliteNetworkController::_doCPGet( sqlite3_reset(_sListNetworkMembers); sqlite3_bind_text(_sListNetworkMembers,1,nwids,16,SQLITE_STATIC); while (sqlite3_step(_sListNetworkMembers) == SQLITE_ROW) { - responseBody.push_back((responseBody.length()) ? ',' : '['); - responseBody.push_back('"'); + responseBody.append((responseBody.length() > 0) ? ",{\"" : "[{\""); responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,0)); - responseBody.push_back('"'); + responseBody.append("\":"); + responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,1)); + responseBody.push_back('}'); } responseBody.push_back(']'); responseContentType = "application/json"; -- cgit v1.2.3 From 7c761dea722621a929d42bdc79e2312d1b30a1e1 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 21 Jul 2015 14:12:22 -0700 Subject: Fix to member listing: I wanted an object with member IDs as keys and member revisions as values, not an array. --- controller/SqliteNetworkController.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index c663d1de..700b3561 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -1395,13 +1395,12 @@ unsigned int SqliteNetworkController::_doCPGet( sqlite3_reset(_sListNetworkMembers); sqlite3_bind_text(_sListNetworkMembers,1,nwids,16,SQLITE_STATIC); while (sqlite3_step(_sListNetworkMembers) == SQLITE_ROW) { - responseBody.append((responseBody.length() > 0) ? ",{\"" : "[{\""); + responseBody.append((responseBody.length() > 0) ? ",\"" : "{\""); responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,0)); responseBody.append("\":"); responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,1)); - responseBody.push_back('}'); } - responseBody.push_back(']'); + responseBody.push_back('}'); responseContentType = "application/json"; return 200; -- cgit v1.2.3