DatabaseUtilities.cpp 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  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. CRect r = DefaultMonitorRect();
  368. CPopup status((r.right - 500), r.bottom - 100, ::GetForegroundWindow());
  369. CString msg = theApp.m_Language.GetString("BackupDbMsg", "Backing up database");
  370. status.Show(StrF(_T("Ditto - %s - %s"), msg, backupPath));
  371. BOOL ret = FALSE;
  372. Log(StrF(_T("Start backing up db, from: %s to %s"), dbPath, backupPath));
  373. CString errorMessage = _T("");
  374. try
  375. {
  376. CFile file;
  377. CFileException ex;
  378. if(file.Open(dbPath, CFile::modeRead|CFile::typeBinary|CFile::shareDenyNone, &ex))
  379. {
  380. ULONGLONG fileSize = file.GetLength();
  381. ULONGLONG totalReadSize = 0;
  382. int percentageComplete = 0;
  383. UINT readBytes = 0;
  384. char *pBuffer = new char[65536];
  385. if(pBuffer != NULL)
  386. {
  387. gzFile f = gzopen(CTextConvert::ConvertToChar(backupPath), "w");
  388. if(f != NULL)
  389. {
  390. do
  391. {
  392. readBytes = file.Read(pBuffer, 65536);
  393. gzwrite(f, pBuffer, readBytes);
  394. totalReadSize+= readBytes;
  395. int percent = (int)((totalReadSize * 100) / fileSize);
  396. if(percent != percentageComplete)
  397. {
  398. percentageComplete = percent;
  399. Log(StrF(_T("backing up db percent done: %d"), percentageComplete));
  400. status.Show(StrF(_T("Ditto - %02d%% %s - %s"), percentageComplete, msg, backupPath));
  401. }
  402. }while(readBytes >= 65536);
  403. gzclose(f);
  404. ret = TRUE;
  405. }
  406. }
  407. file.Close();
  408. }
  409. else
  410. {
  411. TCHAR szCause[255];
  412. ex.GetErrorMessage(szCause, 255);
  413. errorMessage = szCause;
  414. }
  415. }
  416. catch(...)
  417. {
  418. }
  419. if (errorMessage != _T(""))
  420. {
  421. CString cs;
  422. cs.Format(_T("Restore ERROR: %s"), errorMessage);
  423. ::SendMessage(theApp.m_MainhWnd, WM_SHOW_ERROR_MSG, (WPARAM)cs.GetBuffer(cs.GetLength()), 0);
  424. cs.ReleaseBuffer();
  425. }
  426. Log(StrF(_T("Done restoring db, from: %s, errors: %s"), backupPath, errorMessage));
  427. return ret;
  428. }
  429. BOOL RestoreDB(CString backupPath)
  430. {
  431. CRect r = DefaultMonitorRect();
  432. CPopup status((r.right - 500), r.bottom - 100, ::GetForegroundWindow());
  433. CString msg = theApp.m_Language.GetString("RestoreDbMsg", "Restoring database");
  434. status.Show(StrF(_T("Ditto - %s - %s"), msg, backupPath));
  435. BOOL ret = FALSE;
  436. Log(StrF(_T("Start restoring db, from: %s"), backupPath));
  437. CString errorMessage = _T("");
  438. using namespace nsPath;
  439. CPath backupPathPath(backupPath);
  440. CString tempPath = CGetSetOptions::GetPath(PATH_RESTORE_TEMP);
  441. tempPath += backupPathPath.GetName();
  442. try
  443. {
  444. gzFile f = gzopen(CTextConvert::ConvertToChar(backupPath), "r");
  445. if (f != NULL)
  446. {
  447. CFile file;
  448. CFileException ex;
  449. if (file.Open(tempPath, CFile::bufferWrite | CFile::modeCreate, &ex))
  450. {
  451. ULONGLONG totalReadSize = 0;
  452. int readBytes = 0;
  453. char *pBuffer = new char[65536];
  454. int percentageComplete = 0;
  455. do
  456. {
  457. readBytes = gzread(f, pBuffer, 65536);
  458. file.Write(pBuffer, readBytes);
  459. totalReadSize += readBytes;
  460. Log(StrF(_T("restoring db uncompressed bytes read: %d"), readBytes));
  461. } while (readBytes >= 65536);
  462. file.Close();
  463. }
  464. else
  465. {
  466. //errorMessage.Format(_T("Failed to open temp file %s, exception: %s"), tempPath, ex.GetErrorMessage());
  467. }
  468. gzclose(f);
  469. if (ValidDB(tempPath, true))
  470. {
  471. CString defaultDbPath = GetDefaultDBName();
  472. CPath defaultDbPathPath(defaultDbPath);
  473. CString path = defaultDbPathPath.GetPath();
  474. backupPathPath.RenameExtension(_T("db"));
  475. CString newFullPath = path + backupPathPath.GetName();
  476. int i = 1;
  477. while (FileExists(newFullPath))
  478. {
  479. newFullPath.Format(_T("%s%s_%d.db"), path, backupPathPath.GetTitle(), i);
  480. i++;
  481. }
  482. if (MoveFile(tempPath, newFullPath))
  483. {
  484. CGetSetOptions::SetDBPath(newFullPath);
  485. OpenDatabase(newFullPath);
  486. ret = TRUE;
  487. }
  488. else
  489. {
  490. errorMessage.Format(_T("Failed to copy file %s to %s"), tempPath, newFullPath);
  491. }
  492. }
  493. else
  494. {
  495. errorMessage.Format(_T("Unpacked database is not a valid Ditto database"));
  496. }
  497. }
  498. else
  499. {
  500. errorMessage.Format(_T("Failed to open file %s"), tempPath);
  501. }
  502. }
  503. catch (CFileException* pEx)
  504. {
  505. TCHAR cause[255];
  506. pEx->GetErrorMessage(cause, 255);
  507. errorMessage.Format(_T("Exception: %s"), cause);
  508. }
  509. catch (...)
  510. {
  511. errorMessage.Format(_T("Exception: ... catch"));
  512. }
  513. if (errorMessage != _T(""))
  514. {
  515. CString cs;
  516. cs.Format(_T("Restore ERROR: %s"), errorMessage);
  517. ::SendMessage(theApp.m_MainhWnd, WM_SHOW_ERROR_MSG, (WPARAM)cs.GetBuffer(cs.GetLength()), 0);
  518. cs.ReleaseBuffer();
  519. }
  520. Log(StrF(_T("Done restoring db, from: %s, error: %s"), backupPath, errorMessage));
  521. theApp.RefreshView();
  522. return ret;
  523. }
  524. BOOL CreateDB(CString csFile)
  525. {
  526. try
  527. {
  528. CppSQLite3DB db;
  529. db.open(csFile);
  530. db.execDML(_T("PRAGMA auto_vacuum = 1"));
  531. db.execDML(_T("CREATE TABLE Main(")
  532. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  533. _T("lDate INTEGER, ")
  534. _T("mText TEXT, ")
  535. _T("lShortCut INTEGER, ")
  536. _T("lDontAutoDelete INTEGER, ")
  537. _T("CRC INTEGER, ")
  538. _T("bIsGroup INTEGER, ")
  539. _T("lParentID INTEGER, ")
  540. _T("QuickPasteText TEXT, ")
  541. _T("clipOrder REAL, ")
  542. _T("clipGroupOrder REAL, ")
  543. _T("globalShortCut INTEGER, ")
  544. _T("lastPasteDate INTEGER, ")
  545. _T("stickyClipOrder REAL, ")
  546. _T("stickyClipGroupOrder REAL, ")
  547. _T("MoveToGroupShortCut INTEGER, ")
  548. _T("GlobalMoveToGroupShortCut INTEGER);"));
  549. db.execDML(_T("CREATE TABLE Data(")
  550. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  551. _T("lParentID INTEGER, ")
  552. _T("strClipBoardFormat TEXT, ")
  553. _T("ooData BLOB);"));
  554. db.execDML(_T("CREATE TABLE Types(")
  555. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  556. _T("TypeText TEXT);"));
  557. db.execDML(_T("CREATE UNIQUE INDEX Main_ID on Main(lID ASC)"));
  558. db.execDML(_T("CREATE UNIQUE INDEX Data_ID on Data(lID ASC)"));
  559. db.execDML(_T("CREATE INDEX Main_ClipOrder on Main(clipOrder DESC)"));
  560. db.execDML(_T("CREATE INDEX Main_ClipGroupOrder on Main(clipGroupOrder DESC)"));
  561. db.execDML(_T("CREATE INDEX Main_ParentId on Main(lParentID DESC)"));
  562. db.execDML(_T("CREATE INDEX Main_IsGroup on Main(bIsGroup DESC)"));
  563. db.execDML(_T("CREATE INDEX Main_ShortCut on Main(lShortCut DESC)"));
  564. db.execDML(_T("CREATE TRIGGER delete_data_trigger BEFORE DELETE ON Main FOR EACH ROW\n")
  565. _T("BEGIN\n")
  566. _T("INSERT INTO MainDeletes VALUES(old.lID, datetime('now'));\n")
  567. _T("END\n"));
  568. db.execDML(_T("CREATE TABLE CopyBuffers(")
  569. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  570. _T("lClipID INTEGER, ")
  571. _T("lCopyBuffer INTEGER)"));
  572. db.execDML(_T("CREATE TABLE MainDeletes(")
  573. _T("clipID INTEGER,")
  574. _T("modifiedDate)"));
  575. db.execDML(_T("CREATE TRIGGER MainDeletes_delete_data_trigger BEFORE DELETE ON MainDeletes FOR EACH ROW\n")
  576. _T("BEGIN\n")
  577. _T("DELETE FROM CopyBuffers WHERE lClipID = old.clipID;\n")
  578. _T("DELETE FROM Data WHERE lParentID = old.clipID;\n")
  579. _T("END\n"));
  580. db.execDML(_T("CREATE INDEX Main_NoGroup ON Main(bIsGroup ASC, stickyClipOrder DESC, clipOrder DESC);"));
  581. db.execDML(_T("CREATE INDEX Main_InGroup ON Main(lParentId ASC, bIsGroup ASC, stickyClipGroupOrder DESC, clipGroupOrder DESC);"));
  582. db.execDML(_T("CREATE INDEX Data_ParentId_Format ON Data(lParentID COLLATE BINARY ASC, strClipBoardFormat COLLATE NOCASE ASC);"));
  583. db.close();
  584. }
  585. CATCH_SQLITE_EXCEPTION_AND_RETURN(FALSE)
  586. return TRUE;
  587. }
  588. BOOL CompactDatabase()
  589. {
  590. // if(!theApp.CloseDB())
  591. // return FALSE;
  592. //
  593. // CString csDBName = GetDBName();
  594. // CString csTempDBName = csDBName;
  595. // csTempDBName.Replace(".mdb", "TempDBName.mdb");
  596. //
  597. // //Compact the database
  598. // try
  599. // {
  600. // CDaoWorkspace::CompactDatabase(csDBName, csTempDBName);//, dbLangGeneral, 0, "andrew");//DATABASE_PASSWORD);
  601. // }
  602. // catch(CDaoException* e)
  603. // {
  604. // AfxMessageBox(e->m_pErrorInfo->m_strDescription);
  605. // DeleteFile(csTempDBName);
  606. // e->Delete();
  607. // return FALSE;
  608. // }
  609. // catch(CMemoryException* e)
  610. // {
  611. // AfxMessageBox("Memory Exception");
  612. // DeleteFile(csTempDBName);
  613. // e->Delete();
  614. // return FALSE;
  615. // }
  616. //
  617. // //Since compacting the database creates a new db delete the old one and replace it
  618. // //with the compacted db
  619. // if(DeleteFile(csDBName))
  620. // {
  621. // try
  622. // {
  623. // CFile::Rename(csTempDBName, csDBName);
  624. // }
  625. // catch(CFileException *e)
  626. // {
  627. // e->ReportError();
  628. // e->Delete();
  629. // return FALSE;
  630. // }
  631. // }
  632. // else
  633. // AfxMessageBox("Error Compacting Database");
  634. return TRUE;
  635. }
  636. BOOL RepairDatabase()
  637. {
  638. // if(!theApp.CloseDB())
  639. // return FALSE;
  640. // try
  641. // {
  642. // CDaoWorkspace::RepairDatabase(GetDBName());
  643. // }
  644. // catch(CDaoException *e)
  645. // {
  646. // AfxMessageBox(e->m_pErrorInfo->m_strDescription);
  647. // e->Delete();
  648. // return FALSE;
  649. // }
  650. return TRUE;
  651. }
  652. BOOL RemoveOldEntries(bool checkIdleTime)
  653. {
  654. Log(StrF(_T("Beginning of RemoveOldEntries MaxEntries: %d - Keep days: %d"), CGetSetOptions::GetMaxEntries(), CGetSetOptions::GetExpiredEntries()));
  655. try
  656. {
  657. CppSQLite3DB db;
  658. CString csDbPath = CGetSetOptions::GetDBPath();
  659. db.open(csDbPath);
  660. if(CGetSetOptions::GetCheckForMaxEntries())
  661. {
  662. long lMax = CGetSetOptions::GetMaxEntries();
  663. if(lMax >= 0)
  664. {
  665. CClipIDs IDs;
  666. int clipId;
  667. 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);
  668. while(q.eof() == false)
  669. {
  670. int shortcut = q.getIntField(_T("lShortCut"));
  671. int dontDelete = q.getIntField(_T("lDontAutoDelete"));
  672. int parentId = q.getIntField(_T("lParentID"));
  673. double stickyClipOrder = q.getFloatField(_T("stickyClipOrder"));
  674. double stickyClipGroupOrder = q.getFloatField(_T("stickyClipGroupOrder"));
  675. //Only delete entries that have no shortcut and don't have the flag set and aren't in groups and
  676. if(shortcut == 0 &&
  677. dontDelete == 0 &&
  678. parentId <= 0 &&
  679. stickyClipOrder == -(2147483647) &&
  680. stickyClipGroupOrder == -(2147483647))
  681. {
  682. clipId = q.getIntField(_T("lID"));
  683. IDs.Add(clipId);
  684. Log(StrF(_T("From MaxEntries - Deleting Id: %d"), clipId));
  685. }
  686. q.nextRow();
  687. }
  688. if(IDs.GetCount() > 0)
  689. {
  690. IDs.DeleteIDs(false, db);
  691. }
  692. }
  693. }
  694. if(CGetSetOptions::GetCheckForExpiredEntries())
  695. {
  696. long lExpire = CGetSetOptions::GetExpiredEntries();
  697. if(lExpire)
  698. {
  699. CTime now = CTime::GetCurrentTime();
  700. now -= CTimeSpan(lExpire, 0, 0, 0);
  701. CClipIDs IDs;
  702. CppSQLite3Query q = db.execQueryEx(_T("SELECT lID FROM Main ")
  703. _T("WHERE lastPasteDate < %d AND ")
  704. _T("bIsGroup = 0 AND lShortCut = 0 AND lParentID <= 0 AND lDontAutoDelete = 0 AND stickyClipOrder = -(2147483647) AND stickyClipGroupOrder = -(2147483647)"), (int)now.GetTime());
  705. while(q.eof() == false)
  706. {
  707. IDs.Add(q.getIntField(_T("lID")));
  708. Log(StrF(_T("From Clips Expire - Deleting Id: %d"), q.getIntField(_T("lID"))));
  709. q.nextRow();
  710. }
  711. if(IDs.GetCount() > 0)
  712. {
  713. IDs.DeleteIDs(false, db);
  714. }
  715. }
  716. }
  717. int toDeleteCount = db.execScalar(_T("SELECT COUNT(clipID) FROM MainDeletes"));
  718. Log(StrF(_T("Before Deleting emptied out data, count: %d, Idle Seconds: %f"), toDeleteCount, IdleSeconds()));
  719. //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
  720. //to lock up
  721. CppSQLite3Query q = db.execQueryEx(_T("SELECT * FROM MainDeletes LIMIT %d"), CGetSetOptions::GetMainDeletesDeleteCount());
  722. int deleteCount = 0;
  723. while(q.eof() == false)
  724. {
  725. double idleSeconds = IdleSeconds();
  726. if(checkIdleTime == false || idleSeconds > CGetSetOptions::GetIdleSecondsBeforeDelete())
  727. {
  728. //delete any data items sitting out there that the main table data was deleted
  729. //this was done to speed up deleted from the main table
  730. deleteCount = db.execDMLEx(_T("DELETE FROM MainDeletes WHERE clipID=%d"), q.getIntField(_T("clipID")));
  731. }
  732. else
  733. {
  734. Log(StrF(_T("Computer has not been idle long enough to delete clips, Min Idle: %d, current Idle: %d"),
  735. CGetSetOptions::GetIdleSecondsBeforeDelete(), idleSeconds));
  736. break;
  737. }
  738. q.nextRow();
  739. }
  740. toDeleteCount = db.execScalar(_T("SELECT COUNT(clipID) FROM MainDeletes"));
  741. Log(StrF(_T("After Deleting emptied out data rows, Count: %d, toDelete: %d"), deleteCount, toDeleteCount));
  742. }
  743. CATCH_SQLITE_EXCEPTION
  744. Log(_T("End of RemoveOldEntries"));
  745. return TRUE;
  746. }
  747. BOOL EnsureDirectory(CString csPath)
  748. {
  749. TCHAR drive[_MAX_DRIVE];
  750. TCHAR dir[_MAX_DIR];
  751. TCHAR fname[_MAX_FNAME];
  752. TCHAR ext[_MAX_EXT];
  753. SPLITPATH(csPath, drive, dir, fname, ext);
  754. CString csDir(drive);
  755. csDir += dir;
  756. if(FileExists(csDir) == FALSE)
  757. {
  758. if(CreateDirectory(csDir, NULL))
  759. return TRUE;
  760. }
  761. else
  762. return TRUE;
  763. return FALSE;
  764. }
  765. // BOOL RunZippApp(CString csCommandLine)
  766. // {
  767. // CString csLocalPath = GETENV(_T("U3_HOST_EXEC_PATH"));
  768. // FIX_CSTRING_PATH(csLocalPath);
  769. //
  770. // CString csZippApp = GETENV(_T("U3_DEVICE_EXEC_PATH"));
  771. // FIX_CSTRING_PATH(csZippApp);
  772. // csZippApp += "7za.exe";
  773. //
  774. // csZippApp += " ";
  775. // csZippApp += csCommandLine;
  776. //
  777. // Log(csZippApp);
  778. //
  779. // STARTUPINFO StartupInfo;
  780. // PROCESS_INFORMATION ProcessInformation;
  781. //
  782. // ZeroMemory(&StartupInfo, sizeof(StartupInfo));
  783. // StartupInfo.cb = sizeof(StartupInfo);
  784. // ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
  785. //
  786. // StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
  787. // StartupInfo.wShowWindow = SW_HIDE;
  788. //
  789. // BOOL bRet = CreateProcess(NULL, csZippApp.GetBuffer(csZippApp.GetLength()), NULL, NULL, FALSE,
  790. // CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS, NULL, csLocalPath,
  791. // &StartupInfo, &ProcessInformation);
  792. //
  793. // if(bRet)
  794. // {
  795. // WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
  796. //
  797. // DWORD dwExitCode;
  798. // GetExitCodeProcess(ProcessInformation.hProcess, &dwExitCode);
  799. //
  800. // CString cs;
  801. // cs.Format(_T("Exit code from unzip = %d"), dwExitCode);
  802. // Log(cs);
  803. //
  804. // if(dwExitCode != 0)
  805. // {
  806. // bRet = FALSE;
  807. // }
  808. // }
  809. // else
  810. // {
  811. // bRet = FALSE;
  812. // Log(_T("Create Process Failed"));
  813. // }
  814. //
  815. // csZippApp.ReleaseBuffer();
  816. //
  817. // return bRet;
  818. // }
  819. // BOOL CopyDownDatabase()
  820. // {
  821. // BOOL bRet = FALSE;
  822. //
  823. // CString csZippedPath = GETENV(_T("U3_APP_DATA_PATH"));
  824. // FIX_CSTRING_PATH(csZippedPath);
  825. //
  826. // CString csUnZippedPath = csZippedPath;
  827. // csUnZippedPath += "Ditto.db";
  828. //
  829. // csZippedPath += "Ditto.7z";
  830. //
  831. // CString csLocalPath = GETENV(_T("U3_HOST_EXEC_PATH"));
  832. // FIX_CSTRING_PATH(csLocalPath);
  833. //
  834. // if(FileExists(csZippedPath))
  835. // {
  836. // CString csCommandLine;
  837. //
  838. // //e = extract
  839. // //surround command line arguments with quotes
  840. // //-aoa = overight files with extracted files
  841. //
  842. // csCommandLine += "e ";
  843. // csCommandLine += "\"";
  844. // csCommandLine += csZippedPath;
  845. // csCommandLine += "\"";
  846. // csCommandLine += " -o";
  847. // csCommandLine += "\"";
  848. // csCommandLine += csLocalPath;
  849. // csCommandLine += "\"";
  850. // csCommandLine += " -aoa";
  851. //
  852. // bRet = RunZippApp(csCommandLine);
  853. //
  854. // csLocalPath += "Ditto.db";
  855. // }
  856. // else if(FileExists(csUnZippedPath))
  857. // {
  858. // csLocalPath += "Ditto.db";
  859. // bRet = CopyFile(csUnZippedPath, csLocalPath, FALSE);
  860. // }
  861. //
  862. // if(FileExists(csLocalPath) == FALSE)
  863. // {
  864. // Log(_T("Failed to copy files from device zip file"));
  865. // }
  866. //
  867. // g_Opt.nLastDbWriteTime = GetLastWriteTime(csLocalPath);
  868. //
  869. // return bRet;
  870. // }
  871. //BOOL CopyUpDatabase()
  872. //{
  873. // CStringA csZippedPath = "C:\\";//getenv("U3_APP_DATA_PATH");
  874. // FIX_CSTRING_PATH(csZippedPath);
  875. // csZippedPath += "Ditto.zip";
  876. // CStringA csLocalPath = GetDBName();//getenv("U3_HOST_EXEC_PATH");
  877. // //FIX_CSTRING_PATH(csLocalPath);
  878. // //csLocalPath += "Ditto.db";
  879. //
  880. // CZipper Zip;
  881. //
  882. // if(Zip.OpenZip(csZippedPath))
  883. // {
  884. // Zip.AddFileToZip(csLocalPath);
  885. // }
  886. //
  887. // return TRUE;
  888. //}