1 #pragma once
2 
3 #include <biboumi.h>
4 #ifdef USE_DATABASE
5 
6 #include <database/table.hpp>
7 #include <database/column.hpp>
8 #include <database/count_query.hpp>
9 
10 #include <database/engine.hpp>
11 
12 #include <utils/optional_bool.hpp>
13 
14 #include <chrono>
15 #include <string>
16 
17 #include <memory>
18 #include <map>
19 
20 
21 class Database
22 {
23  public:
24   using time_point = std::chrono::system_clock::time_point;
25   struct RecordNotFound: public std::exception {};
26   enum class Paging { first, last };
27 
28   struct Uuid: Column<std::string> { static constexpr auto name = "uuid_"; };
29 
30   struct Owner: UnclearableColumn<std::string> { static constexpr auto name = "owner_"; };
31 
32   struct IrcChanName: UnclearableColumn<std::string> { static constexpr auto name = "ircchanname_"; };
33 
34   struct Channel: UnclearableColumn<std::string> { static constexpr auto name = "channel_"; };
35 
36   struct IrcServerName: UnclearableColumn<std::string> { static constexpr auto name = "ircservername_"; };
37 
38   struct Server: UnclearableColumn<std::string> { static constexpr auto name = "server_"; };
39 
40   struct Date: Column<time_point::rep> { static constexpr auto name = "date_"; };
41 
42   struct Body: Column<std::string> { static constexpr auto name = "body_"; };
43 
44   struct Nick: Column<std::string> { static constexpr auto name = "nick_"; };
45 
46   struct SaslPassword: Column<std::string> { static constexpr auto name = "saslpassword_"; };
47 
48   struct Pass: Column<std::string> { static constexpr auto name = "pass_"; };
49 
50   struct Ports: Column<std::string> { static constexpr auto name = "ports_";
PortsDatabase::Ports51     Ports(): Column<std::string>("6667") {} };
52 
53   struct TlsPorts: Column<std::string> { static constexpr auto name = "tlsports_";
TlsPortsDatabase::TlsPorts54     TlsPorts(): Column<std::string>("6697;6670") {} };
55 
56   struct Username: Column<std::string> { static constexpr auto name = "username_"; };
57 
58   struct Realname: Column<std::string> { static constexpr auto name = "realname_"; };
59 
60   struct AfterConnectionCommand: Column<std::string> { static constexpr auto name = "afterconnectioncommand_"; };
61 
62   struct TrustedFingerprint: Column<std::string> { static constexpr auto name = "trustedfingerprint_"; };
63 
64   struct EncodingOut: Column<std::string> { static constexpr auto name = "encodingout_"; };
65 
66   struct EncodingIn: Column<std::string> { static constexpr auto name = "encodingin_"; };
67 
68   struct MaxHistoryLength: Column<std::int64_t> { static constexpr auto name = "maxhistorylength_";
MaxHistoryLengthDatabase::MaxHistoryLength69     MaxHistoryLength(): Column<std::int64_t>(20) {} };
70 
71   struct RecordHistory: Column<bool> { static constexpr auto name = "recordhistory_";
RecordHistoryDatabase::RecordHistory72     RecordHistory(): Column<bool>(true) {}};
73 
74   struct RecordHistoryOptional: Column<OptionalBool> { static constexpr auto name = "recordhistory_"; };
75 
76   struct VerifyCert: Column<bool> { static constexpr auto name = "verifycert_";
VerifyCertDatabase::VerifyCert77     VerifyCert(): Column<bool>(true) {} };
78 
79   struct Persistent: Column<bool> { static constexpr auto name = "persistent_";
PersistentDatabase::Persistent80     Persistent(): Column<bool>(false) {} };
81 
82   struct GlobalPersistent: Column<bool> { static constexpr auto name = "persistent_";
83     GlobalPersistent(); };
84 
85   struct LocalJid: Column<std::string> { static constexpr auto name = "local"; };
86 
87   struct RemoteJid: Column<std::string> { static constexpr auto name = "remote"; };
88 
89   struct Address: Column<std::string> { static constexpr auto name = "address_"; };
90 
91   struct ThrottleLimit: Column<std::int64_t> { static constexpr auto name = "throttlelimit_";
ThrottleLimitDatabase::ThrottleLimit92       ThrottleLimit(): Column<std::int64_t>(10) {} };
93 
94   using MucLogLineTable = Table<Id, Uuid, Owner, IrcChanName, IrcServerName, Date, Body, Nick>;
95   using MucLogLine = MucLogLineTable::RowType;
96 
97   using GlobalOptionsTable = Table<Id, Owner, MaxHistoryLength, RecordHistory, GlobalPersistent>;
98   using GlobalOptions = GlobalOptionsTable::RowType;
99 
100   using IrcServerOptionsTable = Table<Id, Owner, Server, Pass, TlsPorts, Ports, Username, Realname, VerifyCert, TrustedFingerprint, EncodingOut, EncodingIn, MaxHistoryLength, Address, Nick, SaslPassword, ThrottleLimit>;
101   using IrcServerOptions = IrcServerOptionsTable::RowType;
102 
103   using IrcChannelOptionsTable = Table<Id, Owner, Server, Channel, EncodingOut, EncodingIn, MaxHistoryLength, Persistent, RecordHistoryOptional>;
104   using IrcChannelOptions = IrcChannelOptionsTable::RowType;
105 
106   using RosterTable = Table<LocalJid, RemoteJid>;
107   using RosterItem = RosterTable::RowType;
108 
109   using AfterConnectionCommandsTable = Table<Id, ForeignKey, AfterConnectionCommand>;
110   using AfterConnectionCommands = std::vector<AfterConnectionCommandsTable::RowType>;
111 
112   Database() = default;
113   ~Database() = default;
114 
115   Database(const Database&) = delete;
116   Database(Database&&) = delete;
117   Database& operator=(const Database&) = delete;
118   Database& operator=(Database&&) = delete;
119 
120   static GlobalOptions get_global_options(const std::string& owner);
121   static IrcServerOptions get_irc_server_options(const std::string& owner,
122                                                      const std::string& server);
123   static IrcChannelOptions get_irc_channel_options(const std::string& owner,
124                                                    const std::string& server,
125                                                    const std::string& channel);
126   static IrcChannelOptions get_irc_channel_options_with_server_default(const std::string& owner,
127                                                                        const std::string& server,
128                                                                        const std::string& channel);
129   static IrcChannelOptions get_irc_channel_options_with_server_and_global_default(const std::string& owner,
130                                                                                   const std::string& server,
131                                                                                   const std::string& channel);
132   static AfterConnectionCommands get_after_connection_commands(const IrcServerOptions& server_options);
133   static void set_after_connection_commands(const IrcServerOptions& server_options, AfterConnectionCommands& commands);
134 
135   /**
136    * Get all the lines between (optional) start and end dates, with a (optional) limit.
137    * If after_id is set, only the records after it will be returned.
138    */
139   static std::tuple<bool, std::vector<MucLogLine>> get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server,
140                                               std::size_t limit, const std::string& start="", const std::string& end="",
141                                               const Id::real_type reference_record_id=Id::unset_value, Paging=Paging::first);
142 
143   /**
144    * Get just one single record matching the given uuid, between (optional) end and start.
145    * If it does not exist (or is not between end and start), throw a RecordNotFound exception.
146    */
147   static MucLogLine get_muc_log(const std::string& owner, const std::string& chan_name, const std::string& server, const std::string& uuid, const std::string& start="", const std::string& end="");
148   static std::string store_muc_message(const std::string& owner, const std::string& chan_name, const std::string& server_name,
149                                        time_point date, const std::string& body, const std::string& nick);
150 
151   static void add_roster_item(const std::string& local, const std::string& remote);
152   static bool has_roster_item(const std::string& local, const std::string& remote);
153   static void delete_roster_item(const std::string& local, const std::string& remote);
154   static std::vector<Database::RosterItem> get_contact_list(const std::string& local);
155   static std::vector<Database::RosterItem> get_full_roster();
156 
157   static void close();
158   static void open(const std::string& filename);
159 
160   template <typename TableType>
count(const TableType & table)161   static int64_t count(const TableType& table)
162   {
163     CountQuery query{table.get_name()};
164     return query.execute(*Database::db);
165   }
166 
167   static MucLogLineTable muc_log_lines;
168   static GlobalOptionsTable global_options;
169   static IrcServerOptionsTable irc_server_options;
170   static IrcChannelOptionsTable irc_channel_options;
171   static RosterTable roster;
172   static AfterConnectionCommandsTable after_connection_commands;
173 
174   static std::unique_ptr<DatabaseEngine> db;
175 
176   /**
177    * Some caches, to avoid doing very frequent query requests for a few options.
178    */
179   using CacheKey = std::tuple<std::string, std::string, std::string>;
180 
get_encoding_in(const std::string & owner,const std::string & server,const std::string & channel)181   static EncodingIn::real_type get_encoding_in(const std::string& owner,
182                                         const std::string& server,
183                                         const std::string& channel)
184   {
185     CacheKey channel_key{owner, server, channel};
186     auto it = Database::encoding_in_cache.find(channel_key);
187     if (it == Database::encoding_in_cache.end())
188       {
189         auto options = Database::get_irc_channel_options_with_server_default(owner, server, channel);
190         EncodingIn::real_type result = options.col<Database::EncodingIn>();
191         if (result.empty())
192           result = "ISO-8859-1";
193         it = Database::encoding_in_cache.insert(std::make_pair(channel_key, result)).first;
194       }
195     return it->second;
196   }
invalidate_encoding_in_cache(const std::string & owner,const std::string & server,const std::string & channel)197   static void invalidate_encoding_in_cache(const std::string& owner,
198                                            const std::string& server,
199                                            const std::string& channel)
200   {
201     CacheKey channel_key{owner, server, channel};
202     Database::encoding_in_cache.erase(channel_key);
203   }
invalidate_encoding_in_cache()204   static void invalidate_encoding_in_cache()
205   {
206     Database::encoding_in_cache.clear();
207   }
208 
raw_exec(const std::string & query)209   static auto raw_exec(const std::string& query)
210   {
211     return Database::db->raw_exec(query);
212   }
213 
214  private:
215   static std::string gen_uuid();
216   static std::map<CacheKey, EncodingIn::real_type> encoding_in_cache;
217 };
218 
219 class Transaction
220 {
221 public:
222   Transaction();
223   ~Transaction();
224   bool success{false};
225 };
226 
227 #endif /* USE_DATABASE */
228