unit iac_inout; interface implementation uses windows, messages, commctrl, iac_global, global, mirutils, mircontacts, common, dbsettings, wrapper, editwrapper, io, syswin, m_api; {$include i_cnst_inout.inc} {$resource iac_inout.res} const opt_file = 'file'; const ioObject = 'object'; ioClipboard = 'clipboard'; ioOper = 'oper'; ioCopy = 'copy'; ioFile = 'file'; ioFileVariable = 'modvariables'; ioWrite = 'write'; ioAppend = 'append'; ioEnc = 'enc'; const ACF_CLIPBRD = $00000002; // Clipboard operations, not window ACF_ANSI = $00000004; // File: ANSI or Unicode (UTF8/UTF16) text ACF_COPYTO = $00000008; // Clipboard operations: 'copy to' or 'paste from' ACF_FILE = $00000010; // File operations ACF_FWRITE = $00000020; // read/write file ACF_FAPPEND = $00000040; // append file ACF_UTF8 = $00000080; // File: UTF8 or UTF16 ACF_SIGN = $00000100; // File: with signature or not ACF_FILE_PATH = $00000200; ACF_TEXTSEND = $00000400; // dummy ACF_MESSAGE = 0; const // V2 ACF2_TXT_FILE = $00000001; type tInOutAction = class(tBaseAction) private tfile:pWideChar; public constructor Create(uid:dword); destructor Destroy; override; // function Clone:tBaseAction; override; function DoAction(var WorkData:tWorkData):LRESULT; override; procedure Save(node:pointer;fmt:integer); override; procedure Load(node:pointer;fmt:integer); override; end; //----- Support functions ----- //----- Object realization ----- constructor tInOutAction.Create(uid:dword); begin inherited Create(uid); tfile:=nil; end; destructor tInOutAction.Destroy; begin mFreeMem(tfile); inherited Destroy; end; { function tInOutAction.Clone:tBaseAction; begin result:=.Create(0); Duplicate(result); end; } function tInOutAction.DoAction(var WorkData:tWorkData):LRESULT; var tmp:PWideChar; blob,p:PAnsiChar; w:PWideChar; ccs:TCCSDATA; hContact:TMCONTACT; wnd:HWND; fexist:bool; dbei:TDBEVENTINFO; i:cardinal; cp:integer; fh:THANDLE; lstr:pWideChar; llen:integer; buf:array [0..31] of WideChar; b,b1:array [0..MAX_PATH] of AnsiChar; last:pWideChar; begin result:=0; if WorkData.ResultType=rtInt then last:=pWideChar(IntToStr(buf,WorkData.LastResult)) else last:=pWideChar(WorkData.LastResult); // Clipboard if (flags and ACF_CLIPBRD)<>0 then begin if (flags and ACF_COPYTO)<>0 then CopyToClipboard(last,false) else begin ClearResult(WorkData); WorkData.LastResult:=uint_ptr(PasteFromClipboard(false)); WorkData.ResultType:=rtWide; end; exit; end; // File if (flags and ACF_FILE)<>0 then begin if (flags and ACF_FILE_PATH)<>0 then begin if db_is_contact(WorkData.Parameter)<>0 then hContact:=WorkData.Parameter else hContact:=0; tmp:=ParseVarString(tfile,hContact,last) end else tmp:=tfile; // File write if (flags and (ACF_FAPPEND or ACF_FWRITE))<>0 then begin if (flags and ACF_ANSI)=ACF_ANSI then cp:=1 // Ansi else if (flags and (ACF_UTF8 or ACF_SIGN))=ACF_UTF8 then cp:=2 // UTF8 else if (flags and (ACF_UTF8 or ACF_SIGN))=ACF_SIGN then cp:=4 // Wide+Sign else if (flags and (ACF_UTF8 or ACF_SIGN))=(ACF_UTF8 or ACF_SIGN) then cp:=3 // UTF8+Sign else cp:=0; // Wide case cp of 1: begin llen:=StrLen(WideToAnsi(last,pAnsiChar(lstr),MirandaCP)); end; 2,3: begin llen:=StrLen(WideToUTF8(last,pAnsiChar(lstr))); end; else lstr:=last; llen:=StrLenW(lstr)*SizeOf(WideChar); end; fexist:=FileExists(tmp); // Append file if fexist and ((flags and ACF_FAPPEND)<>0) then begin fh:=Append(tmp); if fh<>THANDLE(INVALID_HANDLE_VALUE) then begin BlockWrite(fh,lstr^,llen); end; if (cp<>0) and (cp<>4) then mFreeMem(lstr); end // Write file else if ((flags and ACF_FWRITE)<>0) or (not fexist and ((flags and ACF_FAPPEND)<>0)) then begin fh:=ReWrite(tmp); if fh<>THANDLE(INVALID_HANDLE_VALUE) then begin if cp=3 then begin i:=SIGN_UTF8; BlockWrite(fh,i,3); // UTF8 sign end else if cp=4 then begin i:=SIGN_UNICODE; BlockWrite(fh,i,2); // UTF16 sign end; BlockWrite(fh,lstr^,llen); if (cp<>0) and (cp<>4) then mFreeMem(lstr); end; end else fh:=THANDLE(INVALID_HANDLE_VALUE); end // File read else begin // remove file - download if StrPosW(tmp,'://')<>nil then // remote begin GetTempPathA(MAX_PATH,b); GetTempFileNameA(b,'wat',GetCurrentTime,b1); GetFile(FastWideToAnsiBuf(tmp,b),b1); if tmp<>tfile then mFreeMem(tmp); FastAnsiToWide(b1,tmp); end else b1[0]:=#0; fh:=Reset(tmp); // process file if fh<>THANDLE(INVALID_HANDLE_VALUE) then begin i:=GetFSize(tmp); mGetMem (w ,i+SizeOf(WideChar)); FillChar(w^,i+SizeOf(WideChar),0); BlockRead(fh,w^,i); if (flags and ACF_ANSI)<>0 then begin AnsiToWide(pAnsiChar(w),lstr,MirandaCP); mFreeMem(w); w:=lstr; end else if (flags and ACF_UTF8)<>0 then begin UTF8ToWide(pAnsiChar(w),lstr); mFreeMem(w); w:=lstr; end else ChangeUnicode(w); ClearResult(WorkData); WorkData.LastResult:=uint_ptr(w); WorkData.ResultType:=rtWide; end; if b1[0]<>#0 then DeleteFileA(b1); end; if fh<>THANDLE(INVALID_HANDLE_VALUE) then CloseHandle(fh); if tmp<>tfile then mFreeMem(tmp); exit; end; // Message wnd:=WaitFocusedWndChild(GetForegroundWindow){GetFocus}; // with Autosend if (flags and ACF_TEXTSEND)<>0 then begin if wnd<>0 then hContact:=WndToContact(wnd) else hContact:=0; if hContact=0 then begin if db_is_contact(WorkData.Parameter)<>0 then hContact:=WorkData.Parameter; end; if hContact=0 then exit; p:=Proto_GetBaseAccountName(hContact); if DBReadByte(hContact,p,'ChatRoom',0)<>1 then begin WidetoUTF8(last,blob); ccs.hContact := hContact; ccs.szProtoService := PSS_MESSAGE; ccs.wParam := 0; ccs.lParam := tlparam(blob); Proto_ChainSend(0, @ccs); dbei.cbSize :=sizeof(dbei); dbei.cbBlob :=StrLen(blob); dbei.pBlob :=pByte(blob); dbei.eventType:=EVENTTYPE_MESSAGE; dbei.timestamp:=GetCurrentTime; dbei.szModule :=p; dbei.flags :=DBEF_SENT or DBEF_UTF; db_event_add(hContact, @dbei); mFreeMem(blob); end else SendToChat(hContact,last); end else begin GetWindowThreadProcessId(GetForegroundWindow,@i); if (i=GetCurrentProcessId) and (wnd<>0) then SendMessageW(wnd,EM_REPLACESEL,1,tlparam(last)) else SendString(0,last); end; end; procedure tInOutAction.Load(node:pointer;fmt:integer); var section: array [0..127] of AnsiChar; pc:pAnsiChar; tmp:pWideChar; flags2:dword; begin inherited Load(node,fmt); case fmt of 0: begin pc:=StrCopyE(section,pAnsiChar(node)); if (flags and ACF_FILE)<>0 then begin StrCopy(pc,opt_file); tfile:=DBReadUnicode(0,DBBranch,section,nil); end; end; 100: begin pc:=StrCopyE(section,pAnsiChar(node)); if (flags and ACF_FILE)<>0 then begin StrCopy(pc,opt_file); tfile:=DBReadUnicode(0,DBBranch,section,nil); end; StrCopy(pc,'flags2'); flags2:=DBReadDWord(0,DBBranch,section,0); if (flags2 and ACF2_TXT_FILE)<>0 then flags:=flags or ACF_FILE_PATH; end; 1: begin tmp:=xmlGetAttrValue(HXML(node),ioObject); if lstrcmpiw(tmp,ioClipboard)=0 then begin flags:=flags or ACF_CLIPBRD; tmp:=xmlGetAttrValue(HXML(node),ioOper); if lstrcmpiw(tmp,ioCopy)=0 then flags:=flags or ACF_COPYTO; end else begin if lstrcmpiw(tmp,ioFile)=0 then begin if StrToInt(xmlGetAttrValue(HXML(node),ioFileVariable))=1 then flags:=flags or ACF_FILE_PATH; flags:=flags or ACF_FILE; StrDupW(tfile,xmlGetAttrValue(HXML(node),ioFile)); tmp:=xmlGetAttrValue(HXML(node),ioOper); if lstrcmpiw(tmp,ioWrite )=0 then flags:=flags or ACF_FWRITE else if lstrcmpiw(tmp,ioAppend)=0 then flags:=flags or ACF_FAPPEND; case StrToInt(xmlGetAttrValue(HXML(node),ioEnc)) of 0: flags:=flags or ACF_ANSI; 1: flags:=flags or ACF_UTF8; 2: flags:=flags or ACF_UTF8 or ACF_SIGN; 3: flags:=flags or 0; 4: flags:=flags or ACF_SIGN; end; end; end; end; { 2: begin pc:=GetParamSectionStr(node,ioObject); if lstrcmpi(tmp,ioClipboard)=0 then begin flags:=flags or ACF_CLIPBRD; pc:=GetParamSectionStr(node,ioOper); if lstrcmpi(pc,ioCopy)=0 then flags:=flags or ACF_COPYTO; // else if lstrcmpi(pc,'paste')=0 then ; end else begin if lstrcmpi(pc,ioFile)=0 then begin flags:=flags or ACF_FILE; if GetParamSectionInt(node,ioFileVariable))=1 then flags:=flags or ACF_FILE_PATH; UTF8ToWide(GetParamSectionStr(node,ioFile),tfile); pc:=GetParamSectionStr(node,ioOper); if lstrcmpi(pc,ioWrite )=0 then flags:=flags or ACF_FWRITE else if lstrcmpi(pc,ioAppend)=0 then flags:=flags or ACF_FAPPEND; case GetParamSectionInt(node,ioEnc)) of 0: flags:=flags or ACF_ANSI; 1: flags:=flags or ACF_UTF8; 2: flags:=flags or ACF_UTF8 or ACF_SIGN; 3: flags:=flags or 0; 4: flags:=flags or ACF_SIGN; end; end; end; end; } end; end; procedure tInOutAction.Save(node:pointer;fmt:integer); var section: array [0..127] of AnsiChar; pc:pAnsiChar; begin inherited Save(node,fmt); case fmt of 0: begin pc:=StrCopyE(section,pAnsiChar(node)); if (flags and ACF_FILE)<>0 then begin StrCopy(pc,opt_file); DBWriteUnicode(0,DBBranch,section,tfile); end; end; { 1: begin end; } 13: begin { ACF_CLIPBRD = $00000002; // Clipboard operations, not window ACF_ANSI = $00000004; // File: ANSI or Unicode (UTF8/UTF16) text ACF_COPYTO = $00000008; // Clipboard operations: 'copy to' or 'paste from' ACF_FILE = $00000010; // File operations ACF_FWRITE = $00000020; // read/write file ACF_FAPPEND = $00000040; // append file ACF_UTF8 = $00000080; // File: UTF8 or UTF16 ACF_SIGN = $00000100; // File: with signature or not ACF_FILE_PATH = $00000200; ACF_TEXTSEND = $00000400; } end; end; end; //----- Dialog realization ----- procedure FillFileName(Dialog:HWND;idc:integer); var pw,ppw:pWideChar; begin mGetMem(pw,1024*SizeOf(WideChar)); ppw:=GetDlgText(Dialog,idc); if ShowDlgW(pw,ppw) then begin SetDlgItemTextW(Dialog,idc,pw); SetEditFlags(Dialog,idc,EF_SCRIPT,0); end; mFreeMem(ppw); mFreeMem(pw); end; procedure MakeTextTypeList(wnd:HWND); begin SendMessage(wnd,CB_RESETCONTENT,0,0); InsertString(wnd,0,'Ansi'); InsertString(wnd,1,'UTF8'); InsertString(wnd,2,'UTF8+sign'); InsertString(wnd,3,'UTF16'); InsertString(wnd,4,'UTF16+sign'); SendMessage(wnd,CB_SETCURSEL,0,0); end; procedure SetSet(Dialog:HWND;num:integer); var lclip,lfile,lmess:boolean; begin case num of 0: begin lclip:=true ; lfile:=false; lmess:=false; end; 1: begin lclip:=false; lfile:=true ; lmess:=false; end; // 2: begin lclip:=false; lfile:=false; lmess:=true; end; else lclip:=false; lfile:=false; lmess:=true; end; EnableWindow(GetDlgItem(Dialog,IDC_CLIP_COPYTO),lclip); EnableWindow(GetDlgItem(Dialog,IDC_CLIP_PASTE ),lclip); EnableWindow(GetDlgItem(Dialog,IDC_FILE_READ ),lfile); EnableWindow(GetDlgItem(Dialog,IDC_FILE_WRITE ),lfile); EnableWindow(GetDlgItem(Dialog,IDC_FILE_APPEND ),lfile); EnableWindow(GetDlgItem(Dialog,IDC_FILE_ENC ),lfile); EnableWindow(GetDlgItem(Dialog,IDC_FILE_FILEBTN),lfile); EnableEditField(Dialog,IDC_FILE_PATH,lfile); EnableWindow(GetDlgItem(Dialog,IDC_TEXT_SEND),lmess); end; procedure ClearFields(Dialog:HWND); begin SetDlgItemTextW(Dialog,IDC_FILE_PATH,nil); SetEditFlags(Dialog,IDC_FILE_PATH,EF_ALL,0); CheckDlgButton(Dialog,IDC_FLAG_CLIP ,BST_UNCHECKED); CheckDlgButton(Dialog,IDC_FLAG_FILE ,BST_UNCHECKED); CheckDlgButton(Dialog,IDC_FLAG_MESSAGE,BST_UNCHECKED); CheckDlgButton(Dialog,IDC_CLIP_COPYTO,BST_UNCHECKED); CheckDlgButton(Dialog,IDC_CLIP_PASTE ,BST_UNCHECKED); CheckDlgButton(Dialog,IDC_FILE_READ ,BST_UNCHECKED); CheckDlgButton(Dialog,IDC_FILE_WRITE ,BST_UNCHECKED); CheckDlgButton(Dialog,IDC_FILE_APPEND,BST_UNCHECKED); CheckDlgButton(Dialog,IDC_TEXT_SEND,BST_UNCHECKED); end; function DlgProc(Dialog:HWND;hMessage:uint;wParam:WPARAM;lParam:LPARAM):LRESULT; stdcall; const NoProcess:boolean=true; var i:integer; begin result:=0; case hMessage of WM_INITDIALOG: begin TranslateDialogDefault(Dialog); MakeTextTypeList(GetDlgItem(Dialog,IDC_FILE_ENC)); MakeEditField(Dialog,IDC_FILE_PATH); end; WM_ACT_SETVALUE: begin NoProcess:=true; ClearFields(Dialog); with tInOutAction(lParam) do begin if (flags and ACF_CLIPBRD)<>0 then begin CheckDlgButton(Dialog,IDC_FLAG_CLIP,BST_CHECKED); if (flags and ACF_COPYTO)<>0 then CheckDlgButton(Dialog,IDC_CLIP_COPYTO,BST_CHECKED) else CheckDlgButton(Dialog,IDC_CLIP_PASTE,BST_CHECKED); SetSet(Dialog,0); end else if (flags and ACF_FILE)<>0 then begin CheckDlgButton(Dialog,IDC_FLAG_FILE,BST_CHECKED); if (flags and ACF_ANSI)<>0 then i:=0 else if (flags and ACF_UTF8)<>0 then begin if (flags and ACF_SIGN)<>0 then i:=2 else i:=1 end else if (flags and ACF_SIGN)<>0 then i:=4 else i:=3; CB_SelectData(GetDlgItem(Dialog,IDC_FILE_ENC),i); if (flags and ACF_FAPPEND)<>0 then CheckDlgButton(Dialog,IDC_FILE_APPEND,BST_CHECKED) else if (flags and ACF_FWRITE )<>0 then CheckDlgButton(Dialog,IDC_FILE_WRITE ,BST_CHECKED) else CheckDlgButton(Dialog,IDC_FILE_READ ,BST_CHECKED); SetDlgItemTextW(Dialog,IDC_FILE_PATH,tfile); SetEditFlags(Dialog,IDC_FILE_PATH,EF_SCRIPT,ord((flags and ACF_FILE_PATH)<>0)); SetSet(Dialog,1); end else begin CheckDlgButton(Dialog,IDC_FLAG_MESSAGE,BST_CHECKED); if (flags and ACF_TEXTSEND)<>0 then CheckDlgButton(Dialog,IDC_TEXT_SEND,BST_CHECKED); SetSet(Dialog,2); end; end; NoProcess:=false; end; WM_ACT_RESET: begin NoProcess:=true; ClearFields(Dialog); CheckDlgButton(Dialog,IDC_FLAG_CLIP,BST_CHECKED); CheckDlgButton(Dialog,IDC_CLIP_COPYTO,BST_CHECKED); CheckDlgButton(Dialog,IDC_FILE_READ,BST_CHECKED); SetSet(Dialog,0); // clipboard NoProcess:=false; end; WM_ACT_SAVE: begin with tInOutAction(lParam) do begin // flags:=0; // Clipboard if IsDlgButtonChecked(Dialog,IDC_FLAG_CLIP)<>BST_UNCHECKED then begin flags:=flags or ACF_CLIPBRD; if IsDlgButtonChecked(Dialog,IDC_CLIP_COPYTO)<>BST_UNCHECKED then flags:=flags or ACF_COPYTO; end // text file else if IsDlgButtonChecked(Dialog,IDC_FLAG_FILE)<>BST_UNCHECKED then begin flags:=flags or ACF_FILE; case CB_GetData(GetDlgItem(Dialog,IDC_FILE_ENC)) of 0: flags:=flags or ACF_ANSI; 1: flags:=flags or ACF_UTF8; 2: flags:=flags or ACF_UTF8 or ACF_SIGN; 3: flags:=flags or 0; 4: flags:=flags or ACF_SIGN; end; tfile:=GetDlgText(Dialog,IDC_FILE_PATH); if (GetEditFlags(Dialog,IDC_FILE_PATH) and EF_SCRIPT)<>0 then flags:=flags or ACF_FILE_PATH; if IsDlgButtonChecked(Dialog,IDC_FILE_APPEND)<>BST_UNCHECKED then flags:=flags or ACF_FAPPEND else if IsDlgButtonChecked(Dialog,IDC_FILE_WRITE)<>BST_UNCHECKED then flags:=flags or ACF_FWRITE; end // Message else begin if IsDlgButtonChecked(Dialog,IDC_TEXT_SEND)<>BST_UNCHECKED then flags:=flags or ACF_TEXTSEND; end; end; end; WM_COMMAND: begin case wParam shr 16 of CBN_SELCHANGE, EN_CHANGE: if not NoProcess then SendMessage(GetParent(GetParent(Dialog)),PSM_CHANGED,0,0); BN_CLICKED: begin case loword(wParam) of IDC_FLAG_CLIP: begin SetSet(Dialog,0); end; IDC_FLAG_FILE: begin SetSet(Dialog,1); end; IDC_FLAG_MESSAGE: begin SetSet(Dialog,2); end; IDC_FILE_FILEBTN: begin FillFileName(Dialog,IDC_FILE_PATH); end; end; SendMessage(GetParent(GetParent(Dialog)),PSM_CHANGED,0,0); end; end; end; WM_HELP: begin result:=1; end; end; end; //----- Export/interface functions ----- var vc:tActModule; function CreateAction:tBaseAction; begin result:=tInOutAction.Create(vc.Hash); end; function CreateDialog(parent:HWND):HWND; begin result:=CreateDialogW(hInstance,'IDD_INOUT',parent,@DlgProc); end; procedure Init; begin vc.Next :=ModuleLink; vc.Name :='In/Out'; vc.Dialog :=@CreateDialog; vc.Create :=@CreateAction; vc.Icon :='IDI_INOUT'; ModuleLink :=@vc; end; begin Init; end.