unit mEncryptUnit;

interface

uses
  Windows, SysUtils, Forms, Classes, ScUtils;

const
  // SafeNet(彩虹)SoftDog(软件狗)
  DT_SN_SD = 5;
  // 飞天R1
  DT_FT_R1 = 6;
  CS_Error = -5;
  CS_NeedAuthorize = -4;
  CS_NoDog = -3;
  CS_DogTypeError = -2;
  CS_VerifyError = -1;
  CS_Ignore = 0;
  CS_Success = 1;
  CS_WantAuthorize = 2;
  CS_NeedUpdate = -6;
  CS_EndDate = -7;
  CS_CloseToLimitDate = -8;

// 0：因某些原因（如加密够忙）而忽略检查，-1：加密检查错误，-2：狗类型不正确；1：清单狗，2：预算狗
function CheckDog: Integer;
function CheckDog1: Integer;

function SimpleCheck: Boolean;
function CheckTimes(AIsOnOpen: Boolean = False): Integer;
function CheckHours: Integer;
function CheckEdition: Integer;
function CheckLimitDate: Integer;
function CheckData: Integer;

function CheckDogAndHint: Boolean;

function DelayCheckDog: Boolean;

function CheckDogErrorCountAndHint: Boolean;

function GetReportOprList: TStrings;
function GetReportOprNum(AOpr: string): Integer;
procedure ClearReportOprList;

function GetReportFuncList: TStrings;
function GetReportFuncNum(AOpr: string): Integer;
procedure ClearReportFuncList;

procedure DogError;

function DogIsBusy: Boolean;

function OpenDog: Integer;

procedure SaveDog;

function UserAuthorize(AAuthorize: array of Byte): Boolean;

function UserUpdateLock(AUpdateData: array of Byte): Boolean;

function EncryptKey: string;

function AuthorizeKey: string;
// 是否受限版固化清单
function IsLimitedFixedBills: Boolean;
// 能否打开固化清单项目(学习版、网络版、标准版)
function CanOpenFixedBills: Boolean;

var
  strHASPID: string;
  bAuthorized: Boolean = True;
  Activations: Integer;
  StartTime, RunTime, LastTime: LongWord;
  EncryptLog: TStringList;
  // 保存当前打开的所有项目
  EvtSaveAll: TInternalEvent;
  // 加密锁版本号
  _DogEdition: Byte = 0;


procedure CreateEncryptLog;

procedure AddEncryptLog(ALog: string);

implementation

uses
  Math, CommonMessages, ScConfig, CryptUtils, mEncryptPwd,
  mSNSEncrypt, mR1Encrypt, mEncryptEditions;

const

  MutexAddressName = 'SmartCost_Mutex_Address_Name';

var
  DogErrorCount: Integer;
  MutexHandle: THandle;
  ReportOprList: TStringList = nil;
  ReportFuncList: TStringList = nil;

// 需要记录加密日志的话，改为TRUE；
// 目前仅为深思网络版写了记录日志的代码
function NeedLog: Boolean;
begin
  Result := ConfigInfo.IsWriteEncryptLog;
end;

procedure CreateEncryptLog;
begin
  EncryptLog := TStringList.Create;
end;

procedure AddEncryptLog(ALog: string);
begin
  if (EncryptLog <> nil) and NeedLog then
    EncryptLog.Add(ALog);
end;

procedure FreeAndSaveEncryptLog;
begin
  if EncryptLog <> nil then
  begin
    if NeedLog then
      EncryptLog.SaveToFile(ExtractFilePath(Application.ExeName) + 'EncryptLog.log');
    FreeAndNil(EncryptLog);
  end;
end;

function EncryptKey: string;
var
  arrResult: array of Byte;
  iOutputLength: Integer;
begin
  iOutputLength := GetOutputLength(ScEncryptKey);
  SetLength(arrResult, iOutputLength);
  Decrypt_Simple(ScEncryptKey, Length(ScEncryptKey), arrResult, iOutputLength);
  SetString(Result, PChar(@arrResult[0]), iOutputLength);
end;

function AuthorizeKey: string;
var
  arrResult: array of Byte;
  iOutputLength: Integer;
begin
  iOutputLength := GetOutputLength(ScAuthorizeKey);
  SetLength(arrResult, iOutputLength);
  Decrypt_Simple(ScAuthorizeKey, Length(ScAuthorizeKey), arrResult, iOutputLength);
  SetString(Result, PChar(@arrResult[0]), iOutputLength);
end;
  
function DogIsBusy: Boolean;
var
  WaitFlag: DWORD;
begin
  if MutexHandle = 0 then
  begin
    Result := False;
    Exit;
  end;
  WaitFlag := WaitForSingleObject(MutexHandle, 0);
  Result := (WaitFlag <> WAIT_OBJECT_0) and (WaitFlag <> WAIT_ABANDONED);
end;

function SimpleCheck: Boolean;
var
  iPort: Integer;
