summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authoraunsane <aunsane@gmail.com>2018-09-23 00:17:09 +0300
committeraunsane <aunsane@gmail.com>2018-09-23 00:17:09 +0300
commit96fb67177a90fb3ce4e18cad0ef1a3c7409e4f2d (patch)
tree8217dfeb2fe25a1f43e19e0290b3feb90c7e61b3 /plugins
parentdafbc9f884d3c696ecaadb9e32b90e831b04427a (diff)
dbx_sqlite: initial commit (not for use)
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Dbx_sqlite/dbx_sqlite.vcxproj39
-rw-r--r--plugins/Dbx_sqlite/dbx_sqlite.vcxproj.filters4
-rw-r--r--plugins/Dbx_sqlite/res/dbx_sqlite.rc196
-rw-r--r--plugins/Dbx_sqlite/res/version.rc9
-rw-r--r--plugins/Dbx_sqlite/src/dbcontacts.cpp101
-rw-r--r--plugins/Dbx_sqlite/src/dbevents.cpp377
-rw-r--r--plugins/Dbx_sqlite/src/dbintf.cpp154
-rw-r--r--plugins/Dbx_sqlite/src/dbintf.h71
-rw-r--r--plugins/Dbx_sqlite/src/dbsettings.cpp368
-rw-r--r--plugins/Dbx_sqlite/src/main.cpp44
-rw-r--r--plugins/Dbx_sqlite/src/resource.h33
-rw-r--r--plugins/Dbx_sqlite/src/stdafx.cxx1
-rw-r--r--plugins/Dbx_sqlite/src/stdafx.h27
-rw-r--r--plugins/Dbx_sqlite/src/version.h13
14 files changed, 1437 insertions, 0 deletions
diff --git a/plugins/Dbx_sqlite/dbx_sqlite.vcxproj b/plugins/Dbx_sqlite/dbx_sqlite.vcxproj
new file mode 100644
index 0000000000..a49f988021
--- /dev/null
+++ b/plugins/Dbx_sqlite/dbx_sqlite.vcxproj
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\libs\sqlite3\sqlite3.vcxproj">
+ <Project>{0c02e395-e73f-47e3-8b95-b7924c0c7a6a}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Dbx_sqlite</ProjectName>
+ <ProjectGuid>{B3494FED-FB8C-43F4-B341-F26A3460203B}</ProjectGuid>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(ProjectDir)..\..\build\vc.common\plugin.props" />
+ </ImportGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\..\libs\sqlite3\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ </ItemDefinitionGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/Dbx_sqlite/dbx_sqlite.vcxproj.filters b/plugins/Dbx_sqlite/dbx_sqlite.vcxproj.filters
new file mode 100644
index 0000000000..fcae13a9d8
--- /dev/null
+++ b/plugins/Dbx_sqlite/dbx_sqlite.vcxproj.filters
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(ProjectDir)..\..\build\vc.common\common.filters" />
+</Project> \ No newline at end of file
diff --git a/plugins/Dbx_sqlite/res/dbx_sqlite.rc b/plugins/Dbx_sqlite/res/dbx_sqlite.rc
new file mode 100644
index 0000000000..94f7d5811b
--- /dev/null
+++ b/plugins/Dbx_sqlite/res/dbx_sqlite.rc
@@ -0,0 +1,196 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "..\src\resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include <winres.h>
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Русский (Россия) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
+LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
+#pragma code_page(1251)
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_SELECT_CRYPTOPROVIDER DIALOGEX 0, 0, 229, 76
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Select crypto provider"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,113,55,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,172,55,50,14
+ COMBOBOX IDC_SELECTCRYPT_COMBO,16,14,199,30,CBS_DROPDOWNLIST,WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT
+ LTEXT "",IDC_CRYPTOPROVIDER_DESCR,18,34,197,14,NOT WS_GROUP
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_SELECT_CRYPTOPROVIDER, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 222
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 69
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // Русский (Россия) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Английский (США) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_LOGIN DIALOGEX 0, 0, 190, 86
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOPMOST | WS_EX_TOOLWINDOW
+CAPTION "Login to Miranda NG"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CONTROL "",IDC_HEADERBAR,"MHeaderbarCtrl",0x0,0,0,190,26
+ CTEXT "",IDC_LANG,158,34,13,13,SS_CENTERIMAGE | NOT WS_GROUP
+ EDITTEXT IDC_USERPASS,21,34,128,14,ES_PASSWORD | ES_AUTOHSCROLL | WS_GROUP
+ DEFPUSHBUTTON "OK",IDOK,36,64,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,102,64,50,14
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,0,55,190,1
+END
+
+IDD_NEWPASS DIALOGEX 0, 0, 190, 102
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOPMOST | WS_EX_TOOLWINDOW
+CAPTION "New password"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CONTROL "Please enter your new password",IDC_HEADERBAR,
+ "MHeaderbarCtrl",0x0,0,0,190,26
+ CTEXT "",IDC_LANG,158,34,13,13,SS_CENTERIMAGE | NOT WS_GROUP
+ EDITTEXT IDC_USERPASS1,21,34,128,14,ES_PASSWORD | ES_AUTOHSCROLL
+ EDITTEXT IDC_USERPASS2,21,54,128,14,ES_PASSWORD | ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,36,84,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,100,84,50,14
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,0,77,190,1
+END
+
+IDD_CHANGEPASS DIALOGEX 0, 0, 190, 148
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOOLWINDOW
+CAPTION "Enter password"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CONTROL "Change password",IDC_HEADERBAR,"MHeaderbarCtrl",0x0,0,0,190,26
+ CTEXT "",IDC_LANG,158,42,13,13,SS_CENTERIMAGE | NOT WS_GROUP
+ EDITTEXT IDC_OLDPASS,21,42,128,14,ES_PASSWORD | ES_AUTOHSCROLL
+ EDITTEXT IDC_USERPASS1,21,77,128,14,ES_PASSWORD | ES_AUTOHSCROLL
+ EDITTEXT IDC_USERPASS2,21,98,128,14,ES_PASSWORD | ES_AUTOHSCROLL
+ DEFPUSHBUTTON "Change",IDOK,11,127,50,14
+ PUSHBUTTON "Remove",IDREMOVE,69,127,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,126,127,50,14
+ LTEXT "New password",IDC_STATIC,11,66,163,10,0,WS_EX_TRANSPARENT
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,0,119,190,1
+ LTEXT "Old password",IDC_STATIC,11,31,140,10,0,WS_EX_TRANSPARENT
+END
+
+IDD_OPTIONS DIALOGEX 0, 0, 318, 176
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Database encryption mode",IDC_STATIC,6,22,305,125
+ CONTROL "Standard",IDC_STANDARD,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,12,38,292,12
+ CONTROL "Total",IDC_TOTAL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,12,95,292,12
+ LTEXT "Only critical data are encrypted (passwords, security tokens, etc). All other settings and history remains unencrypted. Fast and effective, suitable for the most cases",IDC_STATIC,22,54,284,37
+ LTEXT "All string settings and all events in histories are encrypted. It also makes Miranda much slower and creates a risk of losing everything you've stored in a database in case of losing password. Recommended only for paranoid users",IDC_STATIC,22,110,284,33
+ PUSHBUTTON "Set password",IDC_USERPASS,200,153,111,17
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_LOGIN, DIALOG
+ BEGIN
+ END
+
+ IDD_CHANGEPASS, DIALOG
+ BEGIN
+ END
+
+ IDD_OPTIONS, DIALOG
+ BEGIN
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "..\\src\\resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include <winres.h>\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // Английский (США) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/plugins/Dbx_sqlite/res/version.rc b/plugins/Dbx_sqlite/res/version.rc
new file mode 100644
index 0000000000..5a5ddd63ed
--- /dev/null
+++ b/plugins/Dbx_sqlite/res/version.rc
@@ -0,0 +1,9 @@
+// Microsoft Visual C++ generated resource script.
+//
+#ifdef APSTUDIO_INVOKED
+#error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+#include "..\src\version.h"
+
+#include "..\..\build\Version.rc"
diff --git a/plugins/Dbx_sqlite/src/dbcontacts.cpp b/plugins/Dbx_sqlite/src/dbcontacts.cpp
new file mode 100644
index 0000000000..ce9af1ddc4
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/dbcontacts.cpp
@@ -0,0 +1,101 @@
+#include "stdafx.h"
+
+enum {
+ SQL_CTC_STMT_COUNT = 0,
+ SQL_CTC_STMT_ADD,
+ SQL_CTC_STMT_DELETE,
+ SQL_CTC_STMT_NUM
+};
+
+static char *ctc_stmts[SQL_CTC_STMT_NUM] = {
+ "select count(1) from contacts limit 1;",
+ "insert into contacts values (null);",
+ "delete from events where contactid = ?; delete from settings where contactid = ?; delete from contacts where id = ?;"
+};
+
+static sqlite3_stmt *ctc_stmts_prep[SQL_CTC_STMT_NUM] = { 0 };
+
+void CDbxSQLite::InitContacts()
+{
+ for (size_t i = 0; i < SQL_CTC_STMT_NUM; i++)
+ sqlite3_prepare_v3(m_db, ctc_stmts[i], -1, SQLITE_PREPARE_PERSISTENT, &ctc_stmts_prep[i], nullptr);
+
+ // add global contact
+ //m_cache->AddContactToCache(0);
+
+ sqlite3_stmt *stmt = nullptr;
+ sqlite3_prepare_v2(m_db, "select id from contacts;", -1, &stmt, nullptr);
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
+ MCONTACT hContact = sqlite3_column_int64(stmt, 0);
+ DBCachedContact *cc = m_cache->AddContactToCache(hContact);
+ }
+ sqlite3_finalize(stmt);
+}
+
+void CDbxSQLite::UninitContacts()
+{
+ for (size_t i = 0; i < SQL_CTC_STMT_NUM; i++)
+ sqlite3_finalize(ctc_stmts_prep[i]);
+}
+
+LONG CDbxSQLite::GetContactCount()
+{
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = ctc_stmts_prep[SQL_CTC_STMT_COUNT];
+ int rc = sqlite3_step(stmt);
+ int count = sqlite3_column_int(stmt, 0);
+ sqlite3_reset(stmt);
+ return count;
+}
+
+MCONTACT CDbxSQLite::AddContact()
+{
+ MCONTACT hContact = INVALID_CONTACT_ID;
+ {
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = ctc_stmts_prep[SQL_CTC_STMT_ADD];
+ int rc = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ if (rc != SQLITE_DONE)
+ return INVALID_CONTACT_ID;
+ hContact = sqlite3_last_insert_rowid(m_db);
+ }
+
+ DBCachedContact *cc = m_cache->AddContactToCache(hContact);
+ NotifyEventHooks(hContactAddedEvent, hContact);
+
+ return hContact;
+}
+
+LONG CDbxSQLite::DeleteContact(MCONTACT hContact)
+{
+ // global contact cannot be removed
+ if (hContact == 0)
+ return 1;
+
+ {
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = ctc_stmts_prep[SQL_CTC_STMT_DELETE];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ int rc = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ if (rc != SQLITE_DONE)
+ return 1;
+ }
+
+ m_cache->FreeCachedContact(hContact);
+ NotifyEventHooks(hContactDeletedEvent, hContact);
+
+ return 0;
+}
+
+BOOL CDbxSQLite::IsDbContact(MCONTACT hContact)
+{
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ return (cc != nullptr);
+}
+
+LONG CDbxSQLite::GetContactSize(void)
+{
+ return sizeof(DBCachedContact);
+}
diff --git a/plugins/Dbx_sqlite/src/dbevents.cpp b/plugins/Dbx_sqlite/src/dbevents.cpp
new file mode 100644
index 0000000000..bff8362c41
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/dbevents.cpp
@@ -0,0 +1,377 @@
+#include "stdafx.h"
+
+enum {
+ SQL_EVT_STMT_COUNT = 0,
+ SQL_EVT_STMT_ADD,
+ SQL_EVT_STMT_DELETE,
+ SQL_EVT_STMT_BLOBSIZE,
+ SQL_EVT_STMT_GET,
+ SQL_EVT_STMT_GETFLAGS,
+ SQL_EVT_STMT_SETFLAGS,
+ SQL_EVT_STMT_GETCONTACT,
+ SQL_EVT_STMT_FINDFIRST,
+ SQL_EVT_STMT_FINDFIRSTUNREAD,
+ SQL_EVT_STMT_FINDLAST,
+ SQL_EVT_STMT_FINDNEXT,
+ SQL_EVT_STMT_FINDPREV,
+ SQL_EVT_STMT_GETIDBYSRVID,
+ SQL_EVT_STMT_SETSRVID,
+ SQL_EVT_STMT_NUM
+};
+
+static char *evt_stmts[SQL_EVT_STMT_NUM] = {
+ "select count(1) from events limit 1;",
+ "insert into events values (null, ?, ?, ?, ?, ?, ?, ?);",
+ "delete from events where id = ?;",
+ "select size from events where id = ? limit 1;",
+ "select module, timestamp, flags, type, size, blob from events where id = ? limit 1;",
+ "select flags from events where id = ? limit 1;",
+ "update events set flag = ? where id = ?;",
+ "select contactid from events where id = ? limit 1;",
+ "select id from events where contactid = ? order by id limit 1;",
+ "select flags, id from events where contactid = ? order by id;",
+ "select max(id) from events where contactid = ? limit 1;",
+ "select id from events where contactid = ? and id > ? order by id limit 1;",
+ "select id from events where contactid = ? and id < ? order by id desc limit 1;",
+ "select id from events where module = ? and serverid = ? limit 1;",
+ "update events set serverid = ? where id = ?;",
+};
+
+static sqlite3_stmt *evt_stmts_prep[SQL_EVT_STMT_NUM] = { 0 };
+
+void CDbxSQLite::InitEvents()
+{
+ for (size_t i = 0; i < SQL_EVT_STMT_NUM; i++)
+ sqlite3_prepare_v3(m_db, evt_stmts[i], -1, SQLITE_PREPARE_PERSISTENT, &evt_stmts_prep[i], nullptr);
+}
+
+void CDbxSQLite::UninitEvents()
+{
+ for (size_t i = 0; i < SQL_EVT_STMT_NUM; i++)
+ sqlite3_finalize(evt_stmts_prep[i]);
+}
+
+LONG CDbxSQLite::GetEventCount(MCONTACT hContact)
+{
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_COUNT];
+ int rc = sqlite3_step(stmt);
+ int count = sqlite3_column_int(stmt, 0);
+ sqlite3_reset(stmt);
+ return count;
+}
+
+MEVENT CDbxSQLite::AddEvent(MCONTACT hContact, DBEVENTINFO *dbei)
+{
+ if (dbei == nullptr)
+ return 0;
+
+ if (dbei->timestamp == 0)
+ return 0;
+
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 0;
+
+ if (NotifyEventHooks(hEventFilterAddedEvent, hContact, (LPARAM)dbei))
+ return 0;
+
+ MEVENT hDbEvent = 0;
+ {
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_ADD];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_text(stmt, 2, dbei->szModule, mir_strlen(dbei->szModule), nullptr);
+ sqlite3_bind_int64(stmt, 3, dbei->timestamp);
+ sqlite3_bind_int(stmt, 4, dbei->eventType);
+ sqlite3_bind_int64(stmt, 5, dbei->flags);
+ sqlite3_bind_int64(stmt, 6, dbei->cbBlob);
+ sqlite3_bind_blob(stmt, 7, dbei->pBlob, dbei->cbBlob, nullptr);
+ int rc = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ if (rc != SQLITE_DONE)
+ return 0;
+ hDbEvent = sqlite3_last_insert_rowid(m_db);
+ }
+
+ bool neednotify;
+ if (!(dbei->flags & (DBEF_READ | DBEF_SENT)))
+ neednotify = true;
+ //else neednotify = m_safetyMode;
+
+ // notify only in safe mode or on really new events
+ if (neednotify)
+ NotifyEventHooks(hEventAddedEvent, hContact, (LPARAM)hDbEvent);
+
+ return hDbEvent;
+}
+
+BOOL CDbxSQLite::DeleteEvent(MCONTACT hContact, MEVENT hDbEvent)
+{
+ if (hDbEvent == 0)
+ return 1;
+
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+
+ {
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_DELETE];
+ sqlite3_bind_int64(stmt, 1, hDbEvent);
+ int rc = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ if (rc != SQLITE_DONE)
+ return 1;
+ }
+
+ NotifyEventHooks(hEventDeletedEvent, hContact, (LPARAM)hDbEvent);
+
+ return 0;
+}
+
+LONG CDbxSQLite::GetBlobSize(MEVENT hDbEvent)
+{
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_BLOBSIZE];
+ sqlite3_bind_int(stmt, 1, hDbEvent);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_DONE) {
+ sqlite3_reset(stmt);
+ return -1;
+ }
+ LONG res = sqlite3_column_int64(stmt, 0);
+ sqlite3_reset(stmt);
+ return res;
+}
+
+BOOL CDbxSQLite::GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbei)
+{
+ if (hDbEvent == 0)
+ return 1;
+
+ if (dbei == nullptr)
+ return 1;
+
+ if (dbei->cbBlob > 0 && dbei->pBlob == nullptr) {
+ dbei->cbBlob = 0;
+ return 1;
+ }
+
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GET];
+ sqlite3_bind_int64(stmt, 1, hDbEvent);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_ROW) {
+ sqlite3_reset(stmt);
+ return 1;
+ }
+ dbei->szModule = (char*)sqlite3_column_text(stmt, 0);
+ dbei->timestamp = sqlite3_column_int64(stmt, 1);
+ dbei->eventType = sqlite3_column_int(stmt, 2);
+ dbei->flags = sqlite3_column_int64(stmt, 3);
+ dbei->cbBlob = sqlite3_column_int64(stmt, 4);
+ dbei->pBlob = (BYTE*)sqlite3_column_blob(stmt, 5);
+ sqlite3_reset(stmt);
+ return 0;
+}
+
+BOOL CDbxSQLite::MarkEventRead(MCONTACT hContact, MEVENT hDbEvent)
+{
+ if (hDbEvent == 0)
+ return -1;
+
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return -1;
+
+ DWORD flags = 0;
+ {
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GETFLAGS];
+ sqlite3_bind_int64(stmt, 1, hDbEvent);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_ROW) {
+ sqlite3_reset(stmt);
+ return -1;
+ }
+ flags = sqlite3_column_int64(stmt, 0);
+ sqlite3_reset(stmt);
+ }
+
+ if ((flags & DBEF_READ) == DBEF_READ)
+ return flags;
+
+ flags |= DBEF_READ;
+ {
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_SETFLAGS];
+ sqlite3_bind_int(stmt, 1, flags);
+ sqlite3_bind_int64(stmt, 2, hDbEvent);
+ int rc = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ if (rc != SQLITE_DONE)
+ return -1;
+ }
+
+ NotifyEventHooks(hEventMarkedRead, hContact, (LPARAM)hDbEvent);
+
+ return flags;
+}
+
+MCONTACT CDbxSQLite::GetEventContact(MEVENT hDbEvent)
+{
+ if (hDbEvent == 0)
+ return INVALID_CONTACT_ID;
+
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GETCONTACT];
+ sqlite3_bind_int64(stmt, 1, hDbEvent);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_ROW) {
+ sqlite3_reset(stmt);
+ return INVALID_CONTACT_ID;
+ }
+ MCONTACT hContact = sqlite3_column_int64(stmt, 0);
+ sqlite3_reset(stmt);
+ return hContact;
+}
+
+MEVENT CDbxSQLite::FindFirstEvent(MCONTACT hContact)
+{
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 0;
+
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_FINDFIRST];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_ROW) {
+ sqlite3_reset(stmt);
+ return 0;
+ }
+ MEVENT hDbEvent = sqlite3_column_int64(stmt, 0);
+ sqlite3_reset(stmt);
+ return hDbEvent;
+}
+
+MEVENT CDbxSQLite::FindFirstUnreadEvent(MCONTACT hContact)
+{
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 0;
+
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
+ DWORD flags = sqlite3_column_int64(stmt, 0);
+ if (!(flags & (DBEF_READ | DBEF_SENT))) {
+ MEVENT hDbEvent = sqlite3_column_int64(stmt, 1);
+ sqlite3_reset(stmt);
+ return hDbEvent;
+ break;
+ }
+ }
+ sqlite3_reset(stmt);
+ return 0;
+}
+
+MEVENT CDbxSQLite::FindLastEvent(MCONTACT hContact)
+{
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 0;
+
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_FINDLAST];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_ROW) {
+ sqlite3_reset(stmt);
+ return 0;
+ }
+ MEVENT hDbEvent = sqlite3_column_int64(stmt, 0);
+ sqlite3_reset(stmt);
+ return hDbEvent;
+}
+
+MEVENT CDbxSQLite::FindNextEvent(MCONTACT hContact, MEVENT hDbEvent)
+{
+ if (hDbEvent == 0)
+ return 0;
+
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 0;
+
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_FINDNEXT];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_int64(stmt, 2, hDbEvent);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_ROW) {
+ sqlite3_reset(stmt);
+ return 0;
+ }
+ hDbEvent = sqlite3_column_int64(stmt, 0);
+ sqlite3_reset(stmt);
+ return hDbEvent;
+}
+
+MEVENT CDbxSQLite::FindPrevEvent(MCONTACT hContact, MEVENT hDbEvent)
+{
+ if (hDbEvent == 0)
+ return 0;
+
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 0;
+
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_FINDPREV];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_int64(stmt, 2, hDbEvent);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_ROW) {
+ sqlite3_reset(stmt);
+ return 0;
+ }
+ hDbEvent = sqlite3_column_int64(stmt, 0);
+ sqlite3_finalize(stmt);
+ return hDbEvent;
+}
+
+MEVENT CDbxSQLite::GetEventById(LPCSTR szModule, LPCSTR szId)
+{
+ if (szModule == nullptr || szId == nullptr)
+ return 0;
+
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GETIDBYSRVID];
+ sqlite3_bind_text(stmt, 1, szModule, mir_strlen(szModule), nullptr);
+ sqlite3_bind_text(stmt, 2, szId, mir_strlen(szId), nullptr);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_ROW) {
+ sqlite3_reset(stmt);
+ return 0;
+ }
+ MEVENT hDbEvent = sqlite3_column_int64(stmt, 0);
+ sqlite3_reset(stmt);
+ return hDbEvent;
+}
+
+BOOL CDbxSQLite::SetEventId(LPCSTR, MEVENT hDbEvent, LPCSTR szId)
+{
+ if (hDbEvent == 0 || szId == nullptr)
+ return 1;
+
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_SETSRVID];
+ sqlite3_bind_text(stmt, 1, szId, mir_strlen(szId), nullptr);
+ sqlite3_bind_int64(stmt, 2, hDbEvent);
+ int rc = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ return (rc != SQLITE_DONE);
+}
diff --git a/plugins/Dbx_sqlite/src/dbintf.cpp b/plugins/Dbx_sqlite/src/dbintf.cpp
new file mode 100644
index 0000000000..e52da92919
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/dbintf.cpp
@@ -0,0 +1,154 @@
+#include "stdafx.h"
+
+CDbxSQLite::CDbxSQLite(sqlite3 *database)
+ : m_db(database)
+{
+ hContactAddedEvent = CreateHookableEvent(ME_DB_CONTACT_ADDED);
+ hContactDeletedEvent = CreateHookableEvent(ME_DB_CONTACT_DELETED);
+ hEventAddedEvent = CreateHookableEvent(ME_DB_EVENT_ADDED);
+ hEventDeletedEvent = CreateHookableEvent(ME_DB_EVENT_DELETED);
+ hEventFilterAddedEvent = CreateHookableEvent(ME_DB_EVENT_FILTER_ADD);
+ hEventMarkedRead = CreateHookableEvent(ME_DB_EVENT_MARKED_READ);
+ hSettingChangeEvent = CreateHookableEvent(ME_DB_CONTACT_SETTINGCHANGED);
+}
+
+CDbxSQLite::~CDbxSQLite()
+{
+ DestroyHookableEvent(hContactAddedEvent);
+ DestroyHookableEvent(hContactDeletedEvent);
+ DestroyHookableEvent(hEventAddedEvent);
+ DestroyHookableEvent(hEventDeletedEvent);
+ DestroyHookableEvent(hEventFilterAddedEvent);
+ DestroyHookableEvent(hEventMarkedRead);
+ DestroyHookableEvent(hSettingChangeEvent);
+
+ InitContacts();
+ InitEvents();
+ InitSettings();
+
+ if (m_db) {
+ sqlite3_close(m_db);
+ m_db = nullptr;
+ }
+}
+
+
+int CDbxSQLite::Create(const wchar_t *profile)
+{
+ sqlite3 *database = nullptr;
+ ptrA path(mir_utf8encodeW(profile));
+ int rc = sqlite3_open_v2(path, &database, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
+ if (rc != SQLITE_OK)
+ return 1;
+
+ sqlite3_exec(database, "create table contacts (id integer not null primary key);", nullptr, nullptr, nullptr);
+ sqlite3_exec(database, "create table events (id integer not null primary key, contactid integer not null, module varchar(255) not null, timestamp integer not null, type integer not null, flags integer not null, size integer not null, blob any, serverid varchar(64));", nullptr, nullptr, nullptr);
+ sqlite3_exec(database, "create index idx_events1 on events(id, contactid);", nullptr, nullptr, nullptr);
+ sqlite3_exec(database, "create index idx_events2 on events(module, serverid);", nullptr, nullptr, nullptr);
+ sqlite3_exec(database, "create table settings (contactid integer not null, module varchar(255) not null, setting varchar(255) not null, type integer not null, value any, primary key(contactid, module, setting)) without rowid;", nullptr, nullptr, nullptr);
+ sqlite3_exec(database, "create index idx_settings on settings(contactid, module, setting);", nullptr, nullptr, nullptr);
+
+ sqlite3_close(database);
+
+ return 0;
+}
+
+int CDbxSQLite::Check(const wchar_t *profile)
+{
+ FILE *hFile = _wfopen(profile, L"rb");
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return EGROKPRF_CANTREAD;
+
+ char header[16] = {};
+ size_t size = sizeof(header);
+
+ if (fread(header, sizeof(char), size, hFile) != size) {
+ fclose(hFile);
+ return EGROKPRF_CANTREAD;
+ }
+
+ fclose(hFile);
+
+ if (memcmp(header, SQLITE_HEADER_STR, mir_strlen(SQLITE_HEADER_STR)) != 0)
+ return EGROKPRF_UNKHEADER;
+
+ sqlite3 *database = nullptr;
+ ptrA path(mir_utf8encodeW(profile));
+ int rc = sqlite3_open_v2(path, &database, SQLITE_OPEN_READONLY, nullptr);
+ if (rc != SQLITE_OK)
+ return EGROKPRF_DAMAGED;
+
+ sqlite3_close(database);
+
+ return EGROKPRF_NOERROR;
+}
+
+MDatabaseCommon* CDbxSQLite::Load(const wchar_t *profile, int readonly)
+{
+ sqlite3 *database = nullptr;
+ ptrA path(mir_utf8encodeW(profile));
+ int flags = SQLITE_OPEN_READWRITE;
+ if (readonly)
+ flags |= SQLITE_OPEN_READONLY;
+ int rc = sqlite3_open_v2(path, &database, flags, nullptr);
+ if (rc != SQLITE_OK)
+ return nullptr;
+
+ sqlite3_exec(database, "begin transaction;", nullptr, nullptr, nullptr);
+ sqlite3_exec(database, "pragma locking_mode = EXCLUSIVE;", nullptr, nullptr, nullptr);
+ sqlite3_exec(database, "pragma synchronous = NORMAL;", nullptr, nullptr, nullptr);
+ sqlite3_exec(database, "commit;", nullptr, nullptr, nullptr);
+
+ CDbxSQLite *db = new CDbxSQLite(database);
+ db->InitContacts();
+ db->InitEvents();
+ db->InitSettings();
+ return db;
+}
+
+BOOL CDbxSQLite::Compact()
+{
+ sqlite3_exec(m_db, "pragma optimize;", nullptr, nullptr, nullptr);
+ sqlite3_exec(m_db, "vacuum;", nullptr, nullptr, nullptr);
+ return 0;
+}
+
+BOOL CDbxSQLite::Backup(const wchar_t *profile)
+{
+ sqlite3 *database = nullptr;
+ ptrA path(mir_utf8encodeW(profile));
+ int rc = sqlite3_open_v2(path, &database, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
+ if (rc != SQLITE_OK)
+ return FALSE;
+
+ mir_cslock lock(m_csDbAccess);
+
+ sqlite3_backup *backup = sqlite3_backup_init(database, "main", m_db, "main");
+ if (backup) {
+ sqlite3_backup_step(backup, -1);
+ sqlite3_backup_finish(backup);
+ }
+ sqlite3_close(database);
+
+ return 0;
+}
+
+BOOL CDbxSQLite::IsRelational()
+{
+ return 0;
+}
+
+void CDbxSQLite::SetCacheSafetyMode(BOOL)
+{
+}
+
+BOOL CDbxSQLite::MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub)
+{
+ return 0;
+}
+
+BOOL CDbxSQLite::MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub)
+{
+ return 0;
+}
diff --git a/plugins/Dbx_sqlite/src/dbintf.h b/plugins/Dbx_sqlite/src/dbintf.h
new file mode 100644
index 0000000000..4ab30580a5
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/dbintf.h
@@ -0,0 +1,71 @@
+#pragma once
+
+struct CDbxSQLite : public MDatabaseCommon, public MZeroedObject
+{
+private:
+ sqlite3 *m_db;
+
+ HANDLE hContactAddedEvent;
+ HANDLE hContactDeletedEvent;
+ HANDLE hEventAddedEvent;
+ HANDLE hEventDeletedEvent;
+ HANDLE hEventFilterAddedEvent;
+ HANDLE hEventMarkedRead;
+ HANDLE hSettingChangeEvent;
+
+ CDbxSQLite(sqlite3 *database);
+
+ void InitContacts();
+ void UninitContacts();
+
+ void InitEvents();
+ void UninitEvents();
+
+ void InitSettings();
+ void UninitSettings();
+
+public:
+ ~CDbxSQLite();
+
+ static int Create(const wchar_t *profile);
+ static int Check(const wchar_t *profile);
+ static MDatabaseCommon* Load(const wchar_t *profile, int readonly);
+
+ STDMETHODIMP_(BOOL) IsRelational(void) override;
+ STDMETHODIMP_(void) SetCacheSafetyMode(BOOL) override;
+
+ STDMETHODIMP_(LONG) GetContactCount(void) override;
+ STDMETHODIMP_(LONG) DeleteContact(MCONTACT contactID) override;
+ STDMETHODIMP_(MCONTACT) AddContact(void) override;
+ STDMETHODIMP_(BOOL) IsDbContact(MCONTACT contactID) override;
+ STDMETHODIMP_(LONG) GetContactSize(void) override;
+
+ STDMETHODIMP_(LONG) GetEventCount(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) AddEvent(MCONTACT contactID, DBEVENTINFO *dbe) override;
+ STDMETHODIMP_(BOOL) DeleteEvent(MCONTACT contactID, MEVENT hDbEvent) override;
+ STDMETHODIMP_(LONG) GetBlobSize(MEVENT hDbEvent) override;
+ STDMETHODIMP_(BOOL) GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbe) override;
+ STDMETHODIMP_(BOOL) MarkEventRead(MCONTACT contactID, MEVENT hDbEvent) override;
+ STDMETHODIMP_(MCONTACT) GetEventContact(MEVENT hDbEvent) override;
+ STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) FindFirstUnreadEvent(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT contactID, MEVENT hDbEvent) override;
+ STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent) override;
+
+ STDMETHODIMP_(MEVENT) GetEventById(LPCSTR szModule, LPCSTR szId) override;
+ STDMETHODIMP_(BOOL) SetEventId(LPCSTR szModule, MEVENT, LPCSTR szId) override;
+
+ STDMETHODIMP_(BOOL) EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam) override;
+
+ STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic) override;
+ STDMETHODIMP_(BOOL) WriteContactSetting(MCONTACT contactID, DBCONTACTWRITESETTING *dbcws) override;
+ STDMETHODIMP_(BOOL) DeleteContactSetting(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting) override;
+ STDMETHODIMP_(BOOL) EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param) override;
+
+ STDMETHODIMP_(BOOL) MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override;
+ STDMETHODIMP_(BOOL) MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override;
+
+ STDMETHODIMP_(BOOL) Compact() override;
+ STDMETHODIMP_(BOOL) Backup(LPCWSTR) override;
+};
diff --git a/plugins/Dbx_sqlite/src/dbsettings.cpp b/plugins/Dbx_sqlite/src/dbsettings.cpp
new file mode 100644
index 0000000000..121f2c73fc
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/dbsettings.cpp
@@ -0,0 +1,368 @@
+#include "stdafx.h"
+
+enum {
+ SQL_SET_STMT_ENUM = 0,
+ SQL_SET_STMT_GET,
+ SQL_SET_STMT_REPLACE,
+ SQL_SET_STMT_DELETE,
+ SQL_SET_STMT_ENUMMODULE,
+ SQL_SET_STMT_NUM
+};
+
+static char *settings_stmts[SQL_SET_STMT_NUM] = {
+ "select distinct module from settings;",
+ "select type, value from settings where contactid = ? and module = ? and setting = ? limit 1;",
+ "replace into settings(contactid, module, setting, type, value) values (?, ?, ?, ?, ?);",
+ "delete from settings where contactid = ? and module = ? and setting = ?;",
+ "select setting from settings where contactid = ? and module = ?;"
+};
+
+static sqlite3_stmt *settings_stmts_prep[SQL_SET_STMT_NUM] = { 0 };
+
+void CDbxSQLite::InitSettings()
+{
+ for (size_t i = 0; i < SQL_SET_STMT_NUM; i++)
+ sqlite3_prepare_v3(m_db, settings_stmts[i], -1, SQLITE_PREPARE_PERSISTENT, &settings_stmts_prep[i], nullptr);
+ /*DBVARIANT dbv = {};
+ sqlite3_stmt *stmt = nullptr;
+ sqlite3_prepare_v2(m_db, "select contactid, module, setting, type, value from settings", -1, &stmt, nullptr);
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
+ MCONTACT hContact = sqlite3_column_int64(stmt, 0);
+ const char *module = (const char*)sqlite3_column_text(stmt, 1);
+ const char *setting = (const char*)sqlite3_column_text(stmt, 2);
+ dbv.type = sqlite3_column_int(stmt, 3);
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ dbv.bVal = sqlite3_column_int(stmt, 4);
+ break;
+ case DBVT_WORD:
+ dbv.wVal = sqlite3_column_int(stmt, 4);
+ break;
+ case DBVT_DWORD:
+ dbv.dVal = sqlite3_column_int64(stmt, 4);
+ break;
+ case DBVT_ASCIIZ:
+ {
+ char *utf8 = mir_strdup((const char*)sqlite3_column_text(stmt, 4));
+ dbv.pszVal = mir_utf8decode(utf8, nullptr);
+ break;
+ }
+ case DBVT_UTF8:
+ dbv.pszVal = mir_strdup((const char*)sqlite3_column_text(stmt, 4));
+ break;
+ case DBVT_BLOB:
+ continue;
+ }
+ char *cachedSettingName = m_cache->GetCachedSetting(module, setting, mir_strlen(module), mir_strlen(setting));
+ DBVARIANT *cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1);
+ m_cache->SetCachedVariant(&dbv, cachedValue);
+ if(dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_UTF8)
+ mir_free(dbv.pszVal);
+ }
+ sqlite3_finalize(stmt);*/
+}
+
+void CDbxSQLite::UninitSettings()
+{
+ for (size_t i = 0; i < SQL_SET_STMT_NUM; i++)
+ sqlite3_finalize(settings_stmts_prep[i]);
+}
+
+BOOL CDbxSQLite::EnumModuleNames(DBMODULEENUMPROC pFunc, void *param)
+{
+ LIST<char> modules(100);
+ {
+ sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_ENUM];
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
+ const char *value = (const char*)sqlite3_column_text(stmt, 0);
+ modules.insert(mir_strdup(value));
+ }
+ sqlite3_reset(stmt);
+ }
+
+ int result = -1;
+ for (auto &module : modules) {
+ result = pFunc(module, param);
+ mir_free(module);
+ }
+ return result;
+}
+
+BOOL CDbxSQLite::GetContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic)
+{
+ if (szSetting == nullptr || szModule == nullptr)
+ return 1;
+
+ if (hContact) {
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ mir_cslock lock(m_csDbAccess);
+
+ char *cachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, mir_strlen(szModule), mir_strlen(szSetting));
+ DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 0);
+ if (pCachedValue != nullptr) {
+ if (pCachedValue->type == DBVT_UTF8) {
+ int cbOrigLen = dbv->cchVal;
+ char *cbOrigPtr = dbv->pszVal;
+ memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+ if (isStatic) {
+ int cbLen = 0;
+ if (pCachedValue->pszVal != nullptr)
+ cbLen = (int)mir_strlen(pCachedValue->pszVal);
+
+ cbOrigLen--;
+ dbv->pszVal = cbOrigPtr;
+ if (cbLen < cbOrigLen)
+ cbOrigLen = cbLen;
+ memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen);
+ dbv->pszVal[cbOrigLen] = 0;
+ dbv->cchVal = cbLen;
+ }
+ else {
+ dbv->pszVal = (char*)mir_alloc(mir_strlen(pCachedValue->pszVal) + 1);
+ mir_strcpy(dbv->pszVal, pCachedValue->pszVal);
+ }
+ }
+ else memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+
+ return (pCachedValue->type == DBVT_DELETED) ? 1 : 0;
+ }
+
+ // never look db for the resident variable
+ if (cachedSettingName[-1] != 0)
+ return 1;
+
+ sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_GET];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_text(stmt, 2, szModule, mir_strlen(szModule), nullptr);
+ sqlite3_bind_text(stmt, 3, szSetting, mir_strlen(szSetting), nullptr);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_ROW) {
+ sqlite3_reset(stmt);
+ return 1;
+ }
+ dbv->type = (int)sqlite3_column_int(stmt, 0);
+ switch (dbv->type) {
+ case DBVT_DELETED:
+ dbv->type = DBVT_DELETED;
+ sqlite3_reset(stmt);
+ return 2;
+ case DBVT_BYTE:
+ dbv->bVal = sqlite3_column_int(stmt, 1);
+ break;
+ case DBVT_WORD:
+ dbv->wVal = sqlite3_column_int(stmt, 1);
+ break;
+ case DBVT_DWORD:
+ dbv->dVal = sqlite3_column_int64(stmt, 1);
+ break;
+ case DBVT_UTF8:
+ {
+ dbv->cchVal = sqlite3_column_bytes(stmt, 1);
+ const char *value = (const char*)sqlite3_column_text(stmt, 1);
+ if (!isStatic)
+ dbv->pszVal = (char*)mir_alloc(dbv->cchVal + 1);
+ memcpy(dbv->pszVal, value, dbv->cchVal);
+ dbv->pszVal[dbv->cchVal] = 0;
+ break;
+ }
+ case DBVT_BLOB:
+ {
+ dbv->cpbVal = sqlite3_column_bytes(stmt, 1);
+ const char *data = (const char*)sqlite3_column_blob(stmt, 1);
+ if (!isStatic)
+ dbv->pbVal = (BYTE*)mir_alloc(dbv->cpbVal + 1);
+ memcpy(dbv->pbVal, data, dbv->cpbVal);
+ break;
+ }
+ }
+ sqlite3_reset(stmt);
+
+ // add to cache
+ if (dbv->type != DBVT_BLOB) {
+ pCachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1);
+ if (pCachedValue != nullptr)
+ m_cache->SetCachedVariant(dbv, pCachedValue);
+ }
+
+ return 0;
+}
+
+BOOL CDbxSQLite::WriteContactSetting(MCONTACT hContact, DBCONTACTWRITESETTING *dbcws)
+{
+ if (dbcws == nullptr || dbcws->szSetting == nullptr || dbcws->szModule == nullptr)
+ return 1;
+
+ if (hContact) {
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ DBCONTACTWRITESETTING dbcwNotif = *dbcws;
+ // we work only with utf-8 inside
+ switch (dbcwNotif.value.type) {
+ case DBVT_UTF8:
+ dbcwNotif.value.pszVal = mir_strdup(dbcws->value.pszVal);
+ break;
+ case DBVT_ASCIIZ:
+ {
+ ptrA value(mir_utf8encode(dbcws->value.pszVal));
+ dbcwNotif.value.pszVal = NEWSTR_ALLOCA(value);
+ dbcwNotif.value.type = DBVT_UTF8;
+ break;
+ }
+ case DBVT_WCHAR:
+ {
+ T2Utf value(dbcwNotif.value.pwszVal);
+ dbcwNotif.value.pszVal = NEWSTR_ALLOCA(value);
+ dbcwNotif.value.type = DBVT_UTF8;
+ break;
+ }
+ }
+ DBCONTACTWRITESETTING dbcwWork = dbcwNotif;
+ if (dbcwWork.value.type == DBVT_UTF8)
+ dbcwWork.value.cchVal = (WORD)strlen(dbcwWork.value.pszVal);
+
+ mir_cslockfull lock(m_csDbAccess);
+
+ char *cachedSettingName = m_cache->GetCachedSetting(dbcwWork.szModule, dbcwWork.szSetting, mir_strlen(dbcwWork.szModule), mir_strlen(dbcwWork.szSetting));
+ bool isResident = cachedSettingName[-1] != 0;
+
+ // we don't cache blobs
+ if (dbcwWork.value.type != DBVT_BLOB) {
+ DBVARIANT *cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1);
+ if (cachedValue != nullptr) {
+ bool isIdentical = false;
+ if (cachedValue->type == dbcwWork.value.type) {
+ switch (dbcwWork.value.type) {
+ case DBVT_BYTE:
+ isIdentical = cachedValue->bVal == dbcwWork.value.bVal;
+ break;
+ case DBVT_WORD:
+ isIdentical = cachedValue->wVal == dbcwWork.value.wVal;
+ break;
+ case DBVT_DWORD:
+ isIdentical = cachedValue->dVal == dbcwWork.value.dVal;
+ break;
+ case DBVT_UTF8:
+ isIdentical = mir_strcmp(cachedValue->pszVal, dbcwWork.value.pszVal) == 0;
+ break;
+ }
+ if (isIdentical)
+ return 0;
+ }
+ m_cache->SetCachedVariant(&dbcwWork.value, cachedValue);
+ }
+ if (isResident) {
+ lock.unlock();
+ NotifyEventHooks(hSettingChangeEvent, hContact, (LPARAM)&dbcwWork);
+ return 0;
+ }
+ }
+ else m_cache->GetCachedValuePtr(hContact, cachedSettingName, -1);
+
+ sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_REPLACE];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_text(stmt, 2, dbcwWork.szModule, mir_strlen(dbcwWork.szModule), nullptr);
+ sqlite3_bind_text(stmt, 3, dbcwWork.szSetting, mir_strlen(dbcwWork.szSetting), nullptr);
+ sqlite3_bind_int(stmt, 4, dbcwWork.value.type);
+ switch (dbcwWork.value.type) {
+ case DBVT_BYTE:
+ sqlite3_bind_int(stmt, 5, dbcwWork.value.bVal);
+ break;
+ case DBVT_WORD:
+ sqlite3_bind_int(stmt, 5, dbcwWork.value.wVal);
+ break;
+ case DBVT_DWORD:
+ sqlite3_bind_int64(stmt, 5, dbcwWork.value.dVal);
+ break;
+ case DBVT_UTF8:
+ sqlite3_bind_text(stmt, 5, dbcwWork.value.pszVal, dbcwWork.value.cchVal, nullptr);
+ break;
+ case DBVT_BLOB:
+ sqlite3_bind_blob(stmt, 5, dbcwWork.value.pbVal, dbcwWork.value.cpbVal, nullptr);
+ break;
+ }
+ int rc = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ if (rc != SQLITE_DONE)
+ return 1;
+
+ lock.unlock();
+
+ NotifyEventHooks(hSettingChangeEvent, hContact, (LPARAM)&dbcwNotif);
+
+ return 0;
+}
+
+BOOL CDbxSQLite::DeleteContactSetting(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting)
+{
+ if (szSetting == nullptr || szModule == nullptr)
+ return 1;
+
+ if (hContact) {
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, mir_strlen(szModule), mir_strlen(szSetting));
+ if (szCachedSettingName[-1] == 0) // it's not a resident variable
+ {
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_DELETE];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_text(stmt, 2, szModule, mir_strlen(szModule), nullptr);
+ sqlite3_bind_text(stmt, 3, szSetting, mir_strlen(szSetting), nullptr);
+ int rc = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ if (rc != SQLITE_DONE)
+ return 1;
+ }
+ m_cache->GetCachedValuePtr(hContact, szCachedSettingName, -1);
+
+ // notify
+ DBCONTACTWRITESETTING dbcws = { };
+ dbcws.szModule = szModule;
+ dbcws.szSetting = szSetting;
+ dbcws.value.type = DBVT_DELETED;
+ NotifyEventHooks(hSettingChangeEvent, hContact, (LPARAM)&dbcws);
+
+ return 0;
+}
+
+BOOL CDbxSQLite::EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param)
+{
+ if (szModule == nullptr)
+ return -1;
+
+ if (hContact) {
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return -1;
+ }
+
+ LIST<char> settings(100);
+ {
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_ENUMMODULE];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_text(stmt, 2, szModule, mir_strlen(szModule), nullptr);
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
+ const char *value = (const char*)sqlite3_column_text(stmt, 0);
+ settings.insert(mir_strdup(value));
+ }
+ sqlite3_reset(stmt);
+ }
+
+ int result = -1;
+ for (auto &setting : settings) {
+ result = pfnEnumProc(setting, param);
+ mir_free(setting);
+ }
+ return result;
+}
diff --git a/plugins/Dbx_sqlite/src/main.cpp b/plugins/Dbx_sqlite/src/main.cpp
new file mode 100644
index 0000000000..a7ed57bfee
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/main.cpp
@@ -0,0 +1,44 @@
+#include "stdafx.h"
+
+CMPlugin g_plugin;
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_DATABASE, MIID_LAST };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static PLUGININFOEX pluginInfoEx =
+{
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE | STATIC_PLUGIN,
+ // {7C3D0A33-2646-4001-9107-F35EA299D292}
+ { 0x7c3d0a33, 0x2646, 0x4001, { 0x91, 0x7, 0xf3, 0x5e, 0xa2, 0x99, 0xd2, 0x92 } }
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(nullptr, pluginInfoEx)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static DATABASELINK dblink =
+{
+ MDB_CAPS_CREATE | MDB_CAPS_COMPACT,
+ "dbx_sqlite",
+ L"SQLite database driver",
+ &CDbxSQLite::Create,
+ &CDbxSQLite::Check,
+ &CDbxSQLite::Load,
+};
+
+int CMPlugin::Load()
+{
+ RegisterDatabasePlugin(&dblink);
+ return 0;
+}
diff --git a/plugins/Dbx_sqlite/src/resource.h b/plugins/Dbx_sqlite/src/resource.h
new file mode 100644
index 0000000000..135f6899d0
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/resource.h
@@ -0,0 +1,33 @@
+//{{NO_DEPENDENCIES}}
+// Включаемый файл, созданный в Microsoft Visual C++.
+// Используется D:\Others\SVN\MirandaNG\trunk\plugins\Db3x_mmap\res\db3x_mmap.rc
+//
+#define IDREMOVE 3
+#define IDI_ICONPASS 100
+#define IDI_LOGO 101
+#define IDD_LOGIN 102
+#define IDD_NEWPASS 103
+#define IDD_CHANGEPASS 104
+#define IDD_OPTIONS 105
+#define IDD_SELECT_CRYPTOPROVIDER 106
+#define IDC_HEADERBAR 1001
+#define IDC_LANG 1002
+#define IDC_USERPASS 1003
+#define IDC_USERPASS1 1004
+#define IDC_USERPASS2 1005
+#define IDC_OLDPASS 1006
+#define IDC_STANDARD 1007
+#define IDC_TOTAL 1008
+#define IDC_SELECTCRYPT_COMBO 1010
+#define IDC_CRYPTOPROVIDER_DESCR 1011
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 107
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1012
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/plugins/Dbx_sqlite/src/stdafx.cxx b/plugins/Dbx_sqlite/src/stdafx.cxx
new file mode 100644
index 0000000000..fd4f341c7b
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/stdafx.cxx
@@ -0,0 +1 @@
+#include "stdafx.h"
diff --git a/plugins/Dbx_sqlite/src/stdafx.h b/plugins/Dbx_sqlite/src/stdafx.h
new file mode 100644
index 0000000000..0cf1b89f3d
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/stdafx.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <windows.h>
+#include <sqlite3.h>
+
+#include <memory>
+
+#include <newpluginapi.h>
+#include <win2k.h>
+
+#include <m_core.h>
+#include <m_system.h>
+#include <m_database.h>
+#include <m_db_int.h>
+
+#include "dbintf.h"
+#include "resource.h"
+#include "version.h"
+
+constexpr auto SQLITE_HEADER_STR = "SQLite format 3";
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ CMPlugin();
+
+ int Load() override;
+};
diff --git a/plugins/Dbx_sqlite/src/version.h b/plugins/Dbx_sqlite/src/version.h
new file mode 100644
index 0000000000..2eaac196d0
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/version.h
@@ -0,0 +1,13 @@
+#define __MAJOR_VERSION 0
+#define __MINOR_VERSION 95
+#define __RELEASE_NUM 10
+#define __BUILD_NUM 1
+
+#include <stdver.h>
+
+#define __PLUGIN_NAME "Miranda NG SQLite database driver"
+#define __FILENAME "Dbx_sqlite.dll"
+#define __DESCRIPTION "Provides Miranda database support: global settings, contacts, history, settings per contact."
+#define __AUTHOR "Miranda-NG project"
+#define __AUTHORWEB "https://miranda-ng.org/p/Dbx_sqlite/"
+#define __COPYRIGHT "© 2018 Miranda NG team"