database.cpp 8.73 KB
Newer Older
1 2 3
#include "biboumi.h"
#ifdef USE_DATABASE

4
#include <database/database.hpp>
5
#include <uuid/uuid.h>
6
#include <utils/get_first_non_empty.hpp>
7
#include <utils/time.hpp>
8

9 10
#include <config/config.hpp>

11 12
#include <database/index.hpp>

13
#include <sqlite3.h>
14

15 16 17 18 19
sqlite3* Database::db;
Database::MucLogLineTable Database::muc_log_lines("MucLogLine_");
Database::GlobalOptionsTable Database::global_options("GlobalOptions_");
Database::IrcServerOptionsTable Database::irc_server_options("IrcServerOptions_");
Database::IrcChannelOptionsTable Database::irc_channel_options("IrcChannelOptions_");
20
Database::RosterTable Database::roster("roster");
21
std::map<Database::CacheKey, Database::EncodingIn::real_type> Database::encoding_in_cache{};
22

23 24 25
Database::GlobalPersistent::GlobalPersistent():
    Column<bool>{Config::get_bool("persistent_by_default", false)}
{}
26

27
void Database::open(const std::string& filename)
28
{
29 30 31 32 33
  // Try to open the specified database.
  // Close and replace the previous database pointer if it succeeded. If it did
  // not, just leave things untouched
  sqlite3* new_db;
  auto res = sqlite3_open_v2(filename.data(), &new_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
34
  Database::close();
35 36
  if (res != SQLITE_OK)
    {
37
      log_error("Failed to open database file ", filename, ": ", sqlite3_errmsg(new_db));
38
      sqlite3_close(new_db);
39 40 41
      throw std::runtime_error("");
    }
  Database::db = new_db;
42
  Database::muc_log_lines.create(Database::db);
43
  Database::muc_log_lines.upgrade(Database::db);
44
  Database::global_options.create(Database::db);
45
  Database::global_options.upgrade(Database::db);
46
  Database::irc_server_options.create(Database::db);
47
  Database::irc_server_options.upgrade(Database::db);
48
  Database::irc_channel_options.create(Database::db);
49
  Database::irc_channel_options.upgrade(Database::db);
50 51
  Database::roster.create(Database::db);
  Database::roster.upgrade(Database::db);
52
  create_index<Database::Owner, Database::IrcChanName, Database::IrcServerName>(Database::db, "archive_index", Database::muc_log_lines.get_name());
53 54 55
}


56
Database::GlobalOptions Database::get_global_options(const std::string& owner)
57
{
58 59
  auto request = Database::global_options.select();
  request.where() << Owner{} << "=" << owner;
60 61 62 63 64 65 66 67

  Database::GlobalOptions options{Database::global_options.get_name()};
  auto result = request.execute(Database::db);
  if (result.size() == 1)
    options = result.front();
  else
    options.col<Owner>() = owner;
  return options;
68 69
}

70
Database::IrcServerOptions Database::get_irc_server_options(const std::string& owner, const std::string& server)
71
{
72 73
  auto request = Database::irc_server_options.select();
  request.where() << Owner{} << "=" << owner << " and " << Server{} << "=" << server;
74 75 76 77 78 79 80 81 82 83 84

  Database::IrcServerOptions options{Database::irc_server_options.get_name()};
  auto result = request.execute(Database::db);
  if (result.size() == 1)
    options = result.front();
  else
    {
      options.col<Owner>() = owner;
      options.col<Server>() = server;
    }
  return options;
85 86
}

87
Database::IrcChannelOptions Database::get_irc_channel_options(const std::string& owner, const std::string& server, const std::string& channel)
88
{
89 90 91 92
  auto request = Database::irc_channel_options.select();
  request.where() << Owner{} << "=" << owner <<\
          " and " << Server{} << "=" << server <<\
          " and " << Channel{} << "=" << channel;
93 94 95 96 97 98 99 100 101 102 103
  Database::IrcChannelOptions options{Database::irc_channel_options.get_name()};
  auto result = request.execute(Database::db);
  if (result.size() == 1)
    options = result.front();
  else
    {
      options.col<Owner>() = owner;
      options.col<Server>() = server;
      options.col<Channel>() = channel;
    }
  return options;
104 105
}

106 107
Database::IrcChannelOptions Database::get_irc_channel_options_with_server_default(const std::string& owner, const std::string& server,
                                                                                  const std::string& channel)
108 109 110
{
  auto coptions = Database::get_irc_channel_options(owner, server, channel);
  auto soptions = Database::get_irc_server_options(owner, server);
111

112 113 114 115
  coptions.col<EncodingIn>() = get_first_non_empty(coptions.col<EncodingIn>(),
                                                   soptions.col<EncodingIn>());
  coptions.col<EncodingOut>() = get_first_non_empty(coptions.col<EncodingOut>(),
                                                    soptions.col<EncodingOut>());
116

117 118
  coptions.col<MaxHistoryLength>() = get_first_non_empty(coptions.col<MaxHistoryLength>(),
                                                         soptions.col<MaxHistoryLength>());
119 120 121 122

  return coptions;
}

123
Database::IrcChannelOptions Database::get_irc_channel_options_with_server_and_global_default(const std::string& owner, const std::string& server, const std::string& channel)
124 125 126 127 128
{
  auto coptions = Database::get_irc_channel_options(owner, server, channel);
  auto soptions = Database::get_irc_server_options(owner, server);
  auto goptions = Database::get_global_options(owner);

129 130
  coptions.col<EncodingIn>() = get_first_non_empty(coptions.col<EncodingIn>(),
                                                   soptions.col<EncodingIn>());
131

132 133 134 135 136 137
  coptions.col<EncodingOut>() = get_first_non_empty(coptions.col<EncodingOut>(),
                                                    soptions.col<EncodingOut>());

  coptions.col<MaxHistoryLength>() = get_first_non_empty(coptions.col<MaxHistoryLength>(),
                                                         soptions.col<MaxHistoryLength>(),
                                                         goptions.col<MaxHistoryLength>());
138

139 140 141
  return coptions;
}

142 143 144
std::string Database::store_muc_message(const std::string& owner, const std::string& chan_name,
                                        const std::string& server_name, Database::time_point date,
                                        const std::string& body, const std::string& nick)
145
{
146
  auto line = Database::muc_log_lines.row();
147

148 149
  auto uuid = Database::gen_uuid();

150 151 152 153 154 155 156
  line.col<Uuid>() = uuid;
  line.col<Owner>() = owner;
  line.col<IrcChanName>() = chan_name;
  line.col<IrcServerName>() = server_name;
  line.col<Date>() = std::chrono::duration_cast<std::chrono::seconds>(date.time_since_epoch()).count();
  line.col<Body>() = body;
  line.col<Nick>() = nick;
157

158
  line.save(Database::db);
159 160

  return uuid;
161 162
}

163
std::vector<Database::MucLogLine> Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server,
164
                                                   int limit, const std::string& start, const std::string& end)
