DatabaseUtilities.cpp 30 KB

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