DatabaseUtilities.cpp 27 KB

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