unit vsVisualSynapse; interface uses {$IFDEF LINUX} Libc, {$ELSE} Windows {sleep function}, {$ENDIF} Classes, sysutils, {$IFDEF OCX} extctrls, {$ENDIF} syncobjs, dnssend, httpsend, pingsend, slogsend, synautil, blcksock, synsock, synamisc, smtpsend, mimemess, mimepart, ftpsend,vsTypeDef; const MAX_REDIRECT_COUNT = 12; type //we have to re-type some stuff for the callback methods, else clients //have to include corresponding units: {$IFDEF FPC} {$DEFINE VS_SAFE_TYPE} {$ENDIF} {$IFDEF VS_SAFE_TYPE} //FPC compatability and delphi 6 'bug' work around. THookReason = THookSocketReason; TSynapseSocket = TSocksBlockSocket; {$ELSE} //FPC incompatible THookReason = type THookSocketReason; TSynapseSocket = type TSocksBlockSocket; {$ENDIF} //explanation of this re-typing: //if installed component is doubleclicked for some OnEvent //and this event has a THookSocketReason //user would have to _manually_ add the blcksock unit //to his source, which is inconvenient. //delphi 6 does not support this. 5 & 7 do. { hookreasons are: HR_ResolvingBegin, HR_ResolvingEnd, HR_SocketCreate, HR_SocketClose, HR_Bind, HR_Connect, HR_CanRead, HR_CanWrite, HR_Listen, HR_Accept, HR_ReadCount, HR_WriteCount, HR_Wait, HR_Error } THostInfo = record Host: String; Port: String; MetaText: String; // may contain data dependent on protocol MetaData: Pointer; //we leave this nil normally ResultCode : Integer; end; TSocksInfo = record IP, Port, Username, Password:String; Timeout:Integer; Resolver:Boolean; SocksType:TSocksType; end; TVisualSynapse = class; TVisualThread = class; TvsSocksProxyInfo = class; TOnVisualData = procedure (Sender:TVisualSynapse; VSHandle:Integer; Data:String; Query:String; From:THostInfo) of Object; TOnDataStrings = procedure (Sender:TVisualSynapse; VSHandle:Integer; Data:TStrings; Query:String; From:THostInfo) of Object; TOnError = procedure (Sender:TVisualSynapse; VSHandle:Integer; Query:String; ErrorCode:Integer; ErrorMessage:String) of Object; TOnProgress = procedure (Sender:TVisualSynapse; VSHandle:Integer; Reason:THookReason; Value:String; Sock: TSynapseSocket; var Continue:Boolean) of Object; TOnSockStatus = procedure (Sender:TVisualSynapse; VSHandle:Integer; Value:String) of Object; TOnSockInteger = procedure (Sender:TVisualSynapse; VSHandle:Integer; Value:Integer) of Object; TJobType=(jtCreateNew, jtControl); TJob = class (TObject) Handle:Integer; SendBandwidth: Integer; RecvBandWidth: Integer; JobType:TJobType; //defaults to createnew end; TVisualSynapse = class (TComponent) (* {$IFDEF OCX} (TPanel) {$ELSE} {$IFDEF BAREOBJECT} (TObject) {$ELSE} //no switch is default delphi component (TComponent) {$ENDIF} {$ENDIF} *) private FJobs:TList; //always encapsulate access by critical section FCS protected FInfo:THostInfo; FSocksProxyInfo:TvsSocksProxyInfo; FQuery:String; FData:String; FThreads:TList; //TVisualThread; FAutoTLS:Boolean; FMaxThreads:Integer; FSendBandwidth:Integer; FRecvBandwidth:Integer; FCS:TCriticalSection; FJobCount:Integer; FDummyStrings: TStrings; FOnData:TOnVisualData; FOnDataStrings:TOnDataStrings; FOnError:TOnError; FOnResolvingBegin:TOnSockStatus; FOnResolvingEnd:TOnSockStatus; FOnSocketCreate:TOnSockStatus; FOnSocketClose:TOnSockStatus; FOnBind:TOnSockStatus; FOnConnect:TOnSockStatus; FOnCanRead:TOnSockStatus; FOnCanWrite:TOnSockStatus; FOnListen:TOnSockStatus; FOnAccept:TOnSockStatus; FOnReadCount:TOnSockInteger; FOnWriteCount:TOnSockInteger; FOnWait:TOnSockStatus; FOnSockError:TOnSockStatus; FOnProgress: TOnProgress; procedure CreateThread; procedure SetDummyStrings (Value:TStrings); //support function for published TStrings properties function Enqueue (Value:TJob):Integer; //returns jobID public constructor Create (AOwner:TComponent); override; destructor Destroy; override; property Info:THostInfo read FInfo write FInfo; property LastJob:Integer read FJobCount; published property SendBandwidth:Integer read FSendBandwidth write FSendBandwidth; property RecvBandwidth:Integer read FRecvBandwidth write FRecvBandwidth; property MaxThreads:Integer read FMaxThreads write FMaxThreads; property SocksProxyInfo:TvsSocksProxyInfo read FSocksProxyInfo write FSocksProxyInfo; property OnData:TOnVisualData read FOnData write FOnData; property OnDataStrings:TOnDataStrings read FOnDataStrings write FOnDataStrings; property OnError:TOnError read FOnError write FOnError; property OnProgress:TOnProgress read FOnProgress write FOnProgress; property OnResolvingBegin:TOnSockStatus read FOnResolvingBegin write FOnResolvingBegin; property OnResolvingEnd:TOnSockStatus read FOnResolvingEnd write FOnResolvingEnd; property OnSocketCreate:TOnSockStatus read FOnSocketCreate write FOnSocketCreate; property OnSocketClose:TOnSockStatus read FOnSocketClose write FOnSocketClose; property OnBind:TOnSockStatus read FOnBind write FOnBind; property OnConnect:TOnSockStatus read FOnConnect write FOnConnect; property OnCanRead:TOnSockStatus read FOnCanRead write FOnCanRead; property OnCanWrite:TOnSockStatus read FOnCanWrite write FOnCanWrite; property OnListen:TOnSockStatus read FOnListen write FOnListen; property OnAccept:TOnSockStatus read FOnAccept write FOnAccept; property OnReadCount:TOnSockInteger read FOnReadCount write FOnReadCount; property OnWriteCount:TOnSockInteger read FOnWriteCount write FOnWriteCount; property OnWait:TOnSockStatus read FOnWait write FOnWait; property OnSockError:TOnSockStatus read FOnSockError write FOnSockError; end; TVisualClient = class (TVisualSynapse) published //Hide some properties: property OnListen:TOnSockStatus read FOnListen; property OnAccept:TOnSockStatus read FOnAccept; end; TVisualThread = class (TThread) Owner:TVisualSynapse; FData:String; FDataStrings:TStrings; FQuery:String; FInfo:THostInfo; FLastError:Integer; FErrorMsg:String; FSocksInfo:TSocksInfo; FCurrentJob:TJob; FCurrentSock:TSynapseSocket; FContinue:Boolean; //callback event FHookReason:THookSocketReason; FHookValue:String; function GetQueued(Handle:Integer):TObject; //get first command in queue or nil procedure CopySocksInfo (Socket:TSocksBlockSocket); //hook, do an onstatus: procedure SockCallBack (Sender: TObject; Reason: THookSocketReason; const Value: string); procedure SyncOnData; procedure SyncOnError; procedure SyncOnProgress; end; //Support methods: TvsSocksProxyInfo = class (TComponent) private protected public FSocksInfo:TSocksInfo; published property SocksIP: string read FSocksInfo.IP write FSocksInfo.IP; property SocksPort: string read FSocksInfo.Port write FSocksInfo.Port; property SocksUsername: string read FSocksInfo.Username write FSocksInfo.Username; property SocksPassword: string read FSocksInfo.Password write FSocksInfo.Password; property SocksTimeout: integer read FSocksInfo.Timeout write FSocksInfo.Timeout; property SocksResolver: Boolean read FSocksInfo.Resolver write FSocksInfo.Resolver; property SocksType: TSocksType read FSocksInfo.SocksType write FSocksInfo.SocksType; end; // HTTP THTTPInfo = record UserName, UserPass, ProxyHost, ProxyPort, ProxyUser, ProxyPass, UserAgent, IPInterface:String; TimeOut:Integer; KeepAlive:Boolean; FollowRedirect:Boolean; end; THTTPMethod = (hmGet, hmHead, hmPost); THTTPRequest = class (TJob) Method:THTTPMethod; URL:String; PostData:String; HTTPInfo:THTTPInfo; end; THTTPThread = class; TvsVisualHTTP = class (TVisualClient) protected // FThread:THTTPThread; FMethod:THTTPMethod; FPostData:String; FURL: String; FOnHeader:TOnDataStrings; FHTTPInfo:THTTPInfo; function DoHTTP (URL:String):Integer; procedure getURL (URL:String); //calls doHTTP public function Get (URL:String):Integer; //calls doHTTP function Head (URL:String):Integer; function Post (URL, PostData:String):Integer; published property Method:THTTPMethod read FMethod write FMethod; property URL:String read FURL write GetURL; property UserName:String read FHTTPInfo.UserName write FHTTPInfo.UserName; property UserPass:String read FHTTPInfo.UserPass write FHTTPInfo.UserPass; property ProxyHost:String read FHTTPInfo.ProxyHost write FHTTPInfo.ProxyHost; property ProxyPort:String read FHTTPInfo.ProxyPort write FHTTPInfo.ProxyPort; property ProxyUser:String read FHTTPInfo.ProxyUser write FHTTPInfo.ProxyUser; property ProxyPass:String read FHTTPInfo.ProxyPass write FHTTPInfo.ProxyPass; property UserAgent:String read FHTTPInfo.UserAgent write FHTTPInfo.UserAgent; property IPInterface:String read FHTTPInfo.IPInterface write FHTTPInfo.IPInterface; property TimeOut:Integer read FHTTPInfo.TimeOut write FHTTPInfo.TimeOut; property KeepAlive:Boolean read FHTTPInfo.KeepAlive write FHTTPInfo.KeepAlive; property FollowRedirect: Boolean read FHTTPInfo.FollowRedirect write FHTTPInfo.FollowRedirect; property PostData:String read FPostData write FPostData; property OnHeader:TOnDataStrings read FOnHeader write FOnHeader; end; THTTPThread = class (TVisualThread) HTTP:THTTPSend; Req:THTTPRequest; procedure SyncOnHeader; procedure Execute; override; end; //TCP & UDP TUDPRequest = class (TJob) Host:String; Port:String; BindPort:String; BindAdapter:String; Data:String; CloseSocket:Boolean; end; TUDPResponse = class (TJob) Info: THostInfo; Data: String; end; TvsVisualUDP = class (TVisualClient) protected FActive:Boolean; FDualThreaded: Boolean; FRemoteHost:String; FRemotePort:String; FBindAdapter:String; FBindPort:String; FSyncThread: TVisualThread; public procedure Connect (Host, Port:String); procedure SetActive (Value: Boolean); procedure Send (Data:String); procedure SendTo (Host, Port, Data:String); procedure Loaded; override; procedure SetDualThreaded (Value: Boolean); published property Active:Boolean read FActive write SetActive; property Host:String read FRemoteHost write FRemoteHost; property Port:String read FRemotePort write FRemotePort; property BindPort:String read FBindPort write FBindPort; property BindAdapter:String read FBindAdapter write FBindAdapter; property DualThreaded: Boolean read FDualThreaded write SetDualThreaded; end; TUDPThread = class (TVisualThread) FSock:TUDPBlockSocket; FBindPort:String; FBindAdapter:String; procedure Execute; override; end; TUDPSyncThread = class (TVisualThread) CS: TCriticalSection; Queue: TList; procedure Execute; override; end; // TCP TTCPStatus = (tsConnect, tsDisconnect); TTCPRequest = class (TJob); TTCPData = class (TTCPRequest) Data:String; end; TTCPControl = class (TTCPRequest) Info:THostInfo; Status:TTCPStatus; end; TvsVisualTCP = class (TVisualSynapse) protected FActive:Boolean; FInfo:THostInfo; public function Connect (Host, Port:String):Integer; procedure Disconnect (Handle:Integer); procedure DisconnectAll; procedure SetActive(Value:Boolean); //disconnect all? procedure Send (Data:String; Handle:Integer); procedure SendAll (Data:String); //send to all/first/? published property Active:Boolean read FActive write SetActive; property Host:String read FInfo.Host write FInfo.Host; property Port:String read FInfo.Port write FInfo.Port; end; TTCPThread = class (TVisualThread) procedure Execute; override; end; { TTCPServer = class (TVisualSynapse) end; TVisualSMTP = class (TVisualSynapse) protected FAttachments:TStrings; FFrom:String; FTo: TStrings; public function Attach (Value:TFileName); function AddTo; ClearAttachments; function Send; function SendMessage (cTo, cFrom, cSubject, FAttachments; published end; } /// DNS TDNSMethod = (DNS_AUTO, DNS_LOOKUP, DNS_REVERSE, DNS_MX, DNS_TXT, DNS_ALL); TDNSRequest = class (TJob) Method:TDNSMethod; UseNetBios:Boolean; DNSServer:String; Query: String; end; TDNSThread = class; TvsVisualDNS = class (TVisualClient) protected // FThread:TDNSThread; FMethod:TDNSMethod; FUseNetbios:Boolean; FDNSServer:String; // property Thread:TDNSThread read FThread write FThread; public function QueryDNS (Query:String):Integer; procedure SetDNS (Value:String); published property DNSQuery:String read FQuery write SetDNS; property DNSResult:String read FData; property DNSMethod:TDNSMethod read FMethod write FMethod; property UseNetbios:Boolean read FUseNetBios write FUseNetBios; property DNSServer:String read FDNSServer write FDNSServer; end; TDNSThread = class (TVisualThread) DNS:TDNSSend; FDoNetBios:Boolean; procedure Execute; override; end; TPingType = (ptPing, ptTraceRoute, ptTraceResolveHosts); TICMPRequest = class (TJob) pingtype:TPingType; Host:String; end; TvsVisualICMP = class (TVisualClient) protected FHost:String; FPingType:TPingType; FActive:Boolean; public function Ping (Host:String):Integer; function traceroute (Host:String; ResolveHostNames:Boolean):Integer; function PingRequest (Host:String; PingType:TPingType):Integer; procedure DoIt (Value:Boolean); published property SocksProxyInfo:TvsSocksProxyInfo read FSocksProxyInfo; //hide property PingType:TPingType read FPingType write FPingType; property Activate:Boolean read FActive write DoIt; property Host:String read FHost write FHost; end; TICMPThread = class (TVisualThread) procedure Execute; override; end; TAttachment = class (TObject) Data:String; Primary:String; Secondary:String; Filename:TFileName; end; TvsSendMailRequest = class (TJob) From:String; ReplyTo: String; _To:TStrings; Subject:String; Mailer:String; TextMessage:String; HTMLMessage:String; AttachedFiles:TStrings; Attachments:TList; AutoHTML:Boolean; SMTP:String; Headers: TStrings; end; TvsSendMail = class (TVisualSynapse) protected FFrom:String; FReplyTo: String; FTo:TStrings; FSubject:String; FMailer:String; FMessage:String; FHTML:String; FAttachedFiles:TStrings; FAttachments:TList; FAutoHTML:Boolean; FSMTP:String; public FHeaders: TStrings; function getToOne:String; procedure setToOne (Value:String); procedure SetToList(Value:TStrings); procedure setAttachedFiles(Value:TStrings); procedure Attach (Data, Primary, Secondary:String; Filename:TFileName); procedure AttachBinary (Data:String; FileName:TFileName); procedure AttachHTML (Data:String); procedure AttachImage (Data:String; FileName:TFileName); procedure AttachFile (Filename:TFileName); procedure Send; procedure SendTo (From, _To, Subject, TextMessage:String); procedure Clear; // procedure SendToOne (From:String; _To:String; Subject:String; _Message:String); property Attachments:TList read FAttachments; constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property AutoHTML:Boolean read FAutoHTML write FAutoHTML; property From:String read FFrom write FFrom; property ReplyTo: String read FReplyTo write FReplyTo; property ToOne:String read getToOne write setToOne; property ToList:TStrings read FTo write setToList; property Subject:String read FSubject write FSubject; property Mailer:String read FMailer write FMailer; property Text:String read FMessage write FMessage; property HTML:String read FHTML write FHTML; property AttachedFiles:TStrings read FAttachedFiles write setAttachedFiles; property SMTPServer:String read FSMTP write FSMTP; end; TvsSendMailThread = class (TVisualThread) SMTP:TSMTPSend; procedure Execute; override; end; //support function function ResolveHostName (IP:String):String; const MAX_HOSTNAME_LEN = 128; { from IPTYPES.H } MAX_DOMAIN_NAME_LEN = 128; MAX_SCOPE_ID_LEN = 256; MAX_ADAPTER_NAME_LENGTH = 256; MAX_ADAPTER_DESCRIPTION_LENGTH = 128; MAX_ADAPTER_ADDRESS_LENGTH = 8; type TIPAddressString = array[0..4*4-1] of Char; PIPAddrString = ^TIPAddrString; TIPAddrString = record Next : PIPAddrString; IPAddress : TIPAddressString; IPMask : TIPAddressString; Context : Integer; end; PFixedInfo = ^TFixedInfo; TFixedInfo = record { FIXED_INFO } case integer of 0: ( HostName : array[0..MAX_HOSTNAME_LEN+3] of Char; DomainName : array[0..MAX_DOMAIN_NAME_LEN+3] of Char; CurrentDNSServer : PIPAddrString; DNSServerList : TIPAddrString; NodeType : Integer; ScopeId : array[0..MAX_SCOPE_ID_LEN+3] of Char; EnableRouting : Integer; EnableProxy : Integer; EnableDNS : Integer; ); 1: (A:Array[0..2047] of byte); end; PIPAdapterInfo = ^TIPAdapterInfo; TIPAdapterInfo = record { IP_ADAPTER_INFO } Next : PIPAdapterInfo; ComboIndex : Integer; AdapterName : array[0..MAX_ADAPTER_NAME_LENGTH+3] of Char; Description : array[0..MAX_ADAPTER_DESCRIPTION_LENGTH+3] of Char; AddressLength : Integer; Address : array[1..MAX_ADAPTER_ADDRESS_LENGTH] of Byte; Index : Integer; _Type : Integer; DHCPEnabled : Integer; CurrentIPAddress : PIPAddrString; IPAddressList : TIPAddrString; GatewayList : TIPAddrString; DHCPServer : TIPAddrString; HaveWINS : LongBool; PrimaryWINSServer : TIPAddrString; SecondaryWINSServer : TIPAddrString; LeaseObtained : Integer; LeaseExpires : Integer; end; {$IFNDEF LINUX} //ip helper api only supported on windows TGetNetworkParams = function (FI : PFixedInfo; var BufLen : Integer) : Integer; stdcall; TGetAdaptersInfo = function (AI : PIPAdapterInfo; var BufLen : Integer) : Integer; stdcall; {$ENDIF} type //ip helper interface TvsIPHelper = class (TComponent) //After construction, these strings will be created and filled //system wide settings: protected FIPHelperDLL : THandle; {$IFNDEF LINUX} FGetNetworkParams : TGetNetworkParams; FGetAdaptersInfo : TGetAdaptersInfo; {$ENDIF} FHostName : String; FDomainName : String; FCurrentDNSServer : String; FDNSServerList : TStrings; FNodeType : Integer; FScopeId : String; FEnableRouting : Boolean; FEnableProxy : Boolean; FEnableDNS : Boolean; //Filled per adapter: FDNSServers:TStrings; FAdapterIPs:TStrings; FAdapterNames:TStrings; FAdapterDescriptions:TStrings; FAdapterMACs:TStrings; FDHCPServers:TStrings; FGateWays:TStrings; FCurrentIPs:TStrings; FCurrentMasks:TStrings; //LeaseObtained:TList //LeaseExpired:TList //multiples filled per adapter FAllIPS:TStrings; FAllMasks:TStrings; FDummyStrings: TStrings; FDummyString: String; FDummyInt: Integer; FDummyBool: Boolean; public procedure Refresh; procedure SetString (Value:String); //dummy calls to help the object inspector procedure SetStrings (Value:TStrings); //that don't like read-only properties. procedure SetInt (Value:Integer); procedure SetBool (Value:Boolean); constructor Create (AOwner:TComponent); override; destructor Destroy; override; published //the property interfaces property HostName : String read FHostName write SetString; property DomainName : String read FDomainName write SetString; property CurrentDNSServer : String read FCurrentDNSServer write SetString; property DNSServerList : TStrings read FDNSServerList write SetStrings; property NodeType : Integer read FNodeType write SetInt; property ScopeId : String read FScopeId write SetString; property EnableRouting : Boolean read FEnableRouting write SetBool; property EnableProxy : Boolean read FEnableProxy write SetBool; property EnableDNS : Boolean read FEnableDNS write SetBool; //Filled per adapter: property DNSServers:TStrings read FDNSServers write SetStrings; property AdapterIPs:TStrings read FAdapterIPs write SetStrings; property AdapterNames:TStrings read FAdapterNames write SetStrings; property AdapterDescriptions:TStrings read FAdapterDescriptions write SetStrings; property AdapterMACs:TStrings read FAdapterMACs write SetStrings; property DHCPServers:TStrings read FDHCPServers write SetStrings; property GateWays:TStrings read FGateWays write SetStrings; property CurrentIPs:TStrings read FCurrentIPs write SetStrings; property CurrentMasks:TStrings read FCurrentMasks write SetStrings; property AllIPS:TStrings read FAllIPS write SetStrings; property AllMasks:TStrings read FAllMasks write SetStrings; end; //support functions function TextToHTML (Value:String):String; function ResolveIP (HostName:String):String; //function ResolveIPS (HostName:String):TStringList; implementation //{ $ R VisualSynapse.dcr} //{$R *.dcr} function ResolveHostName (IP:String):String; var HE: PHostEnt; P: Integer; begin P:=synsock.inet_addr(PChar(IP)); HE := synsock.GetHostByAddr(@P, SizeOf(P), AF_INET); if Assigned(HE) then Result := HE^.h_name else begin if p<>0 then Result := IP else Result := ''; //invalid anything end; end; function ResolveIP (HostName:String):String; var HE: synsock.PHostEnt; TI: synsock.TInAddr; P: PChar; begin HE := synsock.GetHostByName(PChar(HostName)); if Assigned(HE) then begin {$IFDEF WIN32} TI := synsock.TInAddr(HE^.h_addr^^); {$ELSE} Move (HE^.h_addr^^, TI, SizeOf(TI)); {$ENDIF} P := synsock.inet_ntoa(TI); Result := P; end else Result := '0.0.0.0'; end; (* function ResolveIPS (HostName:String):TStringList; var HE: synsock.PHostEnt; TI: synsock.TInAddr; P: PChar; T: synsock.PInAddr; begin Result := TStringList.Create; HE := synsock.GetHostByName(PChar(HostName)); if Assigned(HE) then begin T:=HE^.h_addr^; while Assigned (Pointer(T^)) do begin {$IFDEF WIN32} TI := synsock.TInAddr(T^); {$ELSE} //Move (HE^.h_addr^^, TI, SizeOf(TI)); Move (T^, TI, SizeOf(TI)); {$ENDIF} P := synsock.inet_ntoa(TI); Result.Add(P); inc (T); end; Result.Add(HE^.h_name); end; end; *) function TextToHTML (Value:String):String; //inserts
tags and adds hyperlinks to http://, www. and email addresses var s:String; begin //optimizer issue: Result := ''; Value := stringReplace (Value, '<', '<', [rfReplaceAll]); Value := stringReplace (Value, '>', '>', [rfReplaceAll]); Value := stringReplace (Value, #13#10, #10, [rfReplaceAll]); Value := stringreplace (Value, #13, #10, [rfReplaceAll]); Value := stringreplace (Value, #10, '
'+#13#10, [rfReplaceAll]); Value := Trim(Value) + ' '; while pos (' ', Value)>0 do begin s:=Copy (Value, 1, pos (' ', Value)-1); Value := copy (Value, pos(' ', Value)+1, maxint); if (pos('http://', lowercase(s))=1) then s:=''+s+''; if (pos ('www.', lowercase(s))=1) then s:=''+s+''; if (pos('@', s)>0) then s:=''+s+''; Result := Result + s + ' '; end; Result := stringreplace (Result, '
', #13#10'
', [rfReplaceAll]); Result := ''#13#10+Result+#13#10''; end; function ExtractMail(V:String):String; var i,j:Integer; begin Result := ''; if pos ('@', V)<0 then exit; i:=pos ('@', V); while (i>1) and not (V[i-1] in ['<', '"', ' ']) do dec(i); j:=i; i:=pos ('@', V); while (i', '"', ' ']) do inc(i); Result := Copy (V, j, i-j+1); end; //synchronized methods: procedure TVisualThread.SyncOnData; var E:TStrings; begin if csDestroying in Owner.ComponentState then exit; if Assigned (Owner.FOnData) then try Owner.FOnData(Owner, FCurrentJob.Handle, FData, FQuery, FInfo); except end; if Assigned (Owner.FOnDataStrings) then try E:=TStringList.Create; //See if there is any data in FDataStrings if FDataStrings.Count > 0 then E.Assign (FDataStrings) //note that we do an extra assign here //if client somehow or another frees E, //thread will not be affected. //it costs some performance, but for safety it is better else // if Data is of reasonable size, fit into FDataStrings // this is a auto conversion that only takes place if there was // no data in FDataStrings begin // if length (FData)<=1024*1024 then // 1Mb max ? E.Text := FData; end; Owner.FOnDataStrings(Owner, FCurrentJob.Handle, E, FQuery, FInfo); E.Free; except end; end; procedure TVisualThread.SyncOnError; begin if Assigned (Owner.FOnError) and not (csDestroying in Owner.ComponentState) then try //to-do: set up some visual error structure if FErrorMsg='' then begin if FLastError > 0 then FErrorMsg := TBlockSocket.GetErrorDesc (FLastError) //try to decode winsock error else begin //fetch from visual synapse error array FErrorMsg := IntToStr(FLastError); end; end; Owner.FOnError (Owner, FCurrentJob.Handle, FQuery, FLastError, FErrorMsg); except end; end; procedure TVisualThread.CopySocksInfo (Socket:TSocksBlockSocket); begin Socket.SocksIP := FSocksInfo.IP; Socket.SocksPort := FSocksInfo.Port; Socket.SocksUsername := FSocksInfo.Username; Socket.SocksTimeout := FSocksInfo.TimeOut; Socket.SocksResolver := FSocksInfo.Resolver; Socket.SocksType := FSocksInfo.SocksType; end; procedure TVisualThread.SockCallBack (Sender: TObject; Reason: THookSocketReason; const Value: string); begin FHookReason := Reason; FHookValue := Value; FContinue := True; if Sender is TSocksBlockSocket then FCurrentSock := TSynapseSocket(Sender) else FCurrentSock := nil; synchronize (SyncOnProgress); if not FContinue then begin FCurrentSock.AbortSocket; //generates new callback, be aware FContinue := False; end; end; procedure TVisualThread.SyncOnProgress; begin if not terminated then try if Assigned (Owner.FOnProgress) then Owner.FOnProgress (Owner, FCurrentJob.Handle, FHookReason, FHookValue, FCurrentSock, FContinue); case FHookReason of HR_ResolvingBegin: if Assigned (Owner.FOnResolvingBegin) then Owner.FOnResolvingBegin (Owner, FCurrentJob.Handle, FHookValue); HR_ResolvingEnd: if Assigned (Owner.FOnResolvingEnd) then Owner.FOnResolvingEnd (Owner, FCurrentJob.Handle, FHookValue); HR_SocketCreate: if Assigned (Owner.FOnSocketCreate) then Owner.FOnSocketCreate (Owner, FCurrentJob.Handle, FHookValue); HR_SocketClose: if Assigned (Owner.FOnSocketClose) then Owner.FOnSocketClose (Owner, FCurrentJob.Handle, FHookValue); HR_Bind: if Assigned (Owner.FOnBind) then Owner.FOnBind (Owner, FCurrentJob.Handle, FHookValue); HR_Connect: if Assigned (Owner.FOnConnect) then Owner.FOnConnect (Owner, FCurrentJob.Handle, FHookValue); HR_CanRead: if Assigned (Owner.FOnCanRead) then Owner.FOnCanRead (Owner, FCurrentJob.Handle, FHookValue); HR_CanWrite: if Assigned (Owner.FOnCanWrite) then Owner.FOnCanWrite (Owner, FCurrentJob.Handle, FHookValue); HR_Listen: if Assigned (Owner.FOnListen) then Owner.FOnListen (Owner, FCurrentJob.Handle, FHookValue); HR_Accept: if Assigned (Owner.FOnAccept) then Owner.FOnAccept (Owner, FCurrentJob.Handle, FHookValue); HR_ReadCount: if Assigned (Owner.FOnReadCount) then Owner.FOnReadCount (Owner, FCurrentJob.Handle, StrToIntDef(FHookValue,0)); HR_WriteCount: if Assigned (Owner.FOnWriteCount) then Owner.FOnWriteCount (Owner, FCurrentJob.Handle, StrToIntDef(FHookValue,0)); HR_Wait: if Assigned (Owner.FOnWait) then Owner.FOnWait (Owner, FCurrentJob.Handle, FHookValue); HR_Error: if Assigned (Owner.FOnSockError) then Owner.FOnSockError (Owner, FCurrentJob.Handle, FHookValue); end; //case except end; end; procedure TVisualSynapse.CreateThread; var FThread:TVisualThread; begin //convenience for further programming //same pointer, but code gets shorter and no overrides needed. //this allows more uniform component access, since it shortens between three classes. //overriding still allowed FThread := nil; if Self is TvsVisualDNS then FThread := TDNSThread.Create (True); if Self is TvsVisualHTTP then FThread := THTTPThread.Create (True); if Self is TvsVisualUDP then FThread := TUDPThread.Create (True); if Self is TvsVisualTCP then FThread := TTCPThread.Create (True); if Self is TvsVisualICMP then FThread := TICMPThread.Create (True); if Self is TvsSendMail then FThread := TvsSendMailThread.Create (True); if FThread<>nil then begin FThread.Owner := Self; FThread.FDataStrings := TStringList.Create; if Assigned (FSocksProxyInfo) then FThread.FSocksInfo := FSocksProxyInfo.FSocksInfo; FThread.Resume; FThreads.Add (FThread); end; end; function TVisualSynapse.Enqueue (Value:TJob): Integer; var FIdle:Boolean; i:Integer; begin if (csDesigning in ComponentState) then begin Value.Free; exit; end; if Value.Handle = 0 then //new or unassigned request begin FIdle := False; //see if there are more threads needed: //do outside critical section since it is read-only atomic what we access here: //this is small overhead for single-threaded (udp/tcp like etc) connections. for i:=0 to FThreads.Count - 1 do begin FIdle := TVisualthread(FThreads[i]).FCurrentJob = nil; if FIdle then break; end; //do this outside critical section as well: if (not FIdle) and (FThreads.Count < FMaxThreads) then //create new thread CreateThread; end; //enqueue the job: FCS.Enter; if Value.Handle = 0 then begin inc (FJobCount); Result := FJobCount; Value.Handle := FJobCount; Value.SendBandwidth := FSendBandwidth; Value.RecvBandWidth := FRecvBandwidth; end else Result := Value.Handle; FJobs.Add (Value); FCS.Leave; end; function TVisualThread.GetQueued (Handle:Integer):TObject; //this is where a thread polls for jobs: var i:Integer; begin Result := nil; if csLoading in Owner.ComponentState then exit; Owner.FCS.Enter; with Owner do begin for i:=0 to FJobs.Count -1 do if ((Handle=0) and (TJob(FJobs[i]).JobType = jtCreateNew)) or (TJob(FJobs[i]).Handle = Handle) then begin Result := FJobs[i]; FJobs.Delete(i); FCurrentJob := Tjob(Result); break; end; end; Owner.FCS.Leave; end; constructor TVisualSynapse.Create (AOwner:TComponent); begin inherited; FThreads := TList.Create; FCS := TCriticalSection.Create; FJobs := TList.Create; FMaxThreads := 1; //adjust for any protocol; user can adjust anyhow: if (Self is TvsVisualHTTP) or (Self is TvsVisualDNS) then FMaxThreads := 16; {$IFDEF OCX} //make invisible at runtime Visible := False; {$ENDIF} end; destructor TVisualSynapse.Destroy; var i:Integer; begin for i := 0 to FThreads.count -1 do with TVisualThread(FThreads[i]) do try //it's a component, so be safe Terminate; WaitFor; FDataStrings.Free; Free; except end; FThreads.Free; for i:=0 to FJobs.Count - 1 do TJob(FJobs[i]).Free; FJobs.Free; FCS.Free; inherited; end; procedure TVisualSynapse.SetDummyStrings (Value:TStrings); begin FDummyStrings := Value; end; function TvsVisualHTTP.DoHTTP; var d:THTTPRequest; begin d:=THTTPRequest.Create; //copy actual properties: d.Method := FMethod; if pos ('://', URL)<=0 then URL := 'http://'+URL; d.URL := URL; if FMethod = hmPost then d.PostData := FPostData; D.HTTPInfo := FHTTPInfo; //and queue: Result := Enqueue(D); end; procedure TvsVisualHTTP.getURL; begin DoHTTP (URL); end; function TvsVisualHTTP.Get; begin FMethod := hmGet; Result := DoHTTP(url); end; function TvsVisualHTTP.Head; begin FMethod := hmHead; Result := DoHTTP(url); end; function TvsVisualHTTP.Post; begin FMethod := hmPost; FPostData := PostData; Result := DoHTTP(url); end; procedure THTTPThread.SyncOnHeader; var E:TStringList; begin if Assigned (TvsVisualHTTP(Owner).FOnHeader) then try //adjust: E:=TStringList.Create; E.Assign (HTTP.Headers); TvsVisualHTTP(Owner).FOnHeader (Owner, FCurrentJob.Handle, E, Req.URL, FInfo); E.Free; except end; end; procedure THTTPThread.Execute; var M:String; SL:TStringList; RedirectCount:Integer; Ok: Boolean; begin HTTP := THTTPSend.Create; while not Terminated do begin Req:=THTTPRequest(GetQueued(0)); if Assigned (Req) then begin //do a nice http request case Req.Method of hmGet: M:='GET'; hmHead: M:='HEAD'; hmPost: M:='POST'; end; HTTP.Document.Size := 0; HTTP.Headers.Clear; if (Req.Method = hmPost) and (Req.PostData<>'') then begin HTTP.Document.Write (Req.PostData[1], length(Req.PostData)); HTTP.MimeType := 'application/x-www-form-urlencoded'; HTTP.Protocol := '1.1'; //HTTP.UserAgent := 'Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7) Gecko/20040614 Firefox/0.8'; HTTP.Headers.Add ('Referer: http://www.google.com/translate_t'); // HTTP.Headers.Add ('Content-Length: '+IntToStr(HTTP.Document.Size)) end; CopySocksInfo (HTTP.Sock); HTTP.Sock.MaxSendBandwidth := Req.SendBandwidth; HTTP.Sock.MaxRecvBandwidth := Req.RecvBandwidth; //copy other stuff, like proxy etc. HTTP.ProxyHost := Req.HTTPInfo.ProxyHost; HTTP.ProxyPort := Req.HTTPInfo.ProxyPort; HTTP.ProxyUser := Req.HTTPInfo.ProxyUser; HTTP.ProxyPass := Req.HTTPInfo.ProxyPass; HTTP.IPInterface := Req.HTTPInfo.IPInterface; {$IFDEF SYNAPSE_VER33} HTTP.Username := Req.HTTPInfo.UserName; HTTP.Password := Req.HTTPInfo.UserPass; {$ELSE} //Version 32 ? //synapse 32 does not support HTTP authentication. // HTTP.Username := Req.HTTPInfo.UserName; // HTTP.Password := Req.HTTPInfo.UserPass; {$ENDIF} HTTP.UserAgent := Req.HTTPInfo.UserAgent; HTTP.KeepAlive := Req.HTTPInfo.KeepAlive; FInfo.Host := Req.URL; FQuery := Req.URL; // HTTP.Sock.OnStatus HTTP.Sock.OnStatus := SockCallBack; if HTTP.HTTPMethod (M, Req.URL) then begin Ok := True; if Req.HTTPInfo.FollowRedirect then //this does not follow redirects like "meta-equiv" in html documents. //only headers are examined. begin //see if there is a redirect RedirectCount := 0; SL := TStringList.Create; //this is safe, because it leaves lines intact and urls //are not supposed to have ': ' (whitespace) in it. SL.Text := StringReplace (HTTP.Headers.Text, ': ', '=', [rfReplaceAll]); while (SL.IndexOfName ('location')>=0) and (RedirectCount < MAX_REDIRECT_COUNT) do begin //ok then, fetch new document HTTP.Clear; if not (HTTP.HTTPMethod (M, SL.Values['location'])) then begin break; //sorry// Ok := False; end else begin SL.Text := StringReplace (HTTP.Headers.Text, ': ', '=', [rfReplaceAll]); inc (RedirectCount); end; end; SL.Free; end; if Ok then begin FInfo.Host := HTTP.TargetHost; FInfo.Port := HTTP.TargetPort; FInfo.MetaText := HTTP.Headers.Text; FInfo.ResultCode := HTTP.ResultCode; SetLength (FData, HTTP.Document.Size); if FData<>'' then HTTP.Document.Read (FData[1], length(FData)); synchronize (SyncOnHeader); synchronize (SyncOnData); end else begin FLastError := HTTP.ResultCode; FErrorMsg := 'Redirected, but failed to fetch document'; synchronize (SyncOnError); end; end else begin FLastError := HTTP.ResultCode; FErrorMsg := HTTP.ResultString; synchronize (SyncOnError); end; Req.Free; end else sleep (200); end; HTTP.Free; end; function TvsVisualTCP.Connect (Host, Port:String):Integer; var C:TTCPControl; begin C := TTCPControl.Create; C.Info.Host := Host; C.Info.Port := Port; C.Status := tsConnect; Result := Enqueue(C); end; procedure TvsVisualTCP.Disconnect (Handle:Integer); var C:TTCPControl; begin C := TTCPControl.Create; C.Handle := Handle; C.Status := tsDisConnect; Enqueue(C); end; procedure TvsVisualTCP.SetActive; //disconnect all? begin if Value then Connect (FInfo.Host, FInfo.Port) else DisconnectAll; FActive := Value; end; procedure TvsVisualTCP.Send (Data:String; Handle:Integer); var J:TTCPData; begin J:=TTCPData.Create; J.Handle := Handle; J.JobType := jtControl; J.Data := Data; Enqueue (J); end; procedure TvsVisualTCP.SendAll (Data:String); //send to all/first/? begin //todo: //loop all //for i:=0 to connected.count -1 do //send (data, connected[i].handle) Send (Data, 0); end; procedure TvsVisualTCP.DisconnectAll; var i:Integer; J:TTCPControl; begin FCS.Enter; for i:=0 to FThreads.Count - 1 do if Assigned (TVisualThread(FThreads[i]).FCurrentJob) then begin J:=TTCPControl.Create; J.Handle := TVisualThread(FThreads[i]).FCurrentJob.Handle; J.Status := tsDisconnect; Enqueue(J); //same thread, nested Critical section. end; FCS.Leave; end; procedure TTCPThread.Execute; var FSock:TTCPBlockSocket; J:TTCPRequest; C:TTCPControl; D:TTCPData; begin FSock := TTCPBlockSocket.Create; FSock.OnStatus := SockCallBack; CopySocksInfo (FSock); while not Terminated do begin J:=TTCPRequest(GetQueued(0)); //get new job if Assigned (J) then begin if (J is TTCPControl){should be} then begin C:=TTCPControl(J); FSock.Connect (C.Info.Host, C.Info.Port); while (FSock.LastError = 0) and (not Terminated) do begin J:=TTCPRequest(GetQueued (C.Handle)); if Assigned(J) then begin if (J is TTCPControl) and (TTCPControl(J).Status = tsDisconnect) then begin FSock.CloseSocket; //todo: clean up eventual remaining stuff break; //break loop end; //outgoing traffic: if (J is TTCPData) then begin FSock.SendString (TTCPData(J).Data); end; J.Free; end; //incoming traffic: if FSock.CanRead (20) then begin FData := FSock.RecvPacket(0); synchronize (syncOnData); end; end; FSock.CloseSocket; C.Free; end else //just ignore, invalid packet J.Free; end else sleep (200); end; end; procedure TvsVisualUDP.Connect (Host, Port:String); begin FRemoteHost := Host; FRemotePort := Port; Active := True; end; procedure TvsVisualUDP.SetActive (Value:Boolean); var U:TUDPRequest; begin if Value = FActive then exit; FActive := Value; if (csLoading in ComponentState) then Exit; U:=TUDPRequest.Create; if Value then begin if FBindPort='' then FBindPort := '0'; if FBindAdapter = '' then FBindAdapter := '0.0.0.0'; U.BindPort := FBindPort; U.BindAdapter := FBindAdapter; end else begin U.CloseSocket := True; end; Enqueue (U); end; procedure TvsVisualUDP.Loaded; begin inherited; if FActive then begin FActive := False; SetActive (True); end; if FDualThreaded then begin FDualThreaded := False; SetDualThreaded (True); end; end; procedure TvsVisualUDP.Send (Data:String); begin SendTo (FRemoteHost, FRemotePort, Data); end; procedure TvsVisualUDP.SendTo (Host, Port, Data:String); var U:TUDPRequest; begin if not FActive then exit; U:=TUDPRequest.Create; U.Host := Host; U.Port := Port; U.Data := Data; Enqueue (U); end; procedure TUDPThread.Execute; var U:TUDPRequest; F:TUDPRequest; Packet: String; J: TUDPResponse; begin FSock := TUDPBlockSocket.Create; FSock.OnStatus := SockCallBack; CopySocksInfo (FSock); F:=TUDPRequest.Create; while not Terminated do begin U := TUDPRequest (GetQueued(0)); if Assigned (U) then begin if U.CloseSocket then FSock.CloseSocket; if (U.BindAdapter<>'') and (U.BindPort <> '') then begin FSock.CloseSocket; FSock.Bind (U.BindAdapter, U.BindPort); F.Handle := U.Handle; end else begin FSock.Connect (U.Host, U.Port); FSock.SendString (U.Data); //if flasterror = 0 then sync onwritedata else sync onerror end; FreeAndNil (U); end; // else begin if FSock.CanRead (0) then begin Packet := FSock.RecvPacket (0); if TvsVisualUDP(Owner).FDualThreaded then begin //put in queue J := TUDPResponse.Create; J.Data := Packet; J.Info.Host := FSock.GetRemoteSinIP; J.Info.Port := IntToStr(FSock.GetRemoteSinPort); TUDPSyncThread(TvsVisualUDP(Owner).FSyncThread).CS.Enter; TUDPSyncThread(TvsVisualUDP(Owner).FSyncThread).Queue.Add (J); TUDPSyncThread(TvsVisualUDP(Owner).FSyncThread).CS.Leave; end else begin //do it now FData := Packet; FInfo.Host := FSock.GetRemoteSinIP; FInfo.Port := IntToStr(FSock.GetRemoteSinPort); FCurrentJob := F; synchronize (SyncOnData); end; end else //probably not connected sleep (50); end; end; FSock.CloseSocket; FSock.Free; end; procedure TUDPSyncThread.Execute; var Q: TList; i: Integer; J: TUDPResponse; begin //if dual-threaded //this thread provides application callback //while the other receives and sends data. Q := TList.Create; while not Terminated do begin CS.Enter; if Queue.Count > 0 then begin for i:=0 to Queue.Count-1 do Q.Add (Queue[i]); Queue.Clear; end; CS.Leave; if Q.Count=0 then sleep (50) else begin //call client for i:=0 to Q.Count - 1 do begin J := TUDPResponse(Q[i]); FInfo := J.Info; FCurrentJob := J; FData := J.Data; synchronize (SyncOnData); J.Free; end; Q.Clear; end; end; end; function TvsVisualDNS.QueryDNS; var D:TDNSRequest; begin D:=TDNSRequest.Create; D.Method := FMethod; D.UseNetBios := FUseNetBIOS; D.Query := Query; D.DNSServer := FDNSServer; Enqueue(D); end; procedure TvsVisualDNS.SetDNS; begin QueryDNS(Value); end; procedure TDNSThread.Execute; var IPH:TvsIPHelper; i,l:Integer; Ffound:Boolean; qtype:Byte; qt:TDNSMethod; {$IFNDEF LINUX} // HE:PHostEnt; P:String; {$ENDIF} D:TDNSRequest; begin DNS:=TDNSSend.Create; DNS.Sock.AbortSocket; CopySocksInfo (DNS.Sock); CopySocksInfo (DNS.TCPSock); // Data:=TStringList.Create; IPH := TvsIPHelper.Create(Self.Owner); while not Terminated do begin D:=TDNSRequest(GetQueued(0)); if Assigned(D) then begin IPH.Refresh; //refresh the helper (?? but may be needed on modem connections) FQuery := D.Query; Ffound := False; for i:=0 to IPH.DNSServers.Count - 1 do //break if found begin if D.DNSServer = '' then DNS.TargetHost := IPH.DNSServers[i] else DNS.TargetHost := D.DNSServer; qt := D.Method; if qt = DNS_AUTO then begin if IsIP(FQuery) then qtype := QTYPE_PTR //reverse lookup else qtype := QTYPE_A; //normal lookup end else case qt of DNS_LOOKUP: qtype:=QTYPE_A; DNS_REVERSE: qtype:=QTYPE_PTR; DNS_MX: qtype:=QTYPE_MX; DNS_TXT: qtype := QTYPE_TXT; DNS_ALL: qtype := QTYPE_ALL; end; if (DNS.DNSQuery (FQuery, QTYPE, FDataStrings)) and (FDataStrings.Count>0) then begin FData := FDataStrings[0]; // if FDataStrings.Count >= 2 then // FData := FDataStrings.Text; synchronize (SyncOnData); FFound := True; break; end; if D.DNSServer <> '' then break; end; {$IFNDEF LINUX} if not FFound and D.UseNetBios then begin //try netbios: { P:=synsock.inet_addr(PChar(FQuery)); HE := synsock.GetHostByAddr(@P, Length(FQuery), AF_INET); if Assigned(HE) then} P := ResolveHostName (FQuery); if P<>'' then begin FDataStrings.Clear; FDataStrings.Add(P); FData := P; synchronize (SyncOnData); FFound := True; end; end; {$ENDIF} //alternatively, on can use the getnameinfo function, on both linux and windows i think. if not Ffound then begin synchronize (SyncOnError); end; D.Free; end else sleep(20); end; IPH.Free; DNS.Free; end; function TvsVisualICMP.Ping; begin Result := PingRequest (Host, ptPing); end; function TvsVisualICMP.traceroute; begin if ResolveHostNames then Result := PingRequest (Host, ptTraceResolveHosts) else Result := PingRequest (Host, ptTraceRoute); end; function TvsVisualICMP.PingRequest; var P:TICMPRequest; begin FHost := Host; FPingType := PingType; P:=TICMPRequest.Create; P.Host := FHost; P.pingtype := FPingType; Enqueue(P); end; procedure TvsVisualICMP.DoIt; begin if Value then PingRequest (FHost, FPingType); end; procedure TICMPThread.Execute; var P:TICMPRequest; i:Integer; Ping: TPingSend; SomeHost:String; ttl : byte; //copied the ping and traceroute routine from pingsend.pas begin //i'm not too sure if socks would work and/or implementations are uniform. // i quote from http://www.socks.permeo.com/TechnicalResources/SOCKSFAQ/SOCKSGeneralFAQ/index.asp {23. Can I use ping/traceroute with SOCKS? SOCKS works with TCP and UDP applications. ping and traceroute are ICMP applications, so strictly speaking, they cannot. Some implementations provide SOCKS implementations of ping and traceroute, but they use vendor-specific protocol extensions.} //anyhow, i skip that for now. while not Terminated do begin P := TICMPRequest (GetQueued(0)); if Assigned (P) then begin if P.PingType = ptPing then begin //straight from pingsend support functions: with TPINGSend.Create do try i := -1; Sock.OnStatus := SockCallBack; //added this //don't know if this is supported by socks server if Ping(P.Host) then if ReplyError = IE_NoError then i := PingTime; finally Free; end; FData := IntToStr(i); if i>=0 then synchronize (SyncOnData) else synchronize (SyncOnError); end else //tracert begin FData := ''; Ping := TPINGSend.Create; Ping.Sock.OnStatus := SockCallBack; try ttl := 1; repeat ping.Sock.TTL := ttl; inc(ttl); if ttl > 30 then Break; if not ping.Ping(P.Host) then begin FData := FData + cAnyHost+ ' Timeout' + CRLF; continue; end; if (ping.ReplyError <> IE_NoError) and (ping.ReplyError <> IE_TTLExceed) then begin SomeHost := Ping.ReplyFrom; if P.PingType = ptTraceResolveHosts then SomeHost := ResolveHostName (SomeHost); FData := FData + SomeHost + ' ' + Ping.ReplyErrorDesc + CRLF; end; SomeHost := Ping.ReplyFrom; if P.PingType = ptTraceResolveHosts then SomeHost := ResolveHostName (SomeHost); FData := FData + SomeHost + ' ' + IntToStr(Ping.PingTime) + CRLF; until ping.ReplyError = IE_NoError; finally Ping.Free; end; if FData<>'' then Synchronize (SyncOnData) else Synchronize (SyncOnError); end; P.Free; end else sleep (20); end; end; //TvsSendMail// constructor TvsSendMail.Create(AOwner: TComponent); begin inherited Create(AOwner); FTo := TStringList.Create; FAttachedFiles := TStringList.Create; FAttachments := TList.Create; FMailer := 'Visual Synapse'; FHeaders := TStringList.Create; FMaxThreads := 16; end; destructor TvsSendMail.Destroy; begin FTo.Free; FAttachedFiles.Free; FAttachments.Free; end; procedure TvsSendMail.setToOne; begin FTo.Clear; FTo.Add (Value); end; function TvsSendMail.getToOne; begin if FTo.Count > 0 then Result := FTo[0] else Result := ''; end; procedure TvsSendMail.SetToList; var i:Integer; begin FTo.Clear; for i:=0 to Value.Count - 1 do if pos ('@', Value[i])>1 then FTo.Add(Value[i]); end; procedure TvsSendMail.setAttachedFiles; begin FAttachedFiles.Assign (Value); end; procedure TvsSendMail.Send; var Job:TvsSendMailRequest; i:Integer; begin //Enqueue job Job := TvsSendMailRequest.Create; Job.From := FFrom; Job.ReplyTo := FReplyTo; Job.Subject := FSubject; Job.TextMessage := FMessage; Job.HTMLMessage := FHTML; Job.Mailer := FMailer; Job._To := TStringList.Create; Job._To.Assign (FTo); Job.AttachedFiles := TStringList.Create; Job.AttachedFiles.Assign (FAttachedFiles); //we need to clear FAttachments, since the thread will clean up: Job.Attachments := FAttachments; FAttachments := TList.Create; Job.AutoHTML := FAutoHTML; Job.SMTP := FSMTP; Job.Headers := TStringList.Create; Job.Headers.Assign (FHeaders); Enqueue(Job); end; procedure TvsSendMail.Clear; var i:Integer; begin FTo.Clear; FAttachments.Clear; for i:=0 to FAttachments.Count - 1 do TAttachment(FAttachments[i]).Free; FAttachedFiles.Clear; FFrom := ''; FSubject := ''; FMessage := ''; FHTML := ''; end; procedure TvsSendMail.Attach; var Attachment:TAttachment; begin Attachment := TAttachMent.Create; Attachment.Data := Data; Attachment.Primary := Primary; Attachment.Secondary := Secondary; Attachment.Filename := FileName; FAttachments.Add (Attachment); end; procedure TvsSendMail.AttachBinary; var Primary,Secondary, Ext:String; i:Integer; begin Primary := ''; Secondary := ''; Ext := ''; i := 0; Attach (Data, Primary, Secondary, FileName); end; procedure TvsSendMail.AttachHTML; begin Attach (Data, 'text', 'html', ''); end; procedure TvsSendMail.AttachImage; begin Attach (Data, '', '', FileName); end; procedure TvsSendMail.AttachFile; begin if FileExists(FileName) and (FAttachedFiles.IndexOf(FileName)<0) then FAttachedFiles.Add (FileName); end; procedure TvsSendMail.SendTo; begin FFrom := From; FTo.Clear; FTo.Add (_To); FSubject := Subject; FMessage := TextMessage; Send; end; procedure TvsSendMailThread.Execute; var SMTP:TSMTPSend; DNSServer:String; DNS:TStrings; sTo:String; i,j,N:Integer; P,S:String; L:Integer; DNSEntry:PIPAddrString; SMTPRelay:TStringList; Success:Boolean; V:String; Job:TvsSendMailRequest; Mime:TMimeMess; MimeBody:TMimePart; MimeText:TMimePart; M:TStrings; MS:TMemoryStream; A:TAttachment; //procedure SendToRaw as copied and adjusted from SMTPSend.pas function SendMail(const MailFrom, MailTo, SMTPHost: string; const MailData: TStrings; const Username, Password: string): Boolean; var SMTP: TSMTPSend; s, t: string; begin Result := False; SMTP := TSMTPSend.Create; try CopySocksInfo (SMTP.Sock); // if you need support for upgrade session to TSL/SSL, uncomment next lines: // SMTP.AutoTLS := True; // if you need support for TSL/SSL tunnel, uncomment next lines: // SMTP.FullSSL := True; SMTP.Sock.MaxBandwidth := Job.SendBandWidth; SMTP.Sock.SetLinger (true, 25); SMTP.Sock.OnStatus := SockCallBack; SMTP.TargetHost := SeparateLeft(SMTPHost, ':'); s := SeparateRight(SMTPHost, ':'); if (s <> '') and (s <> SMTPHost) then SMTP.TargetPort := s; SMTP.Username := Username; SMTP.Password := Password; if SMTP.Login then begin if SMTP.MailFrom(GetEmailAddr(MailFrom), Length(MailData.Text)) then begin s := MailTo; repeat t := GetEmailAddr(FetchEx(s, ',', '"')); if t <> '' then Result := SMTP.MailTo(t); if not Result then Break; until s = ''; if Result then Result := SMTP.MailData(MailData); end; SMTP.Logout; end; finally SMTP.Free; end; end; begin while not Terminated do begin Job := TvsSendMailRequest (GetQueued(0)); if Assigned (Job) then begin try Mime := TMimeMess.Create; M := TStringList.Create; MS := TMemoryStream.Create; //no support for inline images yet... if (Job.AttachedFiles.Count + Job.Attachments.Count > 0) then //Multipart MimeBody := Mime.AddPartMultiPart('mixed', nil) else MimeBody := nil; //Add text + optional html part: if Job.AutoHTML or (Job.HTMLMessage<>'') then begin MimeText := Mime.AddPartMultipart ('alternative', MimeBody); M.Text := Job.TextMessage; Mime.AddPartText (M, MimeText); if Job.HTMLMessage<>'' then M.Text := Job.HTMLMessage else M.Text := TextToHTML (Job.TextMessage); Mime.AddPartHTML (M, MimeText); end else begin M.text := Job.TextMessage; Mime.AddPartText (M, MimeBody); end; //add attachments: if Assigned (MimeBody) then //multipart/mixed begin for i:=0 to Job.AttachedFiles.Count - 1 do try Mime.AddPartbinaryFromFile (Job.AttachedFiles[i], MimeBody); except end; for i:=0 to Job.Attachments.Count - 1 do try A:=TAttachment (Job.Attachments[i]); if (A.Primary = 'text') and (A.Secondary='html') then begin M.Text := A.Data; Mime.AddPartHTML (M, MimeBody); end else begin MS.Size := 0; MS.Write (A.Data[1], length(A.Data)); Mime.AddPartBinary (MS, A.FileName, MimeBody); end; except end; end; M.Free; MS.Free; //set some header info: mime.Header.From := Job.From; mime.Header.ToList.Assign (Job._To); mime.Header.Subject := Job.Subject; mime.Header.XMailer := Job.Mailer; mime.Header.CustomHeaders.AddStrings (Job.Headers); if Job.ReplyTo<>'' then mime.Header.CustomHeaders.Add ('Reply-To: '+Job.ReplyTo); Mime.EncodeMessage; //messagepart => mime.lines SMTPRelay := TStringList.Create; //list of smtp servers we try DNSServer := GetDNS; if (DNSServer='') and (Job.SMTP='') then begin FErrorMsg := 'unable to retrieve dns server'; SyncOnError; end else begin DNS := TStringList.Create; while pos(',', DNSServer)>0 do begin DNS.Add (Copy (DNSServer, 1, pos(',', DNSServer)-1)); DNSServer := Copy (DNSServer, pos(',', DNSServer)+1, maxint); end; if DNSServer<>'' then DNS.Add (DNSServer); for l := 0 to Job._To.Count - 1 do begin V:=ExtractMail(Job._To[l]); sTo := copy (V, pos ('@', V)+1, maxint); //sTo contains the server name from which we want to retrieve the MX record: for i:=0 to DNS.Count - 1 do if GetMailServers (DNS[i], sTo, SMTPRelay) then break; //some people forget to specify MX records //in that case, add plain domain name: if SMTPRelay.Count = 0 then SMTPRelay.Add (sTo); if Job.SMTP <> '' then SMTPRelay.Insert (0, Job.SMTP); if SMTPRelay.Count = 0 then begin FErrorMsg := 'Cannot deliver, no smtp host available'; synchronize (syncOnError); end else begin //Now SMTPRelay contains a list of SMTP servers. This can be the host itself, //or a relaying server. We don't care, we simply try sending the message: Success := False; FData := Mime.Lines.text; // FDataStrings.Assign (Mime.Header.Lines); FQuery := Job.From + ':' + Job._To[l]+':'+Job.Subject; for i:=0 to SMTPRelay.Count -1 do begin if SendMail (Job.From, V, SMTPRelay[i], Mime.Lines, '', '' ) then begin success := true; synchronize (syncOnData); break; end; end; if not Success then begin FErrorMsg := 'Failed to send'; synchronize (SyncOnError); end; end; end; DNS.Free; end; SMTPRelay.Free; Mime.Free; Job._To.Free; Job.AttachedFiles.Free; for i:=0 to Job.Attachments.Count - 1 do TAttachment(Job.Attachments[i]).Free; Job.Attachments.Free; Job.Free; except on E: Exception do begin FData := E.Message; synchronize (syncOnError); end; end; end else sleep (200); end; //thread terminated // FreeOnTerminate := True; //free self end; //TvsIPHelper// constructor TvsIPHelper.Create; begin inherited; FDNSServerList:=TStringList.Create; FDNSServers:=TStringList.Create; FAdapterIPs:=TStringList.Create; FAdapterNames:=TStringList.Create; FAdapterDescriptions:=TStringList.Create; FAdapterMACs:=TStringList.Create; FDHCPServers:=TStringList.Create; FGateWays:=TStringList.Create; FCurrentIPs:=TStringList.Create; FCurrentMasks:=TStringList.Create; // PrimaryIPs:=TStringList.Create; // PrimaryMasks:=TStringList.Create; //LeaseObtained:TList //LeaseExpired:TList //multiples filled per adapter FAllIPS:=TStringList.Create; FAllMasks:=TStringList.Create; {$IFNDEF LINUX} //load libraries FIPHelperDLL := LoadLibrary ('iphlpapi.dll'); if FIPHelperDLL <> 0 then begin FGetNetworkParams := getProcAddress (FIPHelperDLL, 'GetNetworkParams'); FGetAdaptersInfo := getProcAddress (FIPHelperDLL, 'GetAdaptersInfo'); //Now fill structures end; {$ENDIF} Refresh; end; procedure TvsIPHelper.SetString; begin FDummyString := Value; end; procedure TvsIPHelper.SetStrings; begin FDummyStrings := Value; end; procedure TvsIPHelper.SetInt; begin FDummyInt := Value; end; procedure TvsIPHelper.SetBool; begin FDummyBool := Value; end; procedure TvsIPHelper.Refresh; var Data:String; l:Integer; PInfo:PIPAdapterInfo; PIP : PIPAddrString; NWInfo:PFixedInfo; M:String; i:Integer; procedure AddrToStrings (P:PIPAddrString; IP:TStrings; Mask:TStrings); begin while P<>nil do begin if Assigned (IP) then IP.Add(P^.IPAddress); if Assigned (Mask) then Mask.Add(P^.IPMask); P := P^.next; end; end; begin DNSServerList.Clear; DNSServers.Clear; AdapterIPs.Clear; AdapterNames.Clear; AdapterDescriptions.Clear; AdapterMACs.Clear; DHCPServers.Clear; GateWays.Clear; CurrentIPs.Clear; CurrentMasks.Clear; // PrimaryIPs:=TStringList.Create; // PrimaryMasks:=TStringList.Create; //LeaseObtained:TList //LeaseExpired:TList //multiples filled per adapter AllIPS.Clear; AllMasks.Clear; {$IFDEF WIN32} if not Assigned (FGetNetworkParams) or not Assigned (FGetAdaptersInfo) then begin //no w2k/xp platform DNSServers.Add (GetDNS); //cross-platform function declared in synamisc exit; //we have no further info to add end; {$ELSE} exit; {$ENDIF} {$IFDEF WIN32} //Fill Strings with an array of adapters SetLength (Data, 8192); //arbritrary, increase if you expect loads of adapters. PInfo := @Data[1]; l:=length(Data); if 0 = FGetAdaptersInfo (PInfo, l) then //now PInfo contains list of adapters: while (PInfo<>nil) and (Integer(PInfo)<=Integer(@Data[Length(Data)])-SizeOf(TIPAdapterInfo)) do begin AdapterNames.Add (PInfo^.AdapterName); AdapterDescriptions.Add (PInfo^.Description); M:=''; for i:= 1 to PInfo^.AddressLength do M:=M+IntToHex (byte(PInfo^.Address[i]), 2); AdapterMacs.Add (M); if Assigned (PInfo^.CurrentIPAddress) then begin CurrentIPs.Add(String(PInfo^.CurrentIPAddress^.IPAddress)); CurrentMasks.Add(PInfo^.CurrentIPAddress^.IPMask); end; AddrToStrings (@PInfo^.GatewayList, GateWays, nil); AddrToStrings (@PInfo^.DHCPServer, DHCPServers, nil); AddrToStrings (@PInfo^.IPAddressList, AllIPs, AllMasks); PInfo := PInfo^.Next; end; //Now fill system-wide settigs: NWInfo := @Data[1]; if 0=FGetNetworkParams(NWInfo, l) then begin FHostname := NWInfo^.HostName; FDomainName := NWInfo^.DomainName; if Assigned (NWInfo^.CurrentDNSServer) then FCurrentDNSServer := NWInfo^.CurrentDNSServer^.IPAddress; AddrToStrings (@NWINfo^.DNSServerList, FDNSServers, nil); if (FCurrentDNSServer='') and (FDNSServers.Count>0) then FCurrentDNSServer := FDNSServers[0]; FEnableRouting := boolean (NWInfo^.EnableRouting); FEnableProxy := boolean (NWInfo^.EnableProxy); FEnableDNS := boolean(NWInfo^.EnableDNS); FScopeID := NWInfo^.ScopeId; FNodeType := NWInfo^.NodeType; end; {$ENDIF} end; destructor TvsIPHelper.Destroy; begin DNSServerList.Free; DNSServers.Free; AdapterIPs.Free; AdapterNames.Free; AdapterDescriptions.Free; AdapterMACs.Free; DHCPServers.Free; GateWays.Free; CurrentIPs.Free; CurrentMasks.Free; AllIPS.Free; AllMasks.Free; inherited; end; procedure TvsVisualUDP.SetDualThreaded(Value: Boolean); begin if Value <> FDualThreaded then begin if (csLoading in ComponentState) or (csDesigning in ComponentState) then begin FDualThreaded := Value; Exit; end; if Value then begin FSyncThread := TUDPSyncThread.Create (True); FSyncThread.Owner := Self; TUDPSyncThread(FSyncThread).CS := TCriticalSection.Create; TUDPSyncThread(FSyncThread).Queue := TList.Create; FSyncThread.Resume; end else begin FDualThreaded := False; //signal other threads in advance FSyncThread.Terminate; FSyncThread.WaitFor; TUDPSyncThread(FSyncThread).CS.Free; TUDPSyncThread(FSyncThread).Queue.Free; FSyncThread.Free; end; FDualThreaded := Value; end; end; end.