1073 lines
30 KiB
Plaintext
1073 lines
30 KiB
Plaintext
unit Bs1PolicyUnit;
|
|
|
|
interface
|
|
uses
|
|
System.SysUtils, System.Classes, System.Generics.Collections, System.SyncObjs,
|
|
System.JSON, System.IOUtils, Winapi.Windows, Winapi.Messages, Winapi.ActiveX,
|
|
Bs1FltCtrl;
|
|
|
|
type
|
|
|
|
// [1] MTP/Bluetooth 설정 구조체
|
|
TMtpBtConfig = record
|
|
Policy: Integer;
|
|
FileSize: Integer;
|
|
end;
|
|
|
|
// [2] 프로세스 훅 설정 클래스
|
|
THookProcessItem = class
|
|
public
|
|
ProcessName: string; // Key (예: FSQUIRT)
|
|
DeviceType: TStringList;
|
|
Mode: Integer;
|
|
ExceptionPath: TStringList;
|
|
Extension: TStringList;
|
|
ExceptionExt: TStringList; // JSON 키: exceptionExtenstion (오타 주의)
|
|
|
|
constructor Create(AName: string);
|
|
destructor Destroy; override;
|
|
function Clone: THookProcessItem;
|
|
end;
|
|
|
|
// [3] DataFlow 전체 설정 클래스
|
|
TDataFlowConfig = class
|
|
public
|
|
Seq: DWORD; // 기본값 "1"
|
|
GlobalExceptionPath: TStringList; // 공통 예외 경로
|
|
|
|
// DeviceControl (MTP, BT)
|
|
MtpConfig: TMtpBtConfig;
|
|
BtConfig: TMtpBtConfig;
|
|
CDROMConfig: TMtpBtConfig;
|
|
UsbToUsbConfig: TMtpBtConfig;
|
|
// HookProcess
|
|
HookProcesses: TObjectList<THookProcessItem>;
|
|
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Assign(Source: TDataFlowConfig); // 데이터 복사 (UI 취소 기능 지원용)
|
|
end;
|
|
|
|
|
|
TMatchCriteria = class
|
|
public
|
|
PropType: DWORD;
|
|
MatchData: string;
|
|
Enumerator: string;
|
|
constructor Create(APropType: DWORD; AMatchData, AEnum: string);
|
|
function Clone: TMatchCriteria;
|
|
end;
|
|
|
|
TPolicyItem = class
|
|
public
|
|
name: string;
|
|
flag_: DWORD;
|
|
state_: TDeviceState;
|
|
isLog_: Boolean;
|
|
targetGUID_: string;
|
|
matchPropType_: DWORD; // 예: SPDRP_SERVICE, SPDRP_CLASSGUID
|
|
|
|
isBluetooth_: Boolean;
|
|
Matches_: TObjectList<TMatchCriteria>;
|
|
|
|
constructor Create(const AName: string; AFlag: DWORD; AGUIDStr: string; IsBT: Boolean = False);
|
|
destructor Destroy;
|
|
procedure AddMatch(PropType: DWORD; MatchData, Enumerator: string);
|
|
function Clone: TPolicyItem;
|
|
|
|
end;
|
|
|
|
TBs1Policy = class
|
|
private
|
|
|
|
FPolicyFilePath: string;
|
|
FLock: TCriticalSection;
|
|
FPolicies: TObjectList<TPolicyItem>;
|
|
//FExceptionList: TStringList;
|
|
FDataFlowSeq: string;
|
|
FDataFlowConfig: TDataFlowConfig; // DataFlow 설정 객체
|
|
|
|
FUsbExceptList_: TStringList;
|
|
FUsbExceptListLock_: TCriticalSection;
|
|
|
|
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
// 개별 정책 상태 변경 (UI 연동용)
|
|
//procedure UpdatePolicyStateInList(const GuidStr: string; NewState: TPolicyState);
|
|
function CreatePolicy(const Name: string; Flag: TDeviceType; AGUID: string = ''; IsBT: Boolean = False): TPolicyItem;
|
|
// procedure AddPolicy(Flag: DWORD; PropType: DWORD; const MatchData: string; pGUID: string; EnumStr: string; IsBT: Boolean = False);
|
|
procedure AddPolicyObject(APolicy: TPolicyItem);
|
|
procedure UpdatePolicyState(Flag: DWORD; NewState: TDeviceState; NewLogState: Boolean);
|
|
function GetPolicyState(Flag: DWORD; var NewState: TDeviceState; var NewLogState: Boolean): Boolean;
|
|
procedure CopyPoliciesTo(ATargetList: TObjectList<TPolicyItem>);
|
|
function GetPolicyJsonArray: TJSONArray;
|
|
function GetPolicyItem(deviceType: DWORD): TPolicyItem;
|
|
// 파일 입출력 (JSON)
|
|
procedure SavePolicyToFile;
|
|
procedure LoadDataControlConfig;
|
|
|
|
procedure LoadDataFlowConfig;
|
|
procedure SaveDataFlowConfig; // DataFlow 설정 전용 저장 함수
|
|
|
|
property Policies: TObjectList<TPolicyItem> read FPolicies;
|
|
property DataFlowConfig: TDataFlowConfig read FDataFlowConfig;
|
|
//property ExceptionList: TStringList read FExceptionList;
|
|
procedure SaveDeviceControlExceptProcessConfig(ExceptionPath: TStrings);
|
|
procedure LoadDeviceControlExceptProcessConfig(ExceptionPath: TStrings);
|
|
function ParseStringArray(AJSONValue: TJSONValue): TArray<string>;
|
|
|
|
function IsUsbPortExcept(const DeviceInstancePath: string): Boolean;
|
|
function RemoveUsbPortExcept(const Vendor, Product, Serial: string): Boolean;
|
|
function AddUsbPortExcept(const Vendor, Product, Serial: string): Boolean;
|
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
gBs1Policy: TBs1Policy = nil;
|
|
|
|
implementation
|
|
|
|
{ THookProcessItem }
|
|
|
|
constructor THookProcessItem.Create(AName: string);
|
|
begin
|
|
ProcessName := AName;
|
|
ExceptionPath := TStringList.Create;
|
|
Extension := TStringList.Create;
|
|
ExceptionExt := TStringList.Create;
|
|
DeviceType:= TStringList.Create;
|
|
Mode := 0;
|
|
end;
|
|
|
|
destructor THookProcessItem.Destroy;
|
|
begin
|
|
ExceptionPath.Free;
|
|
Extension.Free;
|
|
ExceptionExt.Free;
|
|
DeviceType.Free;
|
|
inherited;
|
|
end;
|
|
|
|
function THookProcessItem.Clone: THookProcessItem;
|
|
begin
|
|
Result := THookProcessItem.Create(Self.ProcessName);
|
|
Result.DeviceType.Assign(Self.DeviceType);
|
|
Result.Mode := Self.Mode;
|
|
Result.ExceptionPath.Assign(Self.ExceptionPath);
|
|
Result.Extension.Assign(Self.Extension);
|
|
Result.ExceptionExt.Assign(Self.ExceptionExt);
|
|
end;
|
|
|
|
{ TDataFlowConfig }
|
|
|
|
constructor TDataFlowConfig.Create;
|
|
begin
|
|
Seq := 1;
|
|
GlobalExceptionPath := TStringList.Create;
|
|
HookProcesses := TObjectList<THookProcessItem>.Create;
|
|
// 기본값
|
|
MtpConfig.Policy := 1; MtpConfig.FileSize := 20;
|
|
BtConfig.Policy := 0; BtConfig.FileSize := 20;
|
|
|
|
|
|
end;
|
|
|
|
destructor TDataFlowConfig.Destroy;
|
|
begin
|
|
GlobalExceptionPath.Free;
|
|
HookProcesses.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TDataFlowConfig.Assign(Source: TDataFlowConfig);
|
|
begin
|
|
Self.Seq := Source.Seq;
|
|
Self.GlobalExceptionPath.Assign(Source.GlobalExceptionPath);
|
|
Self.MtpConfig := Source.MtpConfig;
|
|
Self.BtConfig := Source.BtConfig;
|
|
Self.CDROMConfig := Source.CDROMConfig;
|
|
Self.UsbToUsbConfig := Source.UsbToUsbConfig;
|
|
|
|
Self.HookProcesses.Clear;
|
|
for var Item in Source.HookProcesses do
|
|
Self.HookProcesses.Add(Item.Clone);
|
|
end;
|
|
|
|
{ TMatchCriteria }
|
|
constructor TMatchCriteria.Create(APropType: DWORD; AMatchData, AEnum: string);
|
|
begin
|
|
PropType := APropType;
|
|
MatchData := AMatchData;
|
|
Enumerator := AEnum;
|
|
end;
|
|
|
|
function TMatchCriteria.Clone: TMatchCriteria;
|
|
begin
|
|
Result := TMatchCriteria.Create(self.PropType, self.MatchData, self.Enumerator);
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ TPolicyItem }
|
|
constructor TPolicyItem.Create(const AName: string; AFlag: DWORD; AGUIDStr: string; IsBT: Boolean);
|
|
begin
|
|
Name := AName;
|
|
flag_ := AFlag;
|
|
targetGUID_ := AGUIDStr;
|
|
isBluetooth_ := IsBT;
|
|
state_ := dsEnable; // 기본값
|
|
isLog_:= False;
|
|
matches_ := TObjectList<TMatchCriteria>.Create;
|
|
end;
|
|
|
|
destructor TPolicyItem.Destroy;
|
|
begin
|
|
Matches_.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TPolicyItem.AddMatch(PropType: DWORD; MatchData, Enumerator: string);
|
|
begin
|
|
Matches_.Add(TMatchCriteria.Create(PropType, MatchData, Enumerator));
|
|
end;
|
|
|
|
function TPolicyItem.Clone: TPolicyItem;
|
|
var
|
|
Match: TMatchCriteria;
|
|
begin
|
|
// 기본 필드 복사
|
|
Result := TPolicyItem.Create(self.name, self.flag_, self.targetGUID_, self.isBluetooth_);
|
|
Result.state_ := self.state_;
|
|
Result.isLog_ := self.isLog_;
|
|
Result.matchPropType_ := self.matchPropType_;
|
|
|
|
// Match 리스트 내부 아이템들도 깊은 복사 수행
|
|
for Match in self.Matches_ do
|
|
begin
|
|
Result.Matches_.Add(Match.Clone);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
procedure TDeviceGuardEngine.AddDefaultPolicy(const Name, GuidStr: string; IsBT: Boolean);
|
|
begin
|
|
FLock.Enter;
|
|
try
|
|
// 중복 확인 후 없으면 추가
|
|
for var Item in FPolicies do
|
|
if IsEqualGUID(Item.GUID, StringToGUID(GuidStr)) then Exit;
|
|
|
|
FPolicies.Add(TPolicyItem.Create(Name, GuidStr, psLog, IsBT));
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
procedure TDeviceGuardEngine.UpdatePolicyStateInList(const GuidStr: string; NewState: TPolicyState);
|
|
var
|
|
TargetGUID: TGUID;
|
|
begin
|
|
TargetGUID := StringToGUID(GuidStr);
|
|
FLock.Enter;
|
|
try
|
|
for var Item in FPolicies do
|
|
begin
|
|
if IsEqualGUID(Item.GUID, TargetGUID) then
|
|
begin
|
|
Item.State := NewState;
|
|
Break;
|
|
end;
|
|
end;
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end; }
|
|
{
|
|
procedure TDeviceGuardEngine.AddPolicy(Flag: DWORD; PropType: DWORD; const MatchData: string; pGUID: string; EnumStr: string; IsBT: Boolean);
|
|
begin
|
|
FLock.Enter;
|
|
try
|
|
// Flag 중복 체크
|
|
for var Item in FPolicies do
|
|
if Item.flag_ = Flag then Exit;
|
|
|
|
FPolicies.Add(TPolicyItem.Create(Flag, PropType, MatchData, pGUID, EnumStr, IsBT));
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end; }
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// 헬퍼: JSON 배열 -> 스트링리스트
|
|
procedure JsonArrayToStrings(JArr: TJSONArray; List: TStrings);
|
|
begin
|
|
List.Clear;
|
|
if JArr = nil then Exit;
|
|
for var I := 0 to JArr.Count - 1 do
|
|
List.Add(JArr.Items[I].Value);
|
|
end;
|
|
|
|
// 헬퍼: 스트링리스트 -> JSON 배열
|
|
function StringsToJsonArray(List: TStrings): TJSONArray;
|
|
begin
|
|
Result := TJSONArray.Create;
|
|
for var S in List do
|
|
Result.Add(S);
|
|
end;
|
|
|
|
{ TBs1Policy }
|
|
constructor TBs1Policy.Create;
|
|
begin
|
|
|
|
gBs1Policy:= self;
|
|
FPolicies := TObjectList<TPolicyItem>.Create;
|
|
FLock := TCriticalSection.Create;
|
|
FDataFlowConfig := TDataFlowConfig.Create;
|
|
FPolicyFilePath := ChangeFileExt(ParamStr(0), '.json');
|
|
//
|
|
//FExceptionList := TStringList.Create;
|
|
FDataFlowSeq := '1'; // 기본값
|
|
|
|
FUsbExceptList_ := TStringList.Create;
|
|
FUsbExceptListLock_ := TCriticalSection.Create;
|
|
end;
|
|
|
|
destructor TBs1Policy.Destroy;
|
|
begin
|
|
FUsbExceptList_.Free;
|
|
FUsbExceptListLock_.Free;
|
|
FDataFlowConfig.Free;
|
|
// FExceptionList.Free;
|
|
FPolicies.Free;
|
|
FLock.Free;
|
|
inherited;
|
|
end;
|
|
|
|
function TBs1Policy.AddUsbPortExcept(const Vendor, Product, Serial: string): Boolean;
|
|
var
|
|
Key: string;
|
|
begin
|
|
|
|
Key := Format('USB\VID_%s&PID_%s\%s', [Vendor, Product, Serial]);
|
|
Key := Trim(Key);
|
|
|
|
//OutputDebugStringW(PChar(Format('bs1 (key : %s)', [Key])));
|
|
|
|
FUsbExceptListLock_.Enter;
|
|
try
|
|
// Sorted=True, Duplicates=dupIgnore 설정 덕분에 Add만 호출해도 중복체크 됨
|
|
FUsbExceptList_.Add(Key);
|
|
Result := True;
|
|
finally
|
|
FUsbExceptListLock_.Leave;
|
|
end;
|
|
end;
|
|
|
|
function TBs1Policy.RemoveUsbPortExcept(const Vendor, Product, Serial: string): Boolean;
|
|
var
|
|
Key: string;
|
|
Idx: Integer;
|
|
begin
|
|
Result:= False;
|
|
Key := Format('USB\VID_%s&PID_%s\%s', [Vendor, Product, Serial]);
|
|
Key := Trim(Key);
|
|
|
|
//OutputDebugStringW(PChar(Format('bs1 (key : %s)', [Key])));
|
|
|
|
FUsbExceptListLock_.Enter;
|
|
try
|
|
Idx := FUsbExceptList_.IndexOf(Key);
|
|
if Idx >= 0 then
|
|
begin
|
|
FUsbExceptList_.Delete(Idx);
|
|
Result := True;
|
|
end;
|
|
finally
|
|
FUsbExceptListLock_.Leave;
|
|
end;
|
|
end;
|
|
|
|
function TBs1Policy.IsUsbPortExcept(const DeviceInstancePath: string): Boolean;
|
|
begin
|
|
FUsbExceptListLock_.Enter;
|
|
try
|
|
// IndexOf는 대소문자 무시(CaseSensitive=False)하여 찾음
|
|
Result := FUsbExceptList_.IndexOf(DeviceInstancePath) >= 0;
|
|
finally
|
|
FUsbExceptListLock_.Leave;
|
|
end;
|
|
end;
|
|
|
|
procedure TBs1Policy.CopyPoliciesTo(ATargetList: TObjectList<TPolicyItem>);
|
|
begin
|
|
if ATargetList = nil then Exit;
|
|
|
|
FLock.Enter;
|
|
try
|
|
ATargetList.Clear; // 대상 리스트 초기화 (필요에 따라 주석 처리 가능)
|
|
|
|
for var Item in FPolicies do
|
|
begin
|
|
// 각 아이템을 복제(Clone)하여 대상 리스트에 추가
|
|
// TObjectList가 OwnsObjects=True라면 나중에 ATargetList가 해제될 때 복제된 객체들도 자동 해제됨
|
|
ATargetList.Add(Item.Clone);
|
|
end;
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
function TBs1Policy.GetPolicyItem(deviceType: DWORD): TPolicyItem;
|
|
var
|
|
policyItem: TPolicyItem;
|
|
begin
|
|
FLock.Enter;
|
|
try
|
|
|
|
for var Item in FPolicies do
|
|
begin
|
|
if Item.flag_ = deviceType then
|
|
begin
|
|
Result := Item;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
function TBs1Policy.ParseStringArray(AJSONValue: TJSONValue): TArray<string>;
|
|
var
|
|
LArray: TJSONArray;
|
|
LString: TJSONString;
|
|
I: Integer;
|
|
begin
|
|
Result := [];
|
|
if AJSONValue = nil then Exit;
|
|
|
|
if AJSONValue is TJSONArray then
|
|
begin
|
|
LArray := AJSONValue as TJSONArray;
|
|
SetLength(Result, LArray.Count);
|
|
for I := 0 to LArray.Count - 1 do
|
|
Result[I] := LArray.Items[I].Value;
|
|
end
|
|
else if (AJSONValue is TJSONString) then
|
|
begin
|
|
LString := AJSONValue as TJSONString;
|
|
if not LString.Value.IsEmpty then
|
|
Result := LString.Value.Split(['|']);
|
|
end;
|
|
end;
|
|
|
|
function TBs1Policy.CreatePolicy(const Name: string; Flag: TDeviceType; AGUID: string; IsBT: Boolean): TPolicyItem;
|
|
begin
|
|
Result := TPolicyItem.Create(Name, DWORD(Flag), AGUID, IsBT);
|
|
end;
|
|
|
|
|
|
procedure TBs1Policy.AddPolicyObject(APolicy: TPolicyItem);
|
|
begin
|
|
FLock.Enter;
|
|
try
|
|
// 중복 체크 (Flag 기준)
|
|
for var Item in FPolicies do
|
|
begin
|
|
if Item.flag_ = APolicy.flag_ then
|
|
begin
|
|
APolicy.Free; // 이미 있으면 해제하고 무시
|
|
Exit;
|
|
end;
|
|
end;
|
|
FPolicies.Add(APolicy);
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
function TBs1Policy.GetPolicyJsonArray: TJSONArray;
|
|
var
|
|
JObjPolicy, JObjMatch: TJSONObject;
|
|
JArrMatch: TJSONArray;
|
|
begin
|
|
Result := TJSONArray.Create;
|
|
try
|
|
for var Item in FPolicies do
|
|
begin
|
|
JObjPolicy := TJSONObject.Create;
|
|
JObjPolicy.AddPair('name', Item.Name);
|
|
JObjPolicy.AddPair('flag', TJSONNumber.Create(Item.flag_));
|
|
JObjPolicy.AddPair('state', TJSONNumber.Create(Ord(Item.state_)));
|
|
|
|
// boolean을 int로 저장 (기존 로직 유지)
|
|
if Item.isLog_ then
|
|
JObjPolicy.AddPair('log', TJSONNumber.Create(1))
|
|
else
|
|
JObjPolicy.AddPair('log', TJSONNumber.Create(0));
|
|
|
|
if Item.targetGUID_ <> '' then
|
|
JObjPolicy.AddPair('guid', Item.targetGUID_);
|
|
|
|
// Match 배열 생성
|
|
if Item.matches_.Count > 0 then
|
|
begin
|
|
JArrMatch := TJSONArray.Create;
|
|
for var Match in Item.matches_ do
|
|
begin
|
|
JObjMatch := TJSONObject.Create;
|
|
JObjMatch.AddPair('propType', TJSONNumber.Create(Match.PropType));
|
|
JObjMatch.AddPair('matchData', Match.MatchData);
|
|
JObjMatch.AddPair('enumerator', Match.Enumerator);
|
|
JArrMatch.AddElement(JObjMatch);
|
|
end;
|
|
JObjPolicy.AddPair('match', JArrMatch);
|
|
end;
|
|
|
|
Result.AddElement(JObjPolicy);
|
|
end;
|
|
except
|
|
Result.Free;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TBs1Policy.UpdatePolicyState(Flag: DWORD; NewState: TDeviceState; NewLogState: Boolean);
|
|
begin
|
|
FLock.Enter;
|
|
try
|
|
for var Item in FPolicies do
|
|
begin
|
|
if Item.flag_ = Flag then
|
|
begin
|
|
Item.state_ := NewState;
|
|
Item.isLog_ := NewLogState;
|
|
Break;
|
|
end;
|
|
end;
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
function TBs1Policy.GetPolicyState(Flag: DWORD; var NewState: TDeviceState; var NewLogState: Boolean): Boolean;
|
|
begin
|
|
FLock.Enter;
|
|
try
|
|
Result:= False;
|
|
for var Item in FPolicies do
|
|
begin
|
|
if Item.flag_ = Flag then
|
|
begin
|
|
NewState:= Item.state_;
|
|
NewLogState:= Item.isLog_;
|
|
Result:= True;
|
|
Break;
|
|
end;
|
|
end;
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// [Save] bs1dc.json 파일을 읽어서 deviceControl 부분만 업데이트 후 저장
|
|
// ---------------------------------------------------------------------------
|
|
procedure TBs1Policy.SavePolicyToFile;
|
|
var
|
|
JsonText: string;
|
|
JRootVal: TJSONValue;
|
|
JObjRoot, JObjDataFlow: TJSONObject;
|
|
JArrPolicies: TJSONArray;
|
|
IsNewFile: Boolean;
|
|
begin
|
|
FLock.Enter;
|
|
try
|
|
IsNewFile := not FileExists(FPolicyFilePath);
|
|
JObjRoot := nil;
|
|
|
|
// 1. 기존 파일이 있으면 읽어서 파싱, 없으면 새로 생성
|
|
if not IsNewFile then
|
|
begin
|
|
JsonText := TFile.ReadAllText(FPolicyFilePath);
|
|
JRootVal := TJSONObject.ParseJSONValue(JsonText);
|
|
if JRootVal is TJSONObject then
|
|
JObjRoot := JRootVal as TJSONObject
|
|
else
|
|
JRootVal.Free; // 잘못된 포맷이면 폐기
|
|
end;
|
|
|
|
if JObjRoot = nil then
|
|
begin
|
|
JObjRoot := TJSONObject.Create;
|
|
JObjRoot.AddPair('dataFlowSeq', TJSONNumber.Create(1));
|
|
end;
|
|
|
|
try
|
|
// // 2. 'dataFlow' 객체 찾기 또는 생성
|
|
// if not JObjRoot.TryGetValue<TJSONObject>('dataFlow', JObjDataFlow) then
|
|
// begin
|
|
// JObjDataFlow := TJSONObject.Create;
|
|
// JObjRoot.AddPair('dataFlow', JObjDataFlow);
|
|
// end;
|
|
|
|
// 3. 기존의 'deviceControl' 키가 있다면 제거 (새로 덮어쓰기 위해)
|
|
if JObjRoot.GetValue('deviceControl') <> nil then
|
|
begin
|
|
JObjRoot.RemovePair('deviceControl').Free;
|
|
end;
|
|
|
|
// 4. 현재 정책들을 JSON Array로 변환하여 추가
|
|
JArrPolicies := GetPolicyJsonArray;
|
|
JObjRoot.AddPair('deviceControl', JArrPolicies);
|
|
|
|
// 5. 파일 저장
|
|
// (Format을 하면 보기 좋지만, 용량이 커질 수 있습니다. 필요 시 TJsonFormatType.Indented 사용)
|
|
TFile.WriteAllText(FPolicyFilePath, JObjRoot.ToString);
|
|
|
|
finally
|
|
JObjRoot.Free; // Root를 해제하면 하위 객체들도 모두 해제됨
|
|
end;
|
|
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// [Load 1] bs1dc.json의 dataFlow -> deviceControl (기존 bs1dc 역할) 로드
|
|
// ---------------------------------------------------------------------------
|
|
procedure TBs1Policy.LoadDataControlConfig;
|
|
var
|
|
JsonText: string;
|
|
JRootVal: TJSONValue;
|
|
JArrPolicies, JArrMatch: TJSONArray;
|
|
JObjRoot, JObjPolicy, JObjMatch: TJSONObject;
|
|
FlagVal, StateVal, LogVal, PropVal: Integer;
|
|
NameStr, GuidStr, MatchDataStr, EnumStr: string;
|
|
NewPolicy: TPolicyItem;
|
|
begin
|
|
if not FileExists(FPolicyFilePath) then Exit;
|
|
|
|
FLock.Enter;
|
|
try
|
|
FPolicies.Clear; // 기존 정책 초기화
|
|
|
|
JsonText := TFile.ReadAllText(FPolicyFilePath);
|
|
JRootVal := TJSONObject.ParseJSONValue(JsonText);
|
|
try
|
|
// 루트 확인
|
|
if (JRootVal is TJSONObject) then
|
|
begin
|
|
JObjRoot := JRootVal as TJSONObject;
|
|
|
|
// [수정] 최상위 경로에서 'deviceControl' 찾기
|
|
// 구조: { "dataFlow": {...}, "deviceControl": [...] }
|
|
if JObjRoot.TryGetValue<TJSONArray>('deviceControl', JArrPolicies) then
|
|
begin
|
|
for var I := 0 to JArrPolicies.Count - 1 do
|
|
begin
|
|
JObjPolicy := JArrPolicies.Items[I] as TJSONObject;
|
|
|
|
if JObjPolicy.TryGetValue<string>('name', NameStr) and
|
|
JObjPolicy.TryGetValue<Integer>('flag', FlagVal) and
|
|
JObjPolicy.TryGetValue<Integer>('state', StateVal) then
|
|
begin
|
|
GuidStr := '';
|
|
JObjPolicy.TryGetValue<string>('guid', GuidStr);
|
|
|
|
LogVal := 0;
|
|
JObjPolicy.TryGetValue<Integer>('log', LogVal);
|
|
|
|
// 정책 객체 생성
|
|
var IsBT := (DWORD(FlagVal) = DWORD(BDC_BLUETOOTH));
|
|
NewPolicy := TPolicyItem.Create(NameStr, DWORD(FlagVal), GuidStr, IsBT);
|
|
NewPolicy.state_ := TDeviceState(StateVal);
|
|
NewPolicy.isLog_ := (LogVal <> 0);
|
|
|
|
// match 배열 로드
|
|
if JObjPolicy.TryGetValue<TJSONArray>('match', JArrMatch) then
|
|
begin
|
|
for var K := 0 to JArrMatch.Count - 1 do
|
|
begin
|
|
JObjMatch := JArrMatch.Items[K] as TJSONObject;
|
|
if JObjMatch.TryGetValue<Integer>('propType', PropVal) and
|
|
JObjMatch.TryGetValue<string>('matchData', MatchDataStr) and
|
|
JObjMatch.TryGetValue<string>('enumerator', EnumStr) then
|
|
begin
|
|
NewPolicy.AddMatch(DWORD(PropVal), MatchDataStr, EnumStr);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
FPolicies.Add(NewPolicy);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
JRootVal.Free;
|
|
end;
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TBs1Policy.LoadDataFlowConfig;
|
|
var
|
|
JsonText: string;
|
|
JRoot, JDataFlow, JRule, JDevCtrl, JHookProc, JItem: TJSONObject;
|
|
JArr: TJSONArray;
|
|
JVal: TJSONValue;
|
|
SeqKey: string;
|
|
HookItem: THookProcessItem;
|
|
SeqInt: Integer;
|
|
JArrDevice: TJSONArray;
|
|
StrDevice: string;
|
|
begin
|
|
if not FileExists(FPolicyFilePath) then Exit;
|
|
|
|
FLock.Enter;
|
|
try
|
|
JsonText := TFile.ReadAllText(FPolicyFilePath);
|
|
// ParseJSONValue는 실패 시 nil 반환하므로 체크 필수
|
|
JRoot := TJSONObject.ParseJSONValue(JsonText) as TJSONObject;
|
|
try
|
|
if JRoot = nil then Exit;
|
|
|
|
// 0. dataFlowSeq 값 읽기
|
|
if JRoot.TryGetValue<Integer>('dataFlowSeq', SeqInt) then
|
|
FDataFlowConfig.Seq := SeqInt;
|
|
|
|
// 1. dataFlow 객체 찾기
|
|
if not JRoot.TryGetValue<TJSONObject>('dataFlow', JDataFlow) then Exit;
|
|
|
|
// 2. Seq 키 (예: "6") 찾기
|
|
SeqKey := FDataFlowConfig.Seq.ToString;
|
|
if not JDataFlow.TryGetValue<TJSONObject>(SeqKey, JRule) then Exit;
|
|
|
|
// 3. Exception Path (Global)
|
|
if JRule.TryGetValue<TJSONArray>('exceptionPath', JArr) then
|
|
JsonArrayToStrings(JArr, FDataFlowConfig.GlobalExceptionPath);
|
|
|
|
// 4. deviceControl 로드 (기존 코드 유지)
|
|
if JRule.TryGetValue<TJSONObject>('deviceControl', JDevCtrl) then
|
|
begin
|
|
if JDevCtrl.TryGetValue<TJSONObject>('mtp', JItem) then
|
|
begin
|
|
JItem.TryGetValue<Integer>('policy', FDataFlowConfig.MtpConfig.Policy);
|
|
JItem.TryGetValue<Integer>('fileSize', FDataFlowConfig.MtpConfig.FileSize);
|
|
end;
|
|
if JDevCtrl.TryGetValue<TJSONObject>('bluetooth', JItem) then
|
|
begin
|
|
JItem.TryGetValue<Integer>('policy', FDataFlowConfig.BtConfig.Policy);
|
|
JItem.TryGetValue<Integer>('fileSize', FDataFlowConfig.BtConfig.FileSize);
|
|
end;
|
|
if JDevCtrl.TryGetValue<TJSONObject>('cdrom', JItem) then
|
|
begin
|
|
JItem.TryGetValue<Integer>('policy', FDataFlowConfig.CDROMConfig.Policy);
|
|
JItem.TryGetValue<Integer>('fileSize', FDataFlowConfig.CDROMConfig.FileSize);
|
|
end;
|
|
if JDevCtrl.TryGetValue<TJSONObject>('usbTousb', JItem) then
|
|
begin
|
|
JItem.TryGetValue<Integer>('policy', FDataFlowConfig.UsbToUsbConfig.Policy);
|
|
JItem.TryGetValue<Integer>('fileSize', FDataFlowConfig.UsbToUsbConfig.FileSize);
|
|
end;
|
|
end;
|
|
|
|
// 5. hookProcess 로드
|
|
FDataFlowConfig.HookProcesses.Clear;
|
|
if JRule.TryGetValue<TJSONObject>('hookProcess', JHookProc) then
|
|
begin
|
|
for var Pair in JHookProc do
|
|
begin
|
|
JItem := Pair.JsonValue as TJSONObject;
|
|
HookItem := THookProcessItem.Create(Pair.JsonString.Value); // Key = ProcessName
|
|
|
|
// [검토] deviceType 로드 로직
|
|
HookItem.DeviceType.Clear;
|
|
JVal := JItem.GetValue('deviceType');
|
|
|
|
if JVal <> nil then
|
|
begin
|
|
if JVal is TJSONArray then
|
|
begin
|
|
// 배열인 경우 (["mtp", "cdrom"])
|
|
JsonArrayToStrings(JVal as TJSONArray, HookItem.DeviceType);
|
|
end
|
|
else if JVal is TJSONString then
|
|
begin
|
|
// 문자열인 경우 ("bluetooth")
|
|
if (JVal as TJSONString).Value.Trim <> '' then
|
|
HookItem.DeviceType.Add((JVal as TJSONString).Value);
|
|
end;
|
|
end;
|
|
|
|
JItem.TryGetValue<Integer>('mode', HookItem.Mode);
|
|
|
|
if JItem.TryGetValue<TJSONArray>('exceptionPath', JArr) then
|
|
JsonArrayToStrings(JArr, HookItem.ExceptionPath);
|
|
|
|
if JItem.TryGetValue<TJSONArray>('extension', JArr) then
|
|
JsonArrayToStrings(JArr, HookItem.Extension);
|
|
|
|
if JItem.TryGetValue<TJSONArray>('exceptionExtenstion', JArr) then
|
|
JsonArrayToStrings(JArr, HookItem.ExceptionExt);
|
|
|
|
FDataFlowConfig.HookProcesses.Add(HookItem);
|
|
end;
|
|
end;
|
|
|
|
finally
|
|
JRoot.Free;
|
|
end;
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
procedure TBs1Policy.SaveDataFlowConfig;
|
|
var
|
|
JsonText: string;
|
|
JRoot, JDataFlow, JRule, JDevCtrl, JHookProc, JHookItem, JSubItem: TJSONObject;
|
|
JArrDeviceType: TJSONArray;
|
|
JSeqValue: TJSONValue;
|
|
CurrentSeq, NewSeq: Integer;
|
|
OldSeqStr, NewSeqStr: string;
|
|
begin
|
|
FLock.Enter;
|
|
try
|
|
// 1. 기존 파일 로드 (주석 보존 및 구조 유지 위해)
|
|
JRoot := nil;
|
|
if FileExists(FPolicyFilePath) then
|
|
begin
|
|
try
|
|
JsonText := TFile.ReadAllText(FPolicyFilePath);
|
|
JRoot := TJSONObject.ParseJSONValue(JsonText) as TJSONObject;
|
|
except
|
|
JRoot := nil;
|
|
end;
|
|
end;
|
|
|
|
if JRoot = nil then
|
|
JRoot := TJSONObject.Create;
|
|
|
|
try
|
|
// 2. Seq 관리 (증가 로직)
|
|
CurrentSeq := 0;
|
|
if JRoot.TryGetValue<Integer>('dataFlowSeq', CurrentSeq) then
|
|
begin
|
|
// 성공적으로 읽음
|
|
end;
|
|
|
|
// [주의] 만약 단순히 현재 설정을 저장하는 것이라면 Seq를 증가시키지 않아도 될 수 있습니다.
|
|
// 여기서는 요청하신 로직대로 +1 증가시킵니다.
|
|
NewSeq := CurrentSeq + 1;
|
|
FDataFlowConfig.Seq := NewSeq;
|
|
|
|
OldSeqStr := CurrentSeq.ToString;
|
|
NewSeqStr := NewSeq.ToString;
|
|
|
|
// Root의 Seq 업데이트
|
|
if JRoot.GetValue('dataFlowSeq') <> nil then
|
|
JRoot.RemovePair('dataFlowSeq').Free;
|
|
JRoot.AddPair('dataFlowSeq', TJSONNumber.Create(NewSeq));
|
|
|
|
// 3. dataFlow 객체 확보
|
|
JDataFlow := nil;
|
|
if JRoot.GetValue('dataFlow') <> nil then
|
|
JDataFlow := JRoot.GetValue('dataFlow') as TJSONObject;
|
|
|
|
if JDataFlow = nil then
|
|
begin
|
|
JDataFlow := TJSONObject.Create;
|
|
JRoot.AddPair('dataFlow', JDataFlow);
|
|
end;
|
|
|
|
// 기존 Seq 데이터 삭제 (롤링 업데이트)
|
|
if JDataFlow.GetValue(OldSeqStr) <> nil then
|
|
JDataFlow.RemovePair(OldSeqStr).Free;
|
|
|
|
// 혹시 모를 충돌 방지
|
|
if JDataFlow.GetValue(NewSeqStr) <> nil then
|
|
JDataFlow.RemovePair(NewSeqStr).Free;
|
|
|
|
// 4. 새 규칙 생성 (NewSeq)
|
|
JRule := TJSONObject.Create;
|
|
JDataFlow.AddPair(NewSeqStr, JRule);
|
|
|
|
// ExceptionPath
|
|
JRule.AddPair('exceptionPath', StringsToJsonArray(FDataFlowConfig.GlobalExceptionPath));
|
|
|
|
// DeviceControl
|
|
JDevCtrl := TJSONObject.Create;
|
|
|
|
// MTP
|
|
JSubItem := TJSONObject.Create;
|
|
JSubItem.AddPair('policy', TJSONNumber.Create(FDataFlowConfig.MtpConfig.Policy));
|
|
JSubItem.AddPair('fileSize', TJSONNumber.Create(FDataFlowConfig.MtpConfig.FileSize));
|
|
JDevCtrl.AddPair('mtp', JSubItem);
|
|
// Bluetooth
|
|
JSubItem := TJSONObject.Create;
|
|
JSubItem.AddPair('policy', TJSONNumber.Create(FDataFlowConfig.BtConfig.Policy));
|
|
JSubItem.AddPair('fileSize', TJSONNumber.Create(FDataFlowConfig.BtConfig.FileSize));
|
|
JDevCtrl.AddPair('bluetooth', JSubItem);
|
|
// CDROM
|
|
JSubItem := TJSONObject.Create;
|
|
JSubItem.AddPair('policy', TJSONNumber.Create(FDataFlowConfig.CDROMConfig.Policy));
|
|
JSubItem.AddPair('fileSize', TJSONNumber.Create(FDataFlowConfig.CDROMConfig.FileSize));
|
|
JDevCtrl.AddPair('cdrom', JSubItem);
|
|
// UsbToUsb
|
|
JSubItem := TJSONObject.Create;
|
|
JSubItem.AddPair('policy', TJSONNumber.Create(FDataFlowConfig.UsbToUsbConfig.Policy));
|
|
JSubItem.AddPair('fileSize', TJSONNumber.Create(FDataFlowConfig.UsbToUsbConfig.FileSize));
|
|
JDevCtrl.AddPair('usbTousb', JSubItem);
|
|
|
|
JRule.AddPair('deviceControl', JDevCtrl);
|
|
|
|
// 5. HookProcess 저장 (여기가 핵심)
|
|
JHookProc := TJSONObject.Create;
|
|
for var Item in FDataFlowConfig.HookProcesses do
|
|
begin
|
|
JHookItem := TJSONObject.Create;
|
|
|
|
// [핵심 수정] deviceType 저장 로직 개선
|
|
// 1개면 String, 그 외엔 Array로 저장하여 JSON 포맷 유지
|
|
if Item.DeviceType.Count = 1 then
|
|
begin
|
|
// 문자열로 저장: "deviceType": "bluetooth"
|
|
JHookItem.AddPair('deviceType', Item.DeviceType[0]);
|
|
end
|
|
else
|
|
begin
|
|
// 배열로 저장: "deviceType": ["mtp", "cdrom"]
|
|
JArrDeviceType := TJSONArray.Create;
|
|
for var S in Item.DeviceType do
|
|
begin
|
|
JArrDeviceType.Add(S);
|
|
end;
|
|
JHookItem.AddPair('deviceType', JArrDeviceType);
|
|
end;
|
|
|
|
JHookItem.AddPair('mode', TJSONNumber.Create(Item.Mode));
|
|
JHookItem.AddPair('exceptionPath', StringsToJsonArray(Item.ExceptionPath));
|
|
JHookItem.AddPair('extension', StringsToJsonArray(Item.Extension));
|
|
JHookItem.AddPair('exceptionExtenstion', StringsToJsonArray(Item.ExceptionExt));
|
|
|
|
JHookProc.AddPair(Item.ProcessName, JHookItem);
|
|
end;
|
|
JRule.AddPair('hookProcess', JHookProc);
|
|
|
|
// 파일 저장
|
|
TFile.WriteAllText(FPolicyFilePath, JRoot.ToString);
|
|
|
|
finally
|
|
JRoot.Free;
|
|
end;
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
procedure TBs1Policy.SaveDeviceControlExceptProcessConfig(ExceptionPath: TStrings);
|
|
var
|
|
JsonText: string;
|
|
JRoot, JDataFlow, JConfigObj, JHookItem: TJSONObject;
|
|
begin
|
|
FLock.Enter;
|
|
try
|
|
if FileExists(FPolicyFilePath) then
|
|
begin
|
|
JsonText := TFile.ReadAllText(FPolicyFilePath);
|
|
JRoot := TJSONObject.ParseJSONValue(JsonText) as TJSONObject;
|
|
end
|
|
else
|
|
JRoot := TJSONObject.Create;
|
|
|
|
if JRoot = nil then JRoot := TJSONObject.Create;
|
|
|
|
try
|
|
|
|
if JRoot.GetValue('deviceControlConfig') <> nil then
|
|
begin
|
|
JRoot.RemovePair('deviceControlConfig').Free;
|
|
end;
|
|
|
|
JConfigObj := TJSONObject.Create;
|
|
JConfigObj.AddPair('exceptionPath', StringsToJsonArray(ExceptionPath));
|
|
JRoot.AddPair('deviceControlConfig', JConfigObj);
|
|
|
|
|
|
TFile.WriteAllText(FPolicyFilePath, JRoot.ToString);
|
|
finally
|
|
JRoot.Free;
|
|
end;
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
procedure TBs1Policy.LoadDeviceControlExceptProcessConfig(ExceptionPath: TStrings);
|
|
var
|
|
JsonText: string;
|
|
JRootVal: TJSONValue;
|
|
JRootObj, JConfigObj: TJSONObject;
|
|
JArrExceptions: TJSONArray;
|
|
begin
|
|
// 1. 파일 존재 확인
|
|
if not FileExists(FPolicyFilePath) then Exit;
|
|
|
|
FLock.Enter;
|
|
try
|
|
ExceptionPath.Clear; // 기존 데이터 초기화
|
|
|
|
// 2. 파일 읽기 및 파싱
|
|
JsonText := TFile.ReadAllText(FPolicyFilePath);
|
|
JRootVal := TJSONObject.ParseJSONValue(JsonText);
|
|
|
|
try
|
|
// 루트가 JSON 객체인지 확인
|
|
if (JRootVal <> nil) and (JRootVal is TJSONObject) then
|
|
begin
|
|
JRootObj := JRootVal as TJSONObject;
|
|
|
|
// 3. 'deviceControlConfig' 객체 찾기
|
|
if JRootObj.TryGetValue<TJSONObject>('deviceControlConfig', JConfigObj) then
|
|
begin
|
|
// 4. 그 안에서 'exceptionPath' 배열 찾기
|
|
if JConfigObj.TryGetValue<TJSONArray>('exceptionPath', JArrExceptions) then
|
|
begin
|
|
// 5. 배열 내용을 TStrings(ExceptionPath)에 추가
|
|
for var I := 0 to JArrExceptions.Count - 1 do
|
|
begin
|
|
ExceptionPath.Add(JArrExceptions.Items[I].Value);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
JRootVal.Free; // 파싱된 JSON 객체 해제
|
|
end;
|
|
finally
|
|
FLock.Leave;
|
|
end;
|
|
end;
|
|
|
|
end.
|