begin
  Result := False;
  if SimpleR1Check = DT_FT_R1 then
  begin
    DogType := DT_FT_R1;
    Result := True;
  end;
  if (not Result) and (SimpleSNSCheck = DT_SN_SD) then
  begin
    DogType := DT_SN_SD;
    Result := True;
  end;
  AddEncryptLog('[S] SimpleCheck: DogType = ' + IntToStr(DogType));
end;

function CheckTimes(AIsOnOpen: Boolean): Integer;
begin
  Result := CS_Error;
  case DogType of
    DT_FT_R1:
      Result := CheckR1Times(AIsOnOpen);
    DT_SN_SD:
      Result := CheckSNSTimes(AIsOnOpen);
  end;
end;

function CheckHours: Integer;
begin
  Result := CS_Error;
  case DogType of
    DT_FT_R1:
      Result := CheckR1Hours;
    DT_SN_SD:
      Result := CheckSNSHours;
  end;
end;

function CheckLimitDate: Integer;
begin
  Result := CS_Error;
  case DogType of
    DT_FT_R1:
      Result := CheckR1LimitDate;
    DT_SN_SD:
      Result := CheckSNSLimitDate
    else
      Result := CS_Ignore;
  end;
end;

function CheckEdition: Integer;
begin
  Result := CS_Error;
  SimpleCheck;
  case DogType of
    DT_FT_R1:
      Result := CheckR1Edition;
    DT_SN_SD:
      Result := CheckSNSEdition;
  end;
  {$IFDEF _ScYS2007}
  if Result >= 0 then
    case DogType of
      DT_FT_R1:
        Result := CheckR1AdditionEdition;
      DT_SN_SD:
        Result := CheckSNSAdditionEdition;
    end;
  {$ENDIF}
end;

function CheckData: Integer;
begin
  Result := CS_NoDog;
  SimpleCheck;
  case DogType of
    DT_FT_R1:
      Result := CheckR1Data;
    DT_SN_SD:
      Result := CheckSNSData;
  end;
end;

function CheckDog1: Integer;
begin
  Result := Checkdog;
end;

function CheckDog: Integer;
var
  iResult: Integer;
begin
  {$IFDEF _ScInternal}
  Result := CS_Ignore;
  {$ELSE}
  if DogIsBusy then
  begin
    Result := CS_Ignore;
    Exit;
  end;
  try
    Result := CheckData;
    if (Result = CS_Ignore) or (Result = CS_Success) then
    begin
      iResult := CheckLimitDate;
      if iResult = CS_EndDate then
        Result := iResult;
    end;
  finally
    ReleaseMutex(MutexHandle);
  end;
  {$ENDIF}
end;

function DelayCheckDog: Boolean;
begin
  Result := True;
  case CheckDog of
    CS_VerifyError, CS_DogTypeError, CS_Error, CS_NoDog, CS_NeedAuthorize:
    begin
      Inc(DogErrorCount);
      if DogErrorCount > 2 then
        Result := False;
    end;
  end;
end;

function GetReportOprList: TStrings;
begin
  if not Assigned(ReportOprList) then
    ReportOprList := TStringList.Create;
  Result := ReportOprList;
end;

procedure ClearReportOprList;
var
  iCheckData: Integer;
begin
  case DogType of
    DT_FT_R1, DT_SN_SD:
      GetReportOprList.Clear;
  end;
end;

function GetReportOprNum(AOpr: string): Integer;
begin
  Result := -1;
  case DogType of
    DT_FT_R1:
      Result := GetR1ReportOprNum(AOpr);
    DT_SN_SD:
      Result := GetSNSReportOprNum(AOpr);
  end;
end;

function GetReportFuncList: TStrings;
begin
  if not Assigned(ReportFuncList) then
    ReportFuncList := TStringList.Create;
  Result := ReportFuncList;
end;

procedure ClearReportFuncList;
var
  iCheckData: Integer;
begin
  case DogType of
    DT_FT_R1, DT_SN_SD:
      GetReportFuncList.Clear;
  end;
end;

function GetReportFuncNum(AOpr: string): Integer;
begin
  Result := -1;
  case DogType of
    DT_FT_R1:
      Result := GetR1ReportFuncNum(AOpr);
    DT_SN_SD:
      Result := GetSNSReportFuncNum(AOpr);
  end;
end;

