Alexander Afanasyev | fa2f662 | 2016-12-25 12:28:00 -0800 | [diff] [blame] | 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| 2 | /** |
Alexander Afanasyev | a9369b4 | 2017-01-11 11:58:00 -0800 | [diff] [blame] | 3 | * Copyright (c) 2013-2017, Regents of the University of California. |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 4 | * |
Alexander Afanasyev | fa2f662 | 2016-12-25 12:28:00 -0800 | [diff] [blame] | 5 | * This file is part of ChronoShare, a decentralized file sharing application over NDN. |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 6 | * |
Alexander Afanasyev | fa2f662 | 2016-12-25 12:28:00 -0800 | [diff] [blame] | 7 | * ChronoShare is free software: you can redistribute it and/or modify it under the terms |
| 8 | * of the GNU General Public License as published by the Free Software Foundation, either |
| 9 | * version 3 of the License, or (at your option) any later version. |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 10 | * |
Alexander Afanasyev | fa2f662 | 2016-12-25 12:28:00 -0800 | [diff] [blame] | 11 | * ChronoShare is distributed in the hope that it will be useful, but WITHOUT ANY |
| 12 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
| 13 | * PARTICULAR PURPOSE. See the GNU General Public License for more details. |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 14 | * |
Alexander Afanasyev | fa2f662 | 2016-12-25 12:28:00 -0800 | [diff] [blame] | 15 | * You should have received copies of the GNU General Public License along with |
| 16 | * ChronoShare, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>. |
| 17 | * |
| 18 | * See AUTHORS.md for complete list of ChronoShare authors and contributors. |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 19 | */ |
| 20 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 21 | #include "db-helper.hpp" |
| 22 | #include "core/logging.hpp" |
Alexander Afanasyev | 8e2104a | 2013-01-22 10:56:18 -0800 | [diff] [blame] | 23 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 24 | #include <ndn-cxx/util/digest.hpp> |
| 25 | |
| 26 | namespace ndn { |
| 27 | namespace chronoshare { |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 28 | |
Alexander Afanasyev | 1cf5c43 | 2017-01-13 23:22:15 -0800 | [diff] [blame] | 29 | _LOG_INIT(DbHelper); |
Alexander Afanasyev | 8e2104a | 2013-01-22 10:56:18 -0800 | [diff] [blame] | 30 | |
Alexander Afanasyev | 68f2a95 | 2013-01-08 14:34:16 -0800 | [diff] [blame] | 31 | namespace fs = boost::filesystem; |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 32 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 33 | using util::Sha256; |
| 34 | |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 35 | const std::string INIT_DATABASE = "\ |
Alexander Afanasyev | 0995f32 | 2013-01-22 13:16:46 -0800 | [diff] [blame] | 36 | PRAGMA foreign_keys = ON; \ |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 37 | "; |
| 38 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 39 | DbHelper::DbHelper(const fs::path& path, const std::string& dbname) |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 40 | { |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 41 | fs::create_directories(path); |
Alexander Afanasyev | 8e2104a | 2013-01-22 10:56:18 -0800 | [diff] [blame] | 42 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 43 | int res = sqlite3_open((path / dbname).c_str(), &m_db); |
| 44 | if (res != SQLITE_OK) { |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 45 | BOOST_THROW_EXCEPTION(Error("Cannot open/create database: [" + (path / dbname).string() + "]")); |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 46 | } |
Alexander Afanasyev | 8e2104a | 2013-01-22 10:56:18 -0800 | [diff] [blame] | 47 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 48 | res = sqlite3_create_function(m_db, "hash", 2, SQLITE_ANY, 0, 0, DbHelper::hash_xStep, |
| 49 | DbHelper::hash_xFinal); |
| 50 | if (res != SQLITE_OK) { |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 51 | BOOST_THROW_EXCEPTION(Error("Cannot create function ``hash''")); |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 52 | } |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 53 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 54 | res = sqlite3_create_function(m_db, "is_prefix", 2, SQLITE_ANY, 0, DbHelper::is_prefix_xFun, 0, 0); |
| 55 | if (res != SQLITE_OK) { |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 56 | BOOST_THROW_EXCEPTION(Error("Cannot create function ``is_prefix''")); |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 57 | } |
Alexander Afanasyev | 026eaf3 | 2013-02-23 16:37:14 -0800 | [diff] [blame] | 58 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 59 | res = sqlite3_create_function(m_db, "directory_name", -1, SQLITE_ANY, 0, |
| 60 | DbHelper::directory_name_xFun, 0, 0); |
| 61 | if (res != SQLITE_OK) { |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 62 | BOOST_THROW_EXCEPTION(Error("Cannot create function ``directory_name''")); |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 63 | } |
Alexander Afanasyev | 95f9f55 | 2013-02-26 23:05:20 -0800 | [diff] [blame] | 64 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 65 | res = sqlite3_create_function(m_db, "is_dir_prefix", 2, SQLITE_ANY, 0, |
| 66 | DbHelper::is_dir_prefix_xFun, 0, 0); |
| 67 | if (res != SQLITE_OK) { |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 68 | BOOST_THROW_EXCEPTION(Error("Cannot create function ``is_dir_prefix''")); |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 69 | } |
Alexander Afanasyev | 20fd84a | 2013-02-27 12:17:00 -0800 | [diff] [blame] | 70 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 71 | sqlite3_exec(m_db, INIT_DATABASE.c_str(), NULL, NULL, NULL); |
| 72 | _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db)); |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 73 | } |
| 74 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 75 | DbHelper::~DbHelper() |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 76 | { |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 77 | int res = sqlite3_close(m_db); |
| 78 | if (res != SQLITE_OK) { |
| 79 | // complain |
| 80 | } |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 81 | } |
| 82 | |
Alexander Afanasyev | de1cdd0 | 2012-12-29 14:41:46 -0800 | [diff] [blame] | 83 | void |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 84 | DbHelper::hash_xStep(sqlite3_context* context, int argc, sqlite3_value** argv) |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 85 | { |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 86 | if (argc != 2) { |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 87 | // _LOG_ERROR("Wrong arguments are supplied for ``hash'' function"); |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 88 | sqlite3_result_error(context, "Wrong arguments are supplied for ``hash'' function", -1); |
| 89 | return; |
| 90 | } |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 91 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 92 | if (sqlite3_value_type(argv[0]) != SQLITE_BLOB || sqlite3_value_type(argv[1]) != SQLITE_INTEGER) { |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 93 | // _LOG_ERROR("Hash expects(blob,integer) parameters"); |
| 94 | sqlite3_result_error(context, "Hash expects(blob,integer) parameters", -1); |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 95 | return; |
| 96 | } |
Alexander Afanasyev | 8e2104a | 2013-01-22 10:56:18 -0800 | [diff] [blame] | 97 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 98 | Sha256** digest = reinterpret_cast<Sha256**>(sqlite3_aggregate_context(context, sizeof(Sha256*))); |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 99 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 100 | if (digest == nullptr) { |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 101 | sqlite3_result_error_nomem(context); |
| 102 | return; |
| 103 | } |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 104 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 105 | if (*digest == nullptr) { |
| 106 | *digest = new Sha256(); |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 107 | } |
Alexander Afanasyev | 8e2104a | 2013-01-22 10:56:18 -0800 | [diff] [blame] | 108 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 109 | int nameBytes = sqlite3_value_bytes(argv[0]); |
| 110 | const void* name = sqlite3_value_blob(argv[0]); |
| 111 | sqlite3_int64 seqno = sqlite3_value_int64(argv[1]); |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 112 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 113 | (*digest)->update(reinterpret_cast<const uint8_t*>(name), nameBytes); |
| 114 | (*digest)->update(reinterpret_cast<const uint8_t*>(&seqno), sizeof(sqlite3_int64)); |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | void |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 118 | DbHelper::hash_xFinal(sqlite3_context* context) |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 119 | { |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 120 | Sha256** digest = reinterpret_cast<Sha256**>(sqlite3_aggregate_context(context, sizeof(Sha256*))); |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 121 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 122 | if (digest == nullptr) { |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 123 | sqlite3_result_error_nomem(context); |
| 124 | return; |
| 125 | } |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 126 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 127 | if (*digest == nullptr) { |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 128 | char charNullResult = 0; |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 129 | sqlite3_result_blob(context, &charNullResult, 1, SQLITE_TRANSIENT); |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 130 | return; |
| 131 | } |
Alexander Afanasyev | 8e2104a | 2013-01-22 10:56:18 -0800 | [diff] [blame] | 132 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 133 | shared_ptr<const Buffer> hash = (*digest)->computeDigest(); |
| 134 | sqlite3_result_blob(context, hash->buf(), hash->size(), SQLITE_TRANSIENT); |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 135 | |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 136 | delete *digest; |
Alexander Afanasyev | 71b43e7 | 2012-12-27 01:03:43 -0800 | [diff] [blame] | 137 | } |
Alexander Afanasyev | de1cdd0 | 2012-12-29 14:41:46 -0800 | [diff] [blame] | 138 | |
Alexander Afanasyev | 026eaf3 | 2013-02-23 16:37:14 -0800 | [diff] [blame] | 139 | void |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 140 | DbHelper::is_prefix_xFun(sqlite3_context* context, int argc, sqlite3_value** argv) |
Alexander Afanasyev | 026eaf3 | 2013-02-23 16:37:14 -0800 | [diff] [blame] | 141 | { |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 142 | int len1 = sqlite3_value_bytes(argv[0]); |
| 143 | int len2 = sqlite3_value_bytes(argv[1]); |
Alexander Afanasyev | de1cdd0 | 2012-12-29 14:41:46 -0800 | [diff] [blame] | 144 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 145 | if (len1 == 0) { |
| 146 | sqlite3_result_int(context, 1); |
| 147 | return; |
| 148 | } |
Alexander Afanasyev | 026eaf3 | 2013-02-23 16:37:14 -0800 | [diff] [blame] | 149 | |
| 150 | if (len1 > len2) // first parameter should be at most equal in length to the second one |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 151 | { |
| 152 | sqlite3_result_int(context, 0); |
| 153 | return; |
| 154 | } |
Alexander Afanasyev | 026eaf3 | 2013-02-23 16:37:14 -0800 | [diff] [blame] | 155 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 156 | if (memcmp(sqlite3_value_blob(argv[0]), sqlite3_value_blob(argv[1]), len1) == 0) { |
| 157 | sqlite3_result_int(context, 1); |
| 158 | } |
| 159 | else { |
| 160 | sqlite3_result_int(context, 0); |
| 161 | } |
Alexander Afanasyev | 026eaf3 | 2013-02-23 16:37:14 -0800 | [diff] [blame] | 162 | } |
Alexander Afanasyev | ae43c50 | 2012-12-29 17:26:37 -0800 | [diff] [blame] | 163 | |
Alexander Afanasyev | 95f9f55 | 2013-02-26 23:05:20 -0800 | [diff] [blame] | 164 | void |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 165 | DbHelper::directory_name_xFun(sqlite3_context* context, int argc, sqlite3_value** argv) |
Alexander Afanasyev | 95f9f55 | 2013-02-26 23:05:20 -0800 | [diff] [blame] | 166 | { |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 167 | if (argc != 1) { |
| 168 | sqlite3_result_error(context, "``directory_name'' expects 1 text argument", -1); |
| 169 | sqlite3_result_null(context); |
| 170 | return; |
| 171 | } |
Alexander Afanasyev | 95f9f55 | 2013-02-26 23:05:20 -0800 | [diff] [blame] | 172 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 173 | if (sqlite3_value_bytes(argv[0]) == 0) { |
| 174 | sqlite3_result_null(context); |
| 175 | return; |
| 176 | } |
Alexander Afanasyev | 95f9f55 | 2013-02-26 23:05:20 -0800 | [diff] [blame] | 177 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 178 | boost::filesystem::path filePath( |
| 179 | std::string(reinterpret_cast<const char*>(sqlite3_value_text(argv[0])), |
| 180 | sqlite3_value_bytes(argv[0]))); |
| 181 | std::string dirPath = filePath.parent_path().generic_string(); |
| 182 | // _LOG_DEBUG("directory_name FUN: " << dirPath); |
| 183 | if (dirPath.size() == 0) { |
| 184 | sqlite3_result_null(context); |
| 185 | } |
| 186 | else { |
| 187 | sqlite3_result_text(context, dirPath.c_str(), dirPath.size(), SQLITE_TRANSIENT); |
| 188 | } |
Alexander Afanasyev | 95f9f55 | 2013-02-26 23:05:20 -0800 | [diff] [blame] | 189 | } |
Alexander Afanasyev | 20fd84a | 2013-02-27 12:17:00 -0800 | [diff] [blame] | 190 | |
| 191 | void |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 192 | DbHelper::is_dir_prefix_xFun(sqlite3_context* context, int argc, sqlite3_value** argv) |
Alexander Afanasyev | 20fd84a | 2013-02-27 12:17:00 -0800 | [diff] [blame] | 193 | { |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 194 | int len1 = sqlite3_value_bytes(argv[0]); |
| 195 | int len2 = sqlite3_value_bytes(argv[1]); |
Alexander Afanasyev | 20fd84a | 2013-02-27 12:17:00 -0800 | [diff] [blame] | 196 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 197 | if (len1 == 0) { |
| 198 | sqlite3_result_int(context, 1); |
| 199 | return; |
| 200 | } |
Alexander Afanasyev | 20fd84a | 2013-02-27 12:17:00 -0800 | [diff] [blame] | 201 | |
| 202 | if (len1 > len2) // first parameter should be at most equal in length to the second one |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 203 | { |
| 204 | sqlite3_result_int(context, 0); |
| 205 | return; |
| 206 | } |
Alexander Afanasyev | 20fd84a | 2013-02-27 12:17:00 -0800 | [diff] [blame] | 207 | |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 208 | if (memcmp(sqlite3_value_blob(argv[0]), sqlite3_value_blob(argv[1]), len1) == 0) { |
| 209 | if (len1 == len2) { |
| 210 | sqlite3_result_int(context, 1); |
Alexander Afanasyev | 20fd84a | 2013-02-27 12:17:00 -0800 | [diff] [blame] | 211 | } |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 212 | else { |
| 213 | if (reinterpret_cast<const char*>(sqlite3_value_blob(argv[1]))[len1] == '/') { |
| 214 | sqlite3_result_int(context, 1); |
| 215 | } |
| 216 | else { |
| 217 | sqlite3_result_int(context, 0); |
| 218 | } |
Alexander Afanasyev | 20fd84a | 2013-02-27 12:17:00 -0800 | [diff] [blame] | 219 | } |
Alexander Afanasyev | eda3b7a | 2016-12-25 11:26:40 -0800 | [diff] [blame] | 220 | } |
| 221 | else { |
| 222 | sqlite3_result_int(context, 0); |
| 223 | } |
Alexander Afanasyev | 20fd84a | 2013-02-27 12:17:00 -0800 | [diff] [blame] | 224 | } |
Alexander Afanasyev | c7170bc | 2016-12-25 16:17:26 -0800 | [diff] [blame] | 225 | |
| 226 | } // chronoshare |
| 227 | } // ndn |