DatabaseUtilities.cpp 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058
  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. return ret;
  522. }
  523. BOOL CreateDB(CString csFile)
  524. {
  525. try
  526. {
  527. CppSQLite3DB db;
  528. db.open(csFile);
  529. db.execDML(_T("PRAGMA auto_vacuum = 1"));
  530. db.execDML(_T("CREATE TABLE Main(")
  531. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  532. _T("lDate INTEGER, ")
  533. _T("mText TEXT, ")
  534. _T("lShortCut INTEGER, ")
  535. _T("lDontAutoDelete INTEGER, ")
  536. _T("CRC INTEGER, ")
  537. _T("bIsGroup INTEGER, ")
  538. _T("lParentID INTEGER, ")
  539. _T("QuickPasteText TEXT, ")
  540. _T("clipOrder REAL, ")
  541. _T("clipGroupOrder REAL, ")
  542. _T("globalShortCut INTEGER, ")
  543. _T("lastPasteDate INTEGER, ")
  544. _T("stickyClipOrder REAL, ")
  545. _T("stickyClipGroupOrder REAL, ")
  546. _T("MoveToGroupShortCut INTEGER, ")
  547. _T("GlobalMoveToGroupShortCut INTEGER);"));
  548. db.execDML(_T("CREATE TABLE Data(")
  549. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  550. _T("lParentID INTEGER, ")
  551. _T("strClipBoardFormat TEXT, ")
  552. _T("ooData BLOB);"));
  553. db.execDML(_T("CREATE TABLE Types(")
  554. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  555. _T("TypeText TEXT);"));
  556. db.execDML(_T("CREATE UNIQUE INDEX Main_ID on Main(lID ASC)"));
  557. db.execDML(_T("CREATE UNIQUE INDEX Data_ID on Data(lID ASC)"));
  558. db.execDML(_T("CREATE INDEX Main_ClipOrder on Main(clipOrder DESC)"));
  559. db.execDML(_T("CREATE INDEX Main_ClipGroupOrder on Main(clipGroupOrder DESC)"));
  560. db.execDML(_T("CREATE INDEX Main_ParentId on Main(lParentID DESC)"));
  561. db.execDML(_T("CREATE INDEX Main_IsGroup on Main(bIsGroup DESC)"));
  562. db.execDML(_T("CREATE INDEX Main_ShortCut on Main(lShortCut DESC)"));
  563. db.execDML(_T("CREATE TRIGGER delete_data_trigger BEFORE DELETE ON Main FOR EACH ROW\n")
  564. _T("BEGIN\n")
  565. _T("INSERT INTO MainDeletes VALUES(old.lID, datetime('now'));\n")
  566. _T("END\n"));
  567. db.execDML(_T("CREATE TABLE CopyBuffers(")
  568. _T("lID INTEGER PRIMARY KEY AUTOINCREMENT, ")
  569. _T("lClipID INTEGER, ")
  570. _T("lCopyBuffer INTEGER)"));
  571. db.execDML(_T("CREATE TABLE MainDeletes(")
  572. _T("clipID INTEGER,")
  573. _T("modifiedDate)"));
  574. db.execDML(_T("CREATE TRIGGER MainDeletes_delete_data_trigger BEFORE DELETE ON MainDeletes FOR EACH ROW\n")
  575. _T("BEGIN\n")
  576. _T("DELETE FROM CopyBuffers WHERE lClipID = old.clipID;\n")
  577. _T("DELETE FROM Data WHERE lParentID = old.clipID;\n")
  578. _T("END\n"));
  579. db.execDML(_T("CREATE INDEX Main_NoGroup ON Main(bIsGroup ASC, stickyClipOrder DESC, clipOrder DESC);"));
  580. db.execDML(_T("CREATE INDEX Main_InGroup ON Main(lParentId ASC, bIsGroup ASC, stickyClipGroupOrder DESC, clipGroupOrder DESC);"));
  581. db.execDML(_T("CREATE INDEX Data_ParentId_Format ON Data(lParentID COLLATE BINARY ASC, strClipBoardFormat COLLATE NOCASE ASC);"));
  582. db.close();
  583. }
  584. CATCH_SQLITE_EXCEPTION_AND_RETURN(FALSE)
  585. return TRUE;
  586. }
  587. BOOL CompactDatabase()
  588. {
  589. // if(!theApp.CloseDB())
  590. // return FALSE;
  591. //
  592. // CString csDBName = GetDBName();
  593. // CString csTempDBName = csDBName;
  594. // csTempDBName.Replace(".mdb", "TempDBName.mdb");
  595. //
  596. // //Compact the database
  597. // try
  598. // {
  599. // CDaoWorkspace::CompactDatabase(csDBName, csTempDBName);//, dbLangGeneral, 0, "andrew");//DATABASE_PASSWORD);
  600. // }
  601. // catch(CDaoException* e)
  602. // {
  603. // AfxMessageBox(e->m_pErrorInfo->m_strDescription);
  604. // DeleteFile(csTempDBName);
  605. // e->Delete();
  606. // return FALSE;
  607. // }
  608. // catch(CMemoryException* e)
  609. // {
  610. // AfxMessageBox("Memory Exception");
  611. // DeleteFile(csTempDBName);
  612. // e->Delete();
  613. // return FALSE;
  614. // }
  615. //
  616. // //Since compacting the database creates a new db delete the old one and replace it
  617. // //with the compacted db
  618. // if(DeleteFile(csDBName))
  619. // {
  620. // try
  621. // {
  622. // CFile::Rename(csTempDBName, csDBName);
  623. // }
  624. // catch(CFileException *e)
  625. // {
  626. // e->ReportError();
  627. // e->Delete();
  628. // return FALSE;
  629. // }
  630. // }
  631. // else
  632. // AfxMessageBox("Error Compacting Database");
  633. return TRUE;
  634. }
  635. BOOL RepairDatabase()
  636. {
  637. // if(!theApp.CloseDB())
  638. // return FALSE;
  639. // try
  640. // {
  641. // CDaoWorkspace::RepairDatabase(GetDBName());
  642. // }
  643. // catch(CDaoException *e)
  644. // {
  645. // AfxMessageBox(e->m_pErrorInfo->m_strDescription);
  646. // e->Delete();
  647. // return FALSE;
  648. // }
  649. return TRUE;
  650. }
  651. BOOL RemoveOldEntries(bool checkIdleTime)
  652. {
  653. Log(StrF(_T("Beginning of RemoveOldEntries MaxEntries: %d - Keep days: %d"), CGetSetOptions::GetMaxEntries(), CGetSetOptions::GetExpiredEntries()));
  654. try
  655. {
  656. CppSQLite3DB db;
  657. CString csDbPath = CGetSetOptions::GetDBPath();
  658. db.open(csDbPath);
  659. if(CGetSetOptions::GetCheckForMaxEntries())
  660. {
  661. long lMax = CGetSetOptions::GetMaxEntries();
  662. if(lMax >= 0)
  663. {
  664. CClipIDs IDs;
  665. int clipId;
  666. 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);
  667. while(q.eof() == false)
  668. {
  669. int shortcut = q.getIntField(_T("lShortCut"));
  670. int dontDelete = q.getIntField(_T("lDontAutoDelete"));
  671. int parentId = q.getIntField(_T("lParentID"));
  672. double stickyClipOrder = q.getFloatField(_T("stickyClipOrder"));
  673. double stickyClipGroupOrder = q.getFloatField(_T("stickyClipGroupOrder"));
  674. //Only delete entries that have no shortcut and don't have the flag set and aren't in groups and
  675. if(shortcut == 0 &&
  676. dontDelete == 0 &&
  677. parentId <= 0 &&
  678. stickyClipOrder == -(2147483647) &&
  679. stickyClipGroupOrder == -(2147483647))
  680. {
  681. clipId = q.getIntField(_T("lID"));
  682. IDs.Add(clipId);
  683. Log(StrF(_T("From MaxEntries - Deleting Id: %d"), clipId));
  684. }
  685. q.nextRow();
  686. }
  687. if(IDs.GetCount() > 0)
  688. {
  689. IDs.DeleteIDs(false, db);
  690. }
  691. }
  692. }
  693. if(CGetSetOptions::GetCheckForExpiredEntries())
  694. {
  695. long lExpire = CGetSetOptions::GetExpiredEntries();
  696. if(lExpire)
  697. {
  698. CTime now = CTime::GetCurrentTime();
  699. now -= CTimeSpan(lExpire, 0, 0, 0);
  700. CClipIDs IDs;
  701. CppSQLite3Query q = db.execQueryEx(_T("SELECT lID FROM Main ")
  702. _T("WHERE lastPasteDate < %d AND ")
  703. _T("bIsGroup = 0 AND lShortCut = 0 AND lParentID <= 0 AND lDontAutoDelete = 0 AND stickyClipOrder = -(2147483647) AND stickyClipGroupOrder = -(2147483647)"), (int)now.GetTime());
  704. while(q.eof() == false)
  705. {
  706. IDs.Add(q.getIntField(_T("lID")));
  707. Log(StrF(_T("From Clips Expire - Deleting Id: %d"), q.getIntField(_T("lID"))));
  708. q.nextRow();
  709. }
  710. if(IDs.GetCount() > 0)
  711. {
  712. IDs.DeleteIDs(false, db);
  713. }
  714. }
  715. }
  716. int toDeleteCount = db.execScalar(_T("SELECT COUNT(clipID) FROM MainDeletes"));
  717. Log(StrF(_T("Before Deleting emptied out data, count: %d, Idle Seconds: %f"), toDeleteCount, IdleSeconds()));
  718. //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
  719. //to lock up
  720. CppSQLite3Query q = db.execQueryEx(_T("SELECT * FROM MainDeletes LIMIT %d"), CGetSetOptions::GetMainDeletesDeleteCount());
  721. int deleteCount = 0;
  722. while(q.eof() == false)
  723. {
  724. double idleSeconds = IdleSeconds();
  725. if(checkIdleTime == false || idleSeconds > CGetSetOptions::GetIdleSecondsBeforeDelete())
  726. {
  727. //delete any data items sitting out there that the main table data was deleted
  728. //this was done to speed up deleted from the main table
  729. deleteCount = db.execDMLEx(_T("DELETE FROM MainDeletes WHERE clipID=%d"), q.getIntField(_T("clipID")));
  730. }
  731. else
  732. {
  733. Log(StrF(_T("Computer has not been idle long enough to delete clips, Min Idle: %d, current Idle: %d"),
  734. CGetSetOptions::GetIdleSecondsBeforeDelete(), idleSeconds));
  735. break;
  736. }
  737. q.nextRow();
  738. }
  739. toDeleteCount = db.execScalar(_T("SELECT COUNT(clipID) FROM MainDeletes"));
  740. Log(StrF(_T("After Deleting emptied out data rows, Count: %d, toDelete: %d"), deleteCount, toDeleteCount));
  741. }
  742. CATCH_SQLITE_EXCEPTION
  743. Log(_T("End of RemoveOldEntries"));
  744. return TRUE;
  745. }
  746. BOOL EnsureDirectory(CString csPath)
  747. {
  748. TCHAR drive[_MAX_DRIVE];
  749. TCHAR dir[_MAX_DIR];
  750. TCHAR fname[_MAX_FNAME];
  751. TCHAR ext[_MAX_EXT];
  752. SPLITPATH(csPath, drive, dir, fname, ext);
  753. CString csDir(drive);
  754. csDir += dir;
  755. if(FileExists(csDir) == FALSE)
  756. {
  757. if(CreateDirectory(csDir, NULL))
  758. return TRUE;
  759. }
  760. else
  761. return TRUE;
  762. return FALSE;
  763. }
  764. // BOOL RunZippApp(CString csCommandLine)
  765. // {
  766. // CString csLocalPath = GETENV(_T("U3_HOST_EXEC_PATH"));
  767. // FIX_CSTRING_PATH(csLocalPath);
  768. //
  769. // CString csZippApp = GETENV(_T("U3_DEVICE_EXEC_PATH"));
  770. // FIX_CSTRING_PATH(csZippApp);
  771. // csZippApp += "7za.exe";
  772. //
  773. // csZippApp += " ";
  774. // csZippApp += csCommandLine;
  775. //
  776. // Log(csZippApp);
  777. //
  778. // STARTUPINFO StartupInfo;
  779. // PROCESS_INFORMATION ProcessInformation;
  780. //
  781. // ZeroMemory(&StartupInfo, sizeof(StartupInfo));
  782. // StartupInfo.cb = sizeof(StartupInfo);
  783. // ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
  784. //
  785. // StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
  786. // StartupInfo.wShowWindow = SW_HIDE;
  787. //
  788. // BOOL bRet = CreateProcess(NULL, csZippApp.GetBuffer(csZippApp.GetLength()), NULL, NULL, FALSE,
  789. // CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS, NULL, csLocalPath,
  790. // &StartupInfo, &ProcessInformation);
  791. //
  792. // if(bRet)
  793. // {
  794. // WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
  795. //
  796. // DWORD dwExitCode;
  797. // GetExitCodeProcess(ProcessInformation.hProcess, &dwExitCode);
  798. //
  799. // CString cs;
  800. // cs.Format(_T("Exit code from unzip = %d"), dwExitCode);
  801. // Log(cs);
  802. //
  803. // if(dwExitCode != 0)
  804. // {
  805. // bRet = FALSE;
  806. // }
  807. // }
  808. // else
  809. // {
  810. // bRet = FALSE;
  811. // Log(_T("Create Process Failed"));
  812. // }
  813. //
  814. // csZippApp.ReleaseBuffer();
  815. //
  816. // return bRet;
  817. // }
  818. // BOOL CopyDownDatabase()
  819. // {
  820. // BOOL bRet = FALSE;
  821. //
  822. // CString csZippedPath = GETENV(_T("U3_APP_DATA_PATH"));
  823. // FIX_CSTRING_PATH(csZippedPath);
  824. //
  825. // CString csUnZippedPath = csZippedPath;
  826. // csUnZippedPath += "Ditto.db";
  827. //
  828. // csZippedPath += "Ditto.7z";
  829. //
  830. // CString csLocalPath = GETENV(_T("U3_HOST_EXEC_PATH"));
  831. // FIX_CSTRING_PATH(csLocalPath);
  832. //
  833. // if(FileExists(csZippedPath))
  834. // {
  835. // CString csCommandLine;
  836. //
  837. // //e = extract
  838. // //surround command line arguments with quotes
  839. // //-aoa = overight files with extracted files
  840. //
  841. // csCommandLine += "e ";
  842. // csCommandLine += "\"";
  843. // csCommandLine += csZippedPath;
  844. // csCommandLine += "\"";
  845. // csCommandLine += " -o";
  846. // csCommandLine += "\"";
  847. // csCommandLine += csLocalPath;
  848. // csCommandLine += "\"";
  849. // csCommandLine += " -aoa";
  850. //
  851. // bRet = RunZippApp(csCommandLine);
  852. //
  853. // csLocalPath += "Ditto.db";
  854. // }
  855. // else if(FileExists(csUnZippedPath))
  856. // {
  857. // csLocalPath += "Ditto.db";
  858. // bRet = CopyFile(csUnZippedPath, csLocalPath, FALSE);
  859. // }
  860. //
  861. // if(FileExists(csLocalPath) == FALSE)
  862. // {
  863. // Log(_T("Failed to copy files from device zip file"));
  864. // }
  865. //
  866. // g_Opt.nLastDbWriteTime = GetLastWriteTime(csLocalPath);
  867. //
  868. // return bRet;
  869. // }
  870. //BOOL CopyUpDatabase()
  871. //{
  872. // CStringA csZippedPath = "C:\\";//getenv("U3_APP_DATA_PATH");
  873. // FIX_CSTRING_PATH(csZippedPath);
  874. // csZippedPath += "Ditto.zip";
  875. // CStringA csLocalPath = GetDBName();//getenv("U3_HOST_EXEC_PATH");
  876. // //FIX_CSTRING_PATH(csLocalPath);
  877. // //csLocalPath += "Ditto.db";
  878. //
  879. // CZipper Zip;
  880. //
  881. // if(Zip.OpenZip(csZippedPath))
  882. // {
  883. // Zip.AddFileToZip(csLocalPath);
  884. // }
  885. //
  886. // return TRUE;
  887. //}