procedure DogError;
begin
  CommonMessage(pmtError, [pmbOk], '没有检查到加密锁，或加密锁类型不正确。'#13#10'请购买正版SmartCost。'
      + #13#10'如果插上加密锁后仍有问题，请拨打产品注册电话：' + LoadAuthorizePhone + '。');

  if Assigned(EvtSaveAll) then
  begin
    EvtSaveAll;
    EvtSaveAll := nil;
  end;
  Application.Terminate;
end;

procedure DogVerifyError;
begin
  CommonMessage(pmtError, [pmbOk], '没有检查到加密锁。'#13#10'请购买正版SmartCost。'
      + #13#10'如果插上加密锁后仍有问题，请拨打产品注册电话：' + LoadAuthorizePhone + '。');
  Application.Terminate;
end;

procedure DogTypeError;
begin
  CommonMessage(pmtError, [pmbOk], '加密锁类型不正确，请检查。'
      + #13#10'如果插上正确的加密锁后仍有问题，请拨打产品注册电话：' + LoadAuthorizePhone + '。');
  Application.Terminate;
end;

procedure DogEndDate;
begin
  CommonMessage(pmtError, [pmbOk], '加密锁使用时间限制已到。'
      + #13#10'如果需延长使用时间，请与客服中心联系。');
  Application.Terminate;
end;

function CheckDogAndHint: Boolean;
begin
  Result := True;
  case CheckDog of
    CS_Error:
    begin
      DogError;
      Result := False;
    end;
    CS_NoDog:
    begin
      DogError;
      Result := False;
    end;
    CS_NeedAuthorize:
    begin
      DogVerifyError;
      Result := False;
    end;
    CS_VerifyError:
    begin
      DogVerifyError;
      Result := False;
    end;
    CS_DogTypeError:
    begin
      DogTypeError;
      Result := False;
    end;
    CS_EndDate:
    begin
      DogEndDate;
      Result := False;
    end;
  end;
end;

function CheckDogErrorCountAndHint: Boolean;
begin
  Result := True;
  if DogErrorCount > 2 then
  begin
    DogError;
    Result := False;
  end;
end;

procedure CreateDogMutex;
var
  SA: TSecurityAttributes;
begin
  SA.nLength := SizeOf(SA);
  SA.lpSecurityDescriptor := nil;
  SA.bInheritHandle := False;
  MutexHandle := CreateMutex(@SA, False, PChar(MutexAddressName));
end;

function OpenDog: Integer;
var
  iCheckResult1, iCheckresult2, iEditionResult, iLimitDate: Integer;
begin
  Result := CS_NoDog;
  if DogIsBusy then
  begin
    Result := CS_Ignore;
    Exit;
  end;
  try
    SimpleCheck;
    StartTime := GetTickCount;
    if DogType in [DT_FT_R1, DT_SN_SD] then
      StartTime := StartTime div 1000;
    if (CheckData in [CS_Success, CS_Ignore])  then
    begin
      iEditionResult := CheckEdition;
      if iEditionResult < 0 then
      begin
        if iEditionResult = CS_NeedUpdate then
          Result := CS_NeedUpdate
        else
          Result := CS_DogTypeError;
      end
      else
      begin
        iCheckResult1 := CheckHours;
        iCheckResult2 := CheckTimes(True);
        iLimitDate := CheckLimitDate;
        if iLimitDate = CS_Success then
          Result := iLimitDate
        else if iLimitDate = CS_CloseToLimitDate then
          Result := iLimitDate
        else if iLimitDate = CS_EndDate then
        begin
          Result := iLimitDate;
          Exit;
        end
        else if iLimitDate = CS_Ignore then
        begin
          if (iCheckResult1 = CS_NeedAuthorize) or (iCheckResult2 = CS_NeedAuthorize) then
            Result := CS_NeedAuthorize
          else if (iCheckResult1 = CS_WantAuthorize) or (iCheckResult1 = CS_WantAuthorize) then
            Result := CS_WantAuthorize
          else
            Result := CS_Success;
        end;
      end;
    end;

    if not ((Result = CS_Success) or (Result = CS_WantAuthorize)
        or (Result = CS_NeedAuthorize) or (Result = CS_Ignore)
        or (Result = CS_NeedUpdate) or (Result = CS_CloseToLimitDate)) then
      Exit;

    case DogType of
      DT_FT_R1:
        if OpenR1Dog <> CS_Success then
          Result := CS_Error;
      DT_SN_SD:
        if OpenSNSDog <> CS_Success then
          Result := CS_Error;
    end;

  finally
    ReleaseMutex(MutexHandle);
  end;
end;

procedure SaveDog;
begin
  case DogType of
    DT_FT_R1:
      SaveR1Dog;
    DT_SN_SD:
      SaveSNSDog;
  end;
  FreeAndSaveEncryptLog;
end;

function UserAuthorize(AAuthorize: array of Byte): Boolean;
begin
  Result := False;
  case DogType of
    DT_FT_R1:
      Result := R1UserAuthorize(AAuthorize);
    DT_SN_SD:
      Result := SNSUserAuthorize(AAuthorize);
  end;

end;

function UserUpdateLock(AUpdateData: array of Byte): Boolean;
begin
  Result := False;
  case DogType of
    DT_FT_R1:
      Result := R1UserUpdateLock(AUpdateData);
    DT_SN_SD:
      Result := SNSUserUpdateLock(AUpdateData);
  end;
end;

// 是否受限版固化清单
function IsLimitedFixedBills: Boolean;
begin
  Result := _DogEdition in [eidLimitedFixBills];
end;

// 能否打开固化清单项目
function CanOpenFixedBills: Boolean;
begin
  Result := not (_DogEdition in [0, eidStandard]);
end;

initialization
  DogErrorCount := 0;
  CreateDogMutex;

finalization
  CloseHandle(MutexHandle);
  MutexHandle := 0;

end.







