blob: 7b8115dc2156b5b0804ad0450d74548bd2e18bdd [file] [log] [blame]
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2013-2016, Regents of the University of California.
Alexander Afanasyev71b43e72012-12-27 01:03:43 -08004 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -08005 * This file is part of ChronoShare, a decentralized file sharing application over NDN.
Alexander Afanasyev71b43e72012-12-27 01:03:43 -08006 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -08007 * 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 Afanasyev71b43e72012-12-27 01:03:43 -080010 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -080011 * 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 Afanasyev71b43e72012-12-27 01:03:43 -080014 *
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -080015 * 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 Afanasyev71b43e72012-12-27 01:03:43 -080019 */
20
Alexander Afanasyev242f8772013-01-01 23:26:31 -080021#include "db-helper.h"
Alexander Afanasyev8e2104a2013-01-22 10:56:18 -080022#include "logging.h"
23
Alexander Afanasyevae43c502012-12-29 17:26:37 -080024#include <boost/make_shared.hpp>
25#include <boost/ref.hpp>
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080026#include <boost/throw_exception.hpp>
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080027
Alexander Afanasyev8e2104a2013-01-22 10:56:18 -080028INIT_LOGGER ("DbHelper");
29
Alexander Afanasyevae43c502012-12-29 17:26:37 -080030using namespace boost;
Alexander Afanasyev68f2a952013-01-08 14:34:16 -080031namespace fs = boost::filesystem;
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080032
33const std::string INIT_DATABASE = "\
Alexander Afanasyev0995f322013-01-22 13:16:46 -080034 PRAGMA foreign_keys = ON; \
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080035";
36
Alexander Afanasyeva35756b2013-01-22 16:59:11 -080037DbHelper::DbHelper (const fs::path &path, const std::string &dbname)
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080038{
Alexander Afanasyeva35756b2013-01-22 16:59:11 -080039 fs::create_directories (path);
Alexander Afanasyev8e2104a2013-01-22 10:56:18 -080040
Alexander Afanasyeva35756b2013-01-22 16:59:11 -080041 int res = sqlite3_open((path / dbname).c_str (), &m_db);
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080042 if (res != SQLITE_OK)
43 {
44 BOOST_THROW_EXCEPTION (Error::Db ()
Alexander Afanasyeva35756b2013-01-22 16:59:11 -080045 << errmsg_info_str ("Cannot open/create dabatabase: [" + (path / dbname).string () + "]"));
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080046 }
Alexander Afanasyev8e2104a2013-01-22 10:56:18 -080047
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080048 res = sqlite3_create_function (m_db, "hash", 2, SQLITE_ANY, 0, 0,
Alexander Afanasyevae43c502012-12-29 17:26:37 -080049 DbHelper::hash_xStep, DbHelper::hash_xFinal);
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080050 if (res != SQLITE_OK)
51 {
52 BOOST_THROW_EXCEPTION (Error::Db ()
53 << errmsg_info_str ("Cannot create function ``hash''"));
54 }
55
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080056 res = sqlite3_create_function (m_db, "is_prefix", 2, SQLITE_ANY, 0, DbHelper::is_prefix_xFun, 0, 0);
57 if (res != SQLITE_OK)
58 {
59 BOOST_THROW_EXCEPTION (Error::Db ()
60 << errmsg_info_str ("Cannot create function ``is_prefix''"));
61 }
62
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080063 res = sqlite3_create_function (m_db, "directory_name", -1, SQLITE_ANY, 0, DbHelper::directory_name_xFun, 0, 0);
64 if (res != SQLITE_OK)
65 {
66 BOOST_THROW_EXCEPTION (Error::Db ()
67 << errmsg_info_str ("Cannot create function ``directory_name''"));
68 }
69
Alexander Afanasyev20fd84a2013-02-27 12:17:00 -080070 res = sqlite3_create_function (m_db, "is_dir_prefix", 2, SQLITE_ANY, 0, DbHelper::is_dir_prefix_xFun, 0, 0);
71 if (res != SQLITE_OK)
72 {
73 BOOST_THROW_EXCEPTION (Error::Db ()
74 << errmsg_info_str ("Cannot create function ``is_dir_prefix''"));
75 }
76
Alexander Afanasyev8e2104a2013-01-22 10:56:18 -080077 sqlite3_exec (m_db, INIT_DATABASE.c_str (), NULL, NULL, NULL);
78 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080079}
80
81DbHelper::~DbHelper ()
82{
83 int res = sqlite3_close (m_db);
84 if (res != SQLITE_OK)
85 {
86 // complain
87 }
88}
89
Alexander Afanasyevde1cdd02012-12-29 14:41:46 -080090void
Alexander Afanasyev71b43e72012-12-27 01:03:43 -080091DbHelper::hash_xStep (sqlite3_context *context, int argc, sqlite3_value **argv)
92{
93 if (argc != 2)
94 {
95 // _LOG_ERROR ("Wrong arguments are supplied for ``hash'' function");
96 sqlite3_result_error (context, "Wrong arguments are supplied for ``hash'' function", -1);
97 return;
98 }
Alexander Afanasyevd09871f2013-01-04 22:36:37 -080099 if (sqlite3_value_type (argv[0]) != SQLITE_BLOB ||
Alexander Afanasyev71b43e72012-12-27 01:03:43 -0800100 sqlite3_value_type (argv[1]) != SQLITE_INTEGER)
101 {
Alexander Afanasyevd09871f2013-01-04 22:36:37 -0800102 // _LOG_ERROR ("Hash expects (blob,integer) parameters");
103 sqlite3_result_error (context, "Hash expects (blob,integer) parameters", -1);
Alexander Afanasyev71b43e72012-12-27 01:03:43 -0800104 return;
105 }
Alexander Afanasyev8e2104a2013-01-22 10:56:18 -0800106
Alexander Afanasyev71b43e72012-12-27 01:03:43 -0800107 EVP_MD_CTX **hash_context = reinterpret_cast<EVP_MD_CTX **> (sqlite3_aggregate_context (context, sizeof (EVP_MD_CTX *)));
108
109 if (hash_context == 0)
110 {
111 sqlite3_result_error_nomem (context);
112 return;
113 }
114
115 if (*hash_context == 0)
116 {
117 *hash_context = EVP_MD_CTX_create ();
118 EVP_DigestInit_ex (*hash_context, HASH_FUNCTION (), 0);
119 }
Alexander Afanasyev8e2104a2013-01-22 10:56:18 -0800120
Alexander Afanasyevd09871f2013-01-04 22:36:37 -0800121 int nameBytes = sqlite3_value_bytes (argv[0]);
122 const void *name = sqlite3_value_blob (argv[0]);
Alexander Afanasyev71b43e72012-12-27 01:03:43 -0800123 sqlite3_int64 seqno = sqlite3_value_int64 (argv[1]);
124
125 EVP_DigestUpdate (*hash_context, name, nameBytes);
126 EVP_DigestUpdate (*hash_context, &seqno, sizeof(sqlite3_int64));
127}
128
129void
130DbHelper::hash_xFinal (sqlite3_context *context)
131{
132 EVP_MD_CTX **hash_context = reinterpret_cast<EVP_MD_CTX **> (sqlite3_aggregate_context (context, sizeof (EVP_MD_CTX *)));
133
134 if (hash_context == 0)
135 {
136 sqlite3_result_error_nomem (context);
137 return;
138 }
139
140 if (*hash_context == 0) // no rows
141 {
Alexander Afanasyevde1cdd02012-12-29 14:41:46 -0800142 char charNullResult = 0;
143 sqlite3_result_blob (context, &charNullResult, 1, SQLITE_TRANSIENT); //SQLITE_TRANSIENT forces to make a copy
Alexander Afanasyev71b43e72012-12-27 01:03:43 -0800144 return;
145 }
Alexander Afanasyev8e2104a2013-01-22 10:56:18 -0800146
Alexander Afanasyev71b43e72012-12-27 01:03:43 -0800147 unsigned char *hash = new unsigned char [EVP_MAX_MD_SIZE];
148 unsigned int hashLength = 0;
149
150 int ok = EVP_DigestFinal_ex (*hash_context,
151 hash, &hashLength);
152
153 sqlite3_result_blob (context, hash, hashLength, SQLITE_TRANSIENT); //SQLITE_TRANSIENT forces to make a copy
154 delete [] hash;
Alexander Afanasyev8e2104a2013-01-22 10:56:18 -0800155
Alexander Afanasyev71b43e72012-12-27 01:03:43 -0800156 EVP_MD_CTX_destroy (*hash_context);
157}
Alexander Afanasyevde1cdd02012-12-29 14:41:46 -0800158
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800159void
160DbHelper::is_prefix_xFun (sqlite3_context *context, int argc, sqlite3_value **argv)
161{
162 int len1 = sqlite3_value_bytes (argv[0]);
163 int len2 = sqlite3_value_bytes (argv[1]);
Alexander Afanasyevde1cdd02012-12-29 14:41:46 -0800164
Alexander Afanasyev026eaf32013-02-23 16:37:14 -0800165 if (len1 == 0)
166 {
167 sqlite3_result_int (context, 1);
168 return;
169 }
170
171 if (len1 > len2) // first parameter should be at most equal in length to the second one
172 {
173 sqlite3_result_int (context, 0);
174 return;
175 }
176
177 if (memcmp (sqlite3_value_blob (argv[0]), sqlite3_value_blob (argv[1]), len1) == 0)
178 {
179 sqlite3_result_int (context, 1);
180 }
181 else
182 {
183 sqlite3_result_int (context, 0);
184 }
185}
Alexander Afanasyevae43c502012-12-29 17:26:37 -0800186
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800187void
188DbHelper::directory_name_xFun (sqlite3_context *context, int argc, sqlite3_value **argv)
189{
190 if (argc != 1)
191 {
192 sqlite3_result_error (context, "``directory_name'' expects 1 text argument", -1);
193 sqlite3_result_null (context);
194 return;
195 }
196
197 if (sqlite3_value_bytes (argv[0]) == 0)
198 {
199 sqlite3_result_null (context);
200 return;
201 }
202
203 boost::filesystem::path filePath (std::string (reinterpret_cast<const char*> (sqlite3_value_text (argv[0])), sqlite3_value_bytes (argv[0])));
204 std::string dirPath = filePath.parent_path ().generic_string ();
205 // _LOG_DEBUG ("directory_name FUN: " << dirPath);
206 if (dirPath.size () == 0)
207 {
208 sqlite3_result_null (context);
209 }
210 else
211 {
212 sqlite3_result_text (context, dirPath.c_str (), dirPath.size (), SQLITE_TRANSIENT);
213 }
214}
Alexander Afanasyev20fd84a2013-02-27 12:17:00 -0800215
216void
217DbHelper::is_dir_prefix_xFun (sqlite3_context *context, int argc, sqlite3_value **argv)
218{
219 int len1 = sqlite3_value_bytes (argv[0]);
220 int len2 = sqlite3_value_bytes (argv[1]);
221
222 if (len1 == 0)
223 {
224 sqlite3_result_int (context, 1);
225 return;
226 }
227
228 if (len1 > len2) // first parameter should be at most equal in length to the second one
229 {
230 sqlite3_result_int (context, 0);
231 return;
232 }
233
234 if (memcmp (sqlite3_value_blob (argv[0]), sqlite3_value_blob (argv[1]), len1) == 0)
235 {
236 if (len1 == len2)
237 {
238 sqlite3_result_int (context, 1);
239 }
240 else
241 {
242 if (reinterpret_cast<const char*> (sqlite3_value_blob (argv[1]))[len1] == '/')
243 {
244 sqlite3_result_int (context, 1);
245 }
246 else
247 {
248 sqlite3_result_int (context, 0);
249 }
250 }
251 }
252 else
253 {
254 sqlite3_result_int (context, 0);
255 }
256}