DatabaseUtilities.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143
  1. // DatabaseUtilites.cpp: implementation of the CDatabaseUtilites class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "CP_Main.h"
  6. #include "DatabaseUtilities.h"
  7. #include "ProcessPaste.h"
  8. #include <io.h>
  9. #include "Path.h"
  10. #include "InternetUpdate.h"
  11. #include "zlib/zlib.h"
  12. #include "Shared/TextConvert.h"
  13. using namespace nsPath;
  14. //////////////////////////////////////////////////////////////////////
  15. // Construction/Destruction
  16. //////////////////////////////////////////////////////////////////////
  17. BOOL CreateBackup(CString csPath)
  18. {
  19. CString csOriginal;
  20. int count = 0;
  21. // create a backup of the existing database
  22. do
  23. {
  24. count++;
  25. csOriginal = csPath + StrF(_T(".%03d"), count);
  26. // in case of some weird infinite loop
  27. if( count > 50 )
  28. {
  29. ASSERT(0);
  30. return FALSE;
  31. }
  32. } while( !::CopyFile(csPath, csOriginal, TRUE));
  33. return TRUE;
  34. }
  35. CString GetDBName()
  36. {
  37. return CGetSetOptions::GetDBPath();
  38. }
  39. CString GetOLDDefaultDBName()
  40. {
  41. CString csDefaultPath;
  42. LPMALLOC pMalloc;
  43. if(SUCCEEDED(::SHGetMalloc(&pMalloc)))
  44. {
  45. LPITEMIDLIST pidlPrograms;
  46. SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidlPrograms);
  47. TCHAR string[MAX_PATH];
  48. SHGetPathFromIDList(pidlPrograms, string);
  49. pMalloc->Free(pidlPrograms);
  50. pMalloc->Release();
  51. csDefaultPath = string;
  52. csDefaultPath += "\\Ditto\\";
  53. csDefaultPath += "DittoDB.mdb";
  54. }
  55. return csDefaultPath;
  56. }
  57. CString GetDefaultDBName()
  58. {
  59. CString csDefaultPath = _T("c:\\program files\\Ditto\\");
  60. //If portable then default to the running path
  61. if(CGetSetOptions::GetIsPortableDitto())
  62. {
  63. csDefaultPath.Empty();
  64. }
  65. else
  66. {
  67. csDefaultPath = CGetSetOptions::GetAppDataPath();
  68. }
  69. CString csTempName = csDefaultPath + "Ditto.db";
  70. int i = 1;
  71. while(FileExists(csTempName))
  72. {
  73. csTempName.Format(_T("%sDitto_%d.db"), csDefaultPath, i);
  74. i++;
  75. }
  76. csDefaultPath = csTempName;
  77. return csDefaultPath;
  78. }
  79. BOOL CheckDBExists(CString csDBPath)
  80. {
  81. CPath path(csDBPath);
  82. //If this is the first time running this version then convert the old database to the new db
  83. if(csDBPath.IsEmpty())
  84. {
  85. csDBPath = GetDefaultDBName();
  86. CGetSetOptions::SetDBPath(csDBPath);
  87. }
  88. BOOL bRet = FALSE;
  89. if(FileExists(csDBPath) == FALSE)
  90. {
  91. //if the database is on a shared drive, network share or anything other than C:\ than don't create a new db
  92. //Ditto will wait until that drive is available
  93. int len = 0;
  94. auto rootType = path.GetRootType(&len);
  95. auto driveLetter = path.GetDriveLetter();
  96. if (rootType == ERootType::rtServerShare ||
  97. ((rootType == ERootType::rtDriveCur || rootType == rtDriveRoot) && driveLetter >= 'A' && driveLetter != 'C'))
  98. {
  99. return FALSE;
  100. }
  101. //first try and create create a db at the same path that was selectd
  102. bRet = CreateDB(csDBPath);
  103. //if that didn't work then go back to the default location
  104. if (FileExists(csDBPath) == FALSE)
  105. {
  106. csDBPath = GetDefaultDBName();
  107. nsPath::CPath FullPath(csDBPath);
  108. CString csPath = FullPath.GetPath().GetStr();
  109. if (csPath.IsEmpty() == false && FileExists(csDBPath) == FALSE)
  110. {
  111. CreateDirectory(csPath, NULL);
  112. }
  113. CGetSetOptions::SetDBPath(csDBPath);
  114. bRet = CreateDB(csDBPath);
  115. }
  116. }
  117. else
  118. {
  119. if(ValidDB(csDBPath) == FALSE)
  120. {
  121. //Db existed but was bad
  122. CString csMarkAsBad;
  123. csMarkAsBad = csDBPath;
  124. csMarkAsBad.Replace(_T("."), _T("_BAD."));
  125. CString csPath = GetDefaultDBName();
  126. CString cs;
  127. cs.Format(_T("%s \"%s\",\n")
  128. _T("%s \"%s\",\n")
  129. _T("%s,\n")
  130. _T("\"%s\""),
  131. theApp.m_Language.GetString("Database_Format", "Unrecognized Database Format"),
  132. csDBPath,
  133. theApp.m_Language.GetString("File_Renamed", "the file will be renamed"),
  134. csMarkAsBad,
  135. theApp.m_Language.GetString("New_Database", "and a new database will be created"),
  136. csPath);
  137. AfxMessageBox(cs);
  138. CFile::Rename(csDBPath, csMarkAsBad);
  139. csDBPath = csPath;
  140. bRet = CreateDB(csDBPath);
  141. CGetSetOptions::SetDBPath(csDBPath);
  142. }
  143. else
  144. {
  145. bRet = TRUE;
  146. }
  147. }
  148. if(bRet)
  149. {
  150. bRet = OpenDatabase(csDBPath);
  151. }
  152. return bRet;
  153. }
  154. BOOL IsDatabaseOpen()
  155. {
  156. return theApp.m_db.IsDatabaseOpen();
  157. }
  158. BOOL OpenDatabase(CString dbPath)
  159. {
  160. try
  161. {
  162. CPath path(dbPath);
  163. int len = 0;
  164. auto rootType = path.GetRootType(&len);
  165. auto driveLetter = path.GetDriveLetter();
  166. theApp.m_databaseOnNetworkShare = false;
  167. if (rootType == ERootType::rtServerShare ||
  168. ((rootType == ERootType::rtDriveCur || rootType == rtDriveRoot) && driveLetter >= 'A' && driveLetter != 'C'))
  169. {
  170. theApp.m_databaseOnNetworkShare = true;
  171. }
  172. theApp.m_db.close();
  173. theApp.m_db.open(dbPath);
  174. theApp.m_db.setBusyTimeout(CGetSetOptions::GetDbTimeout());
  175. theApp.m_db.SetRegexCaseInsensitive(CGetSetOptions::GetRegexCaseInsensitive());
  176. return TRUE;
  177. }
  178. CATCH_SQLITE_EXCEPTION
  179. return FALSE;
  180. }
  181. void ReOrderStickyClips(int parentID, CppSQLite3DB &db)
  182. {
  183. try
  184. {
  185. Log(StrF(_T("Start of ReOrderStickyClips, ParentId %d"), parentID));
  186. //groups where created with 0 in these fields, fix them up if they are 0
  187. if(parentID == -1)
  188. {
  189. db.execDMLEx(_T("Update Main Set stickyClipOrder = -(2147483647) where bIsGroup = 1 AND stickyClipOrder = 0"));
  190. db.execDMLEx(_T("Update Main Set stickyClipGroupOrder = -(2147483647) where bIsGroup = 1 AND stickyClipGroupOrder = 0"));
  191. }
  192. CppSQLite3Query qGroup = db.execQueryEx(_T("SELECT lID, mText FROM Main WHERE bIsGroup = 1 AND lParentID = %d"), parentID);
  193. if (qGroup.eof() == false)
  194. {
  195. while (!qGroup.eof())
  196. {
  197. //Get all sticky clips at the top level or group
  198. CString sql = StrF(_T("SELECT lID FROM Main WHERE stickyClipOrder <> -(2147483647) AND lParentID = %d ORDER BY stickyClipOrder DESC"), parentID);
  199. if (parentID > -1)
  200. {
  201. sql = StrF(_T("SELECT lID FROM Main WHERE stickyClipGroupOrder <> -(2147483647) AND lParentID = %d ORDER BY stickyClipGroupOrder DESC"), parentID);
  202. }
  203. CppSQLite3Query qSticky = db.execQueryEx(sql);
  204. int order = 1;
  205. if (qSticky.eof() == false)
  206. {
  207. while (!qSticky.eof())
  208. {
  209. //set the new order
  210. if (parentID > -1)
  211. {
  212. db.execDMLEx(_T("Update Main Set stickyClipGroupOrder = %d where lID = %d"), order, qSticky.getIntField(_T("lID")));
  213. }
  214. else
  215. {
  216. db.execDMLEx(_T("Update Main Set stickyClipOrder = %d where lID = %d"), order, qSticky.getIntField(_T("lID")));
  217. }
  218. qSticky.nextRow();
  219. order--;
  220. }
  221. }
  222. ReOrderStickyClips(qGroup.getIntField(_T("lID")), db);
  223. qGroup.nextRow();
  224. }
  225. }
  226. Log(StrF(_T("End of ReOrderStickyClips, ParentId %d"), parentID));
  227. }
  228. CATCH_SQLITE_EXCEPTION
  229. }
  230. BOOL ValidDB(CString csPath, BOOL bUpgrade)
  231. {
  232. try
  233. {
  234. CppSQLite3DB db;
  235. db.open(csPath);
  236. db.execQuery(_T("SELECT lID, lDate, mText, lShortCut, lDontAutoDelete, ")
  237. _T("CRC, bIsGroup, lParentID, QuickPasteText ")
  238. _T("FROM Main"));
  239. db.execQuery(_T("SELECT lID, lParentID, strClipBoardFormat, ooData FROM Data"));
  240. db.execQuery(_T("SELECT lID, TypeText FROM Types"));
  241. try
  242. {
  243. db.execDML(_T("DROP TRIGGER delete_data_trigger"));
  244. }
  245. catch(CppSQLite3Exception& e)
  246. {
  247. e.errorCode();
  248. }
  249. try
  250. {
  251. db.execDML(_T("DROP TRIGGER delete_copy_buffer_trigger"));
  252. }
  253. catch(CppSQLite3Exception& e)
  254. {
  255. e.errorCode();
  256. }
  257. //This was added later so try to add each time and catch the exception here
  258. try
  259. {
  260. db.execDML(_T("CREATE TRIGGER delete_data_trigger BEFORE DELETE ON Main FOR EACH ROW\n")
  261. _T("BEGIN\n")
  262. _T("INSERT INTO MainDeletes VALUES(old.lID, datetime('now'));\n")
  263. _T("END\n"));
  264. }
  265. catch(CppSQLite3Exception& e)
  266. {
  267. e.errorCode();
  268. }
  269. //This was added later so try to add each time and catch the exception here
  270. try
  271. {
  272. db.execQuery(_T("SELECT lID, lClipID, lCopyBuffer FROM CopyBuffers"));
  273. }
  274. catch(CppSQLite3Exception& e)
  275. {
  276. e.errorCode();
  277. db.execDML(_T("CREATE TABLE CopyBuffers(")
  278. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  279. _T("lClipID INTEGER,")
  280. _T("lCopyBuffer INTEGER)"));
  281. }
  282. //This was added later so try to add each time and catch the exception here
  283. try
  284. {
  285. db.execQuery(_T("SELECT clipId FROM MainDeletes"));
  286. }
  287. catch(CppSQLite3Exception& e)
  288. {
  289. e.errorCode();
  290. db.execDML(_T("CREATE TABLE MainDeletes(")
  291. _T("clipID INTEGER,")
  292. _T("modifiedDate)"));
  293. db.execDML(_T("CREATE TRIGGER MainDeletes_delete_data_trigger BEFORE DELETE ON MainDeletes FOR EACH ROW\n")
  294. _T("BEGIN\n")
  295. _T("DELETE FROM CopyBuffers WHERE lClipID = old.clipID;\n")
  296. _T("DELETE FROM Data WHERE lParentID = old.clipID;\n")
  297. _T("END\n"));
  298. }
  299. try
  300. {
  301. db.execDML(_T("CREATE INDEX Main_ParentId on Main(lParentID DESC)"));
  302. db.execDML(_T("CREATE INDEX Main_IsGroup on Main(bIsGroup DESC)"));
  303. db.execDML(_T("CREATE INDEX Main_ShortCut on Main(lShortCut DESC)"));
  304. }
  305. catch(CppSQLite3Exception& e)
  306. {
  307. e.errorCode();
  308. }
  309. try
  310. {
  311. db.execQuery(_T("SELECT clipOrder, clipGroupOrder FROM Main"));
  312. }
  313. catch(CppSQLite3Exception& e)
  314. {
  315. db.execDML(_T("ALTER TABLE Main ADD clipOrder REAL"));
  316. db.execDML(_T("ALTER TABLE Main ADD clipGroupOrder REAL"));
  317. db.execDML(_T("Update Main set clipOrder = lDate, clipGroupOrder = lDate"));
  318. db.execDML(_T("CREATE INDEX Main_ClipOrder on Main(clipOrder DESC)"));
  319. db.execDML(_T("CREATE INDEX Main_ClipGroupOrder on Main(clipGroupOrder DESC)"));
  320. db.execDML(_T("DROP INDEX Main_Date"));
  321. e.errorCode();
  322. }
  323. try
  324. {
  325. db.execQuery(_T("SELECT globalShortCut FROM Main"));
  326. }
  327. catch(CppSQLite3Exception& e)
  328. {
  329. db.execDML(_T("ALTER TABLE Main ADD globalShortCut INTEGER"));
  330. e.errorCode();
  331. }
  332. try
  333. {
  334. db.execQuery(_T("SELECT lastPasteDate FROM Main"));
  335. }
  336. catch(CppSQLite3Exception& e)
  337. {
  338. db.execDML(_T("ALTER TABLE Main ADD lastPasteDate INTEGER"));
  339. db.execDML(_T("Update Main set lastPasteDate = lDate"));
  340. db.execDMLEx(_T("Update Main set lastPasteDate = %d where lastPasteDate <= 0"), (int)CTime::GetCurrentTime().GetTime());
  341. e.errorCode();
  342. }
  343. try
  344. {
  345. db.execQuery(_T("SELECT stickyClipOrder FROM Main"));
  346. }
  347. catch (CppSQLite3Exception& e)
  348. {
  349. db.execDML(_T("ALTER TABLE Main ADD stickyClipOrder REAL"));
  350. db.execDML(_T("ALTER TABLE Main ADD stickyClipGroupOrder REAL"));
  351. e.errorCode();
  352. }
  353. try
  354. {
  355. CppSQLite3Query q = db.execQuery(_T("PRAGMA index_info(Main_NoGroup);"));
  356. int count = 0;
  357. while (q.eof() == false)
  358. {
  359. count++;
  360. q.nextRow();
  361. }
  362. if(count == 0)
  363. {
  364. db.execDML(_T("Update Main set stickyClipOrder = -(2147483647) where stickyClipOrder IS NULL;"));
  365. db.execDML(_T("Update Main set stickyClipGroupOrder = -(2147483647) where stickyClipGroupOrder IS NULL;"));
  366. db.execDML(_T("Update Main set stickyClipOrder = -(2147483647) where stickyClipOrder = 0;"));
  367. db.execDML(_T("Update Main set stickyClipGroupOrder = -(2147483647) where stickyClipGroupOrder = 0;"));
  368. db.execDML(_T("CREATE INDEX Main_NoGroup ON Main(bIsGroup ASC, stickyClipOrder DESC, clipOrder DESC);"));
  369. db.execDML(_T("CREATE INDEX Main_InGroup ON Main(lParentId ASC, bIsGroup ASC, stickyClipGroupOrder DESC, clipGroupOrder DESC);"));
  370. db.execDML(_T("CREATE INDEX Data_ParentId_Format ON Data(lParentID COLLATE BINARY ASC, strClipBoardFormat COLLATE NOCASE ASC);"));
  371. }
  372. }
  373. catch (CppSQLite3Exception& e)
  374. {
  375. e.errorCode();
  376. }
  377. try
  378. {
  379. db.execQuery(_T("SELECT MoveToGroupShortCut FROM Main"));
  380. db.execQuery(_T("SELECT GlobalMoveToGroupShortCut FROM Main"));
  381. }
  382. catch(CppSQLite3Exception& e)
  383. {
  384. db.execDML(_T("ALTER TABLE Main ADD MoveToGroupShortCut INTEGER"));
  385. db.execDML(_T("ALTER TABLE Main ADD GlobalMoveToGroupShortCut INTEGER"));
  386. e.errorCode();
  387. }
  388. db.execDML(_T("DROP INDEX IF EXISTS Main_NoGroup"));
  389. db.execDML(_T("DROP INDEX IF EXISTS Main_InGroup"));
  390. db.execDML(_T("DROP INDEX IF EXISTS Main_ShortCut"));
  391. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_TopLevelParentID ON Main(lParentId ASC, stickyClipOrder DESC, bIsGroup ASC, clipOrder DESC);"));
  392. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_TopLevel ON Main(stickyClipOrder DESC, bIsGroup ASC, clipOrder DESC);"));
  393. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_InGroup2 ON Main(lParentId ASC, stickyClipGroupOrder DESC, bIsGroup ASC, clipGroupOrder DESC);"));
  394. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_ShortCut2 on Main(lShortCut DESC, globalShortCut DESC)"));
  395. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_MoveToGroup on Main(MoveToGroupShortCut DESC, GlobalMoveToGroupShortCut DESC)"));
  396. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_CRC on Main(CRC ASC)"));
  397. }
  398. CATCH_SQLITE_EXCEPTION_AND_RETURN(FALSE)
  399. return TRUE;
  400. }
  401. BOOL BackupDB(CString dbPath, CString backupPath)
  402. {
  403. CRect r = DefaultMonitorRect();
  404. CPopup status((r.right - 500), r.bottom - 100, ::GetForegroundWindow());
  405. CString msg = theApp.m_Language.GetString("BackupDbMsg", "Backing up database");
  406. status.Show(StrF(_T("Ditto - %s - %s"), msg, backupPath));
  407. BOOL ret = FALSE;
  408. Log(StrF(_T("Start backing up db, from: %s to %s"), dbPath, backupPath));
  409. CString errorMessage = _T("");
  410. try
  411. {
  412. CFile file;
  413. CFileException ex;
  414. if(file.Open(dbPath, CFile::modeRead|CFile::typeBinary|CFile::shareDenyNone, &ex))
  415. {
  416. ULONGLONG fileSize = file.GetLength();
  417. ULONGLONG totalReadSize = 0;
  418. int percentageComplete = 0;
  419. UINT readBytes = 0;
  420. char *pBuffer = new char[65536];
  421. if(pBuffer != NULL)
  422. {
  423. gzFile f = gzopen(CTextConvert::UnicodeToAnsi(backupPath), "w");
  424. if(f != NULL)
  425. {
  426. do
  427. {
  428. readBytes = file.Read(pBuffer, 65536);
  429. gzwrite(f, pBuffer, readBytes);
  430. totalReadSize+= readBytes;
  431. int percent = (int)((totalReadSize * 100) / fileSize);
  432. if(percent != percentageComplete)
  433. {
  434. percentageComplete = percent;
  435. Log(StrF(_T("backing up db percent done: %d"), percentageComplete));
  436. status.Show(StrF(_T("Ditto - %02d%% %s - %s"), percentageComplete, msg, backupPath));
  437. }
  438. }while(readBytes >= 65536);
  439. gzclose(f);
  440. ret = TRUE;
  441. }
  442. }
  443. file.Close();
  444. }
  445. else
  446. {
  447. TCHAR szCause[255];
  448. ex.GetErrorMessage(szCause, 255);
  449. errorMessage = szCause;
  450. }
  451. }
  452. catch(...)
  453. {
  454. }
  455. if (errorMessage != _T(""))
  456. {
  457. CString cs;
  458. cs.Format(_T("Restore ERROR: %s"), errorMessage);
  459. ::SendMessage(theApp.m_MainhWnd, WM_SHOW_ERROR_MSG, (WPARAM)cs.GetBuffer(cs.GetLength()), 0);
  460. cs.ReleaseBuffer();
  461. }
  462. Log(StrF(_T("Done restoring db, from: %s, errors: %s"), backupPath, errorMessage));
  463. return ret;
  464. }
  465. BOOL RestoreDB(CString backupPath)
  466. {
  467. CRect r = DefaultMonitorRect();
  468. CPopup status((r.right - 500), r.bottom - 100, ::GetForegroundWindow());
  469. CString msg = theApp.m_Language.GetString("RestoreDbMsg", "Restoring database");
  470. status.Show(StrF(_T("Ditto - %s - %s"), msg, backupPath));
  471. BOOL ret = FALSE;
  472. Log(StrF(_T("Start restoring db, from: %s"), backupPath));
  473. CString errorMessage = _T("");
  474. using namespace nsPath;
  475. CPath backupPathPath(backupPath);
  476. CString tempPath = CGetSetOptions::GetPath(PATH_RESTORE_TEMP);
  477. tempPath += backupPathPath.GetName();
  478. try
  479. {
  480. gzFile f = gzopen(CTextConvert::UnicodeToAnsi(backupPath), "r");
  481. if (f != NULL)
  482. {
  483. CFile file;
  484. CFileException ex;
  485. if (file.Open(tempPath, CFile::bufferWrite | CFile::modeCreate, &ex))
  486. {
  487. ULONGLONG totalReadSize = 0;
  488. int readBytes = 0;
  489. char *pBuffer = new char[65536];
  490. int percentageComplete = 0;
  491. do
  492. {
  493. readBytes = gzread(f, pBuffer, 65536);
  494. file.Write(pBuffer, readBytes);
  495. totalReadSize += readBytes;
  496. Log(StrF(_T("restoring db uncompressed bytes read: %d"), readBytes));
  497. } while (readBytes >= 65536);
  498. file.Close();
  499. }
  500. else
  501. {
  502. //errorMessage.Format(_T("Failed to open temp file %s, exception: %s"), tempPath, ex.GetErrorMessage());
  503. }
  504. gzclose(f);
  505. if (ValidDB(tempPath, true))
  506. {
  507. CString defaultDbPath = GetDefaultDBName();
  508. CPath defaultDbPathPath(defaultDbPath);
  509. CString path = defaultDbPathPath.GetPath();
  510. backupPathPath.RenameExtension(_T("db"));
  511. CString newFullPath = path + backupPathPath.GetName();
  512. int i = 1;
  513. while (FileExists(newFullPath))
  514. {
  515. newFullPath.Format(_T("%s%s_%d.db"), path, backupPathPath.GetTitle(), i);
  516. i++;
  517. }
  518. if (MoveFile(tempPath, newFullPath))
  519. {
  520. CGetSetOptions::SetDBPath(newFullPath);
  521. OpenDatabase(newFullPath);
  522. ret = TRUE;
  523. }
  524. else
  525. {
  526. errorMessage.Format(_T("Failed to copy file %s to %s"), tempPath, newFullPath);
  527. }
  528. }
  529. else
  530. {
  531. errorMessage.Format(_T("Unpacked database is not a valid Ditto database"));
  532. }
  533. }
  534. else
  535. {
  536. errorMessage.Format(_T("Failed to open file %s"), tempPath);
  537. }
  538. }
  539. catch (CFileException* pEx)
  540. {
  541. TCHAR cause[255];
  542. pEx->GetErrorMessage(cause, 255);
  543. errorMessage.Format(_T("Exception: %s"), cause);
  544. }
  545. catch (...)
  546. {
  547. errorMessage.Format(_T("Exception: ... catch"));
  548. }
  549. if (errorMessage != _T(""))
  550. {
  551. CString cs;
  552. cs.Format(_T("Restore ERROR: %s"), errorMessage);
  553. ::SendMessage(theApp.m_MainhWnd, WM_SHOW_ERROR_MSG, (WPARAM)cs.GetBuffer(cs.GetLength()), 0);
  554. cs.ReleaseBuffer();
  555. }
  556. Log(StrF(_T("Done restoring db, from: %s, error: %s"), backupPath, errorMessage));
  557. theApp.RefreshView();
  558. return ret;
  559. }
  560. BOOL CreateDB(CString csFile)
  561. {
  562. try
  563. {
  564. CppSQLite3DB db;
  565. db.open(csFile);
  566. db.execDML(_T("PRAGMA auto_vacuum = 1"));
  567. db.execDML(_T("CREATE TABLE Main(")
  568. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  569. _T("lDate INTEGER, ")
  570. _T("mText TEXT, ")
  571. _T("lShortCut INTEGER, ")
  572. _T("lDontAutoDelete INTEGER, ")
  573. _T("CRC INTEGER, ")
  574. _T("bIsGroup INTEGER, ")
  575. _T("lParentID INTEGER, ")
  576. _T("QuickPasteText TEXT, ")
  577. _T("clipOrder REAL, ")
  578. _T("clipGroupOrder REAL, ")
  579. _T("globalShortCut INTEGER, ")
  580. _T("lastPasteDate INTEGER, ")
  581. _T("stickyClipOrder REAL, ")
  582. _T("stickyClipGroupOrder REAL, ")
  583. _T("MoveToGroupShortCut INTEGER, ")
  584. _T("GlobalMoveToGroupShortCut INTEGER);"));
  585. db.execDML(_T("CREATE TABLE Data(")
  586. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  587. _T("lParentID INTEGER, ")
  588. _T("strClipBoardFormat TEXT, ")
  589. _T("ooData BLOB);"));
  590. db.execDML(_T("CREATE TABLE Types(")
  591. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  592. _T("TypeText TEXT);"));
  593. db.execDML(_T("CREATE UNIQUE INDEX Main_ID on Main(lID ASC)"));
  594. db.execDML(_T("CREATE UNIQUE INDEX Data_ID on Data(lID ASC)"));
  595. db.execDML(_T("CREATE INDEX Main_ClipOrder on Main(clipOrder DESC)"));
  596. db.execDML(_T("CREATE INDEX Main_ClipGroupOrder on Main(clipGroupOrder DESC)"));
  597. db.execDML(_T("CREATE INDEX Main_ParentId on Main(lParentID DESC)"));
  598. db.execDML(_T("CREATE INDEX Main_IsGroup on Main(bIsGroup DESC)"));
  599. db.execDML(_T("CREATE TRIGGER delete_data_trigger BEFORE DELETE ON Main FOR EACH ROW\n")
  600. _T("BEGIN\n")
  601. _T("INSERT INTO MainDeletes VALUES(old.lID, datetime('now'));\n")
  602. _T("END\n"));
  603. db.execDML(_T("CREATE TABLE CopyBuffers(")
  604. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  605. _T("lClipID INTEGER, ")
  606. _T("lCopyBuffer INTEGER)"));
  607. db.execDML(_T("CREATE TABLE MainDeletes(")
  608. _T("clipID INTEGER,")
  609. _T("modifiedDate)"));
  610. db.execDML(_T("CREATE TRIGGER MainDeletes_delete_data_trigger BEFORE DELETE ON MainDeletes FOR EACH ROW\n")
  611. _T("BEGIN\n")
  612. _T("DELETE FROM CopyBuffers WHERE lClipID = old.clipID;\n")
  613. _T("DELETE FROM Data WHERE lParentID = old.clipID;\n")
  614. _T("END\n"));
  615. db.execDML(_T("CREATE INDEX Data_ParentId_Format ON Data(lParentID COLLATE BINARY ASC, strClipBoardFormat COLLATE NOCASE ASC);"));
  616. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_TopLevelParentID ON Main(lParentId ASC, stickyClipOrder DESC, bIsGroup ASC, clipOrder DESC);"));
  617. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_TopLevel ON Main(stickyClipOrder DESC, bIsGroup ASC, clipOrder DESC);"));
  618. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_InGroup2 ON Main(lParentId ASC, stickyClipGroupOrder DESC, bIsGroup ASC, clipGroupOrder DESC);"));
  619. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_ShortCut2 on Main(lShortCut DESC, globalShortCut DESC)"));
  620. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_MoveToGroup on Main(MoveToGroupShortCut DESC, GlobalMoveToGroupShortCut DESC)"));
  621. db.execDML(_T("CREATE INDEX IF NOT EXISTS Main_CRC on Main(CRC ASC)"));
  622. db.close();
  623. }
  624. CATCH_SQLITE_EXCEPTION_AND_RETURN(FALSE)
  625. return TRUE;
  626. }
  627. BOOL CompactDatabase()
  628. {
  629. // if(!theApp.CloseDB())
  630. // return FALSE;
  631. //
  632. // CString csDBName = GetDBName();
  633. // CString csTempDBName = csDBName;
  634. // csTempDBName.Replace(".mdb", "TempDBName.mdb");
  635. //
  636. // //Compact the database
  637. // try
  638. // {
  639. // CDaoWorkspace::CompactDatabase(csDBName, csTempDBName);//, dbLangGeneral, 0, "andrew");//DATABASE_PASSWORD);
  640. // }
  641. // catch(CDaoException* e)
  642. // {
  643. // AfxMessageBox(e->m_pErrorInfo->m_strDescription);
  644. // DeleteFile(csTempDBName);
  645. // e->Delete();
  646. // return FALSE;
  647. // }
  648. // catch(CMemoryException* e)
  649. // {
  650. // AfxMessageBox("Memory Exception");
  651. // DeleteFile(csTempDBName);
  652. // e->Delete();
  653. // return FALSE;
  654. // }
  655. //
  656. // //Since compacting the database creates a new db delete the old one and replace it
  657. // //with the compacted db
  658. // if(DeleteFile(csDBName))
  659. // {
  660. // try
  661. // {
  662. // CFile::Rename(csTempDBName, csDBName);
  663. // }
  664. // catch(CFileException *e)
  665. // {
  666. // e->ReportError();
  667. // e->Delete();
  668. // return FALSE;
  669. // }
  670. // }
  671. // else
  672. // AfxMessageBox("Error Compacting Database");
  673. return TRUE;
  674. }
  675. BOOL RepairDatabase()
  676. {
  677. // if(!theApp.CloseDB())
  678. // return FALSE;
  679. // try
  680. // {
  681. // CDaoWorkspace::RepairDatabase(GetDBName());
  682. // }
  683. // catch(CDaoException *e)
  684. // {
  685. // AfxMessageBox(e->m_pErrorInfo->m_strDescription);
  686. // e->Delete();
  687. // return FALSE;
  688. // }
  689. return TRUE;
  690. }
  691. BOOL RemoveOldEntries(bool checkIdleTime)
  692. {
  693. Log(StrF(_T("Beginning of RemoveOldEntries MaxEntries: %d - Keep days: %d"), CGetSetOptions::GetMaxEntries(), CGetSetOptions::GetExpiredEntries()));
  694. try
  695. {
  696. CppSQLite3DB db;
  697. CString csDbPath = CGetSetOptions::GetDBPath();
  698. db.open(csDbPath);
  699. if(CGetSetOptions::GetCheckForMaxEntries())
  700. {
  701. long lMax = CGetSetOptions::GetMaxEntries();
  702. if(lMax >= 0)
  703. {
  704. CClipIDs IDs;
  705. int clipId;
  706. CppSQLite3Query q = db.execQueryEx(_T("SELECT lID, lShortCut, lParentID, lDontAutoDelete, stickyClipOrder, stickyClipGroupOrder FROM Main WHERE bIsGroup = 0 ORDER BY clipOrder DESC LIMIT -1 OFFSET %d"), lMax);
  707. while(q.eof() == false)
  708. {
  709. int shortcut = q.getIntField(_T("lShortCut"));
  710. int dontDelete = q.getIntField(_T("lDontAutoDelete"));
  711. int parentId = q.getIntField(_T("lParentID"));
  712. double stickyClipOrder = q.getFloatField(_T("stickyClipOrder"));
  713. double stickyClipGroupOrder = q.getFloatField(_T("stickyClipGroupOrder"));
  714. //Only delete entries that have no shortcut and don't have the flag set and aren't in groups and
  715. if(shortcut == 0 &&
  716. dontDelete == 0 &&
  717. parentId <= 0 &&
  718. stickyClipOrder == -(2147483647) &&
  719. stickyClipGroupOrder == -(2147483647))
  720. {
  721. clipId = q.getIntField(_T("lID"));
  722. IDs.Add(clipId);
  723. Log(StrF(_T("From MaxEntries - Deleting Id: %d"), clipId));
  724. }
  725. q.nextRow();
  726. }
  727. if(IDs.GetCount() > 0)
  728. {
  729. IDs.DeleteIDs(false, db);
  730. }
  731. }
  732. }
  733. if(CGetSetOptions::GetCheckForExpiredEntries())
  734. {
  735. long lExpire = CGetSetOptions::GetExpiredEntries();
  736. if(lExpire)
  737. {
  738. CTime now = CTime::GetCurrentTime();
  739. now -= CTimeSpan(lExpire, 0, 0, 0);
  740. CClipIDs IDs;
  741. CppSQLite3Query q = db.execQueryEx(_T("SELECT lID FROM Main ")
  742. _T("WHERE lastPasteDate < %d AND ")
  743. _T("bIsGroup = 0 AND lShortCut = 0 AND lParentID <= 0 AND lDontAutoDelete = 0 AND stickyClipOrder = -(2147483647) AND stickyClipGroupOrder = -(2147483647)"), (int)now.GetTime());
  744. while(q.eof() == false)
  745. {
  746. IDs.Add(q.getIntField(_T("lID")));
  747. Log(StrF(_T("From Clips Expire - Deleting Id: %d"), q.getIntField(_T("lID"))));
  748. q.nextRow();
  749. }
  750. if(IDs.GetCount() > 0)
  751. {
  752. IDs.DeleteIDs(false, db);
  753. }
  754. }
  755. }
  756. int toDeleteCount = db.execScalar(_T("SELECT COUNT(clipID) FROM MainDeletes"));
  757. Log(StrF(_T("Before Deleting emptied out data, count: %d, Idle Seconds: %f"), toDeleteCount, IdleSeconds()));
  758. //Only delete 1 at a time, was finding that it was taking a long time to delete clips, locking the db and causing other queries
  759. //to lock up
  760. CppSQLite3Query q = db.execQueryEx(_T("SELECT * FROM MainDeletes LIMIT %d"), CGetSetOptions::GetMainDeletesDeleteCount());
  761. int deleteCount = 0;
  762. while(q.eof() == false)
  763. {
  764. double idleSeconds = IdleSeconds();
  765. if(checkIdleTime == false || idleSeconds > CGetSetOptions::GetIdleSecondsBeforeDelete())
  766. {
  767. //delete any data items sitting out there that the main table data was deleted
  768. //this was done to speed up deleted from the main table
  769. deleteCount = db.execDMLEx(_T("DELETE FROM MainDeletes WHERE clipID=%d"), q.getIntField(_T("clipID")));
  770. }
  771. else
  772. {
  773. Log(StrF(_T("Computer has not been idle long enough to delete clips, Min Idle: %d, current Idle: %d"),
  774. CGetSetOptions::GetIdleSecondsBeforeDelete(), idleSeconds));
  775. break;
  776. }
  777. q.nextRow();
  778. }
  779. toDeleteCount = db.execScalar(_T("SELECT COUNT(clipID) FROM MainDeletes"));
  780. Log(StrF(_T("After Deleting emptied out data rows, Count: %d, toDelete: %d"), deleteCount, toDeleteCount));
  781. }
  782. CATCH_SQLITE_EXCEPTION
  783. Log(_T("End of RemoveOldEntries"));
  784. return TRUE;
  785. }
  786. BOOL DeleteNonUsedClips(bool fromAppWindow)
  787. {
  788. Log(_T("Start of delete all non used clips"));
  789. CClipIDs IDs;
  790. CppSQLite3Query q = theApp.m_db.execQueryEx(_T("SELECT lID FROM Main WHERE bIsGroup = 0 AND lShortCut = 0 AND lParentID <= 0 AND lDontAutoDelete = 0 AND stickyClipOrder = -(2147483647) AND stickyClipGroupOrder = -(2147483647)"));
  791. while (q.eof() == false)
  792. {
  793. IDs.Add(q.getIntField(_T("lID")));
  794. Log(StrF(_T("From Clips DeleteNonUsedClips - Deleting Id: %d"), q.getIntField(_T("lID"))));
  795. q.nextRow();
  796. }
  797. int clipsDeleted = IDs.GetCount();
  798. int deletedTableCount = 0;
  799. if (IDs.GetCount() > 0)
  800. {
  801. IDs.DeleteIDs(fromAppWindow, theApp.m_db);
  802. deletedTableCount = theApp.m_db.execDMLEx(_T("DELETE FROM MainDeletes"));
  803. }
  804. Log(StrF(_T("End of delete all non used clips, clips deleted: %d, delete table delted: %d"), clipsDeleted, deletedTableCount));
  805. return TRUE;
  806. }
  807. BOOL EnsureDirectory(CString csPath)
  808. {
  809. TCHAR drive[_MAX_DRIVE];
  810. TCHAR dir[_MAX_DIR];
  811. TCHAR fname[_MAX_FNAME];
  812. TCHAR ext[_MAX_EXT];
  813. SPLITPATH(csPath, drive, dir, fname, ext);
  814. CString csDir(drive);
  815. csDir += dir;
  816. if(FileExists(csDir) == FALSE)
  817. {
  818. if(CreateDirectory(csDir, NULL))
  819. return TRUE;
  820. }
  821. else
  822. return TRUE;
  823. return FALSE;
  824. }
  825. // BOOL RunZippApp(CString csCommandLine)
  826. // {
  827. // CString csLocalPath = GETENV(_T("U3_HOST_EXEC_PATH"));
  828. // FIX_CSTRING_PATH(csLocalPath);
  829. //
  830. // CString csZippApp = GETENV(_T("U3_DEVICE_EXEC_PATH"));
  831. // FIX_CSTRING_PATH(csZippApp);
  832. // csZippApp += "7za.exe";
  833. //
  834. // csZippApp += " ";
  835. // csZippApp += csCommandLine;
  836. //
  837. // Log(csZippApp);
  838. //
  839. // STARTUPINFO StartupInfo;
  840. // PROCESS_INFORMATION ProcessInformation;
  841. //
  842. // ZeroMemory(&StartupInfo, sizeof(StartupInfo));
  843. // StartupInfo.cb = sizeof(StartupInfo);
  844. // ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
  845. //
  846. // StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
  847. // StartupInfo.wShowWindow = SW_HIDE;
  848. //
  849. // BOOL bRet = CreateProcess(NULL, csZippApp.GetBuffer(csZippApp.GetLength()), NULL, NULL, FALSE,
  850. // CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS, NULL, csLocalPath,
  851. // &StartupInfo, &ProcessInformation);
  852. //
  853. // if(bRet)
  854. // {
  855. // WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
  856. //
  857. // DWORD dwExitCode;
  858. // GetExitCodeProcess(ProcessInformation.hProcess, &dwExitCode);
  859. //
  860. // CString cs;
  861. // cs.Format(_T("Exit code from unzip = %d"), dwExitCode);
  862. // Log(cs);
  863. //
  864. // if(dwExitCode != 0)
  865. // {
  866. // bRet = FALSE;
  867. // }
  868. // }
  869. // else
  870. // {
  871. // bRet = FALSE;
  872. // Log(_T("Create Process Failed"));
  873. // }
  874. //
  875. // csZippApp.ReleaseBuffer();
  876. //
  877. // return bRet;
  878. // }
  879. // BOOL CopyDownDatabase()
  880. // {
  881. // BOOL bRet = FALSE;
  882. //
  883. // CString csZippedPath = GETENV(_T("U3_APP_DATA_PATH"));
  884. // FIX_CSTRING_PATH(csZippedPath);
  885. //
  886. // CString csUnZippedPath = csZippedPath;
  887. // csUnZippedPath += "Ditto.db";
  888. //
  889. // csZippedPath += "Ditto.7z";
  890. //
  891. // CString csLocalPath = GETENV(_T("U3_HOST_EXEC_PATH"));
  892. // FIX_CSTRING_PATH(csLocalPath);
  893. //
  894. // if(FileExists(csZippedPath))
  895. // {
  896. // CString csCommandLine;
  897. //
  898. // //e = extract
  899. // //surround command line arguments with quotes
  900. // //-aoa = overight files with extracted files
  901. //
  902. // csCommandLine += "e ";
  903. // csCommandLine += "\"";
  904. // csCommandLine += csZippedPath;
  905. // csCommandLine += "\"";
  906. // csCommandLine += " -o";
  907. // csCommandLine += "\"";
  908. // csCommandLine += csLocalPath;
  909. // csCommandLine += "\"";
  910. // csCommandLine += " -aoa";
  911. //
  912. // bRet = RunZippApp(csCommandLine);
  913. //
  914. // csLocalPath += "Ditto.db";
  915. // }
  916. // else if(FileExists(csUnZippedPath))
  917. // {
  918. // csLocalPath += "Ditto.db";
  919. // bRet = CopyFile(csUnZippedPath, csLocalPath, FALSE);
  920. // }
  921. //
  922. // if(FileExists(csLocalPath) == FALSE)
  923. // {
  924. // Log(_T("Failed to copy files from device zip file"));
  925. // }
  926. //
  927. // g_Opt.nLastDbWriteTime = GetLastWriteTime(csLocalPath);
  928. //
  929. // return bRet;
  930. // }
  931. //BOOL CopyUpDatabase()
  932. //{
  933. // CStringA csZippedPath = "C:\\";//getenv("U3_APP_DATA_PATH");
  934. // FIX_CSTRING_PATH(csZippedPath);
  935. // csZippedPath += "Ditto.zip";
  936. // CStringA csLocalPath = GetDBName();//getenv("U3_HOST_EXEC_PATH");
  937. // //FIX_CSTRING_PATH(csLocalPath);
  938. // //csLocalPath += "Ditto.db";
  939. //
  940. // CZipper Zip;
  941. //
  942. // if(Zip.OpenZip(csZippedPath))
  943. // {
  944. // Zip.AddFileToZip(csLocalPath);
  945. // }
  946. //
  947. // return TRUE;
  948. //}