BSOne.SFC/eCrmHE/EXE_eCrmHomeEdition/Thread/ThdPrintWork.pas

2452 lines
91 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{*******************************************************}
{ }
{ ThdPrintWork }
{ }
{ Copyright (C) 2025 kku }
{ }
{*******************************************************}
unit ThdPrintWork;
interface
uses
Tocsg.Thread, Tocsg.Printer, System.SysUtils, System.Classes,
System.Generics.Collections, Winapi.Windows, Tocsg.Fasoo;
type
PPrtHookJobEnt = ^TPrtHookJobEnt;
TPrtHookJobEnt = record
dtReg: TDateTime;
sPName,
sPrtName,
sDocName,
sPrtDocId: String;
ullPid: ULONGLONG;
end;
TPrtHookJobEntList = TList<PPrtHookJobEnt>;
TThdPrintWork = class(TTgThread)
protected
JobList_: TList<TPrtJobInfo>;
fas_: TTgFasoo;
bIsWorking_: Boolean;
CurJob_: TPrtJobInfo;
PrintAppList_,
IgrPrtSpoolAnal_: TStringList;
bBackupSpl_: Boolean;
sWorkDir_: String;
PrtHookJobEntList_: TPrtHookJobEntList;
// 후킹방식으로 프린트 워터마크 출력 시 예외 되었는지 체크
bPrtWaterExp_: Boolean;
procedure OnPrtHe(Sender: TObject; const Item: PPrtHookJobEnt; Action: TCollectionNotification);
procedure Execute; override;
procedure ProcessPrintJob(aJob: TPrtJobInfo);
procedure AddIgrPrtSpool(sPrtName: String);
function IsSpoolAnal(sPrtName: String): Boolean;
function GetHookJob(sPrtName, sDocName: String; bFindDel: Boolean = false): PPrtHookJobEnt;
procedure RefineHookJob;
public
WordPrtPage: Integer;
Constructor Crate;
Destructor Destroy; override;
procedure AddJob(aJob: TPrtJobInfo);
function HasJob(aJob: TPrtJobInfo): Boolean;
procedure AddHookJob(sPName, sPrtName, sDocName, sPrtDocId: String; ullPid: ULONGLONG);
property IsWorking: Boolean read bIsWorking_;
property PrtWaterExp: Boolean write bPrtWaterExp_;
end;
implementation
uses
Tocsg.Exception, Tocsg.Files, Tocsg.Path, Winapi.ActiveX, Tocsg.Safe,
Tocsg.Strings, Tocsg.Shell, Tocsg.Trace, superobject, ManagerService,
GlobalDefine, IdHTTP, IdSSLOpenSSL, IdMultipartFormData, Tocsg.DateTime,
System.IniFiles, ManagerModel, ManagerPattern, Tocsg.Convert, Condition,
Winapi.WinSpool, ManagerPrint, Tocsg.Process, ThdWebUrl, Tocsg.WndUtil,
Tocsg.Registry, CrmUtil, ProcessSoftcampDRM, Tocsg.Encrypt, System.DateUtils,
Tocsg.DRM.Encrypt, Tocsg.PCRE, ManagerRule, CttSchDefine, ThdExecuteEndNoti,
ManagerCampaign, Tocsg.Valid, AppCtrlDefine, Vcl.Printers, Vcl.Graphics,
Vcl.Imaging.pngimage, DefineHelper, ManagerHook, Tocsg.FileInfo;
{ TThdPrintWork }
Constructor TThdPrintWork.Crate;
var
sPath: String;
ini: TIniFile;
begin
Inherited Create;
bPrtWaterExp_ := false;
JobList_ := TList<TPrtJobInfo>.Create;
bIsWorking_ := false;
bBackupSpl_ := false;
WordPrtPage := 0;
sWorkDir_ := 'C:\ProgramData\HE\';
PrtHookJobEntList_ := TPrtHookJobEntList.Create;;
PrintAppList_ := TStringList.Create;
PrintAppList_.CaseSensitive := false;
sPath := GetProgramFilesDir + DIR_TG + INI_FORCEHE;
var sPrtApps: String;
case CUSTOMER_TYPE of
CUSTOMER_GEC,
CUSTOMER_HDENG : sPrtApps := RPINT_SUPPORT_APPS_HEC;
else sPrtApps := PRINT_SUPPORT_APPS;
end;
if FileExists(sPath) then
begin
try
Guard(ini, TIniFile.Create(sPath));
sPrtApps := sPrtApps + '|' + ini.ReadString('Force', 'PrtWaterApp', '');
bBackupSpl_ := ini.ReadBool('Force', 'BackupSpool', false);
except
// ..
end;
end;
SplitString(sPrtApps, '|', PrintAppList_, false, true);
RefinePrintHookAppList(PrintAppList_);
IgrPrtSpoolAnal_ := TStringList.Create;
IgrPrtSpoolAnal_.CaseSensitive := false;
sPath := GetRunExePathDir + INI_HE;
if FileExists(sPath) then
begin
try
Guard(ini, TIniFile.Create(sPath));
SplitString(ini.ReadString('Print', 'IgrSpool', ''), '|', IgrPrtSpoolAnal_);
except
// ..
end;
end;
end;
Destructor TThdPrintWork.Destroy;
begin
if fas_ <> nil then
FreeAndNil(fas_);
FreeAndNil(IgrPrtSpoolAnal_);
FreeAndNil(PrintAppList_);
FreeAndNil(JobList_);
PrtHookJobEntList_.OnNotify := OnPrtHe;
FreeAndNil(PrtHookJobEntList_);
Inherited;
end;
procedure TThdPrintWork.OnPrtHe(Sender: TObject; const Item: PPrtHookJobEnt; Action: TCollectionNotification);
begin
if Action = cnRemoved then
Dispose(Item);
end;
procedure TThdPrintWork.AddJob(aJob: TPrtJobInfo);
begin
Lock;
try
JobList_.Add(aJob);
finally
Unlock;
end;
end;
function TThdPrintWork.HasJob(aJob: TPrtJobInfo): Boolean;
begin
Result := false;
try
Lock;
try
if CurJob_ = aJob then
Exit(true);
if JobList_.IndexOf(aJob) <> -1 then
Exit(true);
finally
Unlock;
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. HasJob()');
end;
end;
procedure TThdPrintWork.AddHookJob(sPName, sPrtName, sDocName, sPrtDocId: String; ullPid: ULONGLONG);
var
pEnt: PPrtHookJobEnt;
begin
try
New(pEnt);
ZeroMemory(pEnt, SizeOf(TPrtHookJobEnt));
pEnt.dtReg := Now;
pEnt.sPName := sPName;
pEnt.sPrtName := sPrtName;
pEnt.sDocName := sDocName;
pEnt.sPrtDocId := sPrtDocId;
pEnt.ullPid := ullPid;
Lock;
try
PrtHookJobEntList_.Add(pEnt);
finally
Unlock;
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. AddHookJob()');
end;
end;
function TThdPrintWork.GetHookJob(sPrtName, sDocName: String; bFindDel: Boolean = false): PPrtHookJobEnt;
var
i: Integer;
pEnt: PPrtHookJobEnt;
begin
Result := nil;
try
Lock;
try
for i := 0 to PrtHookJobEntList_.Count - 1 do
begin
pEnt := PrtHookJobEntList_[i];
if (CompareText(sPrtName, pEnt.sPrtName) = 0) and
(CompareText(sDocName, pEnt.sDocName) = 0) then
begin
Result := pEnt;
if bFindDel then
PrtHookJobEntList_.Delete(i);
exit;
end;
end;
finally
Unlock;
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. GetHookJob()');
end;
end;
procedure TThdPrintWork.RefineHookJob;
var
i: Integer;
dtNow: TDateTime;
begin
try
dtNow := Now;
Lock;
try
for i := PrtHookJobEntList_.Count - 1 downto 0 do
begin
if MinutesBetween(PrtHookJobEntList_[i].dtReg, dtNow) > 60 then
PrtHookJobEntList_.Delete(i);
end;
finally
Unlock;
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. RefineHookJob()');
end;
end;
procedure TThdPrintWork.AddIgrPrtSpool(sPrtName: String);
begin
try
if IgrPrtSpoolAnal_.IndexOf(sPrtName) = -1 then
begin
IgrPrtSpoolAnal_.Add(sPrtName);
var i: Integer;
var sIgrPrts: String := '';
for i := 0 to IgrPrtSpoolAnal_.Count - 1 do
SumString(sIgrPrts, IgrPrtSpoolAnal_[i], '|');
var sPath: String := GetRunExePathDir + INI_HE;
var ini: TIniFile;
Guard(ini, TIniFile.Create(sPath));
ini.WriteString('Print', 'IgrSpool', sIgrPrts);
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. AddIgrPrtSpool()');
end;
end;
function TThdPrintWork.IsSpoolAnal(sPrtName: String): Boolean;
begin
Result := not IsNoPrintSpoolWater;
exit;
if Result then
Result := IgrPrtSpoolAnal_.IndexOf(sPrtName) = -1;
end;
procedure TThdPrintWork.ProcessPrintJob(aJob: TPrtJobInfo);
var
PO, PPO: TPrefModel;
PP: TPrintPolicy;
pPhJob: PPrtHookJobEnt;
sPrtName, sDocName,
sPort, sPrtDocId,
sChk, sData,
sPName, sPPath,
sDocPath, sPrtIp,
sExtrOcrTxt, sThumbIds,
sExtrOcr180Txt,
sExtrTxt, sSpoolPath,
sEmfDir, sBlockReson: String;
ini: TIniFile;
ExcpList: TStringList;
i, nTotalPages: Integer;
bBlock, bIsDrm, bCfActive,
bWaterMark, bIgrApproval, bApprovalPost: Boolean;
PrtInfo: TPrtJobDevInfo;
OVio, OVioTemp, OEnt: ISuperObject;
PatternEntList: TPatternEntList;
bIsHook, bIsSplAnal, bSpoolEnd: Boolean;
procedure SetProcessName(sName: String);
begin
if sPName <> sName then
begin
sPName := sName;
bIsHook := IsPrintWaterHook;
if bIsHook and not IsHD then
begin
if PrintAppList_.IndexOf(sPName) = -1 then
bIsHook := false;
end;
bIsSplAnal := not bIsHook;
if bIsSplAnal then
bIsSplAnal := IsSpoolAnal(sPrtName);
end;
end;
function FindPrintingFile(sDocName: String; bIgrRB: Boolean = false): String;
var
sDir, sChk, sUserDir: String;
DList: TStringList;
FList: TModFileList;
i, n: Integer;
// Plf: TParserLinkFile;
begin
Result := '';
sDir := GetWindowsDir;
if sDir = '' then
exit;
CoInitialize(nil);
try
Guard(DList, TStringList.Create);
Guard(FList, TModFileList.Create(TModeFileComparer.Create));
sUserDir := UpperCase(sDir[1]) + ':\Users\';
ExtrDirFromDir(sUserDir, DList);
for i := 0 to DList.Count - 1 do
begin
sDir := sUserDir + DList[i] + '\AppData\Roaming\Microsoft\Windows\Recent\';
ExtrModFilesFromDir(sDir, FList);
end;
if FList.Count > 0 then
begin
FList.Sort;
if bIgrRB then
sDocName := StrsReplace(sDocName, ['(', ')', '[', ']'], '0');
for i := 0 to FList.Count - 1 do
begin
if bIgrRB then
sChk := StrsReplace(CutFileExt(FList[i].sFName), ['(', ')', '[', ']'], '0')
else
sChk := CutFileExt(FList[i].sFName);
// 최근문서 파일처리 보완 24_0722 14:54:30 kku
if (sChk.Length > 0) and (sChk[sChk.Length] = ')') then
begin
n := LastIndexOf(' (', sChk);
if n > 0 then
Delete(sChk, n, sChk.Length - n + 1);
end;
// TTgTrace.T('Check RecentFileName = "%s"', [sChk], 3);
if Pos(sChk, sDocName) > 0 then
begin
Result := GetTargetExeFromLink(FList[i].sDir + FList[i].sFName);
if Result = '' then
continue;
// 간혹 사용자 폴더가 이상하게(?) 표시되는 경우가 있다.. 보정 24_0514 10:09:24 kku
if (Result <> '') and (Pos('windows\system32\config\systemprofile', Result.ToLower) > 0) then
begin
TTgTrace.T('Replace path = "%s"', [Result], 3);
sDir := StringReplace(FList[i].sDir, '\AppData\Roaming\Microsoft\Windows\Recent\', '', [rfReplaceAll, rfIgnoreCase]);
sDir := ExtractFileName(sDir);
Result := StringReplace(Result, 'windows\system32\config\systemprofile',
Format('Users\%s', [sDir]), [rfReplaceAll, rfIgnoreCase]);
TTgTrace.T('Replace Path done = "%s"', [Result], 3);
end;
TTgTrace.T('Found Path = "%s", CheckName = "%s", RecentName = "%s"', [Result, sChk, FList[i].sFName], 3);
if DirectoryExists(Result) or not FileExists(Result) then
continue;
// Guard(Plf, TParserLinkFile.Create);
// if Plf.LoadFromFile(sDir + FList[i].sName) then
// begin
// Result := GetLfiValueFromCaption(Plf.LfiEntList, 'Base Path');
// if not FileExists(Result) then
// Result := '';
// end;
exit;
end;
end;
// PDF의 경우... 문서 이름에 파일 이름이 안들어 가는 경우가 있다...
// PDF 파일 최근파일 내역을 찾을 수 없다면 맨 첫번째 있는 PDF 파일을 넘겨준다.. 24_0418 15:10:27 kku
if Pos('.PDF', sDocName.ToUpper) > 0 then
begin
for i := 0 to FList.Count - 1 do
begin
sChk := CutFileExt(FList[i].sFName);
if GetFileExt(sChk).ToUpper = 'PDF' then
begin
Result := sChk;
exit;
end;
end;
end;
end;
finally
CoUninitialize;
end;
end;
procedure SendReqPrint(sExtrTxt, sFName, sPName: String);
var
O: ISuperObject;
sMsg: String;
begin
sExtrTxt := StringReplace(sExtrTxt, #13#10, ' ', [rfReplaceAll]);
if nTotalPages = 0 then
nTotalPages := 1;
sMsg := Format('PRINTER:%s|TEXT:%s|FILENAME:%s|TYPE:6|EVTTIME:%s|PAGE:%d|APP:%s|DESC:None|SCANRESULT:-1',
[sPrtName, sExtrTxt, sFName, FormatDateTime('yyyy-mm-dd hh:nn:ss', Now), nTotalPages, sPName]);
// DirectSendEventLog(URI_USER_ACTION, REQUEST_PRINT_EXCEPT, sMsg);
gMgSvc.SendEventLog(URI_USER_ACTION, REQUEST_PRINT_EXCEPT, sMsg);
end;
function DirectSendPtrFile(sPath: String): Boolean;
var
HTTP: TIdHTTP;
SSL: TIdSSLIOHandlerSocketOpenSSL;
ss: TStringStream;
sBoundary,
sDestFileUrl: String;
Params: TIdMultiPartFormDataStream;
msResp: TMemoryStream;
begin
Result := false;
if not FileExists(sPath) then
exit;
try
sDestFileUrl := gMgSvc.DestServerUrl;
sDestFileUrl := StringReplace(gMgSvc.DestServerUrl, 'agentLogRequest.do', 'agentPrintExcept.do', [rfReplaceAll]);
sDestFileUrl := StringReplace(sDestFileUrl, 'agentLogRequests.do', 'agentPrintExcept.do', [rfReplaceAll]);
sBoundary := Format('%X', [GetLocalIncUtcMin * 6000]);
Guard(SSL, TIdSSLIOHandlerSocketOpenSSL.Create(nil));
SSL.SSLOptions.Method := sslvSSLv23;
SSL.SSLOptions.SSLVersions := [sslvTLSv1_2, sslvTLSv1_1, sslvTLSv1];
Guard(HTTP, TIdHTTP.Create(nil));
HTTP.IOHandler := SSL;
with HTTP do
begin
HandleRedirects := true;
Request.Clear;
Request.UserAgent := 'Mozilla/5.0';
Request.ContentType := 'application/xml';
Request.AcceptCharSet := 'UTF-8';
// Request.Connection := 'Keep-Alive';
// Request.CustomHeaders.Values['Keep-Alive'] := 'timeout=300, max=100';
Request.Connection := 'close';
HTTPOptions := HTTPOptions - [hoKeepOrigProtocol];
HTTPOptions := HTTP.HTTPOptions + [hoForceEncodeParams];
ConnectTimeout := 2000;
ReadTimeout := 2000;
Request.ContentType := Format('multipart/form-data; boundary=%s; charset=utf-8', [sBoundary]);
end;
Guard(msResp, TMemoryStream.Create);
Guard(Params, TIdMultiPartFormDataStream.Create);
with Params.AddFile(ExtractFileName(sPath),
sPath, Format('application/%s', [GetFileExt(sPath)])) do
begin
ContentTransfer := '';
HeaderEncoding := '8'; //8bit
HeaderCharSet := 'utf-8';
Charset := 'utf-8';
end;
Params.Position := 0;
HTTP.Post(sDestFileUrl, Params, msResp);
Result := HTTP.ResponseCode = 200;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. DirectSendPtrFile()');
end;
end;
function GetSpoolCopyPath(sSubDirName: String = 'Task'): String;
var
sHookPName,
sRecentSool,
sSpoolWorkDir: String;
i: Integer;
begin
Result := '';
try
// if CUSTOMER_TYPE = CUSTOMER_CJOV_GLOBAL then
// sSpoolWorkDir := GetRunExePathDir + 'Task\' + sSubDirName + '\' +
// IncludeTrailingPathDelimiter(GetValidFileName(sPrtName, '#'))
// else
sSpoolWorkDir := sWorkDir_ + sSubDirName + '\' +
IncludeTrailingPathDelimiter(GetValidFileName(sPrtName, '#') + Format('-%d', [GetTickCount64]));
DeleteDir(sSpoolWorkDir);
if ForceDirectories(sSpoolWorkDir) then
begin
_Trace('프린터 출력 감지 .. Spool 수집 시작. Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
while (aJob <> nil) and aJob.IsSpooling2 do
begin
if i = 4000 then // 200초간 기다려준다 23_0525 08:03:11 kku
break;
Inc(i);
Sleep(50);
end;
// 스풀링 끝나고 정지가 풀리는 프린터가 있다?
// 의심되서 추가 25_0618 08:45:08 kku
aJob.PausePrtJob;
sRecentSool := GetLastSpoolPath(GetSystemDir + 'spool\PRINTERS\');
_Trace('프린터 출력 감지 .. Spool 확인. Path=%s', [sRecentSool], 2);
{$IF false}
sRecentSool := 'C:\Users\kku\Desktop\이전 바탕화면\spl2pdf_cmd\PPT그라데이션_20250911093157.spl';
if FileExists(sRecentSool) and ForceDirectories('C:\ProgramData\HE\Test_Prt\') then
begin
Result := Format('C:\ProgramData\HE\Test_Prt\%s.spl', [FormatDateTime('yyyymmddhhnnss', Now)]);
CopyFile(PChar(sRecentSool), PChar(Result), false);
end;
{$ELSE}
if FileExists(sRecentSool) then
begin
Result := sSpoolWorkDir + ExtractFileName(sRecentSool);
CopyFile(PChar(sRecentSool), PChar(Result), false);
if bBackupSpl_ and ForceDirectories('C:\ProgramData\HE\') then
CopyFile(PChar(sRecentSool), PChar(Format('C:\ProgramData\HE\%s.spl', [FormatDateTime('yyyymmddhhnnss', Now)])), false);
// Sleep(500);
// if not FileExists(Result) or (GetFileSize_path(Result) = 0) then
// begin
// // 스플 경로가 System 권한으로 접근하기 어려운 경우 현재 사용자 권한으로 다시 시도 24_1212 13:17:40 kku
// var sExe: String := GetRunExePathDir + DIR_CONF + EXE_HLP;
// if FileExists(sExe) then
// begin
// var O: ISuperObject := SO;
// O.I['Cmd'] := HPCMD_COPY_FILE;
// O.S['Src'] := sRecentSool;
// O.S['Dest'] := Result;
// SaveJsonObjToFile(O, GetRunExePathDir + DIR_CONF + DAT_PARAM);
// {$IFDEF DEBUG}
// ExecutePath(sExe);
// {$ELSE}
// ExecuteAppAsUser('explorer.exe', sExe, '', SW_SHOWNORMAL);
// {$ENDIF}
// end;
// end;
if not FileExists(Result) then
begin
Result := '';
_Trace('프린터 출력 감지 .. Spool 수집 실패. Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
end else begin
_Trace('프린터 출력 감지 .. Spool 수집 성공. Printer=%s, Doc=%s, SrcSize=%d, DestSize=%d', [sPrtName, sDocName, GetFileSize_path(sRecentSool), GetFileSize_path(Result)], 2);
end;
end else
TTgTrace.T('Not found spool file..');
{$IFEND}
// 스풀링 끝나면 정확한 정보를 위해 한번더 가져오기 시도
aJob.GetJobDevInfo(PrtInfo);
bSpoolEnd := true;
// 스풀 PJL 포맷이면 정확한 부수 정보 가져오기
if FileExists(Result) and IsPJL(Result) then
begin
if not IsPJLAndLanguagePLW(sSpoolPath) then
begin
var bCollate: Boolean;
var nCopies: Integer := GetQtyFromPJL(sSpoolPath, bCollate);
if (nCopies > 1) and (PrtInfo.dwCopyCount = 1) then
begin
_Trace('PJL 포맷에서 부수정보 다름 확인 .. PJL=%d, Job=%d', [nCopies, PrtInfo.dwCopyCount], 2);
PrtInfo.dwTotalPage := PrtInfo.dwTotalPage div nCopies;
PrtInfo.dwCopyCount := nCopies;
end;
end;
end;
if pPhJob = nil then
begin
pPhJob := GetHookJob(sPrtName, sDocName, true);
if pPhJob <> nil then
begin
bIsHook := true;
bIsSplAnal := not bIsHook;
sPPath := GetProcessPathByPid(pPhJob.ullPid);
if sPPath <> '' then
begin
sPName := ExtractFileName(sPPath);
sPPath := ExtractFilePath(sPPath);
end else
sPName := pPhJob.sPName;
sPrtDocId := pPhJob.sPrtDocId;
_Trace('프린트 Hook 프로세스 확인(ex) : %s', [sPName], 1);
end;
// else begin
// sHookPName := GetRegValueAsString(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\eCrmHomeEdition', 'PrtPName');
// if sHookPName = '' then
// sHookPName := GetRegValueAsString(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\WOW6432Node\eCrmHomeEdition', 'PrtPName');
//
// if sHookPName <> '' then
// begin
// _Trace('프린트 Hook 프로세스 확인 : %s', [sHookPName], 1);
// if sHookPName <> sPName then
// begin
// _Trace('프린트 Hook 프로세스와 인식된 프로세스가 다름. 조치 시도, Cur=%s, New=%s', [sPName, sHookPName], 1);
// SetProcessName(sHookPName);
// end;
// end else begin
// bIsHook := false;
// bIsSplAnal := not bIsHook;
// if bIsSplAnal then
// bIsSplAnal := IsSpoolAnal(sPrtName);
// _Trace('프린트 Hook 프로세스 아님 : %s', [sPName], 1);
// end;
// end;
end;
// DelRegValue(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\eCrmHomeEdition', 'PrtPName');
// DelRegValue(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\WOW6432Node\eCrmHomeEdition', 'PrtPName');
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. GetSpoolCopyPath()');
end;
end;
function GetEmfCopyDir(sSubDirName: String = 'PrtColi'): String;
var
sRecentSool,
sSpoolWorkDir: String;
i: Integer;
begin
Result := '';
try
while (aJob <> nil) and aJob.IsSpooling2 do
begin
if i = 4000 then // 200초간 기다려준다 23_0525 08:03:11 kku
break;
Inc(i);
Sleep(50);
end;
// Sleep(500);
if DirectoryExists(sWorkDir_ + 'PrtCol\') then
begin
if DirectoryExists(sWorkDir_ + sSubDirName) then
DeleteDir(sWorkDir_ + sSubDirName);
if MoveFile_wait(sWorkDir_ + 'PrtCol\', sWorkDir_ + sSubDirName, 3, true) then
Result := IncludeTrailingPathDelimiter(sWorkDir_ + sSubDirName);
end else
if DirectoryExists(sWorkDir_ + sSubDirName) then
Result := IncludeTrailingPathDelimiter(sWorkDir_ + sSubDirName);
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. GetSpoolCopyPath()');
end;
end;
function GetPrintLog(sCode: String): ISuperObject;
var
O, OSub: ISuperObject;
sCompId: String;
sMsg: String;
dt: TDateTime;
begin
Result := nil;
try
if bIsHook then
bWaterMark := (PP.PrintWater <> pwNone) and not bPrtWaterExp_;
sMsg := Format('Printer : %s, Document : %s', [sPrtName, sDocName]);
PO := gMgSvc.ModePolicy;
PPO := gMgSvc.PrefModel;
dt := Now;
O := SO;
if sCode = LOGCODE_EVENT_PRINTER then
O.S['TYP_MSG'] := '@(!)_IC_M'
else
O.S['TYP_MSG'] := '@(!)_IC_P';
O.S['KEY_AGENTID'] := gMgSvc.AgentId;
O.S['KEY_EMPNO'] := gMgSvc.EmpNo;
O.S['KEY_HOSTNAME'] := gMgSvc.UserName;
O.S['KEY_SUBMITTIME'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dt);
O.S['key_submitTime'] := FormatDateTime('yyyy-MM-dd"T"hh:nn:ss.zzz', dt) + gMgSvc.UtcOffset;
O.S['KEY_LOGCODE'] := sCode;
O.S['DETECTION_DATE'] := O.S['KEY_SUBMITTIME']; // 이벤트 발생 시각. REQUEST의 경우, 예외 신청할 이벤트의 발생 시각
O.S['detectionDate'] := O.S['key_submitTime']; // 이벤트 발생 시각. REQUEST의 경우, 예외 신청할 이벤트의 발생 시각
O.S['KEY_SUMMARY'] := sMsg;
// O.S['PARENT_LA_ID'] // 상위 이벤트
O.S['POLICY_ID'] := PO.PolicyId;
// O.S['POLICY_KEY'] // 정책 위반한 경우 (PREVENT, MONITOR), 위반한 정책의 정책 키 값, 수집인 경우 (DEPLOY), 수집 요청 ID
if PO.Print.bCollectOutput then
begin
if PPO.TextLimit > 0 then
begin
if Length(sExtrTxt) > PPO.TextLimit then
SetLength(sExtrTxt, PPO.TextLimit);
if Length(sExtrOcrTxt) > PPO.TextLimit then
SetLength(sExtrOcrTxt, PPO.TextLimit);
end;
O.S['MESSAGE_BODY'] := sExtrTxt; // 정책 위반한 경우 (PREVENT, MONITOR), 원문 (본문) 컨텐츠, 수집인 경우 (DEPLOY), 수집 결과 내용, 원문 수집 정책이 비활성화인경우 수집하지 않음
O.S['OCR_BODY'] := sExtrOcrTxt; // 이미지 OCR 추출된 컨텐츠, 원문 수집 정책이 비활성화인 경우 수집하지 않음
end;
if OVio <> nil then
O.O['RULE_VIOLATED'] := OVio; // 정책 위반 규칙과 위반 개수. (LIST)
if FileExists(sDocPath) then
begin
if PO.Print.bCollectFile then
begin
sCompId := gMgSvc.MakeComponentId(ExtractFileName(sDocPath));
// SendFile(sCompId, 'printLogCollect.do', sDocPath); // 원본 파일 수집은 이걸로 하면 안됨 24_0122 16:52:00 kku
gMgSvc.SendFileNor(false, sCompId, 'quarantineLogCollect.do', sDocPath, PO.PrtMinMB, PO.PrtMaxMB);
O.S['COMPONENT_ID'] := sCompId; // 파일 고유값. 에이전트에서 임의 생성. (이벤트간 동일 파일 판단은 서버에서 하겠습니다.)
end;
O.S['COMPONENT_FILENAME'] := ExtractFileName(sDocPath); // 파일명
O.S['COMPONENT_PATH'] := ExtractFilePath(sDocPath);// 파일 절대 경로
var dtCreate, dtModify, dtAccess: TDateTime;
GetFileDateTime_Local(sDocPath, dtCreate, dtModify, dtAccess);
var dwAttr: DWORD := GetFileAttributes(PChar(sDocPath));
var OTemp: ISuperObject := SO;
OTemp.S['FILESIZE'] := IntToStr(GetFileSize_path(sDocPath));
OTemp.S['LASTMODIFIED'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dtModify);
if dwAttr <> DWORD(-1) then
OTemp.S['READONLY'] := BooleanToStr((dwAttr and FILE_ATTRIBUTE_READONLY) <> 0, 'true', 'false');
if dwAttr <> DWORD(-1) then
OTemp.S['HIDDEN'] := BooleanToStr((dwAttr and FILE_ATTRIBUTE_HIDDEN) <> 0, 'true', 'false');
// OTemp.S['AUTHOR'] // {만든 이}
// OTemp.S['LASTUSER'] // {마지막으로 저장한 사람}
// OTemp.S['FILETYPE'] // {컨텐츠 타입}
// OTemp.S['FILECLS'] // {컨텐츠 형식}
// OTemp.S['PASSWORD'] // {암호적용여부}
// OTemp.S['ENCRYPTED'] := BooleanToStr(pInfo.bDrm, 'true', 'false');
// OTemp.S['CORRUPT'] // {깨진파일/정상파일여부}
// var i: Integer;
// var StrList: TStringList;
// Guard(StrList, TStringList.Create);
// GetFileProp_all(sDocPath, StrList, true);
// for i := 0 to StrList.Count - 1 do
// OTemp.S[StrsReplace(StrList.KeyNames[i], [' '], '_')] := StrList.ValueFromIndex[i];
O.O['COMPONENT_METADATA'] := OTemp;
O.S['COMPONENT_LASTACCESS'] := FormatDateTime('yyyy-mm-dd hh:nn:ss', dtAccess);
end;
O.S['COMPONENT_THUMBNAIL_ID'] := sThumbIds; // 이미지 미리보기 혹은 이미지가 있는경우, 이미지 파일의 파일 고유값. (출력물의 경우 첫 3장 이미지, 캡쳐 이미지 등), 복수개 허용, ";" 구분
// O.S['SOURCE_IP'] // 이벤트 발생 시점의 에이전트 IP. 단, 외부에서 수신하는 이벤트의 경우, 발송지의 IP가 되고, DESTINATION_URL이 에이전트 IP가 된다
// O.S['SOURCE_PORT'] // 송신지 사용 포트
// O.S['EMAIL_SENDER'] // 메일 발신자
// O.S['DESTINATION_URL'] // 수신지 URL (혹은 IP)
O.S['DESTINATION_PORT'] := sPrtIp; // 수신지 포트
// O.S['EMAIL_RECIPIENT'] // 메일 수신자 ; 로 구분
// O.S['EMAIL_SUBJECT'] // 메일 제목
O.S['APPLICATION_NAME'] := sPName; // 사용 APP 이름
O.S['APPLICATION_PATH'] := sPPath;// 사용 APP 절대 경로 (실행 파일 exe의 절대 경로)
O.S['PRINTER_JOBNAME'] := sDocName;
OSub := SO;
OSub.S['PAGEINFO'] := Format('%d/%d', [PrtInfo.dwTotalPage, PrtInfo.dwTotalPage]);
OSub.S['COLOR'] := BooleanToStr(PrtInfo.bColor, 'true', 'false');
OSub.S['WARTERMARK'] := BooleanToStr(bWaterMark, 'true', 'false');
OSub.S['PAPERINFO'] := PrtInfo.sPaperInfo;
O.O['PRINTER_METADATA'] := OSub; // '{"PAGEINFO": "2/2", "COLOR": "true", "WARTERMARK": "false"}';
if bWaterMark then
O.S['RESPONSE_INFO'] := 'WATERMARK';
O.S['REMOVABLE_NAME'] := sPrtName; // USB등과 같은 매체 혹은 프린터 등과 같은 외부 장치 이름
// O.S['REMOVABLE_DEVICEID'] := aExptInfo.sSerial; // USB등과 같은 매체 혹은 프린터 등과 같은 외부 장치 Device ID
// O.S['DEVICE_TYPE'] := IntToStr(Integer(aExptInfo.ReqDevType)); // 의미 없어서 제외 23_0822 12:40:55 kku
// O.S['REMOVABLE_CLASSID'] // USB등과 같은 매체 혹은 프린터 등과 같은 외부 장치 Class ID
// O.S['RESPONSE_INFO'] // 적용된 대응 정보 (DELETE, DRM, POPUP, LOG, EXCEPT 등)
// O.S['RESPONSE_RESULT'] // 적용된 대응 적용 결과 (true / false / pending / decline)
// O.S['REQUEST_COMMENT'] := sComment; // 사용자가 기입하는 요청 코멘트
// O.S['REQUEST_DUEDATE'] := sDueDate; // 사용자가 기입하는 예외 기간 (언제 까지. 최종일)
// O.S['REQUEST_TARGET'] := IntToStr(nTarget); // 사용자가 기입하는 예외 대상 (0 - 해당 pc, 1 - 해당 사용자, 2 - 해당 부서, 3-전체)
O.S['actionGroupId'] := sPrtDocId; // 문서번호
O.I['printCopyCnt'] := PrtInfo.dwCopyCount; // 부수
O.I['printPageCnt'] := PrtInfo.dwTotalPage; // 장수
Result := O;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. SendEventLog()');
end;
end;
procedure SendPrintLog(sCode: String);
var
O: ISuperObject;
begin
try
O := GetPrintLog(sCode);
if O = nil then
begin
_Trace('Fail .. SendPrintLog() .. 1', 3);
exit;
end;
_Trace('SendPrintLog > Code=%s, Msg=%s', [sCode, O.S['KEY_SUMMARY']], 3);
// JSON 포맷으로 잘 변환되서 보내도록 보완 24_0716 10:32:32 kku
gMgSvc.EventLog.Push(O.AsJSon);
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. SendEventLog()');
end;
end;
procedure CancelJob;
begin
if (aJob <> nil) and not aJob.WorkEnd then
begin
if not bSpoolEnd then
aJob.GetJobDevInfo(PrtInfo);
CurJob_ := nil;
aJob.WorkEnd := true;
// SetPrtJobFromHelperApp(aJob, JOB_CONTROL_CANCEL);
// SetPrtJobFromHelperApp(aJob, JOB_CONTROL_DELETE);
aJob.SetPrtJob(JOB_CONTROL_CANCEL);
// Sleep(1000);
// aJob.SetPrtJob(JOB_CONTROL_DELETE);
// Job을 삭제하면 aJob가 해제 되서 아래처럼 처리함 25_0522 16:33:40 kku
aJob := nil;
end;
end;
function CollectPrintInfo(sDocPath, sText, sOcrText, sVioText: String; sThumbIds: String = ''): Boolean;
var
sConv: String;
begin
Result := false;
if bIgrApproval then
exit;
if not IsApproveSupport then
exit;
// 결재자가 할당 되어 있나?
if not gMgSvc.UseApproval then
exit;
// 결재요청 정책이 있나?
if not PO.PrintApproval then
exit;
// 후킹방식은 지원하지 않음
// if IsPrintWaterHook then
// exit;
try
// 한번 멈추고 진행하도록 변경 25_1203 kku
// if PO.PrintApprovalPost then
// begin
// var PrtEnt: TPrtEnt;
// ZeroMemory(@PrtEnt, SizeOf(PrtEnt));
//// PrtEnt.dtReg := Now;
//// PrtEnt.sId := sId;
// PrtEnt.sText := sText;
// PrtEnt.sOcrText := sOcrText;
// PrtEnt.sVioText := sVioText;
// PrtEnt.sThumbIds := sThumbIds;
// PrtEnt.sPName := sPName;
// PrtEnt.sFPath := sDocPath;
//
// PrtEnt.WInfo.sPtrName := PrtInfo.sPtrName;
// PrtEnt.WInfo.sDocName := PrtInfo.sDocName;
// PrtEnt.WInfo.DevMode := TTgRtti.SetTypeToStr<TDeviceMode>(PrtInfo.DevMode);
// PrtEnt.WInfo.dwCopy := PrtInfo.dwCopyCount;
// PrtEnt.WInfo.dwTotalPage := PrtInfo.dwTotalPage;
// PrtEnt.WInfo.bPaperV := PrtInfo.bPaperV;
// PrtEnt.WInfo.bColor := PrtInfo.bColor;
// PrtEnt.WInfo.sPaperInfo := PrtInfo.sPaperInfo;
// PrtEnt.WInfo.sPrtIp := PrinterDriverToIP(PrtInfo.sDrvName);
// PrtEnt.WInfo.sPdfPath := sPort;
// PrtEnt.WInfo.bUseWM := true;
// var sUrl: String := gMgSvc.SendApproval(13, @PrtEnt);
//
// if sUrl = '' then
// begin
// TTgTrace.T('Fail .. CollectPrintInfo(PrintApprovalPost)');
// exit;
// end else begin
// var LogInfo: TLogInfo;
// ZeroMemory(@LogInfo, SizeOf(LogInfo));
// LogInfo.sCode := REQUEST_APPROVAL;
// LogInfo.sDevName := PrtEnt.WInfo.sPtrName;
// LogInfo.sPath := PrtEnt.WInfo.sDocName;
// LogInfo.sActionId := PrtEnt.WInfo.sPrtDocId;
// LogInfo.sSummary := 'Post-Approval Request : Print';
// gMgSvc.SendEventLogEx(@LogInfo, false);
//
// var sHlpExe: String := GetRunExePathDir + DIR_CONF + EXE_HLP;
// if FileExists(sHlpExe) then
// begin
// var O: ISuperObject := SO;
// O.I['RcvWnd'] := Handle;
// O.I['Cmd'] := HPCMD_EXECUTE_FILE;
// O.S['Path'] := gMgSvc.DestIPort + sUrl;
// SaveJsonObjToFile(O, GetRunExePathDir + DIR_CONF + DAT_PARAM);
// {$IFDEF DEBUG}
// ExecutePath(sHlpExe);
// {$ELSE}
// ExecuteAppAsUser('explorer.exe', sHlpExe, '', SW_SHOWNORMAL);
// {$ENDIF}
// end else
// ExecutePath(gMgSvc.DestIPort + sUrl);
//
// bBlock := false; // 차단 헤제
// bApprovalPost := true;
// end;
// end else
begin
sConv := GetRunExePathDir + DIR_CONF + EXE_SPL;
sEmfDir := GetEmfCopyDir;
if (DirectoryExists(sEmfDir) or FileExists(sConv)) and not IsHD then
begin
if (sSpoolPath = '') or not FileExists(sSpoolPath) then
begin
sSpoolPath := GetSpoolCopyPath;
_Trace('CollectPrintInfo() .. GetSpoolCopyPath() .. Path=%s', [sSpoolPath]);
end;
if FileExists(sSpoolPath) then
begin
// 워드는 부수, 한부씩인쇄 값을 가져올 수 없어서 아래처럼 처리
if CompareText('winword.exe', sPName) = 0 then
PrtInfo.DevMode.dmCollate := 1;
if IsPJL(sSpoolPath) then
begin
_Trace('CollectPrintInfo() .. PJL 포맷 확인됨', 1);
if IsPJLAndLanguagePLW(sSpoolPath) then
begin
_Trace('CollectPrintInfo() .. PJL 포맷, PLW 스플 확인됨. 결재 처리 불가.', 1);
exit;
end;
var bCollate: Boolean;
var nCopies: Integer := GetQtyFromPJL(sSpoolPath, bCollate);
if nCopies > 0 then
begin
PrtInfo.dwCopyCount := nCopies;
PrtInfo.DevMode.dmCopies := nCopies;
if bCollate then
PrtInfo.DevMode.dmCollate := 1;
_Trace('CollectPrintInfo() .. PJL 포맷 부수 정보 : %d', [nCopies], 1);
end;
end;
if aJob <> nil then
CancelJob;
if PrtInfo.sPtrName = '' then
PrtInfo.sPtrName := sPrtName;
if PrtInfo.sDocName = '' then
PrtInfo.sDocName := sDocName;
var pEnt: PPrtEnt := gMgSvc.MgPrint.AddPrintInfo(PrtInfo, sPName, sDocPath, sSpoolPath, sEmfDir, sPort, sText, sOcrText, sVioText, sThumbIds, PO.PrintApprovalPost);
Result := pEnt <> nil;
if Result and (gMgSvc.RcvHwnd <> 0) then
begin
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 3, 0);
pEnt.WInfo.sPrtDocId := sPrtDocId;
PostMessage(gMgSvc.RcvHwnd, WM_REQUEST_APPROVAL, 0, NativeInt(pEnt));
end;
end;
end;
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. OnPtrJobNotify() > CollectPrintInfo()');
end;
end;
// function CheckBlockException(var sReason: String): Boolean;
function CheckBlockException: Boolean;
var
i: Integer;
sReason: String; // 차단 이유를 기록하기 위함이었는데 이제 사용하지 않음.. 일단 정리 전에 놔둠
begin
Result := false;
if PP.sPrinterExcepts <> '' then
begin
// 프린터 예외 확인
Guard(ExcpList, TStringList.Create);
SplitString(PP.sPrinterExcepts, '|', ExcpList);
sChk := sPrtName.ToUpper;
for i := 0 to ExcpList.Count - 1 do
begin
if Pos(ExcpList[i], sChk) > 0 then
begin
Result := true;
sReason := 'PTR';
_Trace('프린터 출력 감지 .. 프린터 종류 예외. Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
exit;
end;
end;
end;
if PO.PrinterIpExcept <> '' then
begin
// 프린터 IP 예외 추가 24_1030 13:24:10 kku
var sChkPrtIp: String := StringReplace(sPrtIp, '_', '.', [rfReplaceAll]);
var StrList: TStringList;
Guard(StrList, TStringList.Create);
SplitString(PO.PrinterIpExcept, '|', StrList);
for i := 0 to StrList.Count - 1 do
begin
if Pos(StrList[i], sChkPrtIp) > 0 then
begin
Result := true;
sReason := 'PTR_IP';
_Trace('프린터 출력 감지 .. 프린터 IP 예외. Printer=%s, IP=%s, Doc=%s', [sPrtName, sChkPrtIp, sDocName], 2);
exit;
end;
end;
end;
if (PP.sPrintUrlExcepts <> '') and (gMgSvc.ThdWebUrl <> nil) then
begin
// 브라우저 URL 확인
Guard(ExcpList, TStringList.Create);
SplitString(PP.sPrintUrlExcepts, '|', ExcpList);
if (sPName <> '') and (Pos(LowerCase(sPName), BROWSER_LIST) > 0) then // 브라우저인지 확인
begin
var sUrl: String := '';
try
sUrl := gMgSvc.ThdWebUrl.LastUrl;
except
// ..
end;
if sUrl <> '' then
begin
sChk := UpperCase(sUrl);
for i := 0 to ExcpList.Count - 1 do
begin
if Pos(ExcpList[i], sChk) > 0 then
begin
Result := true;
sReason := 'URL|' + sUrl;
_Trace('프린터 출력 감지 .. URL 예외. Printer=%s, URO=%s, Doc=%s', [sPrtName, sUrl, sDocName], 2);
exit;
end;
end;
end;
end;
end;
if PP.sPrintAppExcepts <> '' then
begin
// App 확인
Guard(ExcpList, TStringList.Create);
SplitString(PP.sPrintAppExcepts, '|', ExcpList);
if sPName <> '' then
begin
sChk := UpperCase(sPName);
for i := 0 to ExcpList.Count - 1 do
begin
if Pos(ExcpList[i], sChk) > 0 then
begin
Result := true;
sReason := 'APP|' + sPName;
_Trace('프린터 출력 감지 .. APP 예외. Printer=%s, PName=%s, Doc=%s', [sPrtName, sPName, sDocName], 2);
exit;
end;
end;
end;
end;
if PP.sPrintDocExcepts <> '' then
begin
// 문서명 확인
Guard(ExcpList, TStringList.Create);
SplitString(PP.sPrintDocExcepts, '|', ExcpList);
sChk := UpperCase(sDocName);
for i := 0 to ExcpList.Count - 1 do
begin
if Pos(ExcpList[i], sChk) > 0 then
begin
Result := true;
sReason := 'APP|' + sPName;
_Trace('프린터 출력 감지 .. 문서이름 예외. Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
break;
end;
end;
end;
end;
function ProcessContentFilter_AfterExit(sTargetTxt: String; bIsOcrTxt: Boolean): Boolean;
var
i, nHits, nTotalHits,
nOrCnt, nAndCnt, nHighCnt: Integer;
sFound, sResult, sSchTxt: String;
begin
Result := false;
if OVio <> nil then
begin
// 1. 문서, 2. OCR 두번 시도될 수 있는데, 문서에서 이미 검출된게 있다면 넘어간다
exit;
end;
sFound := '';
sResult := '';
nOrCnt := 0;
nAndCnt := 0;
nHighCnt := 0;
sSchTxt := '';
nTotalHits := 0;
OVioTemp := TSuperObject.Create(stArray);
for i := 0 to PatternEntList.Count - 1 do
begin
sSchTxt := PatternEntList[i].GetSearchText;
nHits := TTgPcre.GetMatchValues(sTargetTxt, sSchTxt, sFound);
if nHits > 0 then
begin
if gMgSvc.IsNewApi then
begin
if nHits < PatternEntList[i].IfCount then
continue;
if sSchTxt.StartsWith('(?<!\d)\d{2}(01|02|03|04|') then
begin
nHits := 0;
// 주민번호 패턴이라면 검증해준다. 25_0526 15:44:47 kku
var sTemp: String := StringReplace(sFound, '"', '', [rfReplaceAll]);
var HitList: TStringList;
Guard(HitList, TStringList.Create);
SplitString(sTemp, ',', HitList);
var n: Integer;
for n := HitList.Count - 1 downto 0 do
begin
if not IsValidKoreanRegNo(HitList[n]) then
begin
HitList.Delete(n);
continue;
end;
Inc(nHits);
end;
if nHits < PatternEntList[i].IfCount then
continue;
sFound := HitList.CommaText;
end;
if PatternEntList[i].RSeverity = ManagerPattern.rsHigh then
Inc(nHighCnt)
else if PatternEntList[i].IsAnd then
Inc(nAndCnt)
else
Inc(nOrCnt);
SumString(sResult, Format('%s(%d)', [gMgSvc.MgRule.GetRuleNameFromId(PatternEntList[i].Name), nHits]), ',');
end else
SumString(sResult, Format('%s(%d)', [CttCodeToStr(PatternEntList[i].Name), nHits]), ',');
Inc(nTotalHits, nHits);
OEnt := SO;
OEnt.S['RULE_ID'] := PatternEntList[i].Name;
OEnt.S['CNT'] := IntToStr(nHits);
OEnt.S['TEXT'] := RemoveOverlapWords(sFound);
// SumString(sMaskingWords, sFound, ',', true);
if IsUsePrintMask then
begin
if gMgSvc.PrtMaskingStr = '' then
gMgSvc.PrtMaskingStr := sFound
else
gMgSvc.PrtMaskingStr := gMgSvc.PrtMaskingStr + ',' + sFound;
end;
OVioTemp.AsArray.Add(OEnt);
end;
end;
if OVioTemp.AsArray.Length > 0 then
begin
var bFoundOk: Boolean := true;
if gMgSvc.IsNewApi then
begin
if (nHighCnt = 0) and (PatternEntList.AndCount > 0) then
begin
// AND 갯수가 다르다면 X
if nAndCnt <> PatternEntList.AndCount then
bFoundOk := false;
// OR가 조건으로 있는데 검출된 OR가 없다면 X
if (PatternEntList.AndCount <> PatternEntList.Count) and (nOrCnt = 0) then
bFoundOk := false;
end;
end;
if bFoundOk then
OVio := OVioTemp
else
sResult := '';
end;
// if bIsOcrTxt and (nTotalHits = 0) then
// begin
// // 이미지 180도 돌려서 한번더 처리.. 시연을 위해 추가됨, 나중에 비활성 필요
// sFound := '';
// sResult := '';
// sExtrOcr180Txt := ExtrTextFromPrintImgFiles(sExportPath, 180);
//
// // 이거 사용할거면 위 처리처럼 바꿔야함 24_0214 14:02:37 kku
// for i := 0 to PrintPatternEnts_.Count - 1 do
// begin
// nHits := TTgPcre.GetMatchValues(sExtrOcr180Txt, PrintPatternEnts_[i].GetSearchText, sFound);
// if nHits > 0 then
// begin
// Inc(nTotalHits, nHits);
// if gMgSvc.IsNewApi then
// SumString(sResult, Format('%s(%d)', [gMgSvc.MgRule.GetRuleNameFromId(PrintPatternEnts_[i].Name), nHits]), ',')
// else
// SumString(sResult, Format('%s(%d)', [CttCodeToStr(PrintPatternEnts_[i].Name), nHits]), ',');
//
// OEnt := SO;
// OEnt.S['RULE_ID'] := PrintPatternEnts_[i].Name;
// OEnt.S['CNT'] := IntToStr(nHits);
// OEnt.S['TEXT'] := RemoveOverlapWords(sFound);
//
// if OVio = nil then
// OVio := TSuperObject.Create(stArray);
// OVio.AsArray.Add(OEnt);
// end;
// end;
//
// if nTotalHits > 0 then
// sTargetTxt := sExtrOcr180Txt;
// end;
if OVio <> nil then
begin
bBlock := (PP.PrintKind = pkBlock) and (PP.ContentFilter.nHitLimit <= nTotalHits) and not bApprovalPost;
if bBlock then
begin
if CheckBlockException then
begin
// 예외 대상인건 검출되도 차단 안함 25_1106 13:40:07 kku
_Trace('프린터 출력 감지 .. 컨텐츠 감지. 차단 예외됨. Content=%s, Printer=%s, Doc=%s, OCR=%s',
[sResult, sPrtName, sDocName, BooleanToStr(bIsOcrTxt, 'Y', 'N')], 2);
bBlock := false;
exit;
end;
if CollectPrintInfo(sDocPath, sExtrTxt, sExtrOcrTxt, OVio.AsJSon, sThumbIds) then
begin
SendPrintLog(LOGCODE_PREVENT_PRINTER);
if bIsOcrTxt then
_Trace('프린터 출력 감지 .. OCR 컨텐츠 감지, 출력 승인 요청. Content=%s, Printer=%s, Doc=%s',
[sResult, sPrtName, sDocName], 2)
else
_Trace('프린터 출력 감지 .. 컨텐츠 감지, 출력 승인 요청. Content=%s, Printer=%s, Doc=%s',
[sResult, sPrtName, sDocName], 2);
Result := true;
exit;
end;
sBlockReson := 'CTT|' + sResult;
if bIsOcrTxt then
_Trace('프린터 출력 감지 .. OCR 컨텐츠 감지 차단. Content=%s, Printer=%s, Doc=%s',
[sResult, sPrtName, sDocName], 2)
else
_Trace('프린터 출력 감지 .. 컨텐츠 감지 차단. Content=%s, Printer=%s, Doc=%s',
[sResult, sPrtName, sDocName], 2);
end;
end;
end;
Label
LB_DoPrint;
begin
try
if (aJob = nil) or aJob.WorkEnd then
exit;
if (aJob.Document <> '') and aJob.Document.Contains(' *BSOne') then
begin
// SetPrtJobFromHelperApp(aJob, JOB_CONTROL_RESUME);
aJob.ResumePrtJob(true);
exit;
end;
try
if aJob.IsCustomPause then
begin
RefineHookJob;
pPhJob := nil;
ForceDirectories(sWorkDir_);
bIsWorking_ := true;
bApprovalPost := false;
CurJob_ := aJob;
sPrtDocId := '';
sPName := '';
bSpoolEnd := false;
sPPath := GetProcessPathFromWndHandle(aJob.Wnd);
if sPPath <> '' then
begin
SetProcessName(ExtractFileName(sPPath));
sPPath := ExtractFilePath(sPPath);
end else
SetProcessName(GetProcessNameFromWndHandle(GetForegroundWindow));
aJob.GetJobDevInfo(PrtInfo);
sPrtName := aJob.PrinterName;
if sPrtName = '' then
sPrtName := PrtInfo.sPtrName;
sDocName := aJob.Document;
if sDocName = '' then
sDocName := PrtInfo.sDocName;
sPrtIp := PrinterDriverToIP(PrtInfo.sDrvName);
if (sDocName <> '') and sDocName.Contains(' *BSOne') then
begin
// SetPrtJobFromHelperApp(aJob, JOB_CONTROL_RESUME);
aJob.ResumePrtJob(true);
exit;
end;
sPort := aJob.Port;
nTotalPages := aJob.TotalPages;
PO := gMgSvc.ModePolicy;
PPO := gMgSvc.PrefModel;
PP := PO.Print;
bCfActive := PP.ContentFilter.bActive;
OVio := nil;
if gMgSvc.IsPrtWaterExcept then
PP.PrintWater := pwNone;
sData := PPO.PrtNameH;
// 워터마크 예외 체크
if PP.PrintWater <> pwNone then
begin
var StrList: TStringList;
Guard(StrList, TStringList.Create);
if PO.PrtWtExpUrl <> '' then
begin
SplitString(UpperCase(PO.PrtWtExpUrl), '|', StrList);
if (sPName <> '') and (Pos(LowerCase(sPName), BROWSER_LIST) > 0) and (gMgSvc.ThdWebUrl <> nil) then
begin
try
sChk := UpperCase(gMgSvc.ThdWebUrl.LastUrl);
except
// ..
end;
for i := 0 to StrList.Count - 1 do
if Pos(StrList[i], sChk) > 0 then
begin
PP.PrintWater := pwNone;
break;
end;
end;
end;
if (PP.PrintWater <> pwNone) and (PO.PrtWtExpDocName <> '') then
begin
SplitString(UpperCase(PO.PrtWtExpDocName), '|', StrList);
sChk := UpperCase(PrtInfo.sDocName);
for i := 0 to StrList.Count - 1 do
if Pos(StrList[i], sChk) > 0 then
begin
PP.PrintWater := pwNone;
break;
end;
end;
if (PP.PrintWater <> pwNone) and (PO.PrtWtExpPrtName <> '') then
begin
SplitString(UpperCase(PO.PrtWtExpPrtName), '|', StrList);
sChk := UpperCase(PrtInfo.sPtrName);
for i := 0 to StrList.Count - 1 do
if Pos(StrList[i], sChk) > 0 then
begin
PP.PrintWater := pwNone;
break;
end;
end;
if (PP.PrintWater <> pwNone) and (PO.PrtWtExpProcName <> '') then
begin
SplitString(UpperCase(PO.PrtWtExpProcName), '|', StrList);
sChk := UpperCase(sPName);
for i := 0 to StrList.Count - 1 do
if Pos(StrList[i], sChk) > 0 then
begin
PP.PrintWater := pwNone;
break;
end;
end;
if gMgSvc.MgPwe.CountHash > 0 then
begin
_Trace('프린트 워터마크 예외 경로 확인...', 2);
sDocPath := sDocName;
if not FileExists(sDocPath) then
begin
sDocPath := FindPrintingFile(sDocPath);
_Trace('프린트 워터마크 예외 경로 확인 : FindPrintingFile() .. Path="%s"', [sDocPath], 2);
end;
if (sDocPath = '') and (CompareText('notepad.exe', sPName) = 0) then
begin
// Windows 11 메모장에서 문서 이름이 현재 파일이 아닌 단순 "메모장"으로 뜨게된다. 23_0322 10:57:54 kku
sDocPath := GetWindowCaption(GetForegroundWindow);
sDocPath := FindPrintingFile(sDocPath, CompareText(sPName, 'excel.exe') = 0);
_Trace('프린트 워터마크 예외 경로 확인 : FindPrintingFile() 22.. Path="%s"', [sDocPath], 2);
end;
if FileExists(sDocPath) then
begin
_Trace('프린트 워터마크 예외 확인 .. Path="%s"', [sDocPath], 2);
if gMgSvc.MgPwe.HasFileHash(sDocPath) then
begin
_Trace('프린트 워터마크 예외 확인됨 .. Path="%s"', [sDocPath], 2);
PP.PrintWater := pwNone;
end;
end;
end;
end;
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 1, 0);
sEmfDir := '';
sSpoolPath := '';
if ( (PP.PrintWater <> pwNone) or PP.bCollectOutput or PO.IsPrtCollectThum ) then
begin
// if PP.PrintWater <> pwNone then
// CancelJob;
// CJ에서 프린트 정지 하면 스플이 너무 빨리 사라지는 현상이 있어서 미리 구해놓음.. 24_1212 16:11:51 kku
// _Trace('ttttttttt, Path=%s', [GetSpoolCopyPath('WTask')], 9);
sSpoolPath := GetSpoolCopyPath('WTask');
_Trace('GetSpoolCopyPath(1) .. Path=%s', [sSpoolPath]);
if FileExists(sSpoolPath) and
not IsPrintWaterHook and
(PP.PrintWater <> pwNone) then
 CancelJob;
sEmfDir := GetEmfCopyDir;
end;
// 후킹 처리로 생성된 값이 있는지 먼저 확인 25_1103 10:37:28 kku
sData := gMgSvc.RecentUserSid + '\Software\eCrmHomeEdition';
if sPrtDocId = '' then
begin
sPrtDocId := GetRegValueAsString(HKEY_USERS, sData, 'PrtDocId');
if sPrtDocId = '' then
begin
sData := gMgSvc.RecentUserSid + '\Software\WOW6432Node\eCrmHomeEdition';
sPrtDocId := GetRegValueAsString(HKEY_USERS, sData, 'PrtDocId');
end;
end;
// 이거 유지할지 고민... 25_1210 18:35:40 kku
DelRegValue(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\eCrmHomeEdition', 'PrtDocId');
DelRegValue(HKEY_USERS, gMgSvc.RecentUserSid + '\Software\WOW6432Node\eCrmHomeEdition', 'PrtDocId');
if sPrtDocId = '' then
sPrtDocId := StrsReplace(TGUID.NewGuid.ToString, ['{', '}'], '');
try
gMgSvc.PrtMaskingStr := '';
sDocPath := '';
sExtrOcrTxt := '';
sExtrOcr180Txt := '';
sThumbIds := '';
bWaterMark := false;
bIsDrm := false;
bIgrApproval := false;
_Trace('프린터 출력 감지 .. Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
bBlock := (PP.PrintKind = pkBlock) and not bCfActive;
sBlockReson := '';
// 결재요청 예외 체크
if IsApproveSupport and gMgSvc.UseApproval and PO.PrintApproval then
begin
var StrList: TStringList;
Guard(StrList, TStringList.Create);
if PO.PrtApvExpUrl <> '' then
begin
SplitString(UpperCase(PO.PrtApvExpUrl), '|', StrList);
if (sPName <> '') and (Pos(LowerCase(sPName), BROWSER_LIST) > 0) and (gMgSvc.ThdWebUrl <> nil) then
begin
try
sChk := UpperCase(gMgSvc.ThdWebUrl.LastUrl);
except
// ..
end;
for i := 0 to StrList.Count - 1 do
if Pos(StrList[i], sChk) > 0 then
begin
bIgrApproval := true;
break;
end;
end;
end;
if not bIgrApproval and (PO.PrtApvExpDocName <> '') then
begin
SplitString(UpperCase(PO.PrtApvExpDocName), '|', StrList);
sChk := UpperCase(PrtInfo.sDocName);
for i := 0 to StrList.Count - 1 do
if Pos(StrList[i], sChk) > 0 then
begin
bIgrApproval := true;
break;
end;
end;
if not bIgrApproval and (PO.PrtApvExpPrtName <> '') then
begin
SplitString(UpperCase(PO.PrtApvExpPrtName), '|', StrList);
sChk := UpperCase(PrtInfo.sPtrName);
for i := 0 to StrList.Count - 1 do
if Pos(StrList[i], sChk) > 0 then
begin
bIgrApproval := true;
break;
end;
end;
if not bIgrApproval and (PO.PrtApvExpProcName <> '') then
begin
SplitString(UpperCase(PO.PrtApvExpProcName), '|', StrList);
sChk := UpperCase(sPName);
for i := 0 to StrList.Count - 1 do
if Pos(StrList[i], sChk) > 0 then
begin
bIgrApproval := true;
break;
end;
end;
end;
Guard(PatternEntList, TPatternEntList.Create);
if bCfActive then
begin
if gMgSvc.PrintPatterns.Contains('scanoption') then
gMgSvc.SetPatternList(gMgSvc.PrintPatterns, PatternEntList)
else
gMgSvc.SetRuleToPtrnList(gMgSvc.PrintPatterns, PatternEntList);
end;
sDocPath := sDocName;
if not FileExists(sDocPath) then
sDocPath := FindPrintingFile(sDocPath);
var bForceSpoolWater: Boolean := false; // not IsPrintWaterHookForce and (CUSTOMER_TYPE = CUSTOMER_CJONS) and (Pos('CANON', UpperCase(PrtInfo.sPtrName)) = 0);
// if (bForceSpoolWater or not bIsHook) and (CUSTOMER_TYPE = CUSTOMER_GEC) and (PP.PrintWater <> pwNone) then
if CUSTOMER_TYPE = CUSTOMER_GEC then
begin
// sDocPath := 'D:\temp3\abc_e.docx'; //FindPrintingFile(sDocPath);
// 기본 대외비 처리 24_0513 16:28:34 kku
var sLabelName: String := 'None';
gMgSvc.RecentLabel := '대외비(Restricted)';
{$IFDEF DEBUG}
// 출력 시 AIP 원문 수집 테스트
// sDocPath := 'D:\temp3\123_label.docx';
{$ENDIF}
if FileExists(sDocPath) then
begin
// 오피스 파일은 열려 있는 상태에서 레이블 확인이 불가능하다. 복사해서 처리 24_0513 16:43:49 kku
var sTaskDir: String := sWorkDir_ + 'HEC\';
if ForceDirectories(sTaskDir) then
begin
var sDest: String := sTaskDir + ExtractFileName(sDocPath);
if CopyFileAfOpenCheck(sDocPath, sDest) then
begin
var O: ISuperObject := SO;
O.S['src'] := sDest;
O.S['dst'] := sDest + '.tmp';
O.S['regval'] := 'LabelA';
// O.S['ssid'] := RecentUserSid; // 비활성화 해서 HKEY_LOCAL_MACHINE 여기서 만들어지게
DelRegValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\eCrmHomeEdition', 'LabelA');
if SendData(gMgSvc.FindAipMdWnd, 4, O.AsString) = 10 then
begin
// sLabelName := GetRegValueAsString(HKEY_USERS, RecentUserSid + 'SOFTWARE\eCrmHomeEdition', 'ALabel');
sLabelName := GetRegValueAsString(HKEY_LOCAL_MACHINE, 'SOFTWARE\eCrmHomeEdition', 'LabelA');
if sLabelName <> '' then
DelRegValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\eCrmHomeEdition', 'LabelA');
if (sLabelName = '') or (Pos('anyuser', sLabelName.ToLower) > 0) then
begin
PP.PrintWater := pwNone;
gMgSvc.RecentLabel := '';
end else begin
gMgSvc.RecentLabel := sLabelName;
bIsDrm := true;
end;
end;
DeleteFile(PChar(sDest));
end;
end;
end else
_Trace('Fail .. FindPrintingFile() .000. Path="%s"', [sDocPath]);
_Trace('프린트 워터마크 레이블 확인 : FindPrintingFile() .. Path="%s", Label=%s', [sDocPath, gMgSvc.RecentLabel], 2);
end else
if fas_ <> nil then
begin
if FileExists(sDocPath) then
begin
var sTaskDir: String := sWorkDir_ + 'HEC\';
if ForceDirectories(sTaskDir) then
begin
var sDest: String := sTaskDir + ExtractFileName(sDocPath);
if CopyFileAfOpenCheck(sDocPath, sDest) then
begin
var nEncType: Integer := fas_.GetFileType(sDest);
bIsDrm := (nEncType = 103) {FSN}; // or (nEncType = 106) {NX};
if bIsDrm then
_Trace('Fasoo 암호화 확인 : Path="%s"', [sDocPath], 2);
DeleteFile(PChar(sDest));
end;
end;
end else
_Trace('Fail .. FindPrintingFile() .111222. Path="%s"', [sDocPath]);
end else
if UseSoftcampDecrypt then
begin
if FileExists(sDocPath) then
begin
// 오피스 파일은 열려 있는 상태에서 레이블 확인이 불가능하다. 복사해서 처리 24_0513 16:43:49 kku
var sTaskDir: String := sWorkDir_ + 'HEC\';
if ForceDirectories(sTaskDir) then
begin
var sDest: String := sTaskDir + ExtractFileName(sDocPath);
if CopyFileAfOpenCheck(sDocPath, sDest) then
begin
// bIsDrm := DS_IsEncrypted(sDest);
bIsDrm := DSCSIsEncryptedFile(sDest) = 1;
if bIsDrm then
_Trace('소프트캠프 암호화 확인 : Path="%s"', [sDocPath], 2);
DeleteFile(PChar(sDest));
end;
end;
end else
_Trace('Fail .. FindPrintingFile() .111. Path="%s"', [sDocPath]);
end;
if not bIsDrm then
begin
var sTaskDir: String := sWorkDir_ + 'HEC\';
var sDest: String := sTaskDir + ExtractFileName(sDocPath);
if CopyFileAfOpenCheck(sDocPath, sDest) then
begin
bIsDrm := TTgEncrypt.CheckSign(sDest, SIG_DRM);
if bIsDrm then
_Trace('BSOne 암호화 확인 : Path="%s"', [sDocPath], 2);
DeleteFile(PChar(sDest));
end;
end;
// if bBlock then
begin
if not bBlock and PP.bDateBlock then
begin
var dtNow := Now;
if ( (CompareDateTime(PP.dtBlockB, dtNow) = -1) and
(CompareDateTime(PP.dtBlockE, dtNow) = 1) ) then
begin
bBlock := true;
sBlockReson := 'DATE';
_Trace('프린터 출력 감지 .. 기간 차단. Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
end;
end;
if not bBlock then
begin
if ShowTestFun and
( gMgSvc.SharePcPrintBlock or
gMgSvc.WSDPortPrintBlock or
gMgSvc.TcpIpPrintBlock or
gMgSvc.PrintSavingBlock ) then
begin
// _Trace('출력 제어 체크 ..', 5);
// var sHpExe: String := GetRunExePathDir + DIR_CONF + EXE_HP;
// var sPrtInfoPath: String := sWorkDir_ + '#pi.dat';
// if FileExists(sHpExe) then
// begin
// // "공유PC" 프린터 정보를 가져오기 위해서는 이렇게 처리해야함 25_1127 14:19:52 kku
// _Trace('출력 제어 체크 .. 사용자 권한으로 프린터 정보 요청 ..', 5);
// var O: ISuperObject := SO;
// var sParam: String := GetRunExePathDir + DIR_CONF + '#ppi.dat';
// O.I['Cmd'] := HPCDM_PRINT_INFO_LIST;
// O.S['P'] := sPrtInfoPath;
// SaveJsonObjToFile(O, sParam);
//
// {$IFDEF DEBUG}
// ExecuteAppWaitUntilTerminate(sHpExe, Format('-p "%s"', [sParam]), SW_HIDE);
// {$ELSE}
// var PInfo: TProcessInformation := ExecuteAppAsUser('explorer.exe', sHpExe, Format('-p "%s"', [sParam]), SW_HIDE);
//// Sleep(1000);
// if PInfo.dwProcessId <> 0 then
// begin
// var dwExecuteTick: DWORD := GetTickCount;
// while true do
// begin
// if WaitForSingleObject(PInfo.hProcess, 50) <> WAIT_TIMEOUT then
// break;
//
// if (GetTickCount - dwExecuteTick) > 3000 then
// begin
// TerminateProcess(PInfo.hProcess, 999);
// exit;
// end;
// end;
// end;
// {$ENDIF}
// DeleteFile(PChar(sParam));
//
// if FileExists(sPrtInfoPath) then
// _Trace('출력 제어 체크 .. 사용자 권한으로 프린터 정보 요청 .. OK', 5);
// end;
var PrintersInfo: TPrintersInfo;
var pPrtInfo: PPrinterInfo;
Guard(PrintersInfo, TPrintersInfo.Create);
// if FileExists(sPrtInfoPath) then
// begin
// PrintersInfo.LoadFromFile(sPrtInfoPath);
// DeleteFile(PChar(sPrtInfoPath));
// end else
PrintersInfo.RefreshList;
pPrtInfo := PrintersInfo.GetPrtInfoByPrtName(sPrtName);
if pPrtInfo <> nil then
begin
if gMgSvc.SharePcPrintBlock then
begin
if pPrtInfo.PortType = PTShared then
begin
bBlock := true;
sBlockReson := 'SHARE';
_Trace('프린터 출력 감지 .. 공유PC를 통한 출력 차단. Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
end;
end;
if gMgSvc.WSDPortPrintBlock then
begin
if pPrtInfo.PortType = PTWsd then
begin
bBlock := true;
sBlockReson := 'WSD';
_Trace('프린터 출력 감지 .. WSD Port로 연결된 프린터 차단. Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
end;
end;
if gMgSvc.TcpIpPrintBlock then
begin
if pPrtInfo.PortType <> PTTcpIp then
begin
bBlock := true;
sBlockReson := 'NoTCPIP';
_Trace('프린터 출력 감지 .. TCP/IP로 설정이 안된 프린터 차단. Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
end;
end;
if gMgSvc.PrintSavingBlock then
begin
if pPrtInfo.bIsPowerSaveMode then
begin
bBlock := true;
sBlockReson := 'PSAVING';
_Trace('프린터 출력 감지 .. 절전모드 상태 프린터 차단. Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
end;
end;
end;
end;
end;
if bBlock then
bBlock := not CheckBlockException;
// if PP.PrintKind = pkBlock then
// bCfActive := bCfActive and bBlock; // 차단 정책 ON일때 예외일 경우 컨텐츠 필터도 하지 않음 25_0513 17:57:38 kku
if ( bCfActive and
(PatternEntList.Count > 0) ) or
PP.bCollectOutput then
begin
sExtrTxt := '';
sDocPath := sDocName;
_Trace('프린터 출력 감지 .. 원본 파일에서 컨텐츠 추출 .. Path=%s, Printer=%s, Doc=%s', [sDocPath, sPrtName, sDocName], 2);
if not FileExists(sDocPath) then
begin
sDocPath := FindPrintingFile(sDocPath, CompareText(sPName, 'excel.exe') = 0);
_Trace('FindPrintingFile() .. Path="%s"', [sDocPath], 2);
end;
if (sDocPath = '') and (CompareText('notepad.exe', sPName) = 0) then
begin
// Windows 11 메모장에서 문서 이름이 현재 파일이 아닌 단순 "메모장"으로 뜨게된다. 23_0322 10:57:54 kku
sDocPath := GetWindowCaption(GetForegroundWindow);
sDocPath := FindPrintingFile(sDocPath);
if sDocPath = '' then
begin
// 윈 11에서 추가 처리
sDocPath := StrsReplace(sDocName, [' - 메모장', ' - Notepad'], '');
sDocPath := FindPrintingFile(sDocPath);
end;
_Trace('FindPrintingFile() 22.. Path="%s"', [sDocPath], 2);
end;
_Trace('원본 파일에서 컨텐츠 추출 .. 1, Path = "%s"', [sDocPath], 4);
if FileExists(sDocPath) then
begin
_Trace('원본 파일에서 컨텐츠 추출 .. 2', 4);
// todo : DRM 체크. 아래는 사용중인 파일로 오류남. 보완 필요 24_0514 11:06:05 kku
// if not bIsDrm then
// bIsDrm := TTgEncrypt.CheckSign(sDocPath, SIG_DRM);
var sTaskPath: String := sWorkDir_ + 'HEC\';
if ForceDirectories(sTaskPath) then
begin
sTaskPath := sTaskPath + ExtractFileName(sDocPath);
try
if bIsDrm then
begin
{$IFDEF DEBUG}
// 출력 시 AIP 원문 수집 테스트
// sDocPath := 'D:\temp3\123_label.docx';
{$ENDIF}
try
case CUSTOMER_TYPE of
CUSTOMER_GEC :
begin
// AIP
if CopyFileAfOpenCheck(sDocPath, sTaskPath) then
begin
var sTaskDecPath: String := ExtractFilePath(sTaskPath) + 'd_' + ExtractFileName(sTaskPath);
var O: ISuperObject := SO;
O.S['src'] := sTaskPath;
O.S['dst'] := sTaskDecPath;
if SendData(gMgSvc.FindAipMdWnd, 2, O.AsString) = 10 then
begin
sExtrTxt := ExtrTextFromFile(sTaskDecPath, true);
_Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 성공 (AIP 복호화), Length = %d', [Length(sExtrTxt)], 4);
end;
if FileExists(sTaskDecPath) then
DeleteFile(PChar(sTaskDecPath));
end;
end;
else begin
if UseSoftcampDecrypt then
begin
var sTaskDecPath: String := ExtractFilePath(sTaskPath) + 'd_' + ExtractFileName(sTaskPath);
if CopyFileAfOpenCheck(sDocPath, sTaskPath) then
begin
if DSCSForceDecryptFile(sTaskPath, sTaskDecPath) = 1 then
begin
sExtrTxt := ExtrTextFromFile(sTaskDecPath, true);
_Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 성공 (SC 복호화), Length = %d', [Length(sExtrTxt)], 4);
end else
_Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 실패 (SC 복호화), Length = %d', [Length(sExtrTxt)], 4);
end;
if FileExists(sTaskDecPath) then
DeleteFile(PChar(sTaskDecPath));
end else
if fas_ <> nil then
begin
var sTaskDecPath: String := ExtractFilePath(sTaskPath) + 'd_' + ExtractFileName(sTaskPath);
if CopyFileAfOpenCheck(sDocPath, sTaskPath) then
begin
if fas_.DoExtract(sTaskPath, sTaskDecPath) then
begin
sExtrTxt := ExtrTextFromFile(sTaskDecPath, true);
_Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 성공 (Fasoo 복호화), Length = %d', [Length(sExtrTxt)], 4);
end else
_Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 실패 (Fasoo 복호화), Length = %d', [Length(sExtrTxt)], 4);
end;
if FileExists(sTaskDecPath) then
DeleteFile(PChar(sTaskDecPath));
end else begin
var dec: TTgDrmDec;
Guard(dec, TTgDrmDec.Create(sTaskPath));
if dec.DecryptToFile(GetMK, sDocPath) then
begin
sExtrTxt := ExtrTextFromFile(sTaskPath, true);
_Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 성공 (BS 복호화), Length = %d', [Length(sExtrTxt)], 4);
end;
end;
end;
end;
except
on E: Exception do
ETgException.TraceException(Self, E, Format('프린터 출력 감지 .. 원본파일 복호화 실패.. Path=%s', [sDocPath]));
end;
end else begin
// 오피스 파일은 열려 있으면 제대로 처리 안될 가능성 있음 24_0718 09:44:06 kku
if CopyFileAfOpenCheck(sDocPath, sTaskPath) then
begin
sExtrTxt := ExtrTextFromFile(sTaskPath, true);
_Trace('원본 파일에서 컨텐츠 추출 .. 3 .. 성공, Length = %d', [Length(sExtrTxt)], 4);
end;
end;
finally
if FileExists(sTaskPath) then
DeleteFile(PChar(sTaskPath));
end;
end;
if sExtrTxt <> '' then
_Trace('프린터 출력 감지 .. 원본 파일에서 컨텐츠 추출 성공 .. ' +
'Path=%s, Printer=%s, Doc=%s', [sDocPath, sPrtName, sDocName], 2);
end;
end;
if not bBlock and bCfActive and (PatternEntList.Count > 0) then
begin
// 출력 대상 파일에서 컨텐츠 필터 시작 =====================================
// 출력 정보 전송 23_0223 13:38:21 kku
// if FileExists(sDocPath) then
// sChk := ExtractFileName(sDocPath)
// else
// sChk := sDocName;
// SendReqPrint(sExtrTxt, sChk, sPName);
// DirectSendPtrFile(sDocPath);
// 출력 대상 파일에서 컨텐츠 필터 끝 =======================================
// 개인정보 확인
if sExtrTxt <> '' then
begin
if ProcessContentFilter_AfterExit(sExtrTxt, false) then
exit;
end else
_Trace('프린터 출력 감지 .. 원본 파일에서 컨텐츠 추출 실패 .. Path=%s, Printer=%s, Doc=%s', [sDocPath, sPrtName, sDocName], 2);
end;
end;
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0);
// todo : 출력물 수집
// sSpoolPath := '';
// if (sBlockReson = '') and (PP.bCollectOutput and (PP.PrintWater = pwNone)) then // 프린터 워터마크 처리 전 전송하도록 기능 수정 23_0913 13:05:37 kku
if PO.IsPrtCollectThum then // 프린터 워터마크 처리 전 전송하도록 기능 수정 23_0913 13:05:37 kku
begin
sEmfDir := GetEmfCopyDir;
if DirectoryExists(sEmfDir) then
begin
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 0, 0);
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0);
// emf를 png로 변환 후 텍스트 추출
if PO.Print.bCollectOutput then
begin
var bDoExtr: Boolean := true;
var ExtList: TStringList := gMgSvc.PrefModel.PrtOcrTxtExtList;
if ExtList.Count > 0 then
begin
if FileExists(sDocPath) then
begin
var sExt: String := GetFileExt(sDocPath).ToUpper;
if ExtList.IndexOf(sExt) = - 1 then
bDoExtr := false;
end else
if ExtList.IndexOf('*NF') = -1 then
bDoExtr := false;
end;
if bDoExtr then
begin
_Trace('출력 이미지에서 텍스트 추출(E) ..', 3);
sExtrOcrTxt := ExtrTextFromPrintEmfFiles(sEmfDir);
_Trace('출력 이미지에서 텍스트 추출(E) .. OK', 3);
end;
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0);
end;
// emf를 png로 변환 후 수집
sThumbIds := gMgSvc.SendPrintEmfFiles(sEmfDir, sWorkDir_ + 'PrtThum\', PO.PrtCollThumLimit, crtDelete);
if bCfActive then
begin
if ProcessContentFilter_AfterExit(sExtrOcrTxt, true) then
exit;
end;
end else
if bIsSplAnal then
begin
var sConv: String := GetRunExePathDir + DIR_CONF + EXE_SPL;
if FileExists(sConv) and not IsHD then // HEC, GEC에서 OCR 처리 안함 24_0723 10:31:59 kku
begin
if (sSpoolPath = '') or not FileExists(sSpoolPath) then
begin
sSpoolPath := GetSpoolCopyPath;
_Trace('GetSpoolCopyPath(2) .. Path=%s', [sSpoolPath]);
end;
if FileExists(sSpoolPath) then
begin
if bIsSplAnal and IsPJLAndLanguagePLW(sSpoolPath) then
bIsSplAnal := false;
if not IsPrintWaterHook and (PP.PrintWater <> pwNone) then
begin
// 분석이 오래 걸려서 미리 취소 시켜준다. 25_0417 09:22:20 kku
CancelJob;
end;
var sFName: String := GetValidFileName(ExtractFileName(sDocName), '#');
var sExportPath: String := ExtractFilePath(sSpoolPath) + sFName + '.png';
if PrtInfo.sPtrName = '' then
PrtInfo.sPtrName := sPrtName;
if PrtInfo.sDocName = '' then
PrtInfo.sDocName := sDocName;
// 브라우저에서 실물 프린터 출력 시 선명도 많이 떨어진다..
// 아래처럼 옵션 추가 24_0731 15:44:42 kku
if bIsSplAnal then
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 4, 0);
if bIsSplAnal and
ExtractImagesFromSpool(sConv, '-$ 65XSD4234455S4PLET58 -unicode -imgxres 150 -imgyres 150 -imgbitcount 8 "%s" "%s"',
sSpoolPath, 'col_', sExportPath, 300000, 0{PO.PrtCollThumLimit}, PP.PrintWater = pwNone) then
begin
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 0, 0);
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0);
if not FileExists(sSpoolPath) then
sSpoolPath := '';
if PO.Print.bCollectOutput then
begin
var bDoExtr: Boolean := true;
var ExtList: TStringList := gMgSvc.PrefModel.PrtOcrTxtExtList;
if ExtList.Count > 0 then
begin
if FileExists(sDocPath) then
begin
var sExt: String := GetFileExt(sDocPath).ToUpper;
if ExtList.IndexOf(sExt) = - 1 then
bDoExtr := false;
end else
if ExtList.IndexOf('*NF') = -1 then
bDoExtr := false;
end;
if bDoExtr then
begin
_Trace('출력 이미지에서 텍스트 추출 ..', 3);
sExtrOcrTxt := ExtrTextFromPrintImgFiles(sExportPath);
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 2, 0);
_Trace('출력 이미지에서 텍스트 추출 .. OK', 3);
end;
end;
sThumbIds := gMgSvc.SendPrintImgFiles(sExportPath, sWorkDir_ + 'PrtThum\', PO.PrtCollThumLimit, crtDelete);
if bCfActive then
begin
if ProcessContentFilter_AfterExit(sExtrOcrTxt, true) then
exit;
end;
end else begin
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 0, 0);
AddIgrPrtSpool(sPrtName);
bIsSplAnal := false;
end;
// DeleteDir(ExtractFilePath(sExportPath)); // 이거 언제 지워야 하나..
end;
end;
end;
end;
if not bBlock and (PP.PrintWater <> pwNone) then
begin
var sConv: String := GetRunExePathDir + DIR_CONF + EXE_SPL;
// {$IFDEF DEBUG}
// bForceSpoolWater := true;
// {$ENDIF}
if (bForceSpoolWater or not bIsHook) and FileExists(sConv) then
begin
if (sSpoolPath = '') or not FileExists(sSpoolPath) then
begin
sSpoolPath := GetSpoolCopyPath('WTask');
_Trace('GetSpoolCopyPath(3) .. Path=%s', [sSpoolPath]);
end;
if FileExists(sSpoolPath) then
begin
// 워드는 부수, 한부씩인쇄 값을 가져올 수 없어서 아래처럼 처리
if CompareText('winword.exe', sPName) = 0 then
PrtInfo.DevMode.dmCollate := 1;
if bIsSplAnal and IsPJL(sSpoolPath) then
begin
_Trace('PJL 포맷 확인됨', 1);
if not IsPJLAndLanguagePLW(sSpoolPath) then
begin
var bCollate: Boolean;
var nCopies: Integer := GetQtyFromPJL(sSpoolPath, bCollate);
if nCopies > 0 then
begin
PrtInfo.dwCopyCount := nCopies;
PrtInfo.DevMode.dmCopies := nCopies;
if bCollate then
PrtInfo.DevMode.dmCollate := 1;
_Trace('PJL 포맷 부수 정보 : %d', [nCopies], 1);
end;
end else begin
_Trace('PJL 포맷, PLW 스플 확인됨. 스플 분석 중지.', 1);
bIsSplAnal := false; // PJL 포맷에서 LANGUAGE=PLW 사용하면 spl2pdf가 분석하지 못한다 25_0723 15:26:37 kku
end;
end;
if bIsSplAnal then
begin
var sFName: String := GetValidFileName(ExtractFileName(sDocName), '#');
var sExportPath: String := ExtractFilePath(sSpoolPath) + sFName + '.png';
// {$IFNDEF DEBUG}
if IsPrtSpl2Pdf then
begin
case CUSTOMER_TYPE of
CUSTOMER_SHCI,
CUSTOMER_SHSC :
begin
if (CompareText('InsideBank.exe', sPName) <> 0) and
(CompareText('excel.exe', sPName) <> 0) and
(CompareText('POWERPNT.EXE', sPName) <> 0) and
(CompareText('Hwp.exe', sPName) <> 0) then
sExportPath := CutFileExt(sExportPath) + '.pdf';
end;
// CUSTOMER_SHCI :
// begin
// if (CompareText('InsideBank.exe', sPName) <> 0) and
// (CompareText('Hwp.exe', sPName) <> 0) then
// sExportPath := CutFileExt(sExportPath) + '.pdf';
// end;
end;
end;
// {$ENDIF}
if PrtInfo.sPtrName = '' then
begin
PrtInfo.sPtrName := sPrtName;
// TTgTrace.T('[W] Force PtrName="%s"', [PrtInfo.sPtrName]);
end;
if PrtInfo.sDocName = '' then
PrtInfo.sDocName := sDocName;
var sParam: String := Format('-$ 65XSD4234455S4PLET58 -unicode -imgbitcount 24 -imgxres %d -imgyres %d', [PPO.PrtDPI, PPO.PrtDPI]) + ' "%s" "%s"';
if IsPrintWaterHook then
begin
bIsSplAnal := IsSpoolAnal(sPrtName);
if bIsSplAnal then
begin
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 1,
NativeUInt(PChar(IntToStr(PrtInfo.dwTotalPage) + '|' + ExtractFilePath(sSpoolPath) + '|' + sFName)));
// 후킹방식일 경우 분석 실패 체크를 위해 여기서 분석을 해준다 25_0728 13:10:48 kku
if not ExtractImagesFromSpool(sConv, sParam, sSpoolPath, 'prt0_', sExportPath, 300000, 0) then
begin
AddIgrPrtSpool(sPrtName);
bIsSplAnal := false;
end;
end;
if not bIsSplAnal then
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 0, 0);
end else begin
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRINTWATER_PROGRESS, 1,
NativeUInt(PChar(IntToStr(PrtInfo.dwTotalPage) + '|' + ExtractFilePath(sSpoolPath) + '|' + sFName)));
end;
if bIsSplAnal then
begin
// 잡 삭제 직전에 업데이트
if (aJob <> nil) and not aJob.WorkEnd then
CancelJob;
bWaterMark := true;
TThdExecuteEndNoti.Create(gMgSvc.RcvHwnd, GetPrintLog(LOGCODE_EVENT_PRINTER), true, PrtInfo, sPort, sConv, sParam,
sSpoolPath, sExportPath, sPrtDocId).StartThread;
sSpoolPath := ''; // 삭제 안되게 빈값 처리 24_1212 16:39:40 kku
exit;
end;
end;
end else
_Trace('프린터 출력 감지 .. Spool 수집 실패... Printer=%s, Doc=%s', [sPrtName, sDocName], 2);
end else begin
// 워터마크 유효성 체크
if CheckValidPrintWater then // KOCES에서는 워터마크 안되도 차단 안되게함 23_0413 13:35:37 kku
begin
if gMgSvc.RecentPrintDocName <> sDocName then
begin
sBlockReson := 'WT';
_Trace('Fail .. PrintWatermark .. Recent="%s", Doc="%s"', [gMgSvc.RecentPrintDocName, sDocName]);
bBlock := true;
end;
end;
end;
end;
SendMessage(gMgSvc.RcvHwnd, WM_POPUP_PRTW_PROGRESS, 3, 0);
sData := sPrtName + '|' + sDocName;
if bBlock and not bApprovalPost then
begin
if CollectPrintInfo(sDocPath, sExtrTxt, sExtrOcrTxt, '', '') then
begin
SendPrintLog(LOGCODE_PREVENT_PRINTER);
_Trace('프린터 출력 감지 .. 차단, 출력 승인 요청. Printer=%s, Doc=%s',
[sPrtName, sDocName], 2);
exit;
end;
if bApprovalPost then
begin
// 사후 결재 처리를 통해 false로 변경될 수 있다. 25_1127 09:28:25 kku
goto LB_DoPrint;
end;
CancelJob;
sData := sData + '|PV';
if sBlockReson <> '' then
sData := sData + '|' + sBlockReson;
// else if PP.PrintWater <> pwNone then
// sData := sData + '|WT';
if gMgSvc.IsNewApi then
SendPrintLog(LOGCODE_PREVENT_PRINTER)
else
gMgSvc.SendEventLog(URI_USER_ACTION, LOGCODE_PREVENT_PRINTER,
Format('Printer : %s, Document : %s', [sPrtName, sDocName]) + BooleanToStr(sBlockReson = '', '', ', ' + sBlockReson));
end else begin
LB_DoPrint :
if aJob <> nil then
begin
// 스풀중에는 resume 명령이 먹지 않아서 추가 25_0623 10:29:50 kku
while (aJob <> nil) and aJob.IsSpooling2 do
begin
if i = 4000 then // 200초간 기다려준다 23_0525 08:03:11 kku
break;
Inc(i);
Sleep(50);
end;
// SetPrtJobFromHelperApp(aJob, JOB_CONTROL_RESUME);
aJob.ResumePrtJob(true);
end;
if not bIsHook and gMgSvc.IsPrtWaterExcept then // 후킹의 경우 HPCMD_CHECK_PRINTWATER_EXCEPT_EX를 통해 처리, 초기화 함 25_1105 19:39:27 kku
gMgSvc.IsPrtWaterExcept := false;
if gMgSvc.IsNewApi then
SendPrintLog(LOGCODE_EVENT_PRINTER)
else
gMgSvc.SendEventLog(URI_USER_ACTION, LOGCODE_EVENT_PRINTER,
Format('Printer : %s, Document : %s', [sPrtName, sDocName]), false);
end;
if IsDivPopup then
begin
if bBlock then
begin
if PP.bPopup then
gMgSvc.PopupMessage(TYPE_MSG_PREVENT_PRINTER, sData);
end else begin
if PO.PrtAllowPopup then
begin
if (PP.PrintWater <> pwNone) and PPO.PrtWaterPop and (gMgSvc.RecentPrintDocName = sDocName) then
gMgSvc.PopupMessage(TYPE_MSG_EVENT_PRINTWATER, sData)
else
gMgSvc.PopupMessage(TYPE_MSG_PREVENT_PRINTER, sData);
end;
end;
end else begin
if PP.bPopup then
begin
if bBlock or (PP.PrintKind = pkLog) then
begin
gMgSvc.PopupMessage(TYPE_MSG_PREVENT_PRINTER, sData);
end else
if (PP.PrintWater <> pwNone) and PPO.PrtWaterPop then
begin
if (gMgSvc.RecentPrintDocName = sDocName) then
gMgSvc.PopupMessage(TYPE_MSG_EVENT_PRINTWATER, sData);
end;
end;
end;
if aJob <> nil then
aJob.WorkEnd := true;
finally
Finalize(PrtInfo);
if FileExists(sSpoolPath) then
DeleteFile(PChar(sSpoolPath));
{$IFNDEF DEBUG}
if DirectoryExists(sEmfDir) then
DeleteDir(sEmfDir);
{$ENDIF}
end;
end;
finally
bIsWorking_ := false;
CurJob_ := nil;
if pPhJob <> nil then
Dispose(pPhJob);
if aJob <> nil then
aJob.ResumePrtJob(true);
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. ProcessPrintJob()');
end;
end;
procedure TThdPrintWork.Execute;
var
Job: TPrtJobInfo;
bInitFs: Boolean;
begin
// CoInitialize(nil);
// 외부 DRM 사용준비
bInitFs := true;
fas_ := nil;
case CUSTOMER_TYPE of
CUSTOMER_WELFNI : SetDSD_CODE(DSD_CODE_WFNI);
CUSTOMER_WELFND : SetDSD_CODE(DSD_CODE_WFND);
else bInitFs := false;
end;
if bInitFs then
begin
var sFsDir: String := GetRunExePathDir + 'fsdinit';
if not DirectoryExists(sFsDir) then
sFsDir := GetRunExePathDir + DIR_CONF + 'fsdinit';
if DirectoryExists(sFsDir) then
begin
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
fas_ := TTgFasoo.Create(sFsDir);
end;
end;
while not Terminated and not GetWorkStop do
begin
try
Lock;
try
if (JobList_ <> nil) and (JobList_.Count > 0) then
begin
Job := JobList_[0];
JobList_.Delete(0);
end else
Job := nil;
finally
Unlock;
end;
if Job <> nil then
ProcessPrintJob(Job)
else Sleep(50);
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. Execute()');
end;
end;
// CoUninitialize;
end;
end.