BSOne.SFC/eCrmHE/EXE_eCrmHomeEdition/Service/OutlookMonClient.pas

652 lines
20 KiB
Plaintext

{*******************************************************}
{ }
{ OutlookMonClient }
{ }
{ Copyright (C) 2023 kku }
{ }
{*******************************************************}
unit OutlookMonClient;
interface
uses
Tocsg.ClientBase, System.SysUtils, System.Classes, Tocsg.Packet,
Winapi.Windows, Tocsg.Win32, Tocsg.Obj, System.Generics.Collections,
GlobalOutAddInDefine, ManagerModel, GlobalDefine;
type
TOutlookMonClient = class(TTgClientBase)
private
dwExecuteTick_: DWORD;
bTerminated_,
bAllowPopup_,
bAbAllowPopup_: Boolean;
OutPo_: TOutlookPolicy;
OutAB_: TAttachBlockPolicy;
// CltMtx_: TTgMutex;
protected
function GetConnected: Boolean; override;
procedure ConnectedEvent; override;
procedure DisconnectedEvent; override;
procedure ProcessRcvPacket(aRcv: IRcvPacket); override;
public
Constructor Create;
Destructor Destroy; override;
procedure UpdateOutPo(bAllowPopup, bAbAllowPopup: Boolean; aOutPo: TOutlookPolicy; aOutAB: TAttachBlockPolicy; bForce: Boolean = false);
end;
var
gOutClient: TOutlookMonClient = nil;
implementation
uses
Tocsg.Exception, Tocsg.Path, Tocsg.WndUtil, Tocsg.Strings,
Tocsg.Process, Tocsg.Shell, superobject, Tocsg.Registry, Tocsg.Json,
ManagerService, Tocsg.Safe, Tocsg.PCRE, CttSchDefine,
ManagerRule, Tocsg.Files, AppCtrlDefine, Condition, ManagerCampaign;
{ TOutlookMonClient }
Constructor TOutlookMonClient.Create;
begin
Inherited Create('', 0, 5000, 3000);
ASSERT(gOutClient = nil);
gOutClient := Self;
bTerminated_ := false;
bAllowPopup_ := false;
bAbAllowPopup_ := false;
dwExecuteTick_ := 0;
ZeroMemory(@OutPo_, SizeOf(OutPo_));
end;
Destructor TOutlookMonClient.Destroy;
begin
gOutClient := nil;
bTerminated_ := true;
Inherited;
end;
procedure TOutlookMonClient.UpdateOutPo(bAllowPopup, bAbAllowPopup: Boolean; aOutPo: TOutlookPolicy; aOutAB: TAttachBlockPolicy; bForce: Boolean = false);
var
Send: ISendPacket;
MailPo: TOutlookAddInPo;
begin
if IsOutlookABMonitorHook then
begin
if not aOutPo.bActive then
exit;
end else begin
if not aOutPo.bActive and (aOutAB.Kind = abkNone) then
exit;
end;
if not bForce then
begin
if (aOutPo.bActive = OutPo_.bActive) and
(bAllowPopup = bAllowPopup_) and
(aOutPo.bBlock = OutPo_.bBlock) and
(aOutPo.bPopup = OutPo_.bPopup) and
(aOutPo.bSchSubject = OutPo_.bSchSubject) and
(aOutPo.bSchBody = OutPo_.bSchBody) and
(aOutPo.bUseMasking = OutPo_.bUseMasking) and
(aOutPo.bCollect = OutPo_.bCollect) and
(aOutPo.bAttachLog = OutPo_.bAttachLog) and
(aOutPo.ContentFilter.bActive = OutPo_.ContentFilter.bActive) and
(aOutPo.ContentFilter.sPatterns = OutPo_.ContentFilter.sPatterns) and
(aOutPo.ContentFilter.nHitLimit = OutPo_.ContentFilter.nHitLimit) and
( IsOutlookABMonitorHook or
( (aOutAB.Kind = OutAB_.Kind) and
(aOutAB.ContentFilter.bActive = OutAB_.ContentFilter.bActive) and
(aOutAB.ContentFilter.sPatterns = OutAB_.ContentFilter.sPatterns) and
(aOutAB.ContentFilter.nHitLimit = OutAB_.ContentFilter.nHitLimit) and
(aOutAB.bPopup = OutAB_.bPopup) and
(bAbAllowPopup = bAbAllowPopup_) and
(aOutAB.bCollectTxt = OutAB_.bCollectTxt) and
(aOutAB.bCollectFile = OutAB_.bCollectFile) )
)
then exit;
end;
OutPo_ := aOutPo;
OutAB_ := aOutAB;
bAllowPopup_ := bAllowPopup;
bAbAllowPopup_ := bAbAllowPopup;
if Connected then
begin
ZeroMemory(@MailPo, SizeOf(MailPo));
MailPo.bCollectSendMail := true;
MailPo.sRunDir := GetRunExePathDir;
MailPo.sCttSchPtrnPath := MailPo.sRunDir + DAT_PTNSCH;
if OutPo_.ContentFilter.bActive and (OutPo_.ContentFilter.sPatterns <> '') and
(OutPo_.bSchSubject or OutPo_.bSchBody) then
begin
MailPo.bMailCttSch := true;
MailPo.sPatterns := gMgSvc.MgRule.GetRuleSearchStrFromIdsN(OutPo_.ContentFilter.sPatterns);
MailPo.nHitLimit := OutPo_.ContentFilter.nHitLimit;
if OutPo_.bUseMasking then
MailPo.MailCttSchProc := mcspMask
else if OutPo_.bBlock then
MailPo.MailCttSchProc := mcspClear;
end else
if OutPo_.bBlock then
MailPo.MailCttSchProc := mcspClear;
if not IsOutlookABMonitorHook and (aOutAB.Kind <> abkNone) then
begin
MailPo.AttachAB := aOutAB;
MailPo.AttachAB.ContentFilter.sPatterns := gMgSvc.MgRule.GetRuleSearchStrFromIdsN(aOutAB.ContentFilter.sPatterns);
MailPo.sCollectAttachPath := 'C:\ProgramData\HE\ETask\';
DeleteDir(MailPo.sCollectAttachPath);
end;
if OutPo_.bSchSubject and OutPo_.bSchBody then
MailPo.MailCttSchPos := mcsoBoth
else if OutPo_.bSchSubject then
MailPo.MailCttSchPos := mcsoSubject
else if OutPo_.bSchBody then
MailPo.MailCttSchPos := mcsoBody
else
MailPo.bMailCttSch := false;
Send := TTgPacket.Create(OAI_MAILSECU_POLICY);
Send.O['PO'] := TTgJson.ValueToJsonObject<TOutlookAddInPo>(MailPo);
Send.I['RcvHwnd'] := gMgSvc.RcvHwnd;
SendPacket(Send);
end;
end;
function TOutlookMonClient.GetConnected: Boolean;
procedure TryConnection;
var
hFind, hIpc: HWND;
sSid: String;
begin
// hIpc := StrToInt64Def(GetRegValueAsString(HKEY_CURRENT_USER, 'Software\BS1Addin', 'Outlook'), 0);
sSid := GetRegRecentUserSid;
if sSid <> '' then
begin
hIpc := StrToInt64Def(GetRegValueAsString(HKEY_USERS, sSid + '\Software\BS1Addin', 'Outlook'), 0);
if hIpc <> 0 then
ConnectWnd(hIpc);
end;
end;
var
sParam: String;
begin
Result := Inherited;
if not Result and not bTerminated_ and (W2W_ <> nil) then
begin
if (GetTickCount - dwExecuteTick_) > 10000 then // 최소 10초에 한번만 실행 되도록함
begin
dwExecuteTick_ := GetTickCount;
TryConnection;
end;
end;
end;
procedure TOutlookMonClient.ConnectedEvent;
begin
try
Inherited;
SetSendPauseState(false);
_Trace('Connected.');
with gMgSvc.ModePolicy do
UpdateOutPo(OutBodyAllowPopup, OutFileAllowPopup, OutPo, OutlookAB, true); // 이거 여기에 안두면 재실행 시 정책을 못받아감 25_0808 10:27:14 kku
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. ConnectedEvent()');
end;
end;
procedure TOutlookMonClient.DisconnectedEvent;
var
sSid: String;
begin
try
Inherited;
QSendPacket_.Clear;
ZeroMemory(@OutPo_, SizeOf(OutPo_));
// sSid := GetRegRecentUserSid;
// if sSid <> '' then
// DelRegValue(HKEY_USERS, sSid + '\Software\BS1Addin', 'Outlook');
_Trace('Disconnected');
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. DisconnectedEvent()');
end;
end;
procedure TOutlookMonClient.ProcessRcvPacket(aRcv: IRcvPacket);
// function GetVioInfo: ISuperObject;
// var
// EntList, InfoList: TStringList;
// i: Integer;
// begin
// Result := nil;
// try
// if gMgSvc.RecentFoundCode <> '' then
// begin
// Guard(EntList, TStringList.Create);
// SplitString(gMgSvc.RecentFoundCode, RESULT_SEPARATOR, EntList);
// gMgSvc.RecentFoundCode := '';
//
// Result := TSuperObject.Create(stArray);
// var O: ISuperObject;
// Guard(InfoList, TStringList.Create);
// for i := 0 to EntList.Count - 1 do
// begin
// SplitString(EntList[i], '|', InfoList);
// if InfoList.Count > 2 then
// begin
// O := SO;
// O.S['RULE_ID'] := InfoList[0];
// O.S['TEXT'] := RemoveOverlapWords(InfoList[1]);
// O.S['CNT'] := InfoList[2];
// Result.AsArray.Add(O);
// end;
// end;
// end;
// except
// // ..
// end;
// end;
var
sMsg, sCC, sFounds, sCode: String;
bPrevent: Boolean;
PO: TPrefModel;
procedure ProcessMailMessage;
begin
if not PO.OutPo.bActive then
exit;
sMsg := '';
if aRcv.S['Founds'] <> '' then
SumString(sMsg, 'Found : ' + aRcv.S['Founds'], ', ');
// SumString(sMsg, 'To : ' + aRcv.S['To'], ', ');
// if aRcv.S['CC'] <> '' then
// SumString(sMsg, 'CC : ' + aRcv.S['CC'], ', ');
SumString(sMsg, 'Subject : ' + aRcv.S['Subject'], ', ');
bPrevent := true;
if aRcv.B['Block'] then
sCode := PREVENT_OUTLOOK_BODY
else if aRcv.B['Mask'] then
sCode := PREVENT_OUTLOOK_MASKING
else begin
sCode := MONITOR_OUTLOOK_BODY;
bPrevent := false;
end;
if gMgSvc.IsNewApi then
begin
var LogInfo: TLogInfo;
ZeroMemory(@LogInfo, SizeOf(LogInfo));
LogInfo.sCode := sCode;
LogInfo.sDevName := 'OUTLOOK.EXE';
LogInfo.sSummary := sMsg;
if PO.OutPo.bCollect and
(not PO.OutPo.ContentFilter.bActive or (aRcv.S['Founds'] <> '')) then
begin
LogInfo.sSubject := aRcv.S['Subject'];
LogInfo.sBody := aRcv.S['Body'];
LogInfo.sSender := aRcv.S['Sender'];
LogInfo.sRecipient := aRcv.S['Rcvs'];
if aRcv.S['FoundsEx'] <> '' then
begin
try
var EntList: TStringList;
var InfoList: TStringList;
Guard(EntList, TStringList.Create);
SplitString(aRcv.S['FoundsEx'], RESULT_SEPARATOR, EntList);
var i: Integer := 0;
var OA := TSuperObject.Create(stArray);
var O: ISuperObject;
var pEnt: PRuleEnt;
Guard(InfoList, TStringList.Create);
for i := 0 to EntList.Count - 1 do
begin
SplitString(EntList[i], '|', InfoList);
if InfoList.Count > 2 then
begin
O := SO;
pEnt := gMgSvc.MgRule.GetRuleFromRName(InfoList[0]);
if pEnt <> nil then
begin
O.S['RULE_ID'] := pEnt.sId;
O.S['TEXT'] := RemoveOverlapWords(InfoList[1]);
O.S['CNT'] := InfoList[2];
OA.AsArray.Add(O);
end;
end;
end;
if OA.AsArray.Length > 0 then
LogInfo.OVio := OA;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. ExtractFoundEx..');
end;
end;
// LogInfo.sRecipient := aRcv.S['To'];
// if aRcv.S['CC'] <> '' then
// SumString(LogInfo.sRecipient, 'CC:' + aRcv.S['CC'] + ';');
// if aRcv.S['BCC'] <> '' then
// SumString(LogInfo.sRecipient, 'BCC:' + aRcv.S['BCC'] + ';');
gMgSvc.SendEventLogEx(@LogInfo, bPrevent);
if IsDivPopup then
begin
if (bPrevent and OutPo_.bPopup) or
(not bPrevent and bAllowPopup_) then
begin
sMsg := sCode + '|' + aRcv.S['Founds'] + '|' +
aRcv.S['To'] + '|' + aRcv.S['CC'] + '|' + aRcv.S['Subject'];
gMgSvc.PopupMessage(TYPE_MSG_OUTLOOK_CATCH, sMsg);
end;
end else begin
if OutPo_.bPopup then
begin
sMsg := sCode + '|' + aRcv.S['Founds'] + '|' +
aRcv.S['To'] + '|' + aRcv.S['CC'] + '|' + aRcv.S['Subject'];
gMgSvc.PopupMessage(TYPE_MSG_OUTLOOK_CATCH, sMsg);
end;
end;
end else begin
// 컨텐츠 필터 조건이 있고, 검출된거 없다면 무시 24_0516 10:11:32 kku
if OutPo_.ContentFilter.bActive and (OutPo_.ContentFilter.sPatterns <> '') and
(OutPo_.bSchSubject or OutPo_.bSchBody) and (aRcv.S['Founds'] = '') then
exit;
if PO.OutPo.bActive then
begin
// 원문 수집 아닌 일반 발송 수집
gMgSvc.SendEventLogEx(@LogInfo, bPrevent);
if OutPo_.bPopup then
begin
sMsg := sCode + '|' + aRcv.S['Founds'] + '|' +
aRcv.S['To'] + '|' + aRcv.S['CC'] + '|' + aRcv.S['Subject'];
gMgSvc.PopupMessage(TYPE_MSG_OUTLOOK_CATCH, sMsg);
end;
end;
end;
end else
gMgSvc.SendEventLog(URI_USER_ACTION, sCode, sMsg, bPrevent);
end;
procedure ProcessMailAttach;
var
i: Integer;
LogInfo: TLogInfo;
sOutDir, sPath: String;
O, ONoti: ISuperObject;
procedure DeleteAttFile(sPath: String);
begin
if bPrevent then
begin
// 차단 예외 결재 요청 시 필요할 수 있으므로 5분 후 삭제
gMgSvc.ThdReact.AddEnt(crtDelete, sPath, 300);
end else
DeleteFile(PChar(sPath));
end;
begin
if aRcv.O['AFile'] = nil then
exit;
if PO.OutlookAB.Kind = abkNone then
exit;
// aRcv.O['AFile'].S['AttList'] // 첨부파일 목록
sOutDir := aRcv.O['AFile'].S['AttatchDir'];
if PO.OutlookAB.ContentFilter.bActive and
(PO.OutlookAB.ContentFilter.sPatterns <> '') then
begin
if (aRcv.O['AFile'].O['AttFounds'] = nil) or
(aRcv.O['AFile'].O['AttFounds'].DataType <> stArray) or
(aRcv.O['AFile'].A['AttFounds'].Length = 0) then exit;
for i := 0 to aRcv.O['AFile'].A['AttFounds'].Length - 1 do
begin
O := aRcv.O['AFile'].A['AttFounds'].O[i];
sMsg := Format('Process : Outlook.exe, Path : %s', [O.S['FName']]);
Finalize(LogInfo);
ZeroMemory(@LogInfo, SizeOf(LogInfo));
LogInfo.sSummary := sMsg;
LogInfo.sAppName := 'Outlook.exe';
LogInfo.sPath := O.S['FName'];
LogInfo.sSubject := aRcv.S['Subject'];
// LogInfo.sBody := aRcv.S['Body'];
LogInfo.sSender := aRcv.S['Sender'];
LogInfo.sRecipient := aRcv.S['Rcvs'];
if PO.OutlookAB.bCollectTxt then
begin
LogInfo.sBody := O.S['Data'];
end;
if O.S['FoundEx'] <> '' then
begin
try
var EntList: TStringList;
var InfoList: TStringList;
Guard(EntList, TStringList.Create);
SplitString(O.S['FoundEx'], RESULT_SEPARATOR, EntList);
var v: Integer := 0;
var OA := TSuperObject.Create(stArray);
var OEnt: ISuperObject;
var pEnt: PRuleEnt;
Guard(InfoList, TStringList.Create);
for v := 0 to EntList.Count - 1 do
begin
SplitString(EntList[v], '|', InfoList);
if InfoList.Count > 2 then
begin
OEnt := SO;
pEnt := gMgSvc.MgRule.GetRuleFromRName(InfoList[0]);
if pEnt <> nil then
begin
OEnt.S['RULE_ID'] := pEnt.sId;
OEnt.S['TEXT'] := RemoveOverlapWords(InfoList[1]);
OEnt.S['CNT'] := InfoList[2];
OA.AsArray.Add(OEnt);
end;
end;
end;
if OA.AsArray.Length > 0 then
LogInfo.OVio := OA;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. ExtractFoundEx..');
end;
end;
bPrevent := O.B['Block'];
if sOutDir <> '' then
begin
sPath := sOutDir + O.S['FName'];
if FileExists(sPath) then
begin
if PO.OutlookAB.bCollectFile and
(GetFileSize_path(sPath) <= (LONGLONG(PO.OutABLimitMB) * 1048576)) then
begin
LogInfo.sFileCompId := gMgSvc.MakeComponentId(sPath);
// 차단된거면 결재 요청 시 업로드 할 수 있으므로 삭제 딜레이 필요
var nDelay: Integer := 0;
if bPrevent then
nDelay := 300;
gMgSvc.SendFile(LogInfo, 'quarantineLogCollect.do', sPath, PO.OutABMinMB, PO.OutABLimitMB, crtDelete, nDelay);
end else
DeleteAttFile(sPath);
end else continue;
end;
ONoti := SO;
ONoti.S['PName'] := 'Outlook.exe';
ONoti.S['Path'] := O.S['FName'];
ONoti.S['Hash'] := O.S['Hash'];
if bPrevent then
begin
sCode := PREVENT_OUTLOOK_ATTACH;
ONoti.S['OutDir'] := sOutDir;
end else begin
var sFName: String := O.S['FName'];
var sExt: String := GetFileExt(sFName).ToUpper;
// 서명등 본문에 들어가는 이미지를 식별해서 예외 25_1203 10:49:17 kku
if sFName.ToLower.StartsWith('image0') and
( (sExt = 'PNG') or (sExt = 'JPG') or (sExt = 'JPEG') or (sExt = 'BMP')) then
continue;
sCode := MONITOR_OUTLOOK_ATTACH;
ONoti.B['M'] := true;
end;
LogInfo.sCode := sCode;
gMgSvc.SendEventLogEx(@LogInfo, bPrevent);
if IsDivPopup then
begin
if (bPrevent and PO.OutlookAB.bPopup) or
(not bPrevent and PO.OutFileAllowPopup) then
gMgSvc.PopupMessage(TYPE_MSG_PREVENT_ATTACHFILE, ONoti.AsJSon);
end else begin
if PO.OutlookAB.bPopup then
gMgSvc.PopupMessage(TYPE_MSG_PREVENT_ATTACHFILE, ONoti.AsJSon);
end;
end;
end else begin
// 조건 없는 차단, 로그
if (aRcv.O['AFile'].O['AttFounds'] = nil) or
(aRcv.O['AFile'].O['AttFounds'].DataType <> stArray) or
(aRcv.O['AFile'].A['AttFounds'].Length = 0) then exit;
for i := 0 to aRcv.O['AFile'].A['AttFounds'].Length - 1 do
begin
O := aRcv.O['AFile'].A['AttFounds'].O[i];
sMsg := Format('Process : Outlook.exe, Path : %s', [O.S['FName']]);
Finalize(LogInfo);
ZeroMemory(@LogInfo, SizeOf(LogInfo));
LogInfo.sSummary := sMsg;
LogInfo.sAppName := 'Outlook.exe';
LogInfo.sPath := O.S['FName'];
LogInfo.sSubject := aRcv.S['Subject'];
// LogInfo.sBody := aRcv.S['Body'];
LogInfo.sSender := aRcv.S['Sender'];
LogInfo.sRecipient := aRcv.S['Rcvs'];
if PO.OutlookAB.bCollectTxt then
begin
LogInfo.sBody := O.S['Data'];
end;
bPrevent := O.B['Block'];
if sOutDir <> '' then
begin
sPath := sOutDir + O.S['FName'];
if FileExists(sPath) then
begin
if PO.OutlookAB.bCollectFile and
(GetFileSize_path(sPath) <= (LONGLONG(PO.OutABLimitMB) * 1048576)) then
begin
LogInfo.sFileCompId := gMgSvc.MakeComponentId(sPath);
// 차단된거면 결재 요청 시 업로드 할 수 있으므로 삭제 딜레이 필요
var nDelay: Integer := 0;
if bPrevent then
nDelay := 300;
gMgSvc.SendFile(LogInfo, 'quarantineLogCollect.do', sPath, PO.OutABMinMB, PO.OutABLimitMB, crtDelete, nDelay);
end else
DeleteAttFile(sPath);
end else continue;
end;
ONoti := SO;
ONoti.S['PName'] := 'Outlook.exe';
ONoti.S['Path'] := O.S['FName'];
ONoti.S['Hash'] := O.S['Hash'];
if bPrevent then
begin
sCode := PREVENT_OUTLOOK_ATTACH;
ONoti.S['OutDir'] := sOutDir;
end else begin
var sFName: String := O.S['FName'];
var sExt: String := GetFileExt(sFName).ToUpper;
// 서명등 본문에 들어가는 이미지를 식별해서 예외 25_1203 10:49:17 kku
if sFName.ToLower.StartsWith('image0') and
( (sExt = 'PNG') or (sExt = 'JPG') or (sExt = 'JPEG') or (sExt = 'BMP')) then
continue;
sCode := MONITOR_OUTLOOK_ATTACH;
ONoti.B['M'] := true;
end;
LogInfo.sCode := sCode;
gMgSvc.SendEventLogEx(@LogInfo, bPrevent);
if IsDivPopup then
begin
if (bPrevent and PO.OutlookAB.bPopup) or
(not bPrevent and PO.OutFileAllowPopup) then
gMgSvc.PopupMessage(TYPE_MSG_PREVENT_ATTACHFILE, ONoti.AsJSon);
end else begin
if PO.OutlookAB.bPopup then
gMgSvc.PopupMessage(TYPE_MSG_PREVENT_ATTACHFILE, ONoti.AsJSon);
end;
end;
end;
end;
begin
try
case aRcv.Command of
0 : ;
OAI_MAILITEM_RCV_DATA,
OAI_MAILITEM_SEND_DATA :
begin
PO := gMgSvc.ModePolicy;
ProcessMailMessage;
ProcessMailAttach;
end;
else Inherited;
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. ProcessRcvPacket()');
end;
end;
end.