backuprestore.pas 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. unit BackupRestore;
  2. {$mode objfpc}
  3. interface
  4. uses
  5. Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  6. StdCtrls, Buttons, ExtCtrls, Zipper, dbugintf
  7. {$IFDEF MSWINDOWS}
  8. , shlobj {for special folders}
  9. {$ENDIF};
  10. type
  11. { TfmBackupRestore }
  12. TfmBackupRestore = class(TForm)
  13. bbStart: TBitBtn;
  14. cbOperation: TComboBox;
  15. edBackup: TEdit;
  16. edPassword: TEdit;
  17. edTargetDatabase: TEdit;
  18. edHost: TEdit;
  19. edUserName: TEdit;
  20. GroupBox1: TGroupBox;
  21. Image1: TImage;
  22. Label1: TLabel;
  23. Label2: TLabel;
  24. Label3: TLabel;
  25. Label4: TLabel;
  26. Label5: TLabel;
  27. Label6: TLabel;
  28. meLog: TMemo;
  29. OpenDialog1: TOpenDialog;
  30. SaveDialog1: TSaveDialog;
  31. sbBroseBackupFile: TSpeedButton;
  32. sbBrowseTargetdb: TSpeedButton;
  33. procedure bbStartClick(Sender: TObject);
  34. procedure edBackupEditingDone(Sender: TObject);
  35. procedure edTargetDatabaseEditingDone(Sender: TObject);
  36. procedure FormCreate(Sender: TObject);
  37. procedure sbBrowseTargetdbClick(Sender: TObject);
  38. procedure sbBroseBackupFileClick(Sender: TObject);
  39. private
  40. { private declarations }
  41. FDatabase: string; //doesn't really seem to be used anywhere
  42. { if true, user wrote/selected the backup filename himself.
  43. if false, system has generated/can generate a backup filename based on db}
  44. FUserSpecifiedBackupFile: boolean;
  45. // If backup combobox selected and user has not edited backup filename,
  46. // write a system-generated backup filename
  47. procedure SetBackupFileName;
  48. public
  49. procedure Init(Title, Database, User, Password: string);
  50. { public declarations }
  51. end;
  52. var
  53. fmBackupRestore: TfmBackupRestore;
  54. implementation
  55. { TfmBackupRestore }
  56. uses UnitFirebirdServices;
  57. {$IFDEF MSWINDOWS}
  58. function GetDesktopPath: string;
  59. var
  60. DesktopPath: array[0..MaxPathLen] of char; //Allocate memory
  61. begin
  62. DesktopPath := '';
  63. SHGetSpecialFolderPath(0, DesktopPath, CSIDL_DESKTOPDIRECTORY, False);
  64. result:=IncludeTrailingPathDelimiter(DesktopPath);
  65. end;
  66. {$ENDIF}
  67. procedure TfmBackupRestore.sbBroseBackupFileClick(Sender: TObject);
  68. begin
  69. SaveDialog1.DefaultExt:= '.fbk';
  70. if ((cbOperation.ItemIndex = 0) and (SaveDialog1.Execute)) or
  71. ((cbOperation.ItemIndex = 1) and (OpenDialog1.Execute)) then
  72. begin
  73. if cbOperation.ItemIndex = 0 then //backup
  74. begin
  75. edBackup.Text:= SaveDialog1.FileName;
  76. FUserSpecifiedBackupFile:= false; //indicate user explicitly set filename
  77. end
  78. else //restore
  79. edBackup.Text:= OpenDialog1.FileName;
  80. end;
  81. end;
  82. procedure TfmBackupRestore.SetBackupFileName;
  83. var
  84. TargetDir: string;
  85. TargetFile: string;
  86. begin
  87. // Let system generate a sensible backup name based on database
  88. if (cbOperation.ItemIndex = 0 {backup}) and
  89. (not(FUserSpecifiedBackupFile)) then
  90. begin
  91. // Use home directory on *nix, desktop on windows; fallback to
  92. // current dir for any others
  93. TargetDir:= ExtractFilePath(ParamStr(0));
  94. {$IFDEF MSWINDOWS}
  95. TargetDir:= GetDesktopPath;
  96. {$ENDIF}
  97. {$IFDEF UNIX}
  98. TargetDir:= ExpandFileName('~'); //user's home directory
  99. {$ENDIF}
  100. TargetFile:= trim(Sysutils.ExtractFileName(edTargetDatabase.Text));
  101. if LowerCase(ExtractFileExt(TargetFile))='.fdb' then
  102. TargetFile:= ChangeFileExt(TargetFile, '.fbk.zip')
  103. else
  104. TargetFile:= TargetFile + '.fbk.zip';
  105. TargetFile:= formatdatetime('yyyymmdd', Now) + TargetFile;
  106. // edBackup.Text:= TargetDir + TargetFile; // result is ===> /home/username20150125dbname.fbk.zip (in linux this need root permission)
  107. edBackup.Text:=ConcatPaths([TargetDir, TargetFile]); // result is ===> /home/username/20150125dbname.fbk.zip (but this is valid with normal user permission)
  108. end;
  109. end;
  110. procedure TfmBackupRestore.Init(Title, Database, User, Password: string);
  111. begin
  112. FDatabase:= Database;
  113. edUserName.Text:= User;
  114. edPassword.Text:= Password;
  115. // Linux: servername:/path/test.fdb or /path/test.fdb
  116. // Windows: servername:c:\path\test.fdb or c:\path\test.fdb
  117. if Pos(':', Trim(FDatabase)) > 2 then
  118. begin
  119. edHost.Text:= Trim(Copy(FDatabase, 1, Pos(':', FDatabase) - 1));
  120. edTargetDatabase.Text:= Trim(Copy(FDatabase, Pos(':', FDatabase) + 1, Length(FDatabase)));
  121. end
  122. else
  123. begin
  124. // Assume local host for *nix, embedded for Windows
  125. {$IFDEF MSWINDOWS}
  126. edHost.Text := '';
  127. {$ELSE}
  128. edHost.Text := 'localhost';
  129. {$ENDIF}
  130. edTargetDatabase.Text := FDatabase;
  131. end;
  132. SetBackupFileName;
  133. end;
  134. procedure TfmBackupRestore.bbStartClick(Sender: TObject);
  135. var
  136. FireBirdServices: TFirebirdServices;
  137. Res: Ansistring;
  138. TempDir: string; //directory for temp files (including path delimiter)
  139. TempFile: string; //if not empty: used for intermediate file when zipping/unzipping
  140. Unzipper: TUnzipper;
  141. UserFile: string; //file the user chose: either backup destination or restore source
  142. FBKZippedFile: string; //name of fbk file when zip compressing
  143. Zipper: TZipper;
  144. begin
  145. TempDir:= GetTempDir(false);
  146. FireBirdServices:= TFirebirdServices.Create;
  147. try
  148. Screen.Cursor := crHourglass; // inform user of long-running operation
  149. FireBirdServices.VerboseOutput:= True;
  150. meLog.Clear;
  151. with FireBirdServices do
  152. begin
  153. HostName:= edHost.Text;
  154. DBName:= edTargetDatabase.Text;
  155. UserName:= edUserName.Text;
  156. Password:= edPassword.Text;
  157. UserFile:= trim(edBackup.Text);
  158. if LowerCase(ExtractFileExt(UserFile))='.zip' then
  159. begin
  160. if cbOperation.ItemIndex = 0 then
  161. begin
  162. // Backup: set up destination for backup process
  163. TempFile:= GetTempFilename(TempDir,'B');
  164. end
  165. else
  166. begin
  167. // Restore: unzip .fbk into temporary file
  168. TempFile:= sysutils.GetTempFilename;
  169. Unzipper:= TUnzipper.Create;
  170. try
  171. Unzipper.FileName:= UserFile;
  172. Unzipper.OutputPath:= TempDir;
  173. Unzipper.Examine;
  174. if Unzipper.Entries.Count=0 then
  175. begin
  176. ShowMessage(Format('%s contains no files. Aborting.',[UserFile]));
  177. exit;
  178. end;
  179. if Unzipper.Entries.Count<>1 then
  180. begin
  181. ShowMessage(Format('%s has more than 1 files. Only zip files with one .fbk file are supported. Aborting.',[UserFile]));
  182. exit;
  183. end;
  184. meLog.Lines.Add('Going to unzip file ' + UserFile + ':' + Unzipper.Entries[0].DiskFileName + ' into directory ' + TempDir);
  185. Unzipper.UnZipAllFiles; //we know we're unzipping just 1 file
  186. TempFile:= TempDir +
  187. ExtractFileName(Unzipper.Entries[0].DiskFileName);
  188. finally
  189. Unzipper.Free;
  190. end;
  191. end;
  192. end;
  193. if TempFile='' then
  194. BackupFile:= UserFile // no zip files involved
  195. else
  196. {backup to temp, then zip later or
  197. restore from temp file}
  198. BackupFile:= TempFile;
  199. AttachService;
  200. try
  201. if cbOperation.ItemIndex = 0 then
  202. StartBackup
  203. else
  204. StartRestore;
  205. while ServiceQuery(Res) do
  206. meLog.Lines.Add(Res);
  207. if (TempFile<>'' {using zip file}) and
  208. (cbOperation.ItemIndex <> 0 {restore}) then
  209. // Delete temp file when restore from zip is done
  210. begin
  211. Sleep(40); //give file system chance to update locks etc
  212. DeleteFile(TempFile);
  213. end;
  214. finally
  215. DetachService;
  216. end;
  217. meLog.Lines.Add('');
  218. end;
  219. if (tempfile<>'' {user wants zip file}) and
  220. (cbOperation.ItemIndex = 0 {backup}) then
  221. begin
  222. // Zip up the resulting backup
  223. Zipper:= TZipper.Create;
  224. try
  225. Zipper.FileName:= UserFile; //target is the user-selected backup file
  226. // Figure out the name of the .fbk file to be put in the zip file
  227. FBKZippedFile:= ExtractFileName(UserFile);
  228. if LowerCase(ExtractFileExt(FBKZippedFile))='.zip' then
  229. FBKZippedFile:= ChangeFileExt(FBKZippedFile,''); //get rid of ending .zip
  230. if LowerCase(ExtractFileExt(FBKZippedFile))<>'.fbk' then
  231. FBKZippedFile:= FBKZippedFile+'.fbk'; //add extension if not specified
  232. Zipper.Entries.AddFileEntry(TempFile, FBKZippedFile);
  233. meLog.Lines.Add('Going to compress file ' + TempFile +
  234. ' as filename ' + FBKZippedFile +
  235. ' in zip file ' + UserFile);
  236. try
  237. Zipper.ZipAllFiles; //zip up all entries (just 1 in our case)
  238. // Delete temp file containing fbk
  239. Sleep(40); //give filesystem chance to update locks etc
  240. Sysutils.DeleteFile(TempFile);
  241. except
  242. on E: Exception do
  243. begin
  244. meLog.Lines.Add('Error compressing file. Technical details: '+E.Message);
  245. end;
  246. end;
  247. finally
  248. Zipper.Free;
  249. end;
  250. end;
  251. finally
  252. Screen.Cursor := crDefault;
  253. FireBirdServices.Free;
  254. end;
  255. end;
  256. procedure TfmBackupRestore.edBackupEditingDone(Sender: TObject);
  257. begin
  258. if trim(edBackup.Text)='' then
  259. FUserSpecifiedBackupFile:=false
  260. else
  261. FUserSpecifiedBackupFile:=true;
  262. end;
  263. procedure TfmBackupRestore.edTargetDatabaseEditingDone(Sender: TObject);
  264. begin
  265. SetBackupFileName;
  266. end;
  267. procedure TfmBackupRestore.FormCreate(Sender: TObject);
  268. begin
  269. FUserSpecifiedBackupFile:= false; //System can suggest backup filenames
  270. end;
  271. procedure TfmBackupRestore.sbBrowseTargetdbClick(Sender: TObject);
  272. begin
  273. SaveDialog1.DefaultExt:= '.fdb';
  274. if SaveDialog1.Execute then
  275. edTargetDatabase.Text:= SaveDialog1.FileName;
  276. end;
  277. initialization
  278. {$I backuprestore.lrs}
  279. end.