165
{
166 167 168 169
  auto request = Database::muc_log_lines.select();
  request.where() << Database::Owner{} << "=" << owner << \
          " and " << Database::IrcChanName{} << "=" << chan_name << \
          " and " << Database::IrcServerName{} << "=" << server;
170 171 172 173 174

  if (!start.empty())
    {
      const auto start_time = utils::parse_datetime(start);
      if (start_time != -1)
175
        request << " and " << Database::Date{} << ">=" << start_time;
176 177 178 179 180
    }
  if (!end.empty())
    {
      const auto end_time = utils::parse_datetime(end);
      if (end_time != -1)
181
        request << " and " << Database::Date{} << "<=" << end_time;
182
    }
183 184 185 186 187 188 189 190 191

  request.order_by() << Id{} << " DESC ";

  if (limit >= 0)
    request.limit() << limit;

  auto result = request.execute(Database::db);

  return {result.crbegin(), result.crend()};
192 193
}

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
void Database::add_roster_item(const std::string& local, const std::string& remote)
{
  auto roster_item = Database::roster.row();

  roster_item.col<Database::LocalJid>() = local;
  roster_item.col<Database::RemoteJid>() = remote;

  roster_item.save(Database::db);
}

void Database::delete_roster_item(const std::string& local, const std::string& remote)
{
  Query query("DELETE FROM "s + Database::roster.get_name());
  query << " WHERE " << Database::RemoteJid{} << "=" << remote << \
           " AND " << Database::LocalJid{} << "=" << local;

  query.execute(Database::db);
}

bool Database::has_roster_item(const std::string& local, const std::string& remote)
{
  auto query = Database::roster.select();
  query.where() << Database::LocalJid{} << "=" << local << \
        " and " << Database::RemoteJid{} << "=" << remote;

  auto res = query.execute(Database::db);

  return !res.empty();
}

std::vector<Database::RosterItem> Database::get_contact_list(const std::string& local)
{
  auto query = Database::roster.select();
  query.where() << Database::LocalJid{} << "=" << local;

  return query.execute(Database::db);
}

std::vector<Database::RosterItem> Database::get_full_roster()
{
  auto query = Database::roster.select();

  return query.execute(Database::db);
}

239 240
void Database::close()
{
241
  sqlite3_close(Database::db);
242
  Database::db = nullptr;
243
}
244

245 246 247 248 249 250 251 252 253
std::string Database::gen_uuid()
{
  char uuid_str[37];
  uuid_t uuid;
  uuid_generate(uuid);
  uuid_unparse(uuid, uuid_str);
  return uuid_str;
}

254
#endif