unit Connections;

interface

uses
  ADODB, SysUtils, Classes, ZhAPI, Windows;

const
  ProductName = 'Measure';
  EmptyFileVersion = '1.0.0.0';
  FileVersion = '1.0.1.17';
  EncryptVersion = 'Auto1.0';
  SAdoConnectStr = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s;' +
                   'User ID=Admin;Password='''';Persist Security Info=True';

type
  TConnection = class
  private
    FFileName: string;
    FOrgFile: string;
    function GetConnection: TADOConnection; virtual; abstract;
  protected
    procedure AppendLog(const ALog: string);
  public
    procedure Open(const AFileName: string); virtual; abstract;
    procedure Save; virtual; abstract;
    procedure Close;
    function HasOpened: Boolean; virtual; abstract;

    property FileName: string read FFileName;
    property Connection: TADOConnection read GetConnection;
    property OrgFile: string read FOrgFile;
  end;

  TCommonConnection = class(TConnection)
  private
    FConnection: TADOConnection;

    procedure InnerOpen(const AFileName: string);
    function GetConnection: TADOConnection; override;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Open(const AFileName: string); override;
    procedure Save; override;
    function HasOpened: Boolean; override;
  end;

  TDrawingFileHead = packed record
    ProductName: string[20];
    FileType: Integer;
    EncryptVersion: string[20];
    FileVersion: string[20];
    // 创建项目 -- Exe版本号
    CreateExeVersion: string[20];
    // 最后一次打开 -- Exe版本号
    LastestExeVersion: string[20];
    // 历史最大 -- exe版本号
    MaxExeVersion: string[20];
    Reserve: array [0..1024 -21 -21 -21] of Char;
  end;

  TEncryptConnection = class(TCommonConnection)
  private
    FFileHead: TDrawingFileHead;
    function CheckFileValid(const AFileName: string): Boolean;

    procedure Encrypt;
    function Decrypt(const AFileName: string): string;

    procedure RefreshFileHead;
    procedure DamageFileHead(AFileName: string);
    procedure RecoverFileHead(AFileName: string);
    procedure WriteAndAddHead(AFileName: string);
    procedure RemoveHead(const AFileName: string);
    procedure ReadAndRemoveHead(AFileName: string);

    procedure ResetFileHead;
    function CheckFileEncrypt: Boolean; overload;
    function CheckFileEncrypt(AFileName: string): Boolean; overload;
    function GetNeedUpdate: Boolean;
    function GetOverExe: Boolean;
    function GetFileVersion: string;
  public
    destructor Destroy; override;
    procedure Open(const AFileName: string); override;
    procedure Save; override;
    procedure UnEncryptSave;

    procedure UpdateFileVersion;
    procedure SaveDebugFile(const AFileName: string);

    property NeedUpdate: Boolean read GetNeedUpdate;
    property OverExe: Boolean read GetOverExe;
    property CurFileVersion: string read GetFileVersion;
  end;

  procedure SimpleDecrypt(const ASourceFile, AResultFile: string);
  function FileEncrypted(const AFileName: string): Boolean;

implementation

uses
  CompactDB, TransFile, UtilMethods, Globals;

const
  MDBOrgHead: array [0..15] of Byte =
    ($00, $01, $00, $00, $53, $74, $61, $6E, $64, $61, $72, $64, $20, $4A, $65, $74);

  WrongHead: array [0..15] of Char =
    ('z', 'o', 'n', 'g', 'h', 'e', 'n', 'g', 's', 'o', 'f', 't', 'w', 'a', 'r', 'e');

procedure SimpleDecrypt(const ASourceFile, AResultFile: string);

  procedure RemoveHead(const AFileName: string);
  var
    sTempFile: string;
    FileStream, FileStreamTemp: TFileStream;
  begin
    if not FileExists(AFileName) then Exit;
    sTempFile := GetTempFileName;
    try
      FileStream := TFileStream.Create(AFileName, fmOpenRead);
      FileStreamTemp := TFileStream.Create(sTempFile, fmCreate);
      try
        FileStream.Position := SizeOf(TDrawingFileHead);
        FileStreamTemp.CopyFrom(FileStream, FileStream.Size - SizeOf(TDrawingFileHead));
      finally
        FileStream.Free;
        FileStreamTemp.Free;
      end;
      CopyFileOrFolder(sTempFile, AFileName);
    finally
      DeleteFile(PChar(sTempFile));
    end;
  end;

  procedure RecoverFileHead(const AFileName: string);
  var
    sTempFile: string;
    FileStream: TFileStream;
  begin
    sTempFile := ExtractFilePath(AFileName) + ExtractSimpleFileName(AFileName) + '.tmp';
    CopyFileOrFolder(AFileName, sTempFile);
    try
      if FileExists(sTempFile) then
      begin
        FileStream := TFileStream.Create(sTempFile, fmOpenWrite);
        try
          FileStream.Seek($00, soFromBeginning);
          FileStream.Write(MDBOrgHead, SizeOf(MDBOrgHead));
        finally
          FileStream.Free;
        end;
        CopyFileOrFolder(sTempFile, AFileName);
        DeleteFile(PChar(sTempFile));
      end;
    except
      DeleteFile(PChar(sTempFile));
    end;
  end;

var
  sTempFile: string;
begin
  if not FileExists(ASourceFile) then Exit;
  try
    sTempFile := GetTempFileName;
    CopyFileOrFolder(ASourceFile, sTempFile);
    RemoveHead(sTempFile);
    AutoDecryptFile(sTempFile, AResultFile);
    RecoverFileHead(AResultFile);
  finally
    DeleteFile(PChar(sTempFile));
  end;
end;

function FileEncrypted(const AFileName: string): Boolean;
var
  FileHead: TDrawingFileHead;
  FileStream: TFileStream;
begin
  FileStream := TFileStream.Create(AFileName, fmOpenRead);
  try
    FileStream.ReadBuffer(FileHead, SizeOf(TDrawingFileHead));
    Result := FileHead.ProductName = ProductName;
  finally
    FileStream.Free;
  end;
end;

procedure EncryptFile(const AFileName: string);
var
  fileStream: TFileStream;
begin
  fileStream := TFileStream.Create(AFileName, fmOpenReadWrite);
  try
    fileStream.Position := 0;
    fileStream.WriteBuffer(WrongHead, SizeOf(WrongHead));
  finally
    fileStream.Free;
  end;
end;

procedure DecryptFile(const AFileName: string);
var
  fileStream: TFileStream;
begin
  fileStream := TFileStream.Create(AFileName, fmOpenReadWrite);
  try
    fileStream.Position := 0;
    fileStream.WriteBuffer(MDBOrgHead, SizeOf(MDBOrgHead));
  finally
    fileStream.Free;
  end;
end;

{ TEncryptConnection }

procedure TEncryptConnection.Encrypt;
var
  sTempFile, sEncryptFile: string;
begin
  AppendLog('Save File-E');
  if (FFileName <> '') and FileExists(FFileName) then
  begin
    sTempFile := GetTempFileName;
    sEncryptFile := GetTempFileName;
    CopyFileOrFolder(FFileName, sTempFile);
    try
      AutoEncryptFile(sTempFile, sEncryptFile);
      RefreshFileHead;
      WriteAndAddHead(sEncryptFile);
      if CheckFileValid(sEncryptFile) then
      begin
        AppendLog('Check File-E --> Pass');
        CopyFileOrFolder(sEncryptFile, FOrgFile);
      end
      else
      begin
        AppendLog('Check File-E --> Error');
        Abort;
      end;
    finally
      DeleteFile(PChar(sTempFile));
      DeleteFile(PChar(sEncryptFile));
    end;
  end;
  AppendLog('Save File-E --> End');
end;

destructor TEncryptConnection.Destroy;
begin
  Close;
  if (FFileName <> '') and FileExists(FFileName) then
    DeleteFile(PChar(FFileName));
  inherited;
end;

procedure TEncryptConnection.Open(const AFileName: string);
begin
  Close;
  if (FFileName <> '') and FileExists(FFileName) then
    DeleteFile(PChar(FFileName));

  FFileName := Decrypt(AFileName);
  if FileExists(FFileName) then
  begin
    RecoverFileHead(FFileName);
    InnerOpen(FFileName);
    DamageFileHead(FFileName);
  end
  else
    raise Exception.Create(Format('文件[%s]不存在!', [AFileName]));
end;

procedure TEncryptConnection.DamageFileHead(AFileName: string);
var
  sTempFile: string;
  FileStream: TFileStream;
begin
  sTempFile := ExtractFilePath(AFileName) + ExtractSimpleFileName(AFileName) + '.tmp';
  CopyFileOrFolder(AFileName, sTempFile);
  try
    if FileExists(sTempFile) then
    begin
      FileStream := TFileStream.Create(sTempFile, fmOpenWrite);
      try
        FileStream.Seek($00, soFromBeginning);
        FileStream.Write(WrongHead, SizeOf(WrongHead));
      finally
        FileStream.Free;
      end;
      CopyFileOrFolder(sTempFile, AFileName);
      DeleteFile(PChar(sTempFile));
    end;
  except
    DeleteFile(PChar(sTempFile));
  end;
end;

procedure TEncryptConnection.WriteAndAddHead(AFileName: string);
var
  sTempFile: string;
  FileStream, FileStreamTemp: TFileStream;
begin
  if FileExists(AFileName) then
  begin
    sTempFile := ExtractFilePath(FFileName) + ExtractSimpleFileName(FFileName) + '.tmp';
    FileStream := TFileStream.Create(AFileName, fmOpenRead);
    FileStreamTemp := TFileStream.Create(sTempFile, fmCreate);
    try
      try
        FileStream.Position := 0;
        FileStreamTemp.WriteBuffer(FFileHead, SizeOf(TDrawingFileHead));
        FileStreamTemp.CopyFrom(FileStream, FileStream.Size);
      finally
        FileStream.Free;
        FileStreamTemp.Free;
      end;
      CopyFileOrFolder(sTempFile, AFileName);
      DeleteFile(PChar(sTempFile));
    except
      DeleteFile(PChar(sTempFile))
    end;
  end;
end;

procedure TEncryptConnection.ReadAndRemoveHead(AFileName: string);
var
  sTempFile: string;
  FileStream, FileStreamTemp: TFileStream;
begin
  if FileExists(AFileName) then
  begin
    sTempFile := ExtractFilePath(FFileName) + ExtractSimpleFileName(FFileName) + '.tmp';
    FileStream := TFileStream.Create(AFileName, fmOpenRead);
    FileStreamTemp := TFileStream.Create(sTempFile, fmCreate);
    try
      try
        FileStream.Position := 0;
        FileStream.ReadBuffer(FFileHead, SizeOf(TDrawingFileHead));
        FileStreamTemp.CopyFrom(FileStream, FileStream.Size - SizeOf(TDrawingFileHead));
      finally
        FileStream.Free;
        FileStreamTemp.Free;
      end;
      CopyFileOrFolder(sTempFile, AFileName);
      DeleteFile(PChar(sTempFile));
    except
      DeleteFile(PChar(sTempFile))
    end;
  end;
end;

function TEncryptConnection.Decrypt(const AFileName: string): string;
var
  sConnectFile, sTempFile: string;
begin
  FOrgFile := AFileName;
  sConnectFile := GetTempFileName;
  sTempFile := GetTempFileName;
  if FileExists(AFileName) then
  begin
    CopyFileOrFolder(AFileName, sTempFile);
    if CheckFileEncrypt(sTempFile) then
    begin
      ReadAndRemoveHead(sTempFile);
      AutoDecryptFile(sTempFile, sConnectFile);
    end
    else
    begin
      ResetFileHead;
      CopyFileOrFolder(sTempFile, sConnectFile);
    end;
    DecryptFile(sConnectFile);
    DeleteFile(PChar(sTempFile));
  end;
  Result := sConnectFile;
end;

function TEncryptConnection.CheckFileEncrypt(AFileName: string): Boolean;
var
  FileHead: TDrawingFileHead;
  FileStream: TFileStream;
begin
  FileStream := TFileStream.Create(AFileName, fmOpenRead);
  try
    FileStream.ReadBuffer(FileHead, SizeOf(TDrawingFileHead));
    Result := FileHead.ProductName = ProductName;
  finally
    FileStream.Free;
  end;
end;

function TEncryptConnection.CheckFileEncrypt: Boolean;
begin
  Result := FFileHead.ProductName = ProductName;
end;

procedure TEncryptConnection.ResetFileHead;
begin
  if (FFileHead.ProductName <> ProductName) then
  begin
    FFileHead.ProductName := ProductName;
    FFileHead.FileType := 1;
    FFileHead.EncryptVersion := EncryptVersion;
    FFileHead.FileVersion := EmptyFileVersion;
    FFileHead.CreateExeVersion := GetVersion;
    FFileHead.LastestExeVersion := GetVersion;
    FFileHead.MaxExeVersion := GetVersion;
  end;
end;

procedure TEncryptConnection.Save;
begin
  AppendLog('Save File');
  RecoverFileHead(FFileName);
  Encrypt;
  AppendLog('Save File --> End');
end;

procedure TEncryptConnection.RefreshFileHead;
begin
  FFileHead.EncryptVersion := EncryptVersion;
  FFileHead.FileVersion := FileVersion;
  FFileHead.LastestExeVersion := GetVersion;
end;

procedure TEncryptConnection.UpdateFileVersion;
begin
  FFileHead.FileVersion := FileVersion;
end;

procedure TEncryptConnection.RecoverFileHead(AFileName: string);
var
  sTempFile: string;
  FileStream: TFileStream;
begin
  sTempFile := ExtractFilePath(AFileName) + ExtractSimpleFileName(AFileName) + '.tmp';
  CopyFileOrFolder(AFileName, sTempFile);
  try
    if FileExists(sTempFile) then
    begin
      FileStream := TFileStream.Create(sTempFile, fmOpenWrite);
      try
        FileStream.Seek($00, soFromBeginning);
        FileStream.Write(MDBOrgHead, SizeOf(MDBOrgHead));
      finally
        FileStream.Free;
      end;
      CopyFileOrFolder(sTempFile, AFileName);
      DeleteFile(PChar(sTempFile));
    end;
  except
    DeleteFile(PChar(sTempFile));
  end;
end;

function TEncryptConnection.GetNeedUpdate: Boolean;
begin
  Result := CompareCode(FileVersion, FFileHead.FileVersion, '.') > 0;
end;

function TEncryptConnection.GetOverExe: Boolean;
begin
  Result := CompareCode(FileVersion, FFileHead.FileVersion, '.') < 0;
end;

procedure TEncryptConnection.SaveDebugFile(const AFileName: string);
begin
  CopyFileOrFolder(FFileName, AFileName);
  RecoverFileHead(AFileName);
end;

function TEncryptConnection.CheckFileValid(
  const AFileName: string): Boolean;
var
  sConnectFile, sTempFile: string;
  vCon: TADOConnection;
begin
  AppendLog('Check File-E');
  Result := False;
  try
    sConnectFile := GetTempFileName;
    sTempFile := GetTempFileName;
    vCon := TADOConnection.Create(nil);
    vCon.LoginPrompt := False;

    if FileExists(AFileName) then
    begin
      AppendLog('Check File Exist');
      if CopyFileOrFolder(AFileName, sTempFile) then
      begin
        AppendLog('Copy Check File -- Pass');
        if CheckFileEncrypt(sTempFile) then
        begin
          RemoveHead(sTempFile);
          AutoDecryptFile(sTempFile, sConnectFile);
        end
        else
          sConnectFile := sTempFile;
        RecoverFileHead(sConnectFile);
        AppendLog('prepare to Connect Check File');
        vCon.ConnectionString := Format(SAdoConnectStr, [sConnectFile]);
        AppendLog('Checking File');
        vCon.Open;
        Result := True;
        AppendLog('File Valid');
      end;
    end;
  finally
    if Assigned(vCon) then
      vCon.Free;
    if FileExists(sConnectFile) then
      DeleteFile(PChar(sConnectFile));
    if FileExists(sConnectFile) then
      DeleteFile(PChar(sTempFile));
  end;
end;

procedure TEncryptConnection.UnEncryptSave;
begin
  RecoverFileHead(FFileName);
  
end;

function TEncryptConnection.GetFileVersion: string;
begin
  Result := Trim(FFileHead.FileVersion);
end;

procedure TEncryptConnection.RemoveHead(const AFileName: string);
var
  sTempFile: string;
  FileStream, FileStreamTemp: TFileStream;
begin
  if FileExists(AFileName) then
  begin
    sTempFile := GetTempFileName;
    FileStream := TFileStream.Create(AFileName, fmOpenRead);
    FileStreamTemp := TFileStream.Create(sTempFile, fmCreate);
    try
      try
        FileStream.Position := SizeOf(TDrawingFileHead);
        FileStreamTemp.CopyFrom(FileStream, FileStream.Size - SizeOf(TDrawingFileHead));
      finally
        FileStream.Free;
        FileStreamTemp.Free;
      end;
      CopyFileOrFolder(sTempFile, AFileName);
    finally
      if FileExists(sTempFile) then
        DeleteFile(PChar(sTempFile));
    end;
  end;
end;

{ TConnection }

procedure TConnection.AppendLog(const ALog: string);
begin
  if SupportManager.ConfigInfo.IsLog then
    MeasureLog.AppendLogTo(ALog);
end;

procedure TConnection.Close;
begin
  Connection.Close;
end;

{ TCommonConnection }

constructor TCommonConnection.Create;
begin
  FConnection := TADOConnection.Create(nil);
  FConnection.LoginPrompt := False;
end;

destructor TCommonConnection.Destroy;
begin
  Close;
  FConnection.Free;
  inherited;
end;

function TCommonConnection.GetConnection: TADOConnection;
begin
  Result := FConnection;
end;

function TCommonConnection.HasOpened: Boolean;
begin
  Result := FConnection.Connected;
end;

procedure TCommonConnection.Open(const AFileName: string);
begin
  Close;
  if FileExists(AFileName) then
  begin
    //DecryptFile(AFileName);
    InnerOpen(AFileName);
  end
  else
    raise Exception.Create(Format('文件[%s]不存在!', [AFileName]));
end;

procedure TCommonConnection.InnerOpen(const AFileName: string);
begin
  FFileName := AFileName;
  CompactDatabase(FFileName);
  FConnection.ConnectionString := Format(SAdoConnectStr, [FFileName]);
  FConnection.Open;
end;

procedure TCommonConnection.Save;
begin
  inherited;
end;

